scipy.integrate — Numerical Integration¶
The scipy_integrate module wraps scipy.integrate as Clausal predicates. It covers adaptive quadrature (scalar and multi-dimensional), ODE solvers, and sampled-data integration methods.
Import¶
Or via the canonical py.* path:
Tiers¶
Tier 1 — direct value¶
CumulativeTrapezoid, Trapezoid, and Simpson return a NumPy array or scalar directly in RESULT.
Tier 2 — result dict¶
Quadrature and ODE predicates (Quad, DoubleQuad, TripleQuad, NQuad, QuadVec, SolveInitialValueProblem, OdeIntegrate) return a Python dict in RESULT. Use ResultGet(RESULT, FIELD, VALUE) to extract individual fields.
# skip
Quad(++(lambda x: __import__('math').sin(x)), ++(0.0), ++(3.14159), RESULT),
ResultGet(RESULT, 'value', V),
ResultGet(RESULT, 'error', E).
Naming conventions¶
Predicate names use full English words; scipy abbreviations are expanded:
| scipy function | Clausal predicate |
|---|---|
quad |
Quad |
dblquad |
DoubleQuad |
tplquad |
TripleQuad |
nquad |
NQuad |
quad_vec |
QuadVec |
solve_ivp |
SolveInitialValueProblem |
odeint |
OdeIntegrate |
cumulative_trapezoid |
CumulativeTrapezoid |
trapezoid |
Trapezoid |
simpson |
Simpson |
Predicate catalogue¶
Scalar quadrature¶
# skip
Quad(FUNC, A, B, RESULT)
Adaptive integration of FUNC(x) from A to B (QUADPACK DQAGS/DQAGSE).
RESULT: dict {value, error}
Quad(FUNC, A, B, ARGS, RESULT)
ARGS: tuple of extra arguments passed to FUNC as FUNC(x, *ARGS).
Quad(FUNC, A, B, ARGS, LIMIT, EPSABS, EPSREL, RESULT)
LIMIT: max number of sub-intervals (default 50)
EPSABS: absolute error tolerance (default 1.49e-8)
EPSREL: relative error tolerance (default 1.49e-8)
Example:
-import_from(scipy_integrate, [Quad, ResultGet])
IntegrateSin(V) <- (
Quad(++(lambda x: __import__('math').sin(x)), ++(0.0), ++(3.14159265), RESULT),
ResultGet(RESULT, 'value', V)
)
Multi-dimensional quadrature¶
# skip
DoubleQuad(FUNC, A, B, GFUN, HFUN, RESULT)
Double integral of FUNC(y, x) over a<=x<=b, gfun(x)<=y<=hfun(x).
FUNC: callable FUNC(y, x) → scalar
GFUN, HFUN: lower/upper y-limit functions (callables or constants)
RESULT: dict {value, error}
DoubleQuad(FUNC, A, B, GFUN, HFUN, EPSABS, EPSREL, RESULT)
TripleQuad(FUNC, A, B, GFUN, HFUN, QFUN, RFUN, RESULT)
Triple integral of FUNC(z, y, x).
QFUN, RFUN: lower/upper z-limit functions
RESULT: dict {value, error}
NQuad(FUNC, RANGES, RESULT)
N-dimensional integration via iterated quad.
RANGES: list of [lower, upper] pairs, one per variable
RESULT: dict {value, error}
NQuad(FUNC, RANGES, ARGS, RESULT)
ARGS: extra arguments for FUNC.
QuadVec(FUNC, A, B, RESULT)
Adaptive integration of a vector-valued or scalar function.
RESULT: dict {y, err, status, success, message, neval}
ODE solvers¶
# skip
SolveInitialValueProblem(FUN, T_SPAN, Y0, RESULT)
Solve dy/dt = FUN(t, y), y(T_SPAN[0]) = Y0, integrate to T_SPAN[1].
FUN: callable FUN(t, y) → array of derivatives
T_SPAN: [t0, tf] — integration interval
Y0: initial state (list or array)
RESULT: dict {t, y, sol, t_events, y_events, nfev, njev, nlu,
status, message, success}
Note: result.y has shape (n_vars, n_timepoints); y[i, -1] is final
value of variable i.
SolveInitialValueProblem(FUN, T_SPAN, Y0, METHOD, RESULT)
METHOD: 'RK45' (default), 'RK23', 'DOP853', 'Radau', 'BDF', 'LSODA'
SolveInitialValueProblem(FUN, T_SPAN, Y0, METHOD, T_EVAL, RESULT)
T_EVAL: array of time points at which to evaluate the solution.
OdeIntegrate(FUNC, Y0, T, RESULT)
Integrate dy/dt = FUNC(y, t) using LSODA (legacy interface).
FUNC: callable FUNC(y, t) → array
Y0: initial state
T: array of time points (result includes all these points)
RESULT: dict {y} where y has shape (len(T), n_vars)
OdeIntegrate(FUNC, Y0, T, ARGS, RESULT)
ARGS: tuple of extra arguments for FUNC.
Example — exponential decay:
-import_from(scipy_integrate, [SolveInitialValueProblem, ResultGet])
ExponentialDecay(T_FINAL, Y_FINAL) <- (
SolveInitialValueProblem(
++(lambda t, y: [-y[0]]),
++([0.0, float(T_FINAL)]),
++([1.0]),
RESULT),
ResultGet(RESULT, 'success', True),
ResultGet(RESULT, 'y', Y),
Y_FINAL is ++(float(Y[0, -1]))
)
Sampled-data integration¶
These predicates operate on arrays of sample values.
# skip
CumulativeTrapezoid(Y, RESULT)
Cumulatively integrate Y using the composite trapezoidal rule.
Y: 1-D array of sample values (uniform spacing dx=1)
RESULT: array of length len(Y)-1
CumulativeTrapezoid(Y, X, RESULT)
X: sample positions (non-uniform spacing allowed)
Trapezoid(Y, RESULT)
Total trapezoidal integration of Y (uniform dx=1).
RESULT: scalar
Trapezoid(Y, X, RESULT)
X: sample positions.
Simpson(Y, RESULT)
Total integration using composite Simpson's rule (uniform dx=1).
Requires an odd number of sample points (even number of sub-intervals).
RESULT: scalar
Simpson(Y, X, RESULT)
X: sample positions.
Example:
# skip
-import_from(scipy_integrate, [Trapezoid, CumulativeTrapezoid])
TrapezoidIntegral(Y, RESULT) <- Trapezoid(Y, RESULT).
CumulativeIntegral(Y, CUMULATIVE) <- CumulativeTrapezoid(Y, CUMULATIVE).
ResultGet¶
# skip
ResultGet(RESULT, FIELD, VALUE)
Extract a named field from any Tier 2 result dict.
RESULT: dict returned by a Tier 2 predicate
FIELD: a ground string key
VALUE: unified with RESULT[FIELD]
Fails if RESULT is not subscriptable, FIELD is absent, or VALUE does not unify.
Common fields by predicate:
| Predicate | Useful fields |
|---|---|
Quad, DoubleQuad, TripleQuad, NQuad |
'value', 'error' |
QuadVec |
'y', 'err', 'success', 'neval', 'message' |
SolveInitialValueProblem |
't', 'y', 'success', 'message', 'nfev', 'status' |
OdeIntegrate |
'y' |
Complete examples¶
Quadrature: integrate sin over [0, π]¶
-import_from(scipy_integrate, [Quad, ResultGet])
SinIntegral(VALUE) <- (
Quad(++(lambda x: __import__('math').sin(x)),
++(0.0),
++(3.14159265358979),
RESULT),
ResultGet(RESULT, 'value', VALUE)
)
ODE: logistic growth¶
# skip
-import_from(scipy_integrate, [SolveInitialValueProblem, ResultGet])
% dy/dt = r*y*(1 - y/K), y(0) = y0
LogisticGrowth(R, K, Y0, T_FINAL, Y_FINAL) <- (
SolveInitialValueProblem(
++(lambda t, y: [R * y[0] * (1.0 - y[0] / K)]),
++([0.0, float(T_FINAL)]),
++([float(Y0)]),
'RK45',
RESULT),
ResultGet(RESULT, 'success', True),
ResultGet(RESULT, 'y', Y),
Y_FINAL is ++(float(Y[0, -1]))
)
Sampled integration¶
# skip
-import_from(scipy_integrate, [Trapezoid, Simpson, CumulativeTrapezoid])
% Integrate a sampled signal using multiple methods
CompareIntegrals(Y, TRAP, SIMP) <- (
Trapezoid(Y, TRAP),
Simpson(Y, SIMP)
)
Dimensional analysis (Quantity support)¶
All integration predicates (except ODE solvers) are quantity-aware: when
inputs are Quantity(value, dims) objects, units are propagated through the
result automatically.
Array-based quadrature¶
Trapezoid, Simpson, and CumulativeTrapezoid compute output dims as:
| Form | Output dims |
|---|---|
Trapezoid(Y, X, R) |
y_dims + x_dims |
Trapezoid(Y, R) |
y_dims (unit spacing is dimensionless) |
Simpson(Y, X, R) |
y_dims + x_dims |
CumulativeTrapezoid(Y, X, R) |
y_dims + x_dims |
Callable-based quadrature¶
Quad, QuadVec, and other callable quadrature predicates use the same
probe-strip-wrap pattern as scipy_differentiate:
- Probe — call
f(a)once to discover whetherfreturns aQuantity. - Strip and call — pass
a.value,b.valueto scipy; iffis quantity-aware, re-wrap raw values before forwarding tof. - Wrap output —
valueanderrorfields get dimsf_dims + x_dims.
Fast path¶
When no input is a Quantity, scipy is called directly with zero overhead.
ODE solvers (deferred)¶
SolveInitialValueProblem and OdeIntegrate do not yet propagate units.
Their multi-variable state vectors and callable signatures require additional
design work (see SCIPY_UNITS_PLAN.md Phase 4c).
Example¶
-import_from(scipy_integrate, [Trapezoid, Quad, ResultGet])
-import_from(py.units, [Metre, Second, Newton, HasUnits])
% Velocity (m/s) integrated over time (s) gives displacement (m)
Test("trapezoid velocity times time") <- (
Trapezoid(++(numpy.array([0.0(Metre/Second), 10.0(Metre/Second), 20.0(Metre/Second)])),
++(numpy.array([0.0(Second), 1.0(Second), 2.0(Second)])),
R),
HasUnits(R, Metre))
% Quad with quantity-aware function
Test("quad with units") <- (
Quad(++(lambda x: x * 1.0(Newton/Metre)),
0.0(Metre), 1.0(Metre), RESULT),
ResultGet(RESULT, 'value', V),
HasUnits(V, Newton))
Notes¶
- Callables: pass Python functions via
++()— e.g.++(lambda x: math.sin(x)). - Array inputs: Y0, T_SPAN, T_EVAL and sample arrays should be passed as Python lists or NumPy arrays via
++(). - solve_ivp result shape:
result['y']has shape(n_vars, n_timepoints)— rows are variables, columns are time points. Access the last value of variable 0 asy[0, -1]. - odeint result shape:
result['y']has shape(n_timepoints, n_vars)— rows are time points, columns are variables. Access the last value of variable 0 asy[-1, 0]. - Predicates fail (no solution) when scipy raises an exception, or when a bound
RESULTdoes not unify with the computed value.