Skip to content

Commit 3fd68f1

Browse files
authored
Merge pull request #740 from control-toolbox/documentation
Update documentation and test files for control-free example
2 parents 1f0d8f5 + 198eed4 commit 3fd68f1

File tree

7 files changed

+50
-32
lines changed

7 files changed

+50
-32
lines changed

docs/src/assets/Manifest.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,19 +232,19 @@ version = "1.0.6"
232232

233233
[[deps.CTFlows]]
234234
deps = ["CTBase", "CTModels", "DocStringExtensions", "ForwardDiff", "LinearAlgebra", "MLStyle", "MacroTools"]
235-
git-tree-sha1 = "d89a9defa5901dd1d745042c8e1ea5af87977104"
235+
git-tree-sha1 = "536c34374ab27f3d4d6a51d9e92ed89f146ca989"
236236
uuid = "1c39547c-7794-42f7-af83-d98194f657c2"
237-
version = "0.8.19-beta"
237+
version = "0.8.20"
238238
weakdeps = ["OrdinaryDiffEq"]
239239

240240
[deps.CTFlows.extensions]
241241
CTFlowsODE = "OrdinaryDiffEq"
242242

243243
[[deps.CTModels]]
244244
deps = ["CTBase", "DocStringExtensions", "LinearAlgebra", "MLStyle", "MacroTools", "OrderedCollections", "Parameters", "RecipesBase"]
245-
git-tree-sha1 = "c878ed38da4040f618a459c30cec8e53286ddc26"
245+
git-tree-sha1 = "3e9df6b6cb96ccb05051ded9a5d4f76f649f6d0c"
246246
uuid = "34c4fa32-2049-4079-8329-de33c2a22e2d"
247-
version = "0.9.11"
247+
version = "0.9.12-beta"
248248
weakdeps = ["JLD2", "JSON3", "Plots"]
249249

250250
[deps.CTModels.extensions]
@@ -254,9 +254,9 @@ weakdeps = ["JLD2", "JSON3", "Plots"]
254254

255255
[[deps.CTParser]]
256256
deps = ["CTBase", "DocStringExtensions", "MLStyle", "OrderedCollections", "Parameters", "Unicode"]
257-
git-tree-sha1 = "311bad1284ba1ccb23a6efe1d0cf531bdb7c46b9"
257+
git-tree-sha1 = "1a7445af1c937c291371e0f7c0a51a80f5b5d0eb"
258258
uuid = "32681960-a1b1-40db-9bff-a1ca817385d1"
259-
version = "0.8.10-beta"
259+
version = "0.8.12-beta"
260260

261261
[[deps.CTSolvers]]
262262
deps = ["ADNLPModels", "CTBase", "CTModels", "CommonSolve", "DocStringExtensions", "ExaModels", "KernelAbstractions", "NLPModels", "SolverCore"]

docs/src/example-control-free.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ Control-free problems are optimal control problems without a control variable. T
77

88
This page demonstrates two simple examples with known analytical solutions.
99

10-
!!! compat "Upcoming feature"
11-
12-
The syntax shown here uses a workaround with a dummy control variable (`u ∈ R, control`) and a small penalty term (`1e-5*u(t)^2`) in the cost to force `u ≈ 0`, because the parser does not yet support omitting the control declaration. These workaround lines are marked with `# TO REMOVE WHEN POSSIBLE` and will be removed once native control-free syntax is implemented.
13-
1410
First, we import the necessary packages:
1511

