Skip to content

Commit 583d295

Browse files
committed
rename dynamic optimization test file
1 parent 65975d4 commit 583d295

File tree

2 files changed

+353
-37
lines changed

2 files changed

+353
-37
lines changed

test/downstream/dynamic_opt_systems.jl

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
using ModelingToolkit
2+
import JuMP, InfiniteOpt
3+
using DiffEqDevTools, DiffEqBase
4+
using SimpleDiffEq
5+
using OrdinaryDiffEqSDIRK, OrdinaryDiffEqVerner, OrdinaryDiffEqTsit5, OrdinaryDiffEqFIRK
6+
using Ipopt
7+
using DataInterpolations
8+
using CasADi
9+
10+
const M = ModelingToolkit
11+
12+
probtypes = [JuMPDynamicOptProblem, InfiniteOptDynamicOptProblem, CasADiDynamicOptProblem]
13+
solvers = [Ipopt.Optimizer, Ipopt.Optimizer, "opti"]
14+
15+
function test_dynamic_opt_probs(tableaus, true_sol, args...; kwargs...)
16+
for (probtype, solver, tableau) in zip(probtypes, solvers tableaus)
17+
prob = probtype(args...; kwargs...)
18+
sol = solve(prob, solver, tableau, silent = true)
19+
end
20+
end
21+
22+
@testset "ODE Solution, no cost" begin
23+
# Test solving without anything attached.
24+
@parameters α=1.5 β=1.0 γ=3.0 δ=1.0
25+
@variables x(..) y(..)
26+
t = M.t_nounits
27+
D = M.D_nounits
28+
29+
eqs = [D(x(t)) ~ α * x(t) - β * x(t) * y(t),
30+
D(y(t)) ~ -γ * y(t) + δ * x(t) * y(t)]
31+
32+
@mtkbuild sys = ODESystem(eqs, t)
33+
tspan = (0.0, 1.0)
34+
u0map = [x(t) => 4.0, y(t) => 2.0]
35+
parammap ==> 1.5, β => 1.0, γ => 3.0, δ => 1.0]
36+
37+
# Test explicit method.
38+
jprob = JuMPDynamicOptProblem(sys, u0map, tspan, parammap, dt = 0.01)
39+
@test JuMP.num_constraints(jprob.model) == 2 # initials
40+
jsol = solve(jprob, Ipopt.Optimizer, constructRK4, silent = true)
41+
oprob = ODEProblem(sys, u0map, tspan, parammap)
42+
osol = solve(oprob, SimpleRK4(), dt = 0.01)
43+
cprob = CasADiDynamicOptProblem(sys, u0map, tspan, parammap, dt = 0.01)
44+
csol = solve(oprob, "ipopt", constructRK4)
45+
@test jsol.sol.u osol.u
46+
@test csol.sol.u osol.u
47+
48+
# Implicit method.
49+
jsol2 = solve(jprob, Ipopt.Optimizer, constructImplicitEuler, silent = true) # 63.031 ms, 26.49 MiB
50+
osol2 = solve(oprob, ImplicitEuler(), dt = 0.01, adaptive = false) # 129.375 μs, 61.91 KiB
51+
jsol2 = solve(jprob, Ipopt.Optimizer, constructImplicitEuler, silent = true) # 63.031 ms, 26.49 MiB
52+
@test (jsol2.sol.u, osol2.u, rtol = 0.001)
53+
iprob = InfiniteOptDynamicOptProblem(sys, u0map, tspan, parammap, dt = 0.01)
54+
isol = solve(iprob, Ipopt.Optimizer,
55+
derivative_method = InfiniteOpt.FiniteDifference(InfiniteOpt.Backward()),
56+
silent = true) # 11.540 ms, 4.00 MiB
57+
@test (isol2.sol.u, osol2.u, rtol = 0.001)
58+
csol2 = solve(cprob, "ipopt", constructImplicitEuler, silent = true)
59+
@test (csol2.sol.u, osol2.u, rtol = 0.001)
60+
61+
# With a constraint
62+
u0map = Pair[]
63+
guess = [x(t) => 4.0, y(t) => 2.0]
64+
constr = [x(0.6) ~ 3.5, x(0.3) ~ 7.0]
65+
@mtkbuild lksys = ODESystem(eqs, t; constraints = constr)
66+
67+
jprob = JuMPDynamicOptProblem(lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
68+
@test JuMP.num_constraints(jprob.model) == 2
69+
jsol = solve(jprob, Ipopt.Optimizer, constructTsitouras5, silent = true) # 12.190 s, 9.68 GiB
70+
@test jsol.sol(0.6)[1] 3.5
71+
@test jsol.sol(0.3)[1] 7.0
72+
73+
cprob = CasADiDynamicOptProblem(lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
74+
csol = solve(cprob, "ipopt", constructTsitouras5, silent = true)
75+
@test csol.sol(0.6)[1] 3.5
76+
@test csol.sol(0.3)[1] 7.0
77+
78+
iprob = InfiniteOptDynamicOptProblem(
79+
lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
80+
isol = solve(iprob, Ipopt.Optimizer,
81+
derivative_method = InfiniteOpt.OrthogonalCollocation(3), silent = true) # 48.564 ms, 9.58 MiB
82+
sol = isol.sol
83+
@test sol(0.6)[1] 3.5
84+
@test sol(0.3)[1] 7.0
85+
86+
# Test whole-interval constraints
87+
constr = [x(t) 1, y(t) 1]
88+
@mtkbuild lksys = ODESystem(eqs, t; constraints = constr)
89+
iprob = InfiniteOptDynamicOptProblem(
90+
lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
91+
isol = solve(iprob, Ipopt.Optimizer,
92+
derivative_method = InfiniteOpt.OrthogonalCollocation(3), silent = true)
93+
@test all(u -> u > [1, 1], isol.sol.u)
94+
95+
jprob = JuMPDynamicOptProblem(lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
96+
jsol = solve(jprob, Ipopt.Optimizer, constructRadauIA3, silent = true) # 12.190 s, 9.68 GiB
97+
@test all(u -> u > [1, 1], jsol.sol.u)
98+
99+
cprob = CasADiDynamicOptProblem(lksys, u0map, tspan, parammap; guesses = guess, dt = 0.01)
100+
csol = solve(cprob, "ipopt", constructRadauIA3, silent = true)
101+
@test all(u -> u > [1, 1], csol.sol.u)
102+
end
103+
104+
function is_bangbang(input_sol, lbounds, ubounds, rtol = 1e-4)
105+
for v in 1:(length(input_sol.u[1]) - 1)
106+
all(i -> (i[v], bounds[v]; rtol) || (i[v], bounds[u]; rtol), input_sol.u) ||
107+
return false
108+
end
109+
true
110+
end
111+
112+
function ctrl_to_spline(inputsol, splineType)
113+
us = reduce(vcat, inputsol.u)
114+
ts = reduce(vcat, inputsol.t)
115+
splineType(us, ts)
116+
end
117+
118+
@testset "Linear systems" begin
119+
# Double integrator
120+
t = M.t_nounits
121+
D = M.D_nounits
122+
@variables x(..) [bounds = (0.0, 0.25)] v(..)
123+
@variables u(..) [bounds = (-1.0, 1.0), input = true]
124+
constr = [v(1.0) ~ 0.0]
125+
cost = [-x(1.0)] # Maximize the final distance.
126+
@named block = ODESystem(
127+
[D(x(t)) ~ v(t), D(v(t)) ~ u(t)], t; costs = cost, constraints = constr)
128+
block, input_idxs = structural_simplify(block, ([u(t)], []))
129+
130+
u0map = [x(t) => 0.0, v(t) => 0.0]
131+
tspan = (0.0, 1.0)
132+
parammap = [u(t) => 0.0]
133+
jprob = JuMPDynamicOptProblem(block, u0map, tspan, parammap; dt = 0.01)
134+
jsol = solve(jprob, Ipopt.Optimizer, constructVerner8, silent = true)
135+
# Linear systems have bang-bang controls
136+
@test is_bangbang(jsol.input_sol, [-1.0], [1.0])
137+
# Test reached final position.
138+
@test (jsol.sol.u[end][2], 0.25, rtol = 1e-5)
139+
140+
cprob = CasADiDynamicOptProblem(block, u0map, tspan, parammap; dt = 0.01)
141+
csol = solve(cprob, "ipopt", constructVerner8, silent = true)
142+
# Linear systems have bang-bang controls
143+
@test is_bangbang(csol.input_sol, [-1.0], [1.0])
144+
# Test reached final position.
145+
@test (csol.sol.u[end][2], 0.25, rtol = 1e-5)
146+
147+
# Test dynamics
148+
@parameters (u_interp::ConstantInterpolation)(..)
149+
@mtkbuild block_ode = ODESystem([D(x(t)) ~ v(t), D(v(t)) ~ u_interp(t)], t)
150+
spline = ctrl_to_spline(jsol.input_sol, ConstantInterpolation)
151+
oprob = ODEProblem(block_ode, u0map, tspan, [u_interp => spline])
152+
osol = solve(oprob, Vern8(), dt = 0.01, adaptive = false)
153+
@test (jsol.sol.u, osol.u, rtol = 0.05)
154+
@test (csol.sol.u, osol.u, rtol = 0.05)
155+
156+
iprob = InfiniteOptDynamicOptProblem(block, u0map, tspan, parammap; dt = 0.01)
157+
isol = solve(iprob, Ipopt.Optimizer; silent = true)
158+
@test is_bangbang(isol.input_sol, [-1.0], [1.0])
159+
@test (isol.sol.u[end][2], 0.25, rtol = 1e-5)
160+
osol = solve(oprob, ImplicitEuler(); dt = 0.01, adaptive = false)
161+
@test (isol.sol.u, osol.u, rtol = 0.05)
162+
163+
###################
164+
### Bee example ###
165+
###################
166+
# From Lawrence Evans' notes
167+
@variables w(..) q(..) α(t) [input = true, bounds = (0, 1)]
168+
@parameters b c μ s ν
169+
170+
tspan = (0, 4)
171+
eqs = [D(w(t)) ~ -μ * w(t) + b * s * α * w(t),
172+
D(q(t)) ~ -ν * q(t) + c * (1 - α) * s * w(t)]
173+
costs = [-q(tspan[2])]
174+
175+
@named beesys = ODESystem(eqs, t; costs)
176+
beesys, input_idxs = structural_simplify(beesys, ([α], []))
177+
u0map = [w(t) => 40, q(t) => 2]
178+
pmap = [b => 1, c => 1, μ => 1, s => 1, ν => 1, α => 1]
179+
180+
jprob = JuMPDynamicOptProblem(beesys, u0map, tspan, pmap, dt = 0.01)
181+
jsol = solve(jprob, Ipopt.Optimizer, constructTsitouras5, silent = true)
182+
@test is_bangbang(jsol.input_sol, [0.0], [1.0])
183+
iprob = InfiniteOptDynamicOptProblem(beesys, u0map, tspan, pmap, dt = 0.01)
184+
isol = solve(iprob, Ipopt.Optimizer; silent = true)
185+
@test is_bangbang(isol.input_sol, [0.0], [1.0])
186+
cprob = CasADiDynamicOptProblem(beesys, u0map, tspan, pmap; dt = 0.01)
187+
csol = solve(cprob, "ipopt", constructTsitouras5, silent = true)
188+
@test is_bangbang(csol.input_sol, [0.0], [1.0])
189+
190+
@parameters (α_interp::LinearInterpolation)(..)
191+
eqs = [D(w(t)) ~ -μ * w(t) + b * s * α_interp(t) * w(t),
192+
D(q(t)) ~ -ν * q(t) + c * (1 - α_interp(t)) * s * w(t)]
193+
@mtkbuild beesys_ode = ODESystem(eqs, t)
194+
oprob = ODEProblem(beesys_ode,
195+
u0map,
196+
tspan,
197+
merge(Dict(pmap),
198+
Dict(α_interp => ctrl_to_spline(jsol.input_sol, LinearInterpolation))))
199+
osol = solve(oprob, Tsit5(); dt = 0.01, adaptive = false)
200+
@test (osol.u, jsol.sol.u, rtol = 0.01)
201+
@test (osol.u, csol.sol.u, rtol = 0.01)
202+
osol2 = solve(oprob, ImplicitEuler(); dt = 0.01, adaptive = false)
203+
@test (osol2.u, isol.sol.u, rtol = 0.01)
204+
end
205+
206+
@testset "Rocket launch" begin
207+
t = M.t_nounits
208+
D = M.D_nounits
209+
210+
@parameters h_c m₀ h₀ g₀ D_c c Tₘ m_c
211+
@variables h(..) v(..) m(..) [bounds = (m_c, 1)] T(..) [input = true, bounds = (0, Tₘ)]
212+
drag(h, v) = D_c * v^2 * exp(-h_c * (h - h₀) / h₀)
213+
gravity(h) = g₀ * (h₀ / h)
214+
215+
eqs = [D(h(t)) ~ v(t),
216+
D(v(t)) ~ (T(t) - drag(h(t), v(t))) / m(t) - gravity(h(t)),
217+
D(m(t)) ~ -T(t) / c]
218+
219+
(ts, te) = (0.0, 0.2)
220+
costs = [-h(te)]
221+
cons = [T(te) ~ 0, m(te) ~ m_c]
222+
@named rocket = ODESystem(eqs, t; costs, constraints = cons)
223+
rocket, input_idxs = structural_simplify(rocket, ([T(t)], []))
224+
225+
u0map = [h(t) => h₀, m(t) => m₀, v(t) => 0]
226+
pmap = [
227+
g₀ => 1, m₀ => 1.0, h_c => 500, c => 0.5 * (g₀ * h₀), D_c => 0.5 * 620 * m₀ / g₀,
228+
Tₘ => 3.5 * g₀ * m₀, T(t) => 0.0, h₀ => 1, m_c => 0.6]
229+
jprob = JuMPDynamicOptProblem(rocket, u0map, (ts, te), pmap; dt = 0.001, cse = false)
230+
jsol = solve(jprob, Ipopt.Optimizer, constructRadauIIA5, silent = true)
231+
@test jsol.sol.u[end][1] > 1.012
232+
233+
cprob = CasADiDynamicOptProblem(rocket, u0map, (ts, te), pmap; dt = 0.001, cse = false)
234+
csol = solve(cprob, "ipopt"; silent = true)
235+
@test csol.sol.u[end][1] > 1.012
236+
237+
iprob = InfiniteOptDynamicOptProblem(
238+
rocket, u0map, (ts, te), pmap; dt = 0.001)
239+
isol = solve(iprob, Ipopt.Optimizer, silent = true)
240+
@test isol.sol.u[end][1] > 1.012
241+
242+
# Test solution
243+
@parameters (T_interp::CubicSpline)(..)
244+
eqs = [D(h(t)) ~ v(t),
245+
D(v(t)) ~ (T_interp(t) - drag(h(t), v(t))) / m(t) - gravity(h(t)),
246+
D(m(t)) ~ -T_interp(t) / c]
247+
@mtkbuild rocket_ode = ODESystem(eqs, t)
248+
interpmap = Dict(T_interp => ctrl_to_spline(jsol.input_sol, CubicSpline))
249+
oprob = ODEProblem(rocket_ode, u0map, (ts, te), merge(Dict(pmap), interpmap))
250+
osol = solve(oprob, RadauIIA5(); adaptive = false, dt = 0.001)
251+
@test (jsol.sol.u, osol.u, rtol = 0.02)
252+
@test (csol.sol.u, osol.u, rtol = 0.02)
253+
254+
interpmap1 = Dict(T_interp => ctrl_to_spline(isol.input_sol, CubicSpline))
255+
oprob1 = ODEProblem(rocket_ode, u0map, (ts, te), merge(Dict(pmap), interpmap1))
256+
osol1 = solve(oprob1, ImplicitEuler(); adaptive = false, dt = 0.001)
257+
@test (isol.sol.u, osol1.u, rtol = 0.01)
258+
end
259+
260+
@testset "Free final time problems" begin
261+
t = M.t_nounits
262+
D = M.D_nounits
263+
264+
@variables x(..) u(..) [input = true, bounds = (0, 1)]
265+
@parameters tf
266+
eqs = [D(x(t)) ~ -2 + 0.5 * u(t)]
267+
# Integral cost function
268+
costs = [-Symbolics.Integral(t in (0, tf))(x(t) - u(t)), -x(tf)]
269+
consolidate(u) = u[1] + u[2]
270+
@named rocket = ODESystem(eqs, t; costs, consolidate)
271+
rocket, input_idxs = structural_simplify(rocket, ([u(t)], []))
272+
273+
u0map = [x(t) => 17.5]
274+
pmap = [u(t) => 0.0, tf => 8]
275+
jprob = JuMPDynamicOptProblem(rocket, u0map, (0, tf), pmap; steps = 201)
276+
jsol = solve(jprob, Ipopt.Optimizer, constructTsitouras5, silent = true)
277+
@test isapprox(jsol.sol.t[end], 10.0, rtol = 1e-3)
278+
279+
cprob = CasADiDynamicOptProblem(rocket, u0map, (0, tf), pmap; steps = 201)
280+
csol = solve(cprob, "ipopt", constructTsitouras5, silent = true)
281+
@test isapprox(csol.sol.t[end], 10.0, rtol = 1e-3)
282+
283+
iprob = InfiniteOptDynamicOptProblem(rocket, u0map, (0, tf), pmap; steps = 200)
284+
isol = solve(iprob, Ipopt.Optimizer, silent = true)
285+
@test isapprox(isol.sol.t[end], 10.0, rtol = 1e-3)
286+
287+
@variables x(..) v(..)
288+
@variables u(..) [bounds = (-1.0, 1.0), input = true]
289+
@parameters tf
290+
constr = [v(tf) ~ 0, x(tf) ~ 0]
291+
cost = [tf] # Minimize time
292+
293+
@named block = ODESystem(
294+
[D(x(t)) ~ v(t), D(v(t)) ~ u(t)], t; costs = cost, constraints = constr)
295+
block, input_idxs = structural_simplify(block, ([u(t)], []))
296+
297+
u0map = [x(t) => 1.0, v(t) => 0.0]
298+
tspan = (0.0, tf)
299+
parammap = [u(t) => 0.0, tf => 1.0]
300+
jprob = JuMPDynamicOptProblem(block, u0map, tspan, parammap; steps = 51)
301+
jsol = solve(jprob, Ipopt.Optimizer, constructVerner8, silent = true)
302+
@test isapprox(jsol.sol.t[end], 2.0, atol = 1e-5)
303+
304+
cprob = CasADiDynamicOptProblem(block, u0map, (0, tf), parammap; steps = 51)
305+
csol = solve(cprob, "ipopt", constructVerner8, silent = true)
306+
@test isapprox(csol.sol.t[end], 2.0, atol = 1e-5)
307+
308+
iprob = InfiniteOptDynamicOptProblem(block, u0map, tspan, parammap; steps = 51)
309+
isol = solve(iprob, Ipopt.Optimizer, silent = true)
310+
@test isapprox(isol.sol.t[end], 2.0, atol = 1e-5)
311+
end
312+
313+
@testset "Cart-pole problem" begin
314+
t = M.t_nounits
315+
D = M.D_nounits
316+
# gravity, length, moment of Inertia, drag coeff
317+
@parameters g l mₚ mₖ
318+
@variables x(..) θ(..) u(t) [input = true, bounds = (-10, 10)]
319+
320+
s = sin(θ(t))
321+
c = cos(θ(t))
322+
H = [mₖ+mₚ mₚ*l*c
323+
mₚ*l*c mₚ*l^2]
324+
C = [0 -mₚ*D(θ(t))*l*s
325+
0 0]
326+
qd = [D(x(t)), D(θ(t))]
327+
G = [0, mₚ * g * l * s]
328+
B = [1, 0]
329+
330+
tf = 5
331+
rhss = -H \ Vector(C * qd + G - B * u)
332+
eqs = [D(D(x(t))) ~ rhss[1], D(D(θ(t))) ~ rhss[2]]
333+
cons = [θ(tf) ~ π, x(tf) ~ 0, D(θ(tf)) ~ 0, D(x(tf)) ~ 0]
334+
costs = [Symbolics.Integral(t in (0, tf))(u^2)]
335+
tspan = (0, tf)
336+
337+
@named cartpole = ODESystem(eqs, t; costs, constraints = cons)
338+
cartpole, input_idxs = structural_simplify(cartpole, ([u], []))
339+
340+
u0map = [D(x(t)) => 0.0, D(θ(t)) => 0.0, θ(t) => 0.0, x(t) => 0.0]
341+
pmap = [mₖ => 1.0, mₚ => 0.2, l => 0.5, g => 9.81, u => 0]
342+
jprob = JuMPDynamicOptProblem(cartpole, u0map, tspan, pmap; dt = 0.04)
343+
jsol = solve(jprob, Ipopt.Optimizer, constructRK4, silent = true)
344+
@test jsol.sol.u[end] [π, 0, 0, 0]
345+
346+
cprob = CasADiDynamicOptProblem(cartpole, u0map, tspan, pmap; dt = 0.04)
347+
csol = solve(cprob, "ipopt", constructRK4, silent = true)
348+
@test csol.sol.u[end] [π, 0, 0, 0]
349+
350+
iprob = InfiniteOptDynamicOptProblem(cartpole, u0map, tspan, pmap; dt = 0.04)
351+
isol = solve(iprob, Ipopt.Optimizer, silent = true)
352+
@test isol.sol.u[end] [π, 0, 0, 0]
353+
end

0 commit comments

Comments
 (0)