Skip to content

Validation & warm-start

build_initial_guess always ends with a dimension check against the model. The check can also be invoked on its own with validate_initial_guess, and a guess can be warm-started from a previous Solution.

julia
using CTModels
import CTBase

pre = CTModels.PreModel()
CTModels.variable!(pre, 0)
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)
CTModels.time_dependence!(pre; autonomous=true)
ocp = CTModels.build(pre)

Construct, then validate

initial_guess builds an unvalidated InitialGuess; pass it to validate_initial_guess to check it against the model. This separation lets a caller build once and validate against several models.

julia
ig = CTModels.initial_guess(ocp; state = t -> [0.0, 0.0], control = t -> [0.1])
julia
julia> ig === CTModels.validate_initial_guess(ocp, ig)   # returns the same object when valid
true

Dimension mismatch is rejected

Validation samples the guess and compares its shape to the problem dimensions. A state guess of the wrong length is refused:

julia
julia> CTModels.build_initial_guess(ocp, (state = t -> [0.0],))   # 1 ≠ 2 states
IncorrectArgument  _validate_initial_guess, validation.jl:50

│  Initial state function returns incompatible dimension

│  Got       vector of length 1
│  Expected  vector of length 2

│  Context   state function validation
│  Hint      Ensure the state function returns a vector with 2 elements
└─

Warm-start from a solution

Passing a Solution reuses its trajectories as the new guess — the standard warm-start. The state/control dimensions must match between the solution's model and the target model.

julia
# A (fabricated) solution of the same problem
N = 51
T = collect(range(0.0, 1.0; length=N))
X = hcat(cos.(T), -sin.(T))
U = reshape(-cos.(T), N, 1)
P = zeros(N, 2)
sol = CTModels.build_solution(ocp, T, X, U, Float64[], P;
    objective=0.5, iterations=10, constraints_violation=1e-9,
    message="ok", status=:optimal, successful=true)

# Reuse it as an initial guess
warm = CTModels.build_initial_guess(ocp, sol)
julia
julia> warm.state(0.5)
2-element Vector{Float64}:
  0.8775825618903728
 -0.479425538604203

The warm-started guess is a validated InitialGuess, ready to seed the next solve.