Procedural API — solve_complex_ivp¶
solve_complex_ivp() is the main entry point to ZVODE. Pass the
right-hand side, a time span, and an initial condition; get back a
ZVODEResult object with the solution and solver statistics as
attributes (also accessible as dict keys).
from zvode import solve_complex_ivp
sol = solve_complex_ivp(fun, tspan, y0, **options)
# sol.t — 1-D float array of output times, shape (m,)
# sol.y — complex array, shape (n, m)
# sol.nfev — RHS evaluations (int)
# sol.njev — Jacobian evaluations (int)
# sol.nlu — LU decompositions (int)
The complete parameter reference is in the solve_complex_ivp()
docstring.
Note
ZVODE integrates complex-valued ODEs. For best results the right-hand side
f(t, y) should be holomorphic (complex-analytic):
the Cauchy–Riemann equations must hold
for each component. Functions that involve abs, conj, or
real/imaginary-part splitting are not holomorphic; for such systems,
reformulate as an equivalent real system of doubled dimension and use a
real-valued solver.
Output modes¶
Collect every accepted step (default) — pass a 2-element tspan:
sol = solve_complex_ivp(rhs, tspan=(0.0, 10.0), y0=[1.0 + 0j])
# sol.t[0] == 0.0, sol.t[-1] == 10.0; number of points depends on tolerances
Output at specific times — pass three or more values as tspan:
import numpy as np
t_out = np.linspace(0.0, 10.0, 101)
sol = solve_complex_ivp(rhs, tspan=t_out, y0=[1.0 + 0j])
# sol.t is t_out, sol.y.shape == (1, 101)
Endpoint only — save_steps=False returns a scalar sol.t and a
1-D sol.y:
sol = solve_complex_ivp(rhs, tspan=(0.0, 10.0), y0=[1.0 + 0j],
save_steps=False)
# float(sol.t), sol.y.shape == (1,)
Smoothing output with refine¶
The refine parameter inserts additional interpolated output points inside
each accepted step using ZVODE’s built-in dense output (Nordsieck
interpolation). refine=N produces N − 1 intermediate points per step,
yielding smoother plots without extra function evaluations:
sol = solve_complex_ivp(rhs, tspan=(0.0, 10.0), y0=[1.0 + 0j], refine=4)
refine is only effective when save_steps=True; it is ignored in knot
and endpoint-only modes.
When allow_overshoot=True, the solver may step slightly past the endpoint
tspan[-1] to avoid constraining its step size; the last output time may
then lie just beyond tspan[-1].
Choosing a method¶
method='BDF' (default) is for stiff problems; method='Adams' for
non-stiff.
Providing a Jacobian¶
Supplying the Jacobian avoids finite differences and reduces RHS evaluations for stiff problems.
Dense — return an (n, n) array from jac(t, y):
import numpy as np
def rhs(t, y):
return np.array([-100j * y[0] + y[1], -1j * y[1]])
def jac(t, y):
return np.array([[-100j, 1.0], [0.0, -1j]])
sol = solve_complex_ivp(rhs, tspan=(0.0, 5.0),
y0=[1.0 + 0j, 0.0 + 1j],
method='BDF', jac=jac)
Banded — pass lband and uband; return a
(lband + uband + 1, n) array where entry [uband + i − j, j] holds
df[i]/dy[j]. See Banded Jacobians for the compact-storage layout
and a worked example:
sol = solve_complex_ivp(rhs, tspan=(0.0, 5.0), y0=y0,
method='BDF', jac=jac_banded,
lband=1, uband=1)
Backward integration¶
Set tspan in decreasing order; for knot mode pass times strictly
decreasing:
sol = solve_complex_ivp(rhs, tspan=(10.0, 0.0), y0=y_at_t10)
Compiled callbacks¶
For performance-critical work, fun and jac can be compiled C function
pointers (ctypes or numba @cfunc), called directly without re-entering the
Python interpreter on each evaluation. Pass the optional ctx argument to
share a user-data pointer between both compiled callbacks. See
Compiled callbacks for setup and full examples.