Constraints

constraint! adds one constraint at a time to the pre-model. The first positional argument is the kind; the keywords give the bounds, the function or range, and a label used later to read back the constraint and its dual.

KindFormKeywords
:pathnonlinear $\ell \le c(t,x,u,v) \le u$f, lb, ub, label
:boundarynonlinear $\ell \le b(x_0,x_f,v) \le u$f, lb, ub, label
:statebox on state componentsrg, lb, ub, label
:controlbox on control componentsrg, lb, ub, label
:variablebox on variable componentsrg, lb, ub, label

Nonlinear constraint functions are written in place like the dynamics: f(r, t, x, u, v) for :path, f(r, x0, xf, v) for :boundary.

using CTModels

pre = CTModels.PreModel()
CTModels.variable!(pre, 2, "v")
CTModels.time!(pre; t0=0.0, tf=1.0)
CTModels.state!(pre, 2)
CTModels.control!(pre, 1)
CTModels.dynamics!(pre, (r, t, x, u, v) -> (r[1] = x[2]; r[2] = u[1]; nothing))
CTModels.objective!(pre, :min; lagrange=(t, x, u, v) -> u[1]^2)

# Nonlinear path and boundary constraints
CTModels.constraint!(pre, :path;
    f=(r, t, x, u, v) -> (r[1] = x[1] + u[1]; nothing), lb=[0.0], ub=[1.0], label=:p)
CTModels.constraint!(pre, :boundary;
    f=(r, x0, xf, v) -> (r[1] = x0[1]; r[2] = xf[1]; nothing),
    lb=[0.0, 0.0], ub=[0.0, 0.0], label=:bc)

# Box constraints on state, control and variable components
CTModels.constraint!(pre, :state;    rg=1:1, lb=[-1.0], ub=[1.0], label=:x1)
CTModels.constraint!(pre, :control;  rg=1:1, lb=[-10.0], ub=[10.0], label=:u)
CTModels.constraint!(pre, :variable; rg=1:2, lb=[0.0, 0.0], ub=[1.0, 1.0], label=:vbox)

CTModels.time_dependence!(pre; autonomous=true)
ocp = CTModels.build(pre)
The (autonomous) optimal control problem is of the form:

    minimize  J(x, u, v) = ∫ f⁰(x(t), u(t), v) dt, over [0.0, 1.0]

    subject to

        ẋ(t) = f(x(t), u(t), v), t in [0.0, 1.0] a.e.,

        ψ₋ ≤ ψ(x(t), u(t), v) ≤ ψ₊, 
        ϕ₋ ≤ ϕ(x(0.0), x(1.0), v) ≤ ϕ₊, 
        x₋ ≤ x(t) ≤ x₊, 
        u₋ ≤ u(t) ≤ u₊, 
        v₋ ≤ v ≤ v₊, 

    where x(t) ∈ R², u(t) ∈ R and v ∈ R².

Reading constraints back

On the built Model, the constraints are grouped in a ConstraintsModel and queried by dimension or by label:

(CTModels.dim_path_constraints_nl(ocp),
 CTModels.dim_boundary_constraints_nl(ocp),
 CTModels.dim_state_constraints_box(ocp),
 CTModels.dim_control_constraints_box(ocp),
 CTModels.dim_variable_constraints_box(ocp))
(1, 2, 1, 1, 2)

Labels and aliases

Before building, constraints live in a ConstraintsDictType keyed by label. Box constraints obey a per-component uniqueness invariant: if several declarations touch the same component, their bounds are intersected and all their labels are kept as aliases. This is what lets constraint and dual resolve any of the original labels to the merged component — see the Duals guide.

One declaration, one label

Always pass an explicit label. It is the stable handle for the constraint across build, solution reconstruction, and dual extraction; auto-generated labels are harder to track in downstream packages.