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 and method='Adams' for smooth, non-stiff ones. Providing a Jacobian via jac improves efficiency for BDF since it avoids finite-difference approximation of the derivative matrix; for large or banded systems supplying the sparsity structure through lband and uband reduces both memory and work per step.

Parameters:
funcallable or ctypes._CFuncPtr

Right-hand side of the system.

  • Python callable: fun(t, y) -> array_like, where t is a scalar and y is an ndarray of shape (n,); must return an array of the same shape.

  • Compiled callback (ctypes.CFUNCTYPE instance or numba_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_CTYPE from this module as the CFUNCTYPE decorator. For numba, use zvode_fun_sig as the @cfunc signature and pass my_rhs.ctypes.

tspanarray_like

Integration times.

  • Two elements [t0, tf] and save_steps=True (default) → every accepted internal step is collected and returned.

  • Two elements [t0, tf] and save_steps=False → endpoint-only mode: returns a scalar t and a 1-D y.

  • Three or more elements [t0, t1, …, tf] → output returned only at the requested knots (save_steps is 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-cast UserWarning.

rtol, atolfloat or array_like, optional

Relative and absolute local error tolerances. The solver keeps the local error roughly below rtol * |y(i)| + atol for each component. rtol controls relative accuracy (number of correct digits); atol controls absolute accuracy and guards against loss of significance when a component passes through zero. Scalar or per-component arrays are accepted. Defaults are rtol=1e-3, atol=1e-6.

jaccallable, ctypes._CFuncPtr, or None, optional

Jacobian of fun w.r.t. y.

  • Python callable, full (no lband/uband): jac(t, y) -> (n, n) array with J[i, j] = df(i)/dy(j).

  • Python callable, banded (lband/uband set): jac(t, y) -> (lband + uband + 1, n) array where element J[i - j + uband, j] holds df(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: fun can be a Python callable while jac is 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 unless i - 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 width lband + uband + 1. Can be used with jac=None to 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; False returns 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, so if 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) == 2 and save_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 at result.t[k] otherwise.

result.successbool

True iff status >= 0. Always True for a returned result (failure raises ZVODEError instead).

result.statusint

Termination code with SciPy semantics: 0 reached the end of the integration interval, -1 the step failed.

result.messagestr

Human-readable termination reason. Discriminate on status, never by parsing message.

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, ncfn and netf (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=N inserts N - 1 additional interpolated points inside each step for smoother plots; this does not improve the accuracy of the integration. Ignored when save_steps=False or len(tspan) > 2.

allow_overshootbool, optional

When save_steps=True, allow the solver to step past the endpoint tspan[1]. False (default) ensures the last output point is exactly tspan[1]. True lets the solver choose its step size freely, which can occasionally be more efficient, but the last output point may lie slightly beyond tspan[1]. Ignored when save_steps=False or len(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 to 0 (functional iteration) and method='BDF' defaults to 2 (internally generated Jacobian). Providing jac selects 1 (dense) or 4 (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=4 without band arguments) will raise a ValueError or 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. If False, 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_p or None.

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. ZVODEError subclasses RuntimeError (so except RuntimeError keeps working) and carries the partial result accumulated up to the failure point on its result attribute (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 and method='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.0 for pure absolute error control, and atol=0.0 for pure relative error control. Actual (global) errors may exceed these local tolerances; choose them conservatively.

Thread safetysolve_complex_ivp holds 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.CFUNCTYPE instances or numba_cfunc.ctypes) are called directly as C function pointers through the drive_knots / drive_adaptive integration loops, bypassing the Python interpreter on every RHS or Jacobian evaluation.

Lifetime of the callback y — The y passed 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 matches scipy.integrate.ode. The normal usage of reading y and returning a freshly computed array is always safe; only retaining a reference to y (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 solution y(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*z with w(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'] and result.key. A successful solve has success is True and status == 0; a failed one is carried on ZVODEError with success is False and status == -1. The field set is a strict superset of the scipy.integrate.OdeResult fields that exist without dense output and events, so duck-typed code that reads t, y, success, status, message, nfev, njev and nlu works 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.CFUNCTYPE descriptor 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.CFUNCTYPE descriptor 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)

pd is column-major (Fortran order). For a banded Jacobian, element df[i]/dy[j] goes to pd[mu + i - j + j*nrowpd]. See Compiled callbacks for full examples.

zvode.zvode_fun_sig

Numba @cfunc signature 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 @cfunc signature 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: OdeSolver

Solver for complex-valued ODEs using ZVODE (Variable-coefficient, fixed-leading-coefficient).

Implements the scipy.integrate.OdeSolver interface so that it can be passed as the method argument to scipy.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)| + atol for each component. rtol controls relative accuracy (number of correct digits); atol controls absolute accuracy and guards against loss of significance when a component passes through zero. Scalar or per-component arrays are accepted. Defaults are rtol=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) array J[i, j] = df(i)/dy(j). For a banded Jacobian (when lband / uband are set), return an (lband + uband + 1, n) array where J[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 unless i - 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 width lband + uband + 1. Can be used with jac=None to 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 to 2; providing jac selects 1 (dense) or 4 (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. -1 does 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.OdeSolver

Abstract base class implemented by this solver.

scipy.integrate.solve_ivp

Driver function that accepts method=ZVODE (or method=ZVODE_BDF / method=ZVODE_Adams) to use this solver.

Notes

Thread safety: ZVODE is 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 to step() with a single process-wide threading.Lock. Running multiple independent integrations in separate processes (e.g. via multiprocessing) is safe.

Lifetime of the callback y: the y passed 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. Reading y and returning a freshly computed array is always safe; only retaining a reference to y past 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 solution y(t) = exp(i*t). Pass ZVODE as the method argument to scipy.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: ZVODE

ZVODE 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 no jac is given).

For all parameters and attributes see ZVODE. The lmm argument 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: ZVODE

ZVODE 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 no jac is given).

For all parameters and attributes see ZVODE. The lmm argument is fixed to 'Adams'.

Attributes:
step_size

Methods

dense_output()

Compute a local interpolant over the last successful step.

step()

Perform one integration step.