Skip to content

Commit 0e9898b

Browse files
committed
docs: improve bypass documentation and fix GPU examples
- Reorganize explicit mode docs to emphasize bypass/force as primary method - Relegate permissive mode to brief info note (rarely used) - Fix GPU documentation examples to use OptimalControl prefixes - Remove unnecessary Draft meta blocks - Update introspection examples to use correct option names
1 parent 63c69f6 commit 0e9898b

2 files changed

Lines changed: 68 additions & 144 deletions

File tree

docs/src/manual-solve-explicit.md

Lines changed: 42 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
# [Solve: explicit mode](@id manual-solve-explicit)
22

3-
```@meta
4-
Draft = false
5-
```
6-
73
This manual explains the **explicit mode** of the [`solve`](@ref) function, where you pass typed strategy instances directly instead of symbolic tokens. This gives you full control over component configuration and validation.
84

95
For basic usage with symbolic tokens, see [Solve a problem](@ref manual-solve).
@@ -15,7 +11,6 @@ In explicit mode, you create strategy instances with their options, then pass th
1511
```@example explicit
1612
using OptimalControl
1713
using NLPModelsIpopt
18-
using CTDirect, CTSolvers
1914
2015
t0 = 0
2116
tf = 1
@@ -32,9 +27,9 @@ ocp = @def begin
3227
end
3328
3429
# Create strategy instances
35-
disc = Collocation(grid_size=100, scheme=:trapeze)
36-
mod = ADNLP(backend=:optimized)
37-
sol = Ipopt(max_iter=1000, print_level=0)
30+
disc = OptimalControl.Collocation(grid_size=100, scheme=:trapeze)
31+
mod = OptimalControl.ADNLP(backend=:optimized)
32+
sol = OptimalControl.Ipopt(max_iter=1000, print_level=0)
3833
3934
# Solve with explicit components
4035
result = solve(ocp; discretizer=disc, modeler=mod, solver=sol)
@@ -51,13 +46,13 @@ Each strategy is constructed with its options as keyword arguments:
5146

5247
```@example explicit
5348
# Discretizer with custom grid and scheme
54-
disc = Collocation(grid_size=50, scheme=:midpoint)
49+
disc = OptimalControl.Collocation(grid_size=50, scheme=:midpoint)
5550
5651
# Modeler with specific backend
57-
mod = ADNLP(backend=:optimized, show_time=false)
52+
mod = OptimalControl.ADNLP(backend=:optimized, show_time=false)
5853
5954
# Solver with iteration limit and tolerance
60-
sol = Ipopt(max_iter=500, tol=1e-6, print_level=0)
55+
sol = OptimalControl.Ipopt(max_iter=500, tol=1e-6, print_level=0)
6156
nothing # hide
6257
```
6358

@@ -82,7 +77,7 @@ You don't need to specify all three components. Missing ones are auto-completed
8277
```@example explicit
8378
# Only specify the solver
8479
result = solve(ocp;
85-
solver=Ipopt(max_iter=2000, print_level=0),
80+
solver=OptimalControl.Ipopt(max_iter=2000, print_level=0),
8681
display=false
8782
)
8883
nothing # hide
@@ -107,8 +102,8 @@ You can mix and match:
107102
```@example explicit
108103
# Custom discretizer and solver, default modeler
109104
result = solve(ocp;
110-
discretizer=Collocation(grid_size=200, scheme=:trapeze),
111-
solver=Ipopt(max_iter=100, print_level=0),
105+
discretizer=OptimalControl.Collocation(grid_size=200, scheme=:trapeze),
106+
solver=OptimalControl.Ipopt(max_iter=100, print_level=0),
112107
display=false
113108
)
114109
nothing # hide
@@ -122,19 +117,19 @@ All options are passed when creating the strategy instance:
122117

123118
```@example explicit
124119
# Configure Collocation
125-
disc = Collocation(
120+
disc = OptimalControl.Collocation(
126121
grid_size=150,
127122
scheme=:gauss_legendre_2
128123
)
129124
130125
# Configure ADNLP
131-
mod = ADNLP(
126+
mod = OptimalControl.ADNLP(
132127
backend=:optimized,
133128
show_time=true
134129
)
135130
136131
# Configure Ipopt
137-
sol = Ipopt(
132+
sol = OptimalControl.Ipopt(
138133
max_iter=1000,
139134
tol=1e-8,
140135
print_level=5,
@@ -143,30 +138,39 @@ sol = Ipopt(
143138
nothing # hide
144139
```
145140

146-
### Validation modes
141+
### Passing undeclared options
147142

148-
Strategies support two validation modes:
143+
By default, strategies use **strict validation**: any option not declared in the strategy metadata raises an error. This prevents typos and ensures you're using valid options.
149144

150-
- **`:strict`** (default): Rejects unknown options with a detailed error message
151-
- **`:permissive`**: Accepts unknown options with a warning, stores them with `:user` source
145+
However, NLP solvers have many options, and not all of them are declared in OptimalControl's strategy metadata. For example, Ipopt has an option `mumps_print_level` for controlling MUMPS debug output, which is not in the Ipopt strategy metadata.
152146

153-
```@example explicit
154-
# Strict mode (default) - will error on unknown options
155-
solver_strict = Ipopt(max_iter=500, print_level=0)
147+
To pass undeclared options, use `bypass()` (or its alias `force()`):
156148

157-
# Permissive mode - accepts unknown options
158-
solver_permissive = Ipopt(
149+
```@example explicit
150+
# Bypass validation for mumps_print_level
151+
solver = OptimalControl.Ipopt(
159152
max_iter=500,
160153
print_level=0,
161-
custom_option=42; # Unknown option
162-
mode=:permissive
154+
mumps_print_level=bypass(1) # Undeclared option
163155
)
164156
nothing # hide
165157
```
166158

167-
!!! warning "Permissive mode"
159+
!!! note "Alias: force = bypass"
160+
You can use `force` as an alias for `bypass`: `mumps_print_level=force(1)`
161+
162+
!!! warning "Use bypass sparingly"
168163

169-
Use `:permissive` mode only when you need to pass options that aren't declared in the strategy metadata (e.g., experimental solver options). The option will be passed to the underlying solver without validation.
164+
The `bypass` mechanism skips validation for the wrapped option. Use it only when:
165+
166+
- You need to pass an option to the underlying solver that isn't declared in the strategy metadata
167+
- You're certain the option name and value are correct
168+
169+
Bypassed options are passed directly to the solver without type checking or validation.
170+
171+
!!! info "Alternative: permissive mode"
172+
173+
If you have many undeclared options, you can use `mode=:permissive` to disable validation globally. However, this is not recommended as it will also ignore typos in valid option names.
170174

171175
### No routing needed
172176

@@ -178,85 +182,23 @@ In explicit mode, there's no option routing or ambiguity:
178182

179183
```@example explicit
180184
# Each component gets its own options directly
181-
disc = Collocation(grid_size=100)
182-
sol = Ipopt(max_iter=500, tol=1e-6, print_level=0)
185+
disc = OptimalControl.Collocation(grid_size=100)
186+
sol = OptimalControl.Ipopt(max_iter=500, tol=1e-6, print_level=0)
183187
184188
result = solve(ocp; discretizer=disc, solver=sol, display=false)
185189
nothing # hide
186190
```
187191

188-
## When to use explicit mode
189-
190-
Explicit mode is useful when you:
191-
192-
1. **Pre-configure and reuse components** across multiple solves:
193-
194-
```@example explicit
195-
# Configure once
196-
my_solver = Ipopt(max_iter=2000, tol=1e-8, print_level=0)
197-
198-
# Reuse for multiple problems
199-
sol1 = solve(ocp; solver=my_solver, display=false)
200-
201-
# Create a variant
202-
ocp2 = @def begin
203-
t ∈ [ 0, 2 ], time
204-
x ∈ R, state
205-
u ∈ R, control
206-
x(0) == 1
207-
x(2) == 0
208-
ẋ(t) == u(t)
209-
∫( u(t)^2 ) → min
210-
end
211-
212-
sol2 = solve(ocp2; solver=my_solver, display=false)
213-
nothing # hide
214-
```
215-
216-
2. **Need permissive mode** for exotic/undeclared options:
217-
218-
```julia
219-
solver = Ipopt(
220-
max_iter=1000,
221-
experimental_option=123;
222-
mode=:permissive
223-
)
224-
solve(ocp; solver=solver)
225-
```
226-
227-
3. **Want full type safety** and explicit control:
228-
229-
```julia
230-
# Types are explicit, no symbol-to-type resolution
231-
disc :: Collocation = Collocation(grid_size=100)
232-
mod :: ADNLP = ADNLP()
233-
sol :: Ipopt = Ipopt(max_iter=500)
234-
235-
solve(ocp; discretizer=disc, modeler=mod, solver=sol)
236-
```
237-
238-
4. **Use GPU-parameterized types** (see [GPU manual](@ref manual-solve-gpu)):
239-
240-
```julia
241-
using CUDA, MadNLPGPU, ExaModels
242-
243-
disc = Collocation(grid_size=100)
244-
mod = Exa{GPU}()
245-
sol = MadNLP{GPU}()
246-
247-
solve(ocp; discretizer=disc, modeler=mod, solver=sol)
248-
```
249-
250192
## Mixing modes is forbidden
251193

252194
You **cannot** mix symbolic tokens and typed components in the same `solve` call:
253195

254196
```julia
255197
# ERROR: Cannot mix descriptive and explicit modes
256-
solve(ocp, :adnlp, :ipopt; discretizer=Collocation())
198+
solve(ocp, :adnlp, :ipopt; discretizer=OptimalControl.Collocation())
257199

258200
# ERROR: Cannot mix modes
259-
solve(ocp, :collocation; solver=Ipopt())
201+
solve(ocp, :collocation; solver=OptimalControl.Ipopt())
260202
```
261203

262204
Choose one mode:
@@ -270,7 +212,7 @@ Use the introspection tools to examine configured components:
270212

271213
```@example explicit
272214
# Create a configured solver
273-
solver = Ipopt(max_iter=1000, tol=1e-6, print_level=0)
215+
solver = OptimalControl.Ipopt(max_iter=1000, tol=1e-6, print_level=0)
274216
275217
# Get its options
276218
opts = options(solver)
@@ -280,24 +222,21 @@ is_user(opts, :max_iter) # true
280222
```
281223

282224
```@example explicit
283-
is_default(opts, :acceptable_tol) # true (not set by user)
225+
is_default(opts, :mu_strategy) # true (not set by user)
284226
```
285227

286228
```@example explicit
287229
# Get option values
288-
option_value(opts, :max_iter)
230+
opts[:max_iter]
289231
```
290232

291233
```@example explicit
292234
# See all option names
293-
option_names(opts)
235+
keys(opts)
294236
```
295237

296-
For more on introspection, see [Advanced options](@ref manual-solve-advanced).
297-
298238
## See also
299239

300240
- **[Basic solve (descriptive)](@ref manual-solve)**: symbolic token mode
301241
- **[Advanced options](@ref manual-solve-advanced)**: `route_to`, `bypass`, introspection
302242
- **[GPU solving](@ref manual-solve-gpu)**: `Exa{GPU}()` and `MadNLP{GPU}()` types
303-
- **[CTSolvers Strategies](https://control-toolbox.org/CTSolvers.jl/stable/guides/implementing_a_strategy.html)**: strategy implementation guide

0 commit comments

Comments
 (0)