Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions docs/src/flows/accessors.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ the map of what you can pull back out, and from which kind of flow.
using OptimalControl
using OrdinaryDiffEqTsit5
using NLPModelsIpopt
import CTFlows.Systems: # hide
hamiltonian, # hide
hamiltonian_vector_field, # hide
vector_field, # hide
get_hamiltonian_gradient, # hide
get_variable_gradient, # hide
get_pseudo_hamiltonian_gradient, # hide
get_pseudo_variable_gradient # hide
nothing # hide
```

Expand Down
33 changes: 23 additions & 10 deletions docs/src/flows/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Every flow needs an ODE integrator, and none is a hard dependency — load one,

```@example main
using OptimalControl
using OrdinaryDiffEqTsit5
using NLPModelsIpopt

t0 = 0
Expand All @@ -84,20 +85,32 @@ ocp = @def begin
0.5∫(u(t)^2) → min
end

try
Flow(ocp, (x, p) -> p[2])
catch e
println(e)
end
```

```@example main
using OrdinaryDiffEqTsit5
f = Flow(ocp, (x, p) -> p[2])
nothing # hide
```

Every page in this section opens with `using OrdinaryDiffEqTsit5` — no exceptions.
Every page in this section opens with `using OrdinaryDiffEqTsit5` — no exceptions. Forget it
and `Flow` says so at construction time, naming the `using` to add:

```julia
julia> f = Flow(ocp, (x, p) -> p[2])
ERROR: ExtensionError → top-level scope, REPL[6]:1
│ missing dependencies to access SciML options metadata
│ Missing OrdinaryDiffEqTsit5
│ Context Load OrdinaryDiffEqTsit5, OrdinaryDiffEq, or DifferentialEquations to activate the CTSolversSciMLIntegrator extension.
│ Hint Run: using OrdinaryDiffEqTsit5
└─
```

!!! note "Why that block is not executed"

The documentation build loads `OrdinaryDiffEq` once, for the whole site, and a Julia
extension stays armed for the rest of the session — so no page here can demonstrate this
failure live. The transcript above comes from a session loading `OptimalControl` and
`NLPModelsIpopt` and nothing else.

## Choosing an integrator and its options

Expand Down
2 changes: 1 addition & 1 deletion docs/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ never write a flow. See [Installation](@ref getting-started-installation) and
| `OptimalControl.VectorField`, `OptimalControl.Hamiltonian`, … | `VectorField`, `Hamiltonian`, … | exported at top level since v2.1.0-beta; the qualified form still works, it's just no longer necessary |
| `Lie(X, f)` | `ad(X, f)` | renamed; `Lie(...)` now throws |
| `X ⋅ f` | `ad(X, f)` | removed, no operator alias; `X ⋅ f` now throws |
| `HamiltonianLift` | `Lift(f)` to build; `CTLie.LiftedHamiltonianFunction` to name the type | renamed **and** re-parented — see [What changed meaning silently](@ref migration-silent) |
| `HamiltonianLift` | `Lift(f)` to build; `OptimalControl.LiftedHamiltonianFunction` to name the type | renamed **and** re-parented — see [What changed meaning silently](@ref migration-silent) |
| `autonomous=`, `variable=`, `inplace=` (constructor keywords) | `is_autonomous=`, `is_variable=`, `is_inplace=` | prefixed, on `VectorField`, `Hamiltonian`, `@Lie`, and friends |
| `Flow(f)` with `f::Function` | `Flow(VectorField(f))`, `Flow(Hamiltonian(f))`, `Flow(HamiltonianVectorField(f))` | the bare function no longer says what kind of flow to build |
| `Flow(ocp, u, g, μ)` (3 positional) | `Flow(ocp, u; constraint=g, multiplier=μ)` | keywords, and they come as a pair |
Expand Down
6 changes: 3 additions & 3 deletions docs/src/results/plot.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,9 @@ time_grid(sol_flow)
```

For a denser plot, pass `saveat` **when constructing the flow**, not on the call — the call
itself only accepts `variable`/`unsafe` (and `augment`, for costate augmentation). `dense=false`
is required alongside `saveat`, since dense output and `saveat` conflict at the integrator
level:
itself only accepts `variable`/`unsafe` (and `variable_costate`, for costate augmentation).
`dense=false` is required alongside `saveat`, since dense output and `saveat` conflict at the
integrator level:

