Skip to content

Commit 092ea40

Browse files
Merge pull request #836 from SciML/tutorials
add a bunch of tutorials
2 parents 486923c + 118a973 commit 092ea40

File tree

9 files changed

+236
-87
lines changed

9 files changed

+236
-87
lines changed

docs/make.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ makedocs(
1616
"tutorials/higher_order.md",
1717
"tutorials/nonlinear.md",
1818
"tutorials/modelingtoolkitize.md",
19+
"tutorials/optimization.md",
20+
"tutorials/stochastic_diffeq.md",
21+
"tutorials/nonlinear_optimal_control.md"
1922
],
2023
"Basics" => Any[
2124
"basics/AbstractSystem.md",

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ is built on, consult the
5858
- Optimization problems
5959
- Continuous-Time Markov Chains
6060
- Chemical Reactions
61-
- Optimal Control
61+
- Nonlinear Optimal Control
6262

6363
## Model Import Formats
6464

docs/src/tutorials/acausal_components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ are then applied to state equalities between currents and voltages. This
77
specifies a differential-algebraic equation (DAE) system, where the algebraic
88
equations are given by the constraints and equalities between different
99
component variables. We then simplify this to an ODE by eliminating the
10-
equalities before solving. Let's see this in action
10+
equalities before solving. Let's see this in action.
1111

1212
## Copy-Paste Example
1313

docs/src/tutorials/nonlinear.md

Lines changed: 16 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Solving Nonlinear Systems with NLsolve
1+
# Modeling Nonlinear Systems
22

33
In this example we will go one step deeper and showcase the direct function
44
generation capabilities in ModelingToolkit.jl to build nonlinear systems.
@@ -7,7 +7,7 @@ the nonlinear system defined by where the derivatives are zero. We use (unknown)
77
variables for our nonlinear system.
88

99
```julia
10-
using ModelingToolkit
10+
using ModelingToolkit, NonlinearSolve
1111

1212
@variables x y z
1313
@parameters σ ρ β
@@ -17,91 +17,25 @@ eqs = [0 ~ σ*(y-x),
1717
0 ~ x*-z)-y,
1818
0 ~ x*y - β*z]
1919
ns = NonlinearSystem(eqs, [x,y,z], [σ,ρ,β])
20-
nlsys_func = generate_function(ns)[2] # second is the inplace version
21-
```
22-
23-
which generates:
24-
25-
```julia
26-
(var"##MTIIPVar#405", u, p)->begin
27-
@inbounds begin
28-
@inbounds begin
29-
let (x, y, z, σ, ρ, β) = (u[1], u[2], u[3], p[1], p[2], p[3])
30-
var"##MTIIPVar#405"[1] = (*)(σ, (-)(y, x))
31-
var"##MTIIPVar#405"[2] = (-)((*)(x, (-)(ρ, z)), y)
32-
var"##MTIIPVar#405"[3] = (-)((*)(x, y), (*)(β, z))
33-
end
34-
end
35-
end
36-
nothing
37-
end
38-
```
3920

40-
We can use this to build a nonlinear function for use with NLsolve.jl:
21+
guess = [x => 1.0,
22+
y => 0.0,
23+
z => 0.0]
4124

42-
```julia
43-
f = eval(nlsys_func)
44-
du = zeros(3); u = ones(3)
45-
params = (10.0,26.0,2.33)
46-
f(du,u,params)
47-
du
48-
49-
#=
50-
3-element Array{Float64,1}:
51-
0.0
52-
24.0
53-
-1.33
54-
=#
55-
```
25+
ps = [
26+
σ => 10.0
27+
ρ => 26.0
28+
β => 8/3
29+
]
5630

57-
We can similarly ask to generate the in-place Jacobian function:
58-
59-
```julia
60-
j_func = generate_jacobian(ns)[2] # second is in-place
61-
j! = eval(j_func)
31+
prob = NonlinearProblem(ns,guess,ps)
32+
sol = solve(prob,NewtonRaphson())
6233
```
6334

64-
which gives:
35+
We can similarly ask to generate the `NonlinearProblem` with the analytical
36+
Jacobian function:
6537

6638
```julia
67-
:((var"##MTIIPVar#582", u, p)->begin
68-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:70 =#
69-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:71 =#
70-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:71 =# @inbounds begin
71-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:72 =#
72-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:53 =# @inbounds begin
73-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:53 =#
74-
let (x, y, z, σ, ρ, β) = (u[1], u[2], u[3], p[1], p[2], p[3])
75-
var"##MTIIPVar#582"[1] = (*)(σ, -1)
76-
var"##MTIIPVar#582"[2] = (-)(ρ, z)
77-
var"##MTIIPVar#582"[3] = y
78-
var"##MTIIPVar#582"[4] = σ
79-
var"##MTIIPVar#582"[5] = -1
80-
var"##MTIIPVar#582"[6] = x
81-
var"##MTIIPVar#582"[7] = 0
82-
var"##MTIIPVar#582"[8] = (*)(x, -1)
83-
var"##MTIIPVar#582"[9] = (*)(-1, β)
84-
end
85-
end
86-
end
87-
#= C:\Users\accou\.julia\dev\ModelingToolkit\src\utils.jl:74 =#
88-
nothing
89-
end)
39+
prob = NonlinearProblem(ns,guess,ps,jac=true)
40+
sol = solve(prob,NewtonRaphson())
9041
```
91-
92-
Now, we can call `nlsolve` by enclosing our parameters into the functions:
93-
94-
```julia
95-
using NLsolve
96-
nlsolve((out, x) -> f(out, x, params), (out, x) -> j!(out, x, params), ones(3))
97-
```
98-
99-
If one would like the generated function to be a Julia function instead of an expression, and allow this
100-
function to be used from within the same world-age, one simply needs to pass `Val{false}` to tell it to
101-
generate the function, i.e.:
102-
103-
```julia
104-
nlsys_func = generate_function(ns, [x,y,z], [σ,ρ,β], expression=Val{false})[2]
105-
```
106-
107-
which uses RuntimeGeneratedFunctions.jl to build the same world-age function on the fly without eval.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Nonlinear Optimal Control
2+
3+
#### Note: this is still a work in progress!
4+
5+
The `ControlSystem` type is an interesting system because, unlike other
6+
system types, it cannot be numerically solved on its own. Instead, it must be
7+
transformed into another system before solving. Standard methods such as the
8+
"direct method", "multiple shooting", or "discretize-then-optimize" can all be
9+
phrased as symbolic transformations to a `ControlSystem`: this is the strategy
10+
of this methodology.
11+
12+
## Defining a Nonlinear Optimal Control Problem
13+
14+
Here we will start by defining a classic optimal control problem. Let:
15+
16+
```math
17+
x^{′′} = u^3(t)
18+
```
19+
20+
where we want to optimize our controller `u(t)` such that the following is
21+
minimized:
22+
23+
```math
24+
L(\theta) = \sum_i \Vert 4 - x(t_i) \Vert + 2 \Vert x^\prime(t_i) \Vert + \Vert u(t_i) \Vert
25+
```
26+
27+
where ``i`` is measured on (0,8) at 0.01 intervals. To do this, we rewrite the
28+
ODE in first order form:
29+
30+
```math
31+
\begin{aligned}
32+
x^\prime &= v \\
33+
v^′ &= u^3(t) \\
34+
\end{aligned}
35+
```
36+
37+
and thus
38+
39+
```math
40+
L(\theta) = \sum_i \Vert 4 - x(t_i) \Vert + 2 \Vert v(t_i) \Vert + \Vert u(t_i) \Vert
41+
```
42+
43+
is our loss function on the first order system.
44+
45+
Defining such a control system is similar to an `ODESystem`, except we must also
46+
specify a control variable `u(t)` and a loss function. Together, this problem
47+
looks as follows:
48+
49+
```julia
50+
using ModelingToolkit
51+
52+
@variables t x(t) v(t) u(t)
53+
@parameters p[1:2]
54+
D = Differential(t)
55+
56+
loss = (4-x)^2 + 2v^2 + u^2
57+
eqs = [
58+
D(x) ~ v - p[2]*x
59+
D(v) ~ p[1]*u^3 + v
60+
]
61+
62+
sys = ControlSystem(loss,eqs,t,[x,v],[u],p)
63+
```
64+
65+
## Solving a Control Problem via Discretize-Then-Optimize
66+
67+
One common way to solve nonlinear optimal control problems is by transforming
68+
them into an optimization problem by performing a Runge-Kutta discretization
69+
of the differential equation system and imposing equalities between variables
70+
in the same steps. This can be done via the `runge_kutta_discretize` transformation
71+
on the `ControlSystem`. While a tableau `tab` can be specified, it defaults to
72+
a 5th order RadauIIA collocation, which is a common method in the field. To
73+
perform this discretization, we simply need to give a `dt` and a timespan on which
74+
to discretize:
75+
76+
```julia
77+
dt = 0.1
78+
tspan = (0.0,1.0)
79+
sys = runge_kutta_discretize(sys,dt,tspan)
80+
```
81+
82+
Now `sys` is an `OptimizationSystem` which, when solved, gives the values of
83+
`x(t)`, `v(t)`, and `u(t)`. Thus we solve the `OptimizationSystem` using
84+
GalacticOptim.jl:
85+
86+
```julia
87+
u0 = rand(length(states(sys))) # guess for the state values
88+
prob = OptimizationProblem(sys,u0,[0.1,0.1],grad=true)
89+
sol = solve(prob,BFGS())
90+
```
91+
92+
And this is missing some nice interfaces and ignores the equality constraints
93+
right now so the tutorial is not complete.

docs/src/tutorials/optimization.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Modeling Optimization Problems
2+
3+
```julia
4+
using ModelingToolkit, GalacticOptim
5+
6+
@variables x y
7+
@parameters a b
8+
loss = (a - x)^2 + b * (y - x^2)^2
9+
sys = OptimizationSystem(loss,[x,y],[a,b])
10+
11+
u0 = [
12+
x=>1.0
13+
y=>2.0
14+
]
15+
p = [
16+
a => 6.0
17+
b => 7.0
18+
]
19+
20+
prob = OptimizationProblem(sys,u0,p,grad=true,hess=true)
21+
solve(prob,Newton())
22+
```
23+
24+
Needs more text but it's super cool and auto-parallelizes and sparsifies too.
25+
Plus you can hierarchically nest systems to have it generate huge
26+
optimization problems.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Modeling with Stochasticity
2+
3+
All models with `ODESystem` are deterministic. `SDESystem` adds another element
4+
to the model: randomness. This is a
5+
[stochastic differential equation](https://en.wikipedia.org/wiki/Stochastic_differential_equation)
6+
which has a deterministic (drift) component and a stochastic (diffusion)
7+
component. Let's take the Lorenz equation from the first tutorial and extend
8+
it to have multiplicative noise.
9+
10+
```julia
11+
using ModelingToolkit, StochasticDiffEq
12+
13+
# Define some variables
14+
@parameters t σ ρ β
15+
@variables x(t) y(t) z(t)
16+
D = Differential(t)
17+
18+
eqs = [D(x) ~ σ*(y-x),
19+
D(y) ~ x*-z)-y,
20+
D(z) ~ x*y - β*z]
21+
22+
noiseeqs = [0.1*x,
23+
0.1*y,
24+
0.1*z]
25+
26+
de = SDESystem(eqs,noiseeqs,t,[x,y,z],[σ,ρ,β])
27+
28+
u0map = [
29+
x => 1.0,
30+
y => 0.0,
31+
z => 0.0
32+
]
33+
34+
parammap = [
35+
σ => 10.0,
36+
β => 26.0,
37+
ρ => 2.33
38+
]
39+
40+
prob = SDEProblem(de,u0map,(0.0,100.0),parammap)
41+
sol = solve(prob,SOSRI())
42+
```

src/systems/optimization/optimizationsystem.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,54 @@ function DiffEqBase.OptimizationFunction{iip}(f, ::AutoModelingToolkit, x, p = D
242242
end
243243
OptimizationProblem(sys,u0map,parammap,grad=grad,hess=hess).f
244244
end
245+
246+
function Base.show(io::IO, sys::OptimizationSystem)
247+
eqs = equations(sys)
248+
Base.printstyled(io, "Model $(nameof(sys))\n"; bold=true)
249+
# The reduced equations are usually very long. It's not that useful to print
250+
# them.
251+
#Base.print_matrix(io, eqs)
252+
#println(io)
253+
254+
rows = first(displaysize(io)) ÷ 5
255+
limit = get(io, :limit, false)
256+
257+
vars = states(sys); nvars = length(vars)
258+
Base.printstyled(io, "States ($nvars):"; bold=true)
259+
nrows = min(nvars, limit ? rows : nvars)
260+
limited = nrows < length(vars)
261+
d_u0 = has_default_u0(sys) ? default_u0(sys) : nothing
262+
for i in 1:nrows
263+
s = vars[i]
264+
print(io, "\n ", s)
265+
266+
if d_u0 !== nothing
267+
val = get(d_u0, s, nothing)
268+
if val !== nothing
269+
print(io, " [defaults to $val]")
270+
end
271+
end
272+
end
273+
limited && print(io, "\n")
274+
println(io)
275+
276+
vars = parameters(sys); nvars = length(vars)
277+
Base.printstyled(io, "Parameters ($nvars):"; bold=true)
278+
nrows = min(nvars, limit ? rows : nvars)
279+
limited = nrows < length(vars)
280+
d_p = has_default_p(sys) ? default_p(sys) : nothing
281+
for i in 1:nrows
282+
s = vars[i]
283+
print(io, "\n ", s)
284+
285+
if d_p !== nothing
286+
val = get(d_p, s, nothing)
287+
if val !== nothing
288+
print(io, " [defaults to $val]")
289+
end
290+
end
291+
end
292+
limited && print(io, "\n")
293+
294+
return nothing
295+
end

test/controlsystem.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ dt = 0.1
1515
tspan = (0.0,1.0)
1616
sys = runge_kutta_discretize(sys,dt,tspan)
1717

18-
u0 = rand(112) # guess for the state values
19-
prob = OptimizationProblem(sys,u0,[0.1],grad=true)
20-
18+
u0 = rand(length(states(sys))) # guess for the state values
19+
prob = OptimizationProblem(sys,u0,[0.1,0.1],grad=true)
20+
sol = solve(prob,BFGS())

0 commit comments

Comments
 (0)