Raising this as a question rather than a proposal — I hit it while writing flow-API tests for OptimalControl.jl and could not tell whether the current behaviour is intended.
What happens today
is_autonomous removes the time argument uniformly across the three control-law kinds. Measured on a Fixed, variable-free OCP (CTBase 0.28):
| kind |
is_autonomous=true |
is_autonomous=false |
OpenLoop |
u() |
u(t) |
ClosedLoop |
u(x) |
u(t, x) |
DynClosedLoop |
u(x, p) |
u(t, x, p) |
The rule is consistent. Its consequence for OpenLoop is that the autonomous form takes no arguments, since time is the only thing an open loop depends on:
Flow(ocp, OpenLoop(t -> 0.0)) # MethodError at integration time
Flow(ocp, OpenLoop(() -> 0.0)) # ok — a constant control
Flow(ocp, OpenLoop(t -> 0.0; is_autonomous=false)) # ok — u(t)
Why I am asking
OpenLoop(t -> 0.0) reads as obviously correct, and the failure is a bare MethodError raised when the flow is run, not when the law is constructed — so the mistake surfaces some distance from its cause:
MethodError: no method matching (::var"#6#7")()
Two readings seem possible, and I do not have a view on which is right:
- The trait is the invariant.
is_autonomous means "does not read t", uniformly, and OpenLoop is simply the degenerate case where that leaves nothing. An autonomous open loop is a constant, which is a coherent thing to be able to express.
- The kind is the invariant.
OpenLoop means u(t) by definition, so t would always be passed and is_autonomous would not apply to this kind (or would be rejected for it).
Whichever way it settles, a construction-time check or a MethodError that mentions the expected arity would make the current behaviour much easier to discover.
Context
Found while writing test/suite/flows/test_flow_api.jl in control-toolbox/OptimalControl.jl#826, which now pins the table above so the answer — whatever it is — stays pinned.
🤖 Generated with Claude Code
Raising this as a question rather than a proposal — I hit it while writing flow-API tests for OptimalControl.jl and could not tell whether the current behaviour is intended.
What happens today
is_autonomousremoves the time argument uniformly across the three control-law kinds. Measured on aFixed, variable-free OCP (CTBase 0.28):is_autonomous=trueis_autonomous=falseOpenLoopu()u(t)ClosedLoopu(x)u(t, x)DynClosedLoopu(x, p)u(t, x, p)The rule is consistent. Its consequence for
OpenLoopis that the autonomous form takes no arguments, since time is the only thing an open loop depends on:Why I am asking
OpenLoop(t -> 0.0)reads as obviously correct, and the failure is a bareMethodErrorraised when the flow is run, not when the law is constructed — so the mistake surfaces some distance from its cause:Two readings seem possible, and I do not have a view on which is right:
is_autonomousmeans "does not readt", uniformly, andOpenLoopis simply the degenerate case where that leaves nothing. An autonomous open loop is a constant, which is a coherent thing to be able to express.OpenLoopmeansu(t)by definition, sotwould always be passed andis_autonomouswould not apply to this kind (or would be rejected for it).Whichever way it settles, a construction-time check or a
MethodErrorthat mentions the expected arity would make the current behaviour much easier to discover.Context
Found while writing
test/suite/flows/test_flow_api.jlin control-toolbox/OptimalControl.jl#826, which now pins the table above so the answer — whatever it is — stays pinned.🤖 Generated with Claude Code