Skip to content

Compatibility

This section is a living compatibility reference for the Flow constructors. For each way of building a flow — from a vector field, a Hamiltonian vector field, a Hamiltonian, a SciML function/problem, or an optimal control problem — a dedicated page lists the supported state types, scalar types, and call styles in a table, with a minimal, executable example for every supported cell.

Two properties keep these pages trustworthy:

  • Probe-backed. Each page's table is generated from a capability probe under probe/ that runs every combination against the current source and records what works, what warns, and what fails. The CPU probe is probe/cpu/probe_cpu.jl.

  • Executed on build. Every ✓ / ⚠ example is a Documenter @example block re-run on each documentation build, so a page cannot silently drift from the code.

Scope: the 9 per-constructor pages below cover CPU, with every example executed on build. GPU is covered by a separate page, sourced from a dated probe run rather than build-executed — see that page's note on why.

This work tracks issue #343.

Pages

ConstructorBuildsPageStatus
(cross-cutting)Shape contract✅ live
Flow(::VectorField)StateFlowFlow(VectorField)✅ live
Flow(::HamiltonianVectorField)HamiltonianFlowFlow(HamiltonianVectorField)✅ live
Flow(::Hamiltonian)HamiltonianFlowFlow(Hamiltonian)✅ live
Flow(::ODEFunction) / Flow(::ODEProblem)StateFlow / SciMLProblemFlowFlow(SciML)✅ live
Flow(ocp) (control-free)OptimalControlFlowFlow(ocp)✅ live
Flow(ocp, law)OptimalControlFlow / ControlledFlowFlow(ocp, law)✅ live
Flow(h̃, law)HamiltonianFlowFlow(h̃, law)✅ live
Flow(h̃vf, law)HamiltonianFlowFlow(h̃vf, law)✅ live
Flow(fc, law)ControlledFlowFlow(fc, law)✅ live
(all of the above, on GPU)GPU✅ live (probe-sourced, not build-executed)