1612
```@example main
@@ -46,13 +42,11 @@ ocp1 = @def begin
4642
p ∈ R, variable # growth rate to estimate
4743
t ∈ [0, 10], time
4844
x ∈ R, state
49-
u ∈ R, control # TO REMOVE WHEN POSSIBLE
5045
5146
x(0) == 2.0
52-
5347
ẋ(t) == p * x(t)
5448
55-
∫((x(t) - data(t))^2 + 1e-5*u(t)^2) → min # fit to observed data # TO REMOVE u(t) when possible
49+
∫((x(t) - data(t))^2) → min # fit to observed data
5650
end
5751
nothing # hide
5852
```
@@ -103,15 +97,14 @@ ocp2 = @def begin
10397
ω ∈ R, variable # pulsation to optimize
10498
t ∈ [0, 1], time
10599
x = (q, v) ∈ R², state
106-
u ∈ R, control # TO REMOVE WHEN POSSIBLE
107100
108101
q(0) == 1.0
109102
v(0) == 0.0
110103
q(1) == 0.0 # final condition
111104
112105
ẋ(t) == [v(t), -ω^2 * q(t)]
113106
114-
ω^2 + 1e-5*∫(u(t)^2) → min # minimize pulsation # TO REMOVE u(t) when possible
107+
ω^2 → min # minimize pulsation
115108
end
116109
nothing # hide
117110
```

docs/src/example-double-integrator-energy.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,10 @@ We are now ready to solve the shooting equations.
135135
# auxiliary in-place NLE function
136136
nle!(s, p0, _) = s[:] = S(p0)
137137
138-
# initial guess for the Newton solver
139-
p0_guess = [1, 1]
138+
# initial guess for the Newton solver from the direct solution
139+
t = time_grid(direct_sol) # the time grid as a vector
140+
p = costate(direct_sol) # the costate as a function of time
141+
p0_guess = p(t0) # initial costate
140142
141143
# NLE problem with initial guess
142144
prob = NonlinearProblem(nle!, p0_guess)

docs/src/example-double-integrator-time.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,14 @@ nothing # hide
9090
Let us solve it with a direct method (we set the number of time steps to 200):
9191

9292
```@example main
93-
sol = solve(ocp; grid_size=200)
93+
direct_sol = solve(ocp; grid_size=200)
9494
nothing # hide
9595
```
9696

9797
and plot the solution:
9898

9999
```@example main
100-
plt = plot(sol; label="Direct", size=(800, 600))
100+
plt = plot(direct_sol; label="Direct", size=(800, 600))
101101
```
102102

103103
!!! note "Nota bene"
@@ -161,15 +161,24 @@ We are now ready to solve the shooting equations:
161161
# in-place shooting function
162162
nle!(s, ξ, λ) = shoot!(s, ξ[1:2], ξ[3], ξ[4])
163163
164-
# initial guess: costate and final time
165-
ξ_guess = [0.1, 0.1, 0.5, 1]
164+
# initial guess for the Newton solver from direct method
165+
t = time_grid(direct_sol) # the time grid as a vector
166+
p = costate(direct_sol) # the costate as a function of time
167+
p0_guess = p(t0) # initial costate
168+
tf_guess = variable(direct_sol) # final time
169+
170+
# find switching time t1 where p2(t) changes sign
171+
p2_values = [p(ti)[2] for ti in t]
172+
t1_guess = t[findfirst(i -> i > 1 && p2_values[i] * p2_values[i-1] < 0, 2:length(p2_values))]
173+
174+
ξ_guess = [p0_guess[1], p0_guess[2], t1_guess, tf_guess]
166175
167176
# NLE problem
168177
prob = NonlinearProblem(nle!, ξ_guess)
169178
170179
# resolution of the shooting equations
171-
sol = solve(prob; show_trace=Val(true))
172-
p0, t1, tf = sol.u[1:2], sol.u[3], sol.u[4]
180+
shoot_sol = solve(prob; show_trace=Val(true))
181+
p0, t1, tf = shoot_sol.u[1:2], shoot_sol.u[3], shoot_sol.u[4]
173182
174183
# print the solution
175184
println("\np0 = ", p0, "\nt1 = ", t1, "\ntf = ", tf)
@@ -182,10 +191,10 @@ Finally, we reconstruct and plot the solution obtained by the indirect method:
182191
φ = f_max * (t1, f_min)
183192
184193
# compute the solution: state, costate, control...
185-
flow_sol = φ((t0, tf), x0, p0; saveat=range(t0, tf, 200))
194+
indirect_sol = φ((t0, tf), x0, p0; saveat=range(t0, tf, 200))
186195
187196
# plot the solution on the previous plot
188-
plot!(plt, flow_sol; label="Indirect", color=2, linestyle=:dash)
197+
plot!(plt, indirect_sol; label="Indirect", color=2, linestyle=:dash)
189198
```
190199

