Skip to content

Commit 072824c

Browse files
committed
chore: move the docstrings to the interface file
1 parent b5b0f44 commit 072824c

File tree

4 files changed

+141
-149
lines changed

4 files changed

+141
-149
lines changed

ext/MTKCasADiDynamicOptExt.jl

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,7 @@ function (M::MXLinearInterpolation)(τ)
6161
end
6262
end
6363

64-
"""
65-
CasADiDynamicOptProblem(sys::System, u0, tspan, p; dt, steps)
66-
67-
Convert an System representing an optimal control system into a CasADi model
68-
for solving using optimization. Must provide either `dt`, the timestep between collocation
69-
points (which, along with the timespan, determines the number of points), or directly
70-
provide the number of points as `steps`.
71-
72-
The optimization variables:
73-
- a vector-of-vectors U representing the unknowns as an interpolation array
74-
- a vector-of-vectors V representing the controls as an interpolation array
75-
76-
The constraints are:
77-
- The set of user constraints passed to the System via `constraints`
78-
- The solver constraints that encode the time-stepping used by the solver
79-
"""
80-
function MTK.CasADiDynamicOptProblem(sys::System, u0map, tspan, pmap;
64+
function MTK.CasADiDynamicOptProblem(sys::ODESystem, u0map, tspan, pmap;
8165
dt = nothing,
8266
steps = nothing,
8367
guesses = Dict(), kwargs...)
@@ -212,15 +196,11 @@ function add_solve_constraints!(prob::CasADiDynamicOptProblem, tableau)
212196
solver_opti
213197
end
214198

215-
"""
216-
CasADi Collocation solver.
217-
- solver: an optimization solver such as Ipopt. Should be given as a string or symbol in all lowercase, e.g. "ipopt"
218-
- tableau: An ODE RK tableau. Load a tableau by calling a function like `constructRK4` and may be found at https://docs.sciml.ai/DiffEqDevDocs/stable/internals/tableaus/. If this argument is not passed in, the solver will default to Radau second order.
219-
"""
220199
struct CasADiCollocation <: AbstractCollocation
221200
solver::Union{String, Symbol}
222201
tableau::DiffEqBase.ODERKTableau
223202
end
203+
224204
MTK.CasADiCollocation(solver, tableau = MTK.constructDefault()) = CasADiCollocation(solver, tableau)
225205

226206
function MTK.prepare_and_optimize!(prob::CasADiDynamicOptProblem, solver::CasADiCollocation; verbose = false, solver_options = Dict(), plugin_options = Dict(), kwargs...)

ext/MTKInfiniteOptExt.jl

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -72,40 +72,14 @@ function MTK.add_constraint!(m::InfiniteOptModel, expr::Union{Equation, Inequali
7272
end
7373
MTK.set_objective!(m::InfiniteOptModel, expr) = @objective(m.model, Min, expr)
7474

75-
"""
76-
JuMPDynamicOptProblem(sys::System, u0, tspan, p; dt)
77-
78-
Convert a System representing an optimal control system into a JuMP model
79-
for solving using optimization. Must provide either `dt`, the timestep between collocation
80-
points (which, along with the timespan, determines the number of points), or directly
81-
provide the number of points as `steps`.
82-
83-
The optimization variables:
84-
- a vector-of-vectors U representing the unknowns as an interpolation array
85-
- a vector-of-vectors V representing the controls as an interpolation array
86-
87-
The constraints are:
88-
- The set of user constraints passed to the System via `constraints`
89-
- The solver constraints that encode the time-stepping used by the solver
90-
"""
91-
function MTK.JuMPDynamicOptProblem(sys::System, u0map, tspan, pmap;
75+
function MTK.JuMPDynamicOptProblem(sys::ODESystem, u0map, tspan, pmap;
9276
dt = nothing,
9377
steps = nothing,
9478
guesses = Dict(), kwargs...)
9579
MTK.process_DynamicOptProblem(JuMPDynamicOptProblem, InfiniteOptModel, sys, u0map, tspan, pmap; dt, steps, guesses, kwargs...)
9680
end
9781

98-
"""
99-
InfiniteOptDynamicOptProblem(sys::System, u0map, tspan, pmap; dt)
100-
101-
Convert System representing an optimal control system into a InfiniteOpt model
102-
for solving using optimization. Must provide `dt` for determining the length
103-
of the interpolation arrays.
104-
105-
Related to `JuMPDynamicOptProblem`, but directly adds the differential equations
106-
of the system as derivative constraints, rather than using a solver tableau.
107-
"""
108-
function MTK.InfiniteOptDynamicOptProblem(sys::System, u0map, tspan, pmap;
82+
function MTK.InfiniteOptDynamicOptProblem(sys::ODESystem, u0map, tspan, pmap;
10983
dt = nothing,
11084
steps = nothing,
11185
guesses = Dict(), kwargs...)
@@ -115,6 +89,7 @@ function MTK.InfiniteOptDynamicOptProblem(sys::System, u0map, tspan, pmap;
11589
end
11690

11791
MTK.lowered_integral(model, expr, lo, hi) = model.tₛ * InfiniteOpt.(arg, model.model[:t], lo, hi)
92+
MTK.lowered_derivative(model, i) = (model.U[i], model.model[:t])
11893

11994
function MTK.process_integral_bounds(model, integral_span, tspan)
12095
if MTK.is_free_final(model) && isequal(integral_span, tspan)
@@ -132,8 +107,6 @@ function MTK.add_initial_constraints!(m::InfiniteOptModel, u0, u0_idxs, ts)
132107
end
133108
end
134109

135-
MTK.lowered_derivative(model, i) = (model.U[i], model.model[:t])
136-
137110
function MTK.fixed_t_map(model::InfiniteOptModel, x_ops, c_ops, exprs)
138111
Dict([[x_ops[i] => model.U[i] for i in 1:length(model.U)];
139112
[c_ops[i] => model.V[i] for i in 1:length(model.V)]])
@@ -191,22 +164,12 @@ function add_solve_constraints!(prob::JuMPDynamicOptProblem, tableau)
191164
end
192165
end
193166

194-
"""
195-
JuMP Collocation solver.
196-
- solver: a optimization solver such as Ipopt
197-
- tableau: An ODE RK tableau. Load a tableau by calling a function like `constructRK4` and may be found at https://docs.sciml.ai/DiffEqDevDocs/stable/internals/tableaus/. If this argument is not passed in, the solver will default to Radau second order.
198-
"""
199167
struct JuMPCollocation <: AbstractCollocation
200168
solver::Any
201169
tableau::DiffEqBase.ODERKTableau
202170
end
203171
MTK.JuMPCollocation(solver, tableau = MTK.constructDefault()) = JuMPCollocation(solver, tableau)
204172

205-
"""
206-
InfiniteOpt Collocation solver.
207-
- solver: an optimization solver such as Ipopt
208-
- `derivative_method`: the method used by InfiniteOpt to compute derivatives. The list of possible options can be found at https://infiniteopt.github.io/InfiniteOpt.jl/stable/guide/derivative/. Defaults to FiniteDifference(Backward()).
209-
"""
210173
struct InfiniteOptCollocation <: AbstractCollocation
211174
solver::Any
212175
derivative_method::InfiniteOpt.AbstractDerivativeMethod

ext/MTKPyomoDynamicOptExt.jl

Lines changed: 70 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ struct PyomoDAEVar
1414
v::Py
1515
end
1616
(v::PyomoDAEVar)(t) = v.v[:, t]
17-
getindex(v::PyomoDAEVar, i::Union{Num, Symbolic}, t::Union{Num, Symbolic}) = wrap(Term{symeltype(A)}(getindex, [A, unwrap(i), unwrap(t)]))
17+
getindex(v::PyomoDAEVar, i::Union{Num, Symbolic}, t::Union{Num, Symbolic}) = wrap(Term{symeltype(v)}(getindex, [v, unwrap(i), unwrap(t)]))
18+
getindex(v::PyomoDAEVar, i::Int) = wrap(Term{symeltype(v)}(getindex, [v, unwrap(i), Colon()]))
1819

1920
for ff in [acos, log1p, acosh, log2, asin, tan, atanh, cos, log, sin, log10, sqrt]
2021
f = nameof(ff)
@@ -25,9 +26,9 @@ const SymbolicConcreteModel = Symbolics.symstruct(ConcreteModel)
2526

2627
struct PyomoModel
2728
model::ConcreteModel
28-
U
29-
V
30-
tₛ::Union{Int}
29+
U::PyomoDAEVar
30+
V::PyomoDAEVar
31+
tₛ::Union{Int, Py}
3132
is_free_final::Bool
3233
model_sym::SymbolicConcreteModel
3334
t_sym::Union{Num, BasicSymbolic}
@@ -54,36 +55,30 @@ struct PyomoDynamicOptProblem{uType, tType, isinplace, P, F, K} <:
5455
end
5556
end
5657

57-
"""
58-
PyomoDynamicOptProblem(sys::ODESystem, u0, tspan, p; dt, steps)
59-
60-
Convert an ODESystem representing an optimal control system into a Pyomo model
61-
for solving using optimization. Must provide either `dt`, the timestep between collocation
62-
points (which, along with the timespan, determines the number of points), or directly
63-
provide the number of points as `steps`.
64-
"""
6558
function MTK.PyomoDynamicOptProblem(sys::ODESystem, u0map, tspan, pmap;
66-
dt = nothing,
67-
steps = nothing,
59+
dt = nothing, steps = nothing,
6860
guesses = Dict(), kwargs...)
6961
prob = MTK.process_DynamicOptProblem(PyomoDynamicOptProblem, PyomoModel, sys, u0map, tspan, pmap; dt, steps, guesses, kwargs...)
62+
prob.wrapped_model.model.dU = pyomo.DerivativeVar(prob.wrapped_model.model.U, wrt = model.t)
7063
MTK.add_equational_constraints!(prob.wrapped_model, sys, pmap, tspan)
7164
prob
7265
end
7366

7467
MTK.generate_internal_model(m::Type{PyomoModel}) = pyomo.ConcreteModel()
68+
7569
function MTK.generate_time_variable!(m::ConcreteModel, tspan, tsteps)
70+
m.steps = length(tsteps)
7671
m.t = pyomo.ContinuousSet(initialize = collect(tsteps), bounds = tspan)
7772
end
7873

7974
function MTK.generate_state_variable!(m::ConcreteModel, u0, ns, ts)
8075
m.u_idxs = pyomo.RangeSet(1, ns)
81-
pyomo.Var(m.u_idxs, m.t)
76+
PyomoDAEVar(pyomo.Var(m.u_idxs, m.t))
8277
end
8378

8479
function MTK.generate_input_variable!(m::ConcreteModel, u0, nc, ts)
8580
m.v_idxs = pyomo.RangeSet(1, nc)
86-
pyomo.Var(m.v_idxs, m.t)
81+
PyomoDAEVar(pyomo.Var(m.v_idxs, m.t))
8782
end
8883

8984
function MTK.generate_timescale(m::ConcreteModel, guess, is_free_t)
@@ -111,99 +106,89 @@ function add_initial_constraints!(model::PyomoModel, u0, u0_idxs)
111106
end
112107
end
113108

114-
function substitute_fixed_t_vars!(model::PyomoModel, sys, exprs)
115-
stidxmap = Dict([v => i for (i, v) in enumerate(unknowns(sys))])
116-
ctidxmap = Dict([v => i for (i, v) in enumerate(MTK.unbound_inputs(sys))])
117-
iv = MTK.get_iv(sys)
118-
119-
for cons in jconstraints
120-
consvars = MTK.vars(cons)
121-
for st in consvars
109+
function fixed_t_map(m::PyomoModel, sys, exprs)
110+
stidxmap = Dict([v => i for (i, v) in x_ops])
111+
ctidxmap = Dict([v => i for (i, v) in c_ops])
112+
mU = Symbolics.symbolic_getproperty(model_sym, :U)
113+
mV = Symbolics.symbolic_getproperty(model_sym, :V)
114+
fixed_t_map = Dict()
115+
for expr in exprs
116+
vars = MTK.vars(exprs)
117+
for st in vars
122118
MTK.iscall(st) || continue
123119
x = MTK.operation(st)
124120
t = only(MTK.arguments(st))
125121
MTK.symbolic_type(t) === MTK.NotSymbolic() || continue
126-
if haskey(stidxmap, x(iv))
127-
idx = stidxmap[x(iv)]
128-
cv = :U
129-
else
130-
idx = ctidxmap[x(iv)]
131-
cv = :V
132-
end
133-
model.t.add(t)
134-
cons = Symbolics.substitute(cons, Dict(x(t) => model.cv[idx, t]))
122+
m.model.t.add(t)
123+
fixed_t_map[x(t)] = haskey(stidxmap, x) ? mU[stidxmap[x], t] : mV[ctidxmap[x], t]
135124
end
136125
end
126+
fixed_t_map
137127
end
138128

139-
function MTK.substitute_model_vars(pmodel::PyomoModel, sys, pmap, exprs, tspan)
140-
@unwrap model, model_sym, idx_sym, t_sym = pmodel
141-
x_ops = [MTK.operation(MTK.unwrap(st)) for st in unknowns(sys)]
142-
c_ops = [MTK.operation(MTK.unwrap(ct)) for ct in MTK.unbound_inputs(sys)]
129+
function MTK.free_t_map(model, x_ops, c_ops)
143130
mU = Symbolics.symbolic_getproperty(model_sym, :U)
144131
mV = Symbolics.symbolic_getproperty(model_sym, :V)
132+
Dict([[x(tₛ) => mU[i, end] for (i, x) in enumerate(x_ops)];
133+
[c(tₛ) => mV[i, end] for (i, c) in enumerate(c_ops)]])
134+
end
145135

146-
(ti, tf) = tspan
147-
if MTK.symbolic_type(tf) === MTK.ScalarSymbolic()
148-
_tf = model.tₛ + ti
149-
exprs = map(c -> Symbolics.fast_substitute(c, Dict(tf => _tf)), exprs)
150-
free_t_map = Dict([[x(tₛ) => mU[i, end] for (i, x) in enumerate(x_ops)];
151-
[c(tₛ) => mV[i, end] for (i, c) in enumerate(c_ops)]])
152-
exprs = map(c -> Symbolics.fixpoint_sub(c, free_t_map), exprs)
153-
end
136+
function MTK.whole_t_map(model)
137+
mU = Symbolics.symbolic_getproperty(model_sym, :U)
138+
mV = Symbolics.symbolic_getproperty(model_sym, :V)
139+
Dict([[v => mU[i, t_sym] for (i, v) in enumerate(sts)];
140+
[v => mV[i, t_sym] for (i, v) in enumerate(cts)]])
141+
end
154142

155-
whole_interval_map = Dict([[v => mU[i, t_sym] for (i, v) in enumerate(sts)];
156-
[v => mV[i, t_sym] for (i, v) in enumerate(cts)]])
157-
exprs = map(c -> Symbolics.fixpoint_sub(c, whole_interval_map), exprs)
158-
exprs
159-
end
160-
161-
function MTK.substitute_integral!(model::PyomoModel, exprs, tspan)
162-
intmap = Dict()
163-
for int in MTK.collect_applied_operators(exprs, Symbolics.Integral)
164-
op = MTK.operation(int)
165-
arg = only(arguments(MTK.value(int)))
166-
lo, hi = MTK.value.((op.domain.domain.left, op.domain.domain.right))
167-
if MTK.is_free_final(model) && isequal((lo, hi), tspan)
168-
(lo, hi) = (0, 1)
169-
elseif MTK.is_free_final(model)
170-
error("Free final time problems cannot handle partial timespans.")
171-
end
172-
intmap[int] = model.tₛ * InfiniteOpt.(arg, model.model[:t], lo, hi)
173-
end
174-
exprs = map(c -> Symbolics.substitute(c, intmap), exprs)
143+
function MTK.lowered_integral(m::PyomoModel, arg)
144+
@unpack model, model_sym, t_sym = m
145+
arg_f = Symbolics.build_function(arg, model_sym, t_sym)
146+
Integral(model.t, wrt = model.t, rule=arg_f)
175147
end
176148

177-
function MTK.substitute_differentials(model::PyomoModel, sys, eqs)
178-
pmodel = prob.model
179-
@unpack model, model_sym, t_sym, idx_sym = pmodel
180-
model.dU = pyomo.DerivativeVar(model.U, wrt = model.t)
149+
MTK.process_integral_bounds(model, integral_span, tspan) = integral_span
181150

151+
function MTK.lowered_derivative(m::PyomoModel, i)
182152
mdU = Symbolics.symbolic_getproperty(model_sym, :dU)
183-
mU = Symbolics.symbolic_getproperty(model_sym, :U)
184-
mtₛ = Symbolics.symbolic_getproperty(model_sym, :tₛ)
185-
diffsubmap = Dict([D(mU[i, t_sym]) => mdU[i, t_sym] for i in 1:length(unknowns(sys))])
186-
187-
diff_eqs = substitute_model_vars(model, sys, pmap, diff_equations(sys))
188-
diff_eqs = map(e -> Symbolics.substitute(e, diffsubmap), diff_eqs)
189-
[mtₛ * eq.rhs - eq.lhs == 0 for eq in diff_eqs]
153+
mdU[i, t_sym]
190154
end
191155

192156
struct PyomoCollocation <: AbstractCollocation
193-
solver::Any
194-
derivative_method
157+
solver::Union{String, Symbol}
158+
derivative_method::Pyomo.DiscretizationMethod
195159
end
196-
MTK.PyomoCollocation(solver, derivative_method = 1) = PyomoCollocation(solver, derivative_method)
197160

198-
function MTK.prepare_and_optimize!()
199-
end
200-
function MTK.get_U_values()
161+
MTK.PyomoCollocation(solver, derivative_method = LagrangeRadau(5)) = PyomoCollocation(solver, derivative_method)
162+
163+
function MTK.prepare_and_optimize!(prob::PyomoDynamicOptProblem, collocation; verbose, kwargs...)
164+
m = prob.wrapped_model.model
165+
dm = collocation.derivative_method
166+
discretizer = TransformationFactory(dm)
167+
ncp = is_finite_difference(dm) ? 1 : dm.np
168+
discretizer.apply_to(model, wrt = m.t, nfe = m.steps, ncp = ncp, scheme = scheme_string(dm))
169+
solver = SolverFactory(string(collocation.solver))
170+
solver.solve(m)
201171
end
202-
function MTK.get_V_values()
172+
173+
function MTK.get_U_values(m::PyomoModel)
174+
[pyomo.value(model.U[i]) for i in model.U]
203175
end
204-
function MTK.get_t_values()
176+
177+
function MTK.get_V_values(m::PyomoModel)
178+
[pyomo.value(model.V[i]) for i in model.V]
205179
end
206-
function MTK.successful_solve()
180+
181+
function MTK.get_t_values(m::PyomoModel)
182+
[pyomo.value(model.t[i]) for i in model.t]
207183
end
208184

185+
function MTK.successful_solve(m::PyomoModel)
186+
ss = m.solver.status
187+
tc = m.solver.termination_condition
188+
if ss == opt.SolverStatus.ok && (tc == opt.TerminationStatus.optimal || tc == opt.TerminationStatus.locallyOptimal)
189+
return true
190+
else
191+
return false
192+
end
193+
end
209194
end

0 commit comments

Comments
 (0)