At a glance

  • Flow(VectorField) and Flow(HamiltonianVectorField) report the same result on CPU: every state/costate type tested works — scalar, Vector, Matrix (batched columns), MVector / SVector, MMatrix / SMatrix, with Real, Complex, or ForwardDiff.Dual elements — in both the point and trajectory call styles. The only caveat in both cases is that an in-place vector field with an immutable initial condition (SVector / SMatrix) works but emits a performance warning. Both are also "1-D = scalar" end to end (point and trajectory) — a scalar or length-1 Vector/SVector state always collapses to a scalar — since the fix for #357; see the Shape contract page.

  • Flow(Hamiltonian) (AD-backed, no in-place variant) supports every real container the same way, but its default backend (AutoForwardDiff) does not support Complex states. To differentiate the flow itself with respect to (x0, p0) (e.g. for shooting), wrap the call in an outer ForwardDiff.jacobian/gradient — never construct a Dual by hand and pass it as x0/p0. It is also 1-D = scalar end to end, via the same coercion machinery as Flow(HamiltonianVectorField) (see #357).

  • Flow(SciML) covers two constructors with very different mechanics. Flow(::ODEFunction) goes through the same CTFlows pipeline as Flow(VectorField) and reports the identical result, including the 1-D = scalar collapse above (every container works; in-place + immutable state warns and falls back). Flow(::ODEProblem) bypasses that pipeline entirely — SciMLBase.remake feeds the new state straight to the original function, with no CTFlows-level guard, and no shape coercion: a length-1 Vector state stays a length-1 Vector, by design (see the Shape contract page for why this constructor is deliberately left out of the fix). For a problem built from an out-of-place function, every state type remakes cleanly; for one built in-place, remaking with an immutable state (including a bare scalar) is a hard, native SciML error — not a warning with a fallback like everywhere else on this site. Build the ODEProblem from an out-of-place function if you need to call it with immutable states.

  • Flow(ocp) and Flow(ocp, law) — unlike every flow above, the state dimension is fixed at OCP construction (state!(pre, n)), so no single flow accepts every container size; two problems (n=1, n=2) stand in for that pattern on both pages. Until 2026-07-24, an OCP-derived fixed-size internal buffer broke batched Matrix states everywhere and SVector states specifically on the non-AD (state-only / OpenLoop/ClosedLoop) paths (#358) — fixed: the buffer is now sized from the runtime shape of the value passed in, so the full Matrix/MMatrix/ SMatrix/SVector/MVector family works on every path, matching every other constructor on this site. The remaining ✗ cells are the AD-backed paths (Flow(ocp), Flow(ocp, DynClosedLoop)) rejecting Complex and hand-built Dual, for the same reason as Flow(Hamiltonian). Two things are not broken, worth calling out explicitly: adding constraint=/multiplier= to a DynClosedLoop flow never changes which state types work (measured, not assumed), and :total/:partial agree exactly for a stationary feedback law. One asymmetry is new to these pages: Flow(ocp, OpenLoop/ClosedLoop) accepts a ForwardDiff.Dual state at a point call but not in a trajectory call (the objective computation is not Dual-transparent).

  • Flow(h̃, law) is the pseudo-Hamiltonian counterpart of Flow(ocp, DynClosedLoop), but without an OCP — only DynClosedLoop is accepted (OpenLoop/ClosedLoop are rejected with PreconditionError, since a pseudo-Hamiltonian needs the costate). PseudoHamiltonianSystem/Data.ComposedHamiltonian wrap the user's directly, with no OCP-derived buffer to size, so — like Flow(ocp, DynClosedLoop) since the #358 fix — the full Matrix/MMatrix/SMatrix family works, on both :total and :partial. Its compatibility profile otherwise matches Flow(Hamiltonian) exactly: every Real container works, Complex fails (AD-backed), and a hand-built Dual collides with the flow's own internal AD.

  • Flow(h̃vf, law) is the vector-field counterpart of Flow(h̃, law) — same DynClosedLoop-only restriction, but h̃vf supplies the derivatives directly instead of an AD-differentiated scalar , so there is no hamiltonian_type option. Measured: its profile matches Flow(HamiltonianVectorField) exactly, including Complex and hand-built Dual (both fail on Flow(h̃, law), both work here — no internal AD to collide with). The only caveat is the same in-place + immutable-u0 (SVector/SMatrix) performance warning as Flow(HamiltonianVectorField).

  • Flow(fc, law) is the controlled-vector-field counterpart of Flow(h̃, law) — only OpenLoop/ClosedLoop accepted (DynClosedLoop rejected, needs the costate). Data.ControlledVectorField has no in-place variant and, without an OCP, no fixed-size buffer either, so it builds the same plain out-of-place VectorFieldSystem as Flow(VectorField). Measured: fully green, no unsupported combination at all — the most permissive of the four no-OCP/OCP "control law" pages. Flow(ocp, law)'s OpenLoop/ClosedLoop path now matches it on every state-shape axis; the one remaining difference is the trajectory-Dual restriction, which comes from the OCP's own objective computation (not Dual-transparent) and has no counterpart here since Flow(fc, law) computes no objective at all.

  • GPU covers all constructors on NVIDIA/CUDA, sourced from a dated probe/gpu run rather than build-executed (no device in the Documenter build). Headline finding: AutoMooncake is the only AD backend that survives every measured device call shapeAutoZygote breaks once a call reduces over a device array or mutates one, AutoEnzyme breaks on GPU array reductions (sum/mapreduce) specifically, which real Hamiltonians always contain. AD-free flows (VectorField, HamiltonianVectorField, ODEProblem) need no keyword at all — just pass CuArrays.

This completes the compatibility table for every Flow constructor, on both CPU and GPU.

See also

  • Flows overview — the data → systems → flows → trajectories pipeline.

  • Building a flow — the shortcut constructor and explicit pipeline.

  • Integrating — call styles, variable parameters, and integrator options.