Displaying models and solutions
The CTModels.Display module provides Base.show extensions that render Model and PreModel objects in a human-readable mathematical format. It also hosts a RecipesBase.plot stub that is specialised by the CTModelsPlots extension when Plots.jl is loaded.
Setup
Displaying a built Model
When you print a Model in the REPL (or any IO context with MIME"text/plain"), CTModels renders the OCP in standard mathematical notation — objective, dynamics, constraints, and variable spaces:
pre = CTModels.PreModel()
CTModels.variable!(pre, 0)
CTModels.time!(pre; t0=0.0, tf=1.0)
CTModels.state!(pre, 2)
CTModels.control!(pre, 1)
function dynamics!(r, t, x, u, v)
r[1] = x[2]
r[2] = u[1]
return nothing
end
CTModels.dynamics!(pre, dynamics!)
CTModels.objective!(pre, :min; lagrange=(t, x, u, v) -> u[1]^2)
function boundary!(r, x0, xf, v)
r[1] = x0[1]
r[2] = x0[2] - 1
r[3] = xf[1]
r[4] = xf[2] + 1
return nothing
end
CTModels.constraint!(pre, :boundary; f=boundary!, lb=zeros(4), ub=zeros(4), label=:bc)
CTModels.constraint!(pre, :state; rg=1:1, lb=[0.0], ub=[0.1], label=:x1_box)
CTModels.constraint!(pre, :control; rg=1:1, lb=[-10.0], ub=[10.0], label=:u_box)
CTModels.time_dependence!(pre; autonomous=true)
ocp = CTModels.build(pre)
ocp # displays the modelThe (autonomous) optimal control problem is of the form:
minimize J(x, u) = ∫ f⁰(x(t), u(t)) dt, over [0.0, 1.0]
subject to
ẋ(t) = f(x(t), u(t)), t in [0.0, 1.0] a.e.,
ϕ₋ ≤ ϕ(x(0.0), x(1.0)) ≤ ϕ₊,
x₋ ≤ x(t) ≤ x₊,
u₋ ≤ u(t) ≤ u₊,
where x(t) ∈ R² and u(t) ∈ R.The output includes:
An (autonomous) or (non autonomous) qualifier.
The objective
J(x, u) = …with Mayer and/or Lagrange terms.The dynamics
ẋ(t) = f(t, x(t), u(t)).Constraint lines for path, boundary, and box constraints (only those that are present).
A where clause listing the state, control, and variable spaces.
Displaying a PreModel
A PreModel can be displayed at any stage of construction. If the problem is not yet consistent (missing components, incomplete declarations), only the abstract definition — if any — is shown. Once the pre-model is consistent, the full mathematical formulation is rendered:
pre2 = CTModels.PreModel()
CTModels.variable!(pre2, 0)
CTModels.time!(pre2; t0=0.0, tf=1.0)
CTModels.state!(pre2, 1)
CTModels.control!(pre2, 1)
CTModels.dynamics!(pre2, (r, t, x, u, v) -> (r[1] = u[1]; return nothing))
CTModels.objective!(pre2, :min; lagrange=(t, x, u, v) -> u[1]^2)
CTModels.time_dependence!(pre2; autonomous=true)
pre2 # consistent PreModel displays the mathematical formThe (autonomous) optimal control problem is of the form:
minimize J(x, u) = ∫ f⁰(x(t), u(t)) dt, over [0.0, 1.0]
subject to
ẋ(t) = f(x(t), u(t)), t in [0.0, 1.0] a.e.,
where x(t) ∈ R and u(t) ∈ R.An empty PreModel produces no output:
CTModels.PreModel() # nothing is printedAbstract (symbolic) definitions
If a Definition has been attached to the model via definition!, its symbolic expression is printed under an "Abstract definition:" header before the mathematical formulation. This is useful when the OCP originates from a macro-based DSL (e.g. OptimalControl.jl) that stores the original user code.
When no definition is set (EmptyDefinition), this section is skipped silently.
Plotting solutions
The Display module registers a RecipesBase.plot method for AbstractSolution. Without Plots.jl loaded, calling it throws an ExtensionError:
using CTModels
sol = CTModels.build_solution(
ocp,
collect(range(0.0, 1.0; length=10)),
zeros(10, 2),
zeros(10, 1),
Float64[],
zeros(10, 2);
objective=0.0,
iterations=0,
constraints_violation=0.0,
message="",
status=:dummy,
successful=true,
)
try
CTModels.plot(sol)
catch e
println(typeof(e))
endUndefVarErrorWhen Plots.jl is loaded, the CTModelsPlots extension provides full plot recipes. See Plotting for details.
See also
Building a model — how to assemble a
PreModeland callbuild.Plotting — plot recipes for solutions.
Model— API reference for the model type.PreModel— API reference for the pre-model type.