scipy.differentiate — Numerical Differentiation¶
The scipy_differentiate module wraps scipy.differentiate as Clausal predicates. It provides high-accuracy numerical derivatives, Jacobians, and Hessians using Richardson extrapolation.
Import¶
Or via the canonical py.* path:
Tier¶
All predicates are Tier 2 — result record: they return a dict with named fields that can be accessed via ResultGet. The predicate always succeeds if SciPy can evaluate the function; a success field in the result indicates whether the requested accuracy was achieved.
Naming conventions¶
The Diff prefix from the spec is dropped since these predicates live in the scipy_differentiate module. No abbreviations are used.
| scipy function | Clausal predicate |
|---|---|
differentiate.derivative |
Derivative |
differentiate.jacobian |
Jacobian |
differentiate.hessian |
Hessian |
Predicate catalogue¶
Derivative(F, X, RESULT)¶
Derivative(F, X, ARGS, RESULT)¶
Compute the scalar derivative of F at point X using Richardson extrapolation.
F: a callablef(x)orf(x, *args)— must accept NumPy scalar inputs (usenumpy.sin, notmath.sin)X: scalar evaluation point (float or numpy scalar)ARGS: optional list of extra positional arguments to pass toFRESULT: result dict — see fields below
# skip
Derivative(++(numpy.sin), 0.0, R),
ResultGet(R, 'df', DF) % DF ≈ 1.0
Derivative(++(lambda x, a: a*x**2), 3.0, [5.0], R),
ResultGet(R, 'df', DF) % DF ≈ 30.0 (d/dx 5x² = 10x; at x=3 → 30)
Result fields:
| field | description |
|---|---|
'x' |
evaluation point (echo of X) |
'df' |
derivative value |
'error' |
estimated absolute error |
'success' |
True if requested tolerance was achieved |
'status' |
integer status code (0 = success) |
'nfev' |
number of function evaluations |
'nit' |
number of Richardson extrapolation iterations |
Jacobian(F, X, RESULT)¶
Compute the Jacobian matrix of a vector-valued function F at point X.
F: a callablef(x)returning a 1-D NumPy array — must accept NumPy array inputsX: 1-D NumPy array of shape(n,)RESULT: result dict — see fields below
# skip
Jacobian(++(lambda x: numpy.array([x[0]**2, x[1]**3])),
++(numpy.array([2.0, 3.0])), R),
ResultGet(R, 'df', J) % J ≈ [[4, 0], [0, 27]]
Result fields (note: 'x' and 'nit' are not present for Jacobian):
| field | description |
|---|---|
'df' |
Jacobian matrix (ndarray, shape (m, n)) |
'error' |
estimated error (ndarray, same shape as df) |
'success' |
bool array (same shape as df), True per element where tolerance met |
'status' |
integer status code array |
'nfev' |
number of function evaluations |
Hessian(F, X, RESULT)¶
Compute the Hessian matrix of a scalar-valued function F at point X.
F: a callablef(x)returning a scalar — must accept NumPy array inputsX: 1-D NumPy array of shape(n,)RESULT: result dict — see fields below
# skip
Hessian(++(lambda x: x[0]**2 + x[1]**2),
++(numpy.array([1.0, 2.0])), R),
ResultGet(R, 'ddf', H) % H ≈ [[2, 0], [0, 2]]
Result fields (note: 'x', 'nit', and 'nfev' are not present for Hessian):
| field | description |
|---|---|
'ddf' |
Hessian matrix (ndarray, shape (n, n)) |
'error' |
estimated error (ndarray, same shape as ddf) |
'success' |
bool array, True per element where tolerance met |
'status' |
integer status code array |
ResultGet(RESULT, FIELD, VALUE)¶
Extract a named field from a differentiation result dict.
RESULT: a result dict returned byDerivative,Jacobian, orHessianFIELD: string key — one of the field names listed aboveVALUE: unified withRESULT[FIELD]
Fails if FIELD is not present in RESULT.
# skip
Derivative(++(lambda x: x**3), 2.0, R),
ResultGet(R, 'df', DF), % DF ≈ 12.0
ResultGet(R, 'error', ERR) % ERR is the estimated error
Example¶
# skip
-import_from(scipy_differentiate, [Derivative, Jacobian, ResultGet])
-import_from(numpy, [Array])
% Numerical derivative of x³ at x = 2 (exact answer: 12)
CubeDerivative(DF) <-
Derivative(++(lambda x: x**3), 2.0, R),
ResultGet(R, 'df', DF).
% Jacobian of f(x) = [x₀², x₁³] at [1, 2]
QuadraticJacobian(J) <-
F is ++(lambda x: __import__('numpy').array([x[0]**2, x[1]**3])),
X is ++(__import__('numpy').array([1.0, 2.0])),
Jacobian(F, X, R),
ResultGet(R, 'df', J).
Notes¶
NumPy-compatible functions¶
scipy.differentiate evaluates functions with NumPy scalar or array inputs internally. Always use NumPy equivalents:
- Use numpy.sin, numpy.exp, numpy.log — not math.sin, math.exp, math.log
- Lambda functions like lambda x: x**2 work fine since ** is overloaded for NumPy scalars
Partial failures¶
For Jacobian and Hessian, success is a boolean array — some elements may be False if the function is poorly conditioned at that point. The predicate still succeeds; inspect the success field to determine which elements converged.
success=False policy¶
Following the cross-cutting convention in SCIPY_PORT.md: a result with success=False still allows the predicate to succeed. The caller checks the success field. This makes it possible to inspect partial results.
Dimensional analysis (Quantity support)¶
All differentiation predicates are quantity-aware: when X is a
Quantity(value, dims), units are propagated through the result
automatically.
How it works¶
-
Probe — the wrapper calls
f(x_quantity)once to discover whetherfreturns aQuantity(with output dims) or a plain number. Iffcannot accept aQuantity(e.g. it indexes into the array withx[0]), the probe fails gracefully andfis called with raw values instead. -
Strip and call —
x.valueis passed to scipy; iffis quantity-aware, the wrapper re-wraps each raw value in aQuantitybefore forwarding tofand strips the result. -
Wrap output — result dict fields are wrapped with the correct output dims:
| Predicate | df / ddf dims |
error dims |
|---|---|---|
Derivative |
f_dims - x_dims |
same as df |
Jacobian |
f_dims - x_dims |
same as df |
Hessian |
f_dims - 2 * x_dims |
same as ddf |
The 'x' field is wrapped with the input dims.
Fast path¶
When X is a plain number or array (not a Quantity), scipy is called
directly with zero additional overhead.
Example¶
-import_from(scipy_differentiate, [Derivative, ResultGet])
-import_from(py.units, [Metre, Newton, HasUnits])
% f: Metre -> Newton (linear), so df/dx has units Newton/Metre
Test("derivative units") <- (
K is ++(
__import__('clausal.terms', fromlist=['Quantity']).Quantity(
9.8,
__import__('clausal.modules.py.units', fromlist=['Newton','Metre']).Newton
/ __import__('clausal.modules.py.units', fromlist=['Newton','Metre']).Metre
)),
Derivative(++(lambda x, k=K: x * k), 1.0(Metre), R),
ResultGet(R, 'df', DF),
HasUnits(DF, Newton/Metre))
Limitations¶
Quantitydoes not support__getitem__(array indexing). Functions that usex[0],x[1], etc. cannot be probed for output units. The wrapper falls back to passing raw values;df/ddfwill be plain (unwrapped) but'x'in the result is still wrapped with input dims.- The probe consumes one extra function evaluation. For any non-trivial
fthis is negligible relative to scipy's own evaluation count.