191200
!!! note

docs/src/example-singular-control.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Let's plot the solution:
7777

7878
```@example main
7979
opt = (state_bounds_style=:none, control_bounds_style=:none)
80-
plt = plot(direct_sol; label="direct", size=(800, 800), opt...)
80+
plt = plot(direct_sol; label="Direct", size=(800, 800), opt...)
8181
```
8282

8383
## Singular control by hand
@@ -190,7 +190,7 @@ u_s(\theta) = \sin^2\theta.
190190
Let's overlay this on the numerical solution:
191191

192192
```@example main
193-
T = time_grid(direct_sol, :control)
193+
T = time_grid(direct_sol)
194194
θ(t) = state(direct_sol)(t)[3]
195195
us(t) = sin(θ(t))^2
196196
plot!(plt, T, us; subplot=7, line=:dash, lw=2, label="us (hand)")
@@ -340,7 +340,7 @@ nothing # hide
340340
Plot the indirect solution alongside the direct solution:
341341

342342
```@example main
343-
plot!(plt, indirect_sol; label="indirect", color=2, linestyle=:dash, opt...)
343+
plot!(plt, indirect_sol; label="Indirect", color=2, linestyle=:dash, opt...)
344344
```
345345

346346
The indirect and direct solutions match very well, confirming that our singular control computation is correct.

docs/src/manual-solve.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ Each strategy declares its available options. You can inspect them using `descri
230230

231231
### Discretizer options
232232

233+
The collocation discretizer supports multiple integration schemes:
234+
235+
- `:trapeze` - Trapezoidal rule (second-order accurate)
236+
- `:midpoint` - Midpoint rule (second-order accurate)
237+
- `:euler` or `:euler_explicit` or `:euler_forward` - Explicit Euler method (first-order accurate)
238+
- `:euler_implicit` or `:euler_backward` - Implicit Euler method (first-order accurate, more stable for stiff problems)
239+
240+
!!! note "Additional schemes with ADNLP modeler"
241+
242+
When using the `:adnlp` modeler, two additional high-order collocation schemes are available:
243+
244+
- `:gauss_legendre_2` - 2-point Gauss-Legendre collocation (fourth-order accurate)
245+
- `:gauss_legendre_3` - 3-point Gauss-Legendre collocation (sixth-order accurate)
246+
247+
These schemes provide higher accuracy but require more computational effort.
248+
233249
```@example main
234250
describe(:collocation)
235251
```

test/problems/control_free.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ function ExponentialGrowth()
2929
p R, variable # growth rate to estimate
3030
t [0, 10], time
3131
x R, state
32-
u R, control # TO REMOVE WHEN POSSIBLE
3332

3433
x(0) == 2.0
3534

3635
(t) == p * x(t)
3736

38-
((x(t) - data(t))^2 + 1e-5*u(t)^2) min # fit to observed data # TO REMOVE u(t) when possible
37+
((x(t) - data(t))^2) min # fit to observed data
3938
end
4039

4140
return (
@@ -73,15 +72,14 @@ function HarmonicOscillator()
7372
ω R, variable # pulsation to optimize
7473
t [0, 1], time
7574
x = (q, v) R², state
76-
u R, control # TO REMOVE WHEN POSSIBLE
7775

7876
q(0) == 1.0
7977
v(0) == 0.0
8078
q(1) == 0.0 # final condition
8179

8280
(t) == [v(t), -ω^2 * q(t)]
8381

84-
ω^2 + 1e-5*(u(t)^2) min # minimize pulsation # TO REMOVE u(t) when possible
82+
ω^2 min # minimize pulsation
8583
end
8684

8785
return (

0 commit comments

Comments
 (0)