Flow(fc, law) compatibility
This page is a living compatibility reference for Flow(fc, law), built directly from a Data.ControlledVectorField
Scope: CPU, default AD backend. The table is generated from probe/cpu/probe_cpu.jl. GPU compatibility is a separate effort — see probe/gpu.
As documented in Control laws, only an OpenLoop or ClosedLoop law is accepted here — DynClosedLoop is rejected, since it needs the costate p, which a state flow does not have. The control is eliminated via Data.ComposedVectorField Data.ControlledVectorField has no in-place variant at all, so g is always out-of-place, and Systems.build_system(g) builds exactly the same kind of VectorFieldSystem as Flow(VectorField)'s out-of-place path.
The dynamics used on this page are the same as Control laws/Flow(ocp, law)'s example,
at
Last probed: 2026-07-23
This page's table shape — which containers and axes are tested — reflects probe/cpu/probe_cpu.jl as of this date. Every ✓/✗ cell below is still re-executed on every documentation build regardless (see Compatibility overview) — only the scope of what's tested can go stale, not the results shown.
fc = Data.ControlledVectorField((x, u) -> u .- x)
law_cl = Data.ClosedLoop(x -> -x)
law_ol = Data.OpenLoop(t -> 1.0)
flow_cl = Flows.Flow(fc, law_cl; reltol=1e-8)
flow_ol = Flows.Flow(fc, law_ol; reltol=1e-8)ControlledFlow
├─ system: VectorFieldSystem
│ ├─ time_dependence: NonAutonomous
│ ├─ variable_dependence: Fixed
│ └─ ComposedVectorField: non-autonomous, fixed (no variable), out-of-place
│ natural call: f(t, x)
│ uniform call: f(t, x, v)
└─ integrator: SciML{CPU} (instance, id=:sciml)
├─ internalnorm = real_norm [default]
├─ alg = Tsit5 [default]
├─ reltol = 1.0e-8 [user]
├─ save_everystep = auto [default]
├─ abstol = 1.0e-8 [default]
├─ save_start = auto [default]
└─ dense = auto [default]
Tip: use describe(SciML{CPU}) to see all available options.Compatibility table
| State type | point | traj |
|---|---|---|
Scalar Real | ✓ | ✓ |
Vector/MVector/SVector Real | ✓ | ✓ |
Matrix/MMatrix/SMatrix Real (batch) | ✓ | ✓ |
Scalar/Vector/MVector/SVector/Matrix/SMatrix Complex | ✓ | ✓ |
ForwardDiff.Dual scalar/Vector/MVector/SVector | ✓ | ✓ |
Fully green, measured on ClosedLoop — no unsupported combination, matching Flow(ocp, law)'s OpenLoop/ClosedLoop table on every state-shape axis since the Matrix/SVector fix there (#358). The one remaining difference is the trajectory-Dual case: that table's OCP objective computation is not Dual-transparent, whereas Flow(fc, law) computes no objective at all, so it has no counterpart restriction. OpenLoop shares the same mechanics as ClosedLoop here (only the control-law arity differs — u(t,v) vs u(t,x,v)) and is spot-checked below rather than re-measured cell by cell.
Real, Complex, and Dual states — ClosedLoop
julia> flow_cl(0.0, 1.0, 1.0) # scalar, ≈ exp(-2)
0.13533528340900094julia> flow_cl(0.0, SA[1.0, 2.0], 1.0)
2-element SVector{2, Float64} with indices SOneTo(2):
0.1353352833695603
0.2706705667391206
julia> sol = flow_cl((0.0, 1.0), [1.0, 2.0]);
julia> Trajectories.state(sol)(1.0)
2-element Vector{Float64}:
0.1353352833695603
0.2706705667391206julia> flow_cl(0.0, MMatrix{2,2}(1.0, 3.0, 2.0, 4.0), 1.0) # mutable
2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
0.135335 0.270671
0.406006 0.541341
julia> flow_cl(0.0, SMatrix{2,2}(1.0, 3.0, 2.0, 4.0), 1.0) # immutable
2×2 SMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
0.135335 0.270671
0.406006 0.541341julia> flow_cl(0.0, 1.0 + 2.0im, 1.0)
0.13533528334369263 + 0.27067056668738526imjulia> x0 = ForwardDiff.Dual(1.0, 1.0)
Dual{Nothing}(1.0,1.0)
julia> xf = flow_cl(0.0, x0, 1.0)
Dual{Nothing}(0.13533528340900094,0.13533528340900094)
julia> (ForwardDiff.value(xf), ForwardDiff.partials(xf, 1)) # ≈ (exp(-2), exp(-2)) — no AD collision
(0.13533528340900094, 0.13533528340900094)Unlike Flow(h̃, law) or Flow(ocp, law)'s DynClosedLoop path, there is no internal AD here, so a hand-built Dual never collides with anything — it just propagates a sensitivity through the integration, exactly like Flow(VectorField).
OpenLoop
julia> flow_ol(0.0, 1.0, 1.0) # ≈ 1 + (1.0 - 1) * exp(-1) = 1.0
1.0
julia> flow_ol(0.0, 0.0, 1.0) # ≈ 1 + (0.0 - 1) * exp(-1)
0.632120558757575julia> flow_ol(0.0, SA[1.0 + 2.0im, 0.0 + 0.0im], 1.0)
2-element SVector{2, ComplexF64} with indices SOneTo(2):
1.0 + 0.7357588824910012im
0.6321205587544994 + 0.0imDynClosedLoop — rejected
julia> try
Flows.Flow(fc, Data.DynClosedLoop((x, p) -> p); reltol=1e-8)
catch e
showerror(stdout, e)
end
PreconditionError → #_flow_from_controlled_vf#48, building.jl:518
│
│ Flow(fc, law) requires an OpenLoop or ClosedLoop control law
│
│ Reason a DynClosedLoop law u(t,x,p,v) needs the costate p, which a state flow of a vector field does not have
│
│ Context Flow(fc::ControlledVectorField, law::ControlLaw) — feedback dispatch
│ Hint use OpenLoop(u) or ClosedLoop(u); for a DynClosedLoop law use Flow(h̃, law) or Flow(ocp, law)
└─See also
Control laws — the guide page this reference expands on (
OpenLoop,ClosedLoop,Flow(fc, law)vsFlow(ocp, law)).Flow(VectorField)compatibility — the exhaustive state-container gallery this page's mechanics inherit (out-of-place, no AD).Flow(h̃, law)compatibility — the pseudo-Hamiltonian counterpart (DynClosedLooponly, AD-backed).Flow(ocp, law)compatibility — the OCP-based counterpart, whose fixed-size-bufferMatrix/SVectorrestrictions (#358) never applied here and are now fixed there too.Compatibility overview — the per-constructor feature matrix and the other flow types.
CTFlows.Flows.Flow,CTFlows.Flows.ControlledFlow— the flow types.CTBase.Data.ControlledVectorField,CTBase.Data.OpenLoop,CTBase.Data.ClosedLoop— the data wrappers.