Components
The four declaration verbs populate the spaces of the problem on a PreModel. Each may be called once; calling it twice, or with inconsistent dimensions, raises a structured exception.
| Verb | Declares | Stores into pre.… |
|---|---|---|
state! | state space $x \in \mathbb{R}^n$ | pre.state |
control! | control space $u \in \mathbb{R}^m$ | pre.control |
variable! | optimisation variable $v \in \mathbb{R}^q$ | pre.variable |
time! | the interval $[t_0, t_f]$ | pre.times |
using CTModels
import CTBase
pre = CTModels.PreModel()Dimension, name and components
A space has a dimension, a display name, and per-component names. Omitted names are generated ("x", then "x₁", "x₂", …). Each declaration accepts String or Symbol.
CTModels.state!(pre, 2, "x", ["q", "w"])
(CTModels.dimension(pre.state), # 2
CTModels.name(pre.state), # "x"
CTModels.components(pre.state)) # ["q", "w"](2, "x", ["q", "w"])CTModels.control!(pre, 1) # default name "u"
CTModels.components(pre.control)1-element Vector{String}:
"u"The optimisation variable
variable! declares the time-independent decision variable $v$ (free final time, design parameters, …). A dimension of 0 means no variable: pre.variable then stays an EmptyVariableModel.
CTModels.variable!(pre, 2, "v")
(CTModels.dimension(pre.variable), CTModels.components(pre.variable))(2, ["v₁", "v₂"])Time: fixed and free ends
time! sets the interval. Each end is either fixed (a value t0=/tf=) or free (an index ind0=/indf= into $v$, optimised by the solver).
CTModels.time!(pre; t0=0.0, tf=1.0) # both ends fixed
(CTModels.time_name(pre.times),
CTModels.has_fixed_initial_time(pre.times),
CTModels.has_fixed_final_time(pre.times))("t", true, true)A free final time stored at index 2 of $v$ (declared above with dimension 2):
pre2 = CTModels.PreModel()
CTModels.variable!(pre2, 2, "v")
CTModels.time!(pre2; t0=0.0, indf=2)
(CTModels.has_free_final_time(pre2.times), CTModels.final_time(pre2.times, [0.0, 1.5]))(true, 1.5)For a free time the value is read from $v$, hence final_time takes the variable vector. See Types and traits for the Fixed/Free distinction.
Naming rules
Names must be unique across all components of the problem. The validation (OCP/Validation/) rejects empty names, duplicates within a declaration, and collisions with names already declared elsewhere:
pre3 = CTModels.PreModel()
CTModels.state!(pre3, 2, "x", ["a", "b"])
try
CTModels.control!(pre3, 1, "a") # "a" already names a state component
catch e
e isa CTBase.Exceptions.IncorrectArgument
endtrueThese rules guarantee that a label like :a resolves unambiguously to one component when reading a solution or a constraint.