API reference¶
Procedural API¶
- zvode.solve_complex_ivp(fun, tspan, y0, *, rtol=0.001, atol=1e-06, jac=None, ctx=None, method='BDF', lband=None, uband=None, save_steps=True, refine=1, allow_overshoot=False, first_step=None, min_step=0.0, max_step=inf, max_num_steps=1000000, max_order=None, miter=None, save_jac=True)¶
Integrate a complex-valued ODE initial value problem.
Solves:
dy/dt = f(t, y), y(t0) = y0, y, f ∈ ℂⁿ
The solver automatically selects and adjusts its order and step size at each step to meet the requested tolerances. Use
method='BDF'(default) for stiff systems andmethod='Adams'for smooth, non-stiff ones. Providing a Jacobian viajacimproves efficiency for BDF since it avoids finite-difference approximation of the derivative matrix; for large or banded systems supplying the sparsity structure throughlbandandubandreduces both memory and work per step.- Parameters:
- funcallable or ctypes._CFuncPtr
Right-hand side of the system.
Python callable:
fun(t, y) -> array_like, wheretis a scalar andyis an ndarray of shape(n,); must return an array of the same shape.Compiled callback (
ctypes.CFUNCTYPEinstance ornumba_cfunc.ctypes): called directly as a C function pointer, bypassing the Python interpreter on every RHS evaluation. The C-level signature is:void fun(int neq, double t, const double complex *y, double complex *dy, void *ctx);
Use
ZVODE_FUN_CTYPEfrom this module as theCFUNCTYPEdecorator. For numba, usezvode_fun_sigas the@cfuncsignature and passmy_rhs.ctypes.
- tspanarray_like
Integration times.
Two elements
[t0, tf]andsave_steps=True(default) → every accepted internal step is collected and returned.Two elements
[t0, tf]andsave_steps=False→ endpoint-only mode: returns a scalartand a 1-Dy.Three or more elements
[t0, t1, …, tf]→ output returned only at the requested knots (save_stepsis ignored). The solver uses its own internal steps to advance between knots and evaluates the solution at each requested time; the accuracy at those points equals the accuracy at internal steps. Providing many intermediate knots has little effect on computational efficiency.
- y0array_like, shape (n,)
Initial state; cast to
complex128. Pass a complex array even when the initial value is purely real to suppress the implicit-castUserWarning.- rtol, atolfloat or array_like, optional
Relative and absolute local error tolerances. The solver keeps the local error roughly below
rtol * |y(i)| + atolfor each component.rtolcontrols relative accuracy (number of correct digits);atolcontrols absolute accuracy and guards against loss of significance when a component passes through zero. Scalar or per-component arrays are accepted. Defaults arertol=1e-3,atol=1e-6.- jaccallable, ctypes._CFuncPtr, or None, optional
Jacobian of
funw.r.t.y.Python callable, full (no lband/uband):
jac(t, y) -> (n, n)array withJ[i, j] = df(i)/dy(j).Python callable, banded (lband/uband set):
jac(t, y) -> (lband + uband + 1, n)array where elementJ[i - j + uband, j]holdsdf(i)/dy(j).Compiled callback: C-level signature:
void jac(int neq, double t, const double complex *y, int ml, int mu, double complex *pd, int nrowpd, void *ctx);
Mixed mode is supported:
funcan be a Python callable whilejacis a compiled callback, or vice versa.- lband, ubandint or None, optional
Lower and upper half-bandwidths of a banded Jacobian, i.e.,
jac[i, j]is assumed zero unlessi - lband <= j <= i + uband. Must be non-negative integers. When either is set, the banded Jacobian path is used and the other defaults to 0. The full band has widthlband + uband + 1. Can be used withjac=Noneto have the solver estimate the Jacobian by finite differences within the band only, reducing the number of function evaluations compared to a full finite-difference Jacobian.- method{‘BDF’, ‘Adams’}, optional
Linear multistep method.
'BDF'(default) for stiff problems (max order 5);'Adams'for non-stiff (max order 12).- save_stepsbool, optional
When tspan has exactly two elements, controls whether every accepted internal step is stored.
True(default) collects all steps;Falsereturns only the endpoint. Note: ZVODE always uses adaptive time-stepping regardless of this flag — it only governs what output is captured.
- Returns:
- resultZVODEResult
Dict-like object with attribute access. Duck-type compatible with
scipy.integrate.OdeResult, soif not result.success: print(result.message)works verbatim. Always contains:- result.tfloat or ndarray, shape (m,)
Output time(s). A scalar float in endpoint-only mode (
len(tspan) == 2andsave_steps=False); a 1-D array otherwise.- result.yndarray, shape (n,) or (n, m), complex128
Solution state(s). A 1-D array in endpoint-only mode; a 2-D Fortran-order array with
result.y[:, k]the state atresult.t[k]otherwise.- result.successbool
Trueiffstatus >= 0. AlwaysTruefor a returned result (failure raisesZVODEErrorinstead).- result.statusint
Termination code with SciPy semantics:
0reached the end of the integration interval,-1the step failed.- result.messagestr
Human-readable termination reason. Discriminate on
status, never by parsingmessage.- result.nfevint
Number of right-hand side evaluations.
- result.njevint
Number of Jacobian evaluations.
- result.nluint
Number of LU decompositions.
Also carries the cumulative counters
nsteps,nni,ncfnandnetf(internal steps, nonlinear iterations, nonlinear convergence failures, and local error-test failures).
- Other Parameters:
- ctxctypes.c_void_p or None, optional
Optional shared user-data pointer passed as the last argument to both compiled callbacks on every invocation.
None(default) passes a NULL pointer. Ignored when all callbacks are plain Python callables. The caller is responsible for keeping the referent alive for the duration of the integration.- refineint, optional
Number of output points per accepted step when
save_steps=True.refine=1(default) records only the step endpoints.refine=NinsertsN - 1additional interpolated points inside each step for smoother plots; this does not improve the accuracy of the integration. Ignored whensave_steps=Falseorlen(tspan) > 2.- allow_overshootbool, optional
When
save_steps=True, allow the solver to step past the endpointtspan[1].False(default) ensures the last output point is exactlytspan[1].Truelets the solver choose its step size freely, which can occasionally be more efficient, but the last output point may lie slightly beyondtspan[1]. Ignored whensave_steps=Falseorlen(tspan) > 2.- first_stepfloat or None, optional
Initial step size. Chosen automatically if not given.
- min_stepfloat, optional
Minimum allowed step size. Default 0.
- max_stepfloat, optional
Maximum allowed step size. Default
np.inf, i.e., the step size is not bounded and determined solely by the solver.- max_num_stepsint, optional
Maximum number of internal steps between two consecutive output points. Default 1 000 000. Lower this to cap computational work when function evaluations are expensive; an error is raised if the budget is exhausted before the next output point.
- max_orderint or None, optional
Maximum integration order. Capped at 12 for Adams and 5 for BDF.
- miter{0, 1, 2, 3, 4, 5} or None, optional
Iteration method used by the corrector. Normally inferred from method, jac, and the band arguments. Without jac,
method='Adams'defaults to0(functional iteration) andmethod='BDF'defaults to2(internally generated Jacobian). Providing jac selects1(dense) or4(banded). Pass this argument only to override the automatic selection — for instance to force diagonal (3) or finite-difference Jacobian generation even when a jac callable is supplied. Use with care: an inconsistent combination (e.g.miter=4without band arguments) will raise aValueErroror cause a solver failure.- save_jacbool, optional
If
True(default), the solver retains a copy of the Jacobian to reuse when rebuilding the Newton iteration matrix, reducing Jacobian evaluations at the cost of extra memory. IfFalse, no copy is kept and the Jacobian is recomputed whenever the iteration matrix needs updating. Ignored for functional iteration (miter=0) or diagonal approximation (miter=3).
- Raises:
- ValueError
On invalid input.
- TypeError
If ctx is not a
ctypes.c_void_porNone.- ZVODEError
If the solver cannot advance to the next output point. Possible causes include exceeding max_num_steps internal steps, overly tight tolerances, repeated error-test or convergence failures (possibly indicating a bad Jacobian or wrong method), or an error weight becoming zero because a solution component vanished and
atol=0.ZVODEErrorsubclassesRuntimeError(soexcept RuntimeErrorkeeps working) and carries the partial result accumulated up to the failure point on itsresultattribute (success=False,status=-1).
- Warns:
- UserWarning
If y0 has a real dtype (it will be cast to
complex128).- UserWarning
If ctx is provided but all callbacks are plain Python callables (ctx is not passed to Python callables and will be ignored).
Notes
Stiffness and method selection — Use
method='BDF'(the default) for stiff problems andmethod='Adams'for smooth, non-stiff ones.Analyticity requirement for BDF — When solving a stiff system with the BDF method, the right-hand side fun must be analytic: each component f(i) must be an analytic function of each y(j), so that the partial derivative df(i)/dy(j) is a unique complex number. This property is critical to the way ZVODE solves the dense or banded linear systems that arise in the stiff case. For a complex stiff ODE system where fun is not analytic, ZVODE is likely to have convergence failures; instead use a real-valued solver on the equivalent real system of doubled dimension.
Error control — The solver controls the root-mean-square (rms) norm of the estimated local error vector e = (e(i)) such that:
rms-norm(e(i) / EWT(i)) <= 1,
where:
EWT(i) = rtol * abs(y(i)) + atol (scalar tolerances) EWT(i) = rtol * abs(y(i)) + atol(i) (array tolerances)
Use
rtol=0.0for pure absolute error control, andatol=0.0for pure relative error control. Actual (global) errors may exceed these local tolerances; choose them conservatively.Thread safety —
solve_complex_ivpholds a process-wide lock for the entire integration. Concurrent calls from multiple threads will queue rather than run in parallel. Use multiprocessing for parallel independent integrations.C-level callbacks — Compiled callbacks (
ctypes.CFUNCTYPEinstances ornumba_cfunc.ctypes) are called directly as C function pointers through thedrive_knots/drive_adaptiveintegration loops, bypassing the Python interpreter on every RHS or Jacobian evaluation.Lifetime of the callback
y— Theypassed to a Python fun or jac callback is a read-only view onto the solver’s internal workspace, valid only for the duration of that call; its contents are overwritten as the integration advances and the underlying buffer is released when the solver returns. This matchesscipy.integrate.ode. The normal usage of readingyand returning a freshly computed array is always safe; only retaining a reference toy(or a slice/view of it) past the call is not.References
[Brown1989]P. N. Brown, G. D. Byrne, and A. C. Hindmarsh, “VODE: A Variable-Coefficient ODE Solver,” SIAM J. Sci. Stat. Comput., 10(5), pp. 1038-1051, 1989. https://doi.org/10.1137/0910062
Examples
Trace the unit circle:
dy/dt = i*y,y(0) = 1, analytic solutiony(t) = exp(i*t). After one full revolution the state returns to 1:>>> import math >>> from zvode import solve_complex_ivp >>> sol = solve_complex_ivp(lambda t, y: 1j*y, tspan=[0, 2*math.pi], y0=[1+0j]) >>> bool(abs(sol.y[0, -1] - 1.0) < 1e-2) # back near start after one loop True
Two-equation system
dw/dt = -i*w**2*z,dz/dt = i*zwithw(0) = 1/2.1,z(0) = 1. Analytic solution:z(t) = exp(i*t),w(t) = 1/(z(t) + 1.1). After one full revolution both unknowns return to their initial values:>>> def fun(t, y): ... w, z = y ... return [-1j*w**2*z, 1j*z] ... >>> sol = solve_complex_ivp(fun, tspan=[0, 2*math.pi], y0=[1/2.1+0j, 1+0j]) >>> bool(abs(sol.y[0, -1] - 1/2.1) < 1e-2) # w returns to initial value True >>> bool(abs(sol.y[1, -1] - 1.0) < 1e-2) # z returns to 1 True
Result type¶
- class zvode.solve.ZVODEResult¶
Result of
solve_complex_ivp(); a dict with attribute access.All fields are accessible both as
result['key']andresult.key. A successful solve hassuccess is Trueandstatus == 0; a failed one is carried onZVODEErrorwithsuccess is Falseandstatus == -1. The field set is a strict superset of thescipy.integrate.OdeResultfields that exist without dense output and events, so duck-typed code that readst,y,success,status,message,nfev,njevandnluworks verbatim.Methods
clear()copy()fromkeys(iterable[, value])Create a new dictionary with keys from iterable and values set to value.
get(key[, default])Return the value for key if key is in the dictionary, else default.
items()keys()pop(key[, default])If the key is not found, return the default if given; otherwise, raise a KeyError.
popitem(/)Remove and return a (key, value) pair as a 2-tuple.
setdefault(key[, default])Insert key with a value of default if key is not in the dictionary.
update([E, ]**F)If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
values()
Compiled callback types¶
These ctypes descriptors define the C-level calling convention for compiled callbacks. Use them as decorators (ctypes) or for casting DLL function pointers. See Compiled callbacks for usage examples.
- zvode.ZVODE_FUN_CTYPE ctypes.CFUNCTYPE(None, c_int, c_double, c_void_p, c_void_p, c_void_p)¶
ctypes.CFUNCTYPEdescriptor for compiled RHS callbacks.Use as a decorator to expose a ctypes-based RHS with the expected signature, or pass to
ctypes.cast()to wrap a function pointer loaded from a shared library:@ZVODE_FUN_CTYPE def my_rhs(neq, t, y_ptr, dy_ptr, ctx): ... # or, for a DLL function: rhs = ctypes.cast(lib.my_rhs, ZVODE_FUN_CTYPE)
See Compiled callbacks for full examples.
- zvode.ZVODE_JAC_CTYPE ctypes.CFUNCTYPE(None, c_int, c_double, c_void_p, c_int, c_int, c_void_p, c_int, c_void_p)¶
ctypes.CFUNCTYPEdescriptor for compiled Jacobian callbacks.Use as a decorator to expose a ctypes-based Jacobian with the expected signature, or pass to
ctypes.cast()to wrap a function pointer loaded from a shared library:@ZVODE_JAC_CTYPE def my_jac(neq, t, y_ptr, ml, mu, pd_ptr, nrowpd, ctx): ... # or, for a DLL function: jac = ctypes.cast(lib.my_jac, ZVODE_JAC_CTYPE)
pdis column-major (Fortran order). For a banded Jacobian, elementdf[i]/dy[j]goes topd[mu + i - j + j*nrowpd]. See Compiled callbacks for full examples.
- zvode.zvode_fun_sig¶
Numba
@cfuncsignature for the RHS callback. Constructed on first access so that numba is never imported at package load time.Equivalent C signature:
void fun(int neq, double t, const double complex *y, double complex *dy, void *ctx);
- zvode.zvode_jac_sig¶
Numba
@cfuncsignature for the Jacobian callback. Constructed on first access so that numba is never imported at package load time.Equivalent C signature:
void jac(int neq, double t, const double complex *y, int ml, int mu, double complex *pd, int nrowpd, void *ctx);
OdeSolver API¶
- class zvode.ZVODE(fun, t0, y0, t_bound, *, lmm='BDF', rtol=0.001, atol=1e-06, first_step=None, min_step=0.0, max_step=inf, jac=None, lband=None, uband=None, max_order=None, miter=None, jsv=1, **extraneous)¶
Bases:
OdeSolverSolver for complex-valued ODEs using ZVODE (Variable-coefficient, fixed-leading-coefficient).
Implements the
scipy.integrate.OdeSolverinterface so that it can be passed as themethodargument toscipy.integrate.solve_ivp():sol = scipy.integrate.solve_ivp(fun, tspan, y0, method=ZVODE)
ZVODE solves the initial value problem for stiff or non-stiff systems of first-order complex ODEs:
dy/dt = f(t, y), y(t0) = y0
where y is a complex vector. It is based on the EPISODE/EPISODEB packages and implements Adams (non-stiff) and BDF (stiff) methods with orders up to 12 and 5 respectively.
Note
When using ZVODE for a stiff system, fun must be analytic (i.e., each component f(i) must be an analytic function of each y(j)). For a complex stiff system where fun is not analytic, use a real-valued solver on the equivalent real system of doubled dimension.
- Parameters:
- funcallable
Right-hand side of the system,
f(t, y). The output must be array-like with the same shape as y.- t0float
Initial value of the independent variable.
- y0array_like, shape (n,)
Initial state; will be cast to
complex128.- t_boundfloat
Boundary time. Integration will not proceed past this value; also determines the direction of integration.
- lmm{‘BDF’, ‘Adams’}, optional
Linear multistep method.
'BDF'(default) is recommended for stiff problems (max order 5);'Adams'is recommended for non-stiff problems (max order 12).- rtol, atolfloat or array_like, optional
Relative and absolute local error tolerances. The solver keeps the local error roughly below
rtol * |y(i)| + atolfor each component.rtolcontrols relative accuracy (number of correct digits);atolcontrols absolute accuracy and guards against loss of significance when a component passes through zero. Scalar or per-component arrays are accepted. Defaults arertol=1e-3,atol=1e-6.- first_stepfloat, optional
Initial step size. Chosen automatically if not given.
- min_stepfloat, optional
Minimum allowed step size. Default 0.
- max_stepfloat, optional
Maximum allowed step size. Default
np.inf, i.e., the step size is not bounded and determined solely by the solver.- jaccallable or None, optional
Jacobian matrix of f with respect to y,
jac(t, y). For a full Jacobian, return an(n, n)arrayJ[i, j] = df(i)/dy(j). For a banded Jacobian (when lband / uband are set), return an(lband + uband + 1, n)array whereJ[i-j+uband, j] = df(i)/dy(j). If not supplied, BDF approximates the Jacobian by finite differences (miter=2); Adams uses functional iteration and needs no Jacobian (miter=0).- lband, ubandint or None, optional
Lower and upper half-bandwidths of a banded Jacobian, i.e.,
jac[i, j]is assumed zero unlessi - lband <= j <= i + uband. Must be non-negative integers. When either is set, the banded Jacobian path is used and the other defaults to 0. The full band has widthlband + uband + 1. Can be used withjac=Noneto have the solver estimate the Jacobian by finite differences within the band only, reducing the number of function evaluations compared to a full finite-difference Jacobian.- max_orderint, optional
Maximum integration order. Capped at 12 for Adams and 5 for BDF.
- miter{0, 1, 2, 3, 4, 5}, optional
Iteration method override. Normally inferred from lmm, jac, and lband/uband: Adams without jac defaults to
0; BDF without jac defaults to2; providing jac selects1(dense) or4(banded).0 – functional iteration (no Jacobian; default for Adams)
1 – chord with user-supplied full Jacobian
2 – chord with internally generated full Jacobian (default for BDF)
3 – chord with diagonal Jacobian approximation
4 – chord with user-supplied banded Jacobian
5 – chord with internally generated banded Jacobian
- jsv{1, -1}, optional
Jacobian-saving flag.
1(default) retains a copy of the Jacobian to reuse when rebuilding the Newton iteration matrix.-1does not retain a copy; the Jacobian is recomputed whenever the iteration matrix needs updating. Ignored when no full Jacobian matrix is stored, i.e. for functional iteration (miter=0) and the diagonal approximation (miter=3).
- Attributes:
- nint
Number of equations.
- statusstr
Current solver status:
'running','finished', or'failed'.- tfloat
Current time.
- yndarray
Current state vector.
- t_boundfloat
Boundary time.
- nfevint
Number of right-hand side evaluations.
- njevint
Number of Jacobian evaluations.
- nluint
Number of LU decompositions.
Methods
dense_output()Compute a local interpolant over the last successful step.
step()Perform one integration step.
- Raises:
- ValueError
On invalid input.
See also
scipy.integrate.OdeSolverAbstract base class implemented by this solver.
scipy.integrate.solve_ivpDriver function that accepts
method=ZVODE(ormethod=ZVODE_BDF/method=ZVODE_Adams) to use this solver.
Notes
Thread safety:
ZVODEis not thread-safe. The underlying Fortran library stores solver state in process-global COMMON blocks, so stepping any two instances concurrently from different threads — even distinct objects — will corrupt that shared state. Protect all calls tostep()with a single process-widethreading.Lock. Running multiple independent integrations in separate processes (e.g. viamultiprocessing) is safe.Lifetime of the callback
y: theypassed to fun (and to jac) is a read-only view onto the solver’s internal workspace, valid only for the duration of that call; its contents are overwritten as the integration advances. Readingyand returning a freshly computed array is always safe; only retaining a reference toypast the call is not.References
[Brown1989ZVODE]P. N. Brown, G. D. Byrne, and A. C. Hindmarsh, “VODE: A Variable-Coefficient ODE Solver,” SIAM J. Sci. Stat. Comput., 10(5), pp. 1038-1051, 1989. https://doi.org/10.1137/0910062
Examples
Trace the unit circle:
dy/dt = i*y,y(0) = 1, analytic solutiony(t) = exp(i*t). PassZVODEas themethodargument toscipy.integrate.solve_ivp():>>> import math >>> from scipy.integrate import solve_ivp >>> from zvode import ZVODE >>> sol = solve_ivp(lambda t, y: 1j*y, [0, 2*math.pi], [1+0j], method=ZVODE) >>> bool(abs(sol.y[0, -1] - 1.0) < 1e-2) # back near start after one loop True
- class zvode.ZVODE_BDF(fun, t0, y0, t_bound, **kwargs)¶
Bases:
ZVODEZVODE with the BDF (stiff) linear multistep method.
Recommended for stiff problems, typically combined with Newton iteration using an internally generated Jacobian (
miter=2, the default when nojacis given).For all parameters and attributes see
ZVODE. Thelmmargument is fixed to'BDF'.- Attributes:
- step_size
Methods
dense_output()Compute a local interpolant over the last successful step.
step()Perform one integration step.
- class zvode.ZVODE_Adams(fun, t0, y0, t_bound, **kwargs)¶
Bases:
ZVODEZVODE with the Adams (non-stiff) linear multistep method.
Recommended for non-stiff problems, typically combined with fixed-point (functional) iteration (
miter=0, the default when nojacis given).For all parameters and attributes see
ZVODE. Thelmmargument is fixed to'Adams'.- Attributes:
- step_size
Methods
dense_output()Compute a local interpolant over the last successful step.
step()Perform one integration step.