```@example main
fine_grid = range(t0, tf, 100)
Expand Down
63 changes: 51 additions & 12 deletions docs/src/solve/choosing-a-method.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ There are 12 methods: every `{adnlp, exa} × {ipopt, madnlp, uno, madncl, knitro
This list is not fixed prose to memorize — it is exactly what `methods()` returns, so it's
printed live rather than quoted as a number anywhere on this page.

## A problem to try them on

Every `solve` call on this page uses the same problem — the double integrator, with its
dynamics written coordinatewise so that both the `:adnlp` and the `:exa` modeler accept it:

```@example main
ocp = @def begin
t ∈ [0, 1], time
x = (q, v) ∈ R², state
u ∈ R, control
x(0) == [-1, 0]
x(1) == [0, 0]
∂(q)(t) == v(t)
∂(v)(t) == u(t)
∫(0.5u(t)^2) → min
end
nothing # hide
```

## Partial descriptions

`solve(ocp, :madnlp)` doesn't need the other three tokens — they're completed for you.
Expand Down Expand Up @@ -112,30 +131,50 @@ describe(:cpu)

`:collocation` accepts a `scheme` option (alias `disc_method`):

| Value | Notes |
| --- | --- |
| `:trapeze` | second-order |
| `:midpoint` | second-order, **default** |
| `:euler`, `:euler_explicit`, `:euler_forward` | first-order, explicit |
| `:euler_implicit`, `:euler_backward` | first-order, implicit |
| `:gauss_legendre_2` | fourth-order, **`:adnlp` modeler only** |
| `:gauss_legendre_3` | sixth-order, **`:adnlp` modeler only** |
| `:variable` | variable-step ODE-based discretization |
| Value | Notes | `:adnlp` | `:exa` |
| --- | --- | :-: | :-: |
| `:trapeze` | second-order | ✅ | ✅ |
| `:midpoint` | second-order, **default** | ✅ | ✅ |
| `:euler` | first-order, explicit | ✅ | ✅ |
| `:euler_implicit` | first-order, implicit | ✅ | ✅ |
| `:euler_explicit`, `:euler_forward` | aliases of `:euler` | ✅ | ✗ |
| `:euler_backward` | alias of `:euler_implicit` | ✅ | ✗ |
| `:gauss_legendre_2` | fourth-order | ✅ | ✗ |
| `:gauss_legendre_3` | sixth-order | ✅ | ✗ |
| `:variable` | variable-step, ODE-based | ✗ | ✗ |

plus `grid_size` (default `250`) or an explicit, possibly non-uniform, `time_grid`.

!!! warning "Gauss-Legendre schemes are `:adnlp`-only"
Every cell above was checked by solving with that scheme. Two of the results need spelling out.

!!! warning "Under `:exa`, only the four canonical names work"

`:gauss_legendre_2`/`:gauss_legendre_3` are rejected under `:exa` — confirmed live:
`:exa` takes `:trapeze`, `:midpoint`, `:euler` and `:euler_implicit`, and nothing else.
Both Gauss-Legendre schemes are out — and so are the aliases, which is the surprising part:
`:euler_forward` is rejected where `:euler` is accepted, though under `:adnlp` the two name
the same scheme. Confirmed live:

```@example main
using Logging
# ExaModels warns about a deprecated `ExaCore()` call on every model it
# builds; silenced here so the scheme error is the only output.
try
solve(ocp, :exa; scheme=:gauss_legendre_2, display=false)
with_logger(NullLogger()) do
solve(ocp, :exa; scheme=:gauss_legendre_2, display=false)
end
catch e
println(e)
end
```

!!! warning "`:variable` is advertised but does not run"

The `describe(:collocation)` output above lists `:variable`, and the registry accepts it,
but no modeler can use it: the scheme dispatches to `CTDirect.VariableStepODE`, whose
implementation is not compiled into CTDirect, so a solve raises
`UndefVarError(:VariableStepODE, …, CTDirect)` rather than any typed error. It is kept in
the table only because the registry listing above advertises it.

## Advanced: the strategy registry

`strategy_ids`, `type_from_id`, and `available_parameters` (and the [`create_registry`](@ref)
Expand Down
Loading