Solver Interface
Solvers are currently organized into the following type tree:
The solver options type is a lightweight container for all of the options the user can specify, such as tolerance values, printing verbosity (highly recommended), Boolean flags, etc. We highly suggest using Parameters.jl to create this and easily specify the default options. All solver options should be mutable (e.g. mutable struct NewSolverOptions{T} <: AbstractSolverOptions{T}
)
The solver type, on the other hand, is meant to contain all of the variables needed for the solve, including the model, objective, constraints, and other information originally in the Problem
. This information is "duplicated" in the solver since oftentimes the solver with perform modifications to these when setting up the solve. For example, the AugmentedLagrangianSolver
creates an ALObjective
and uses that as it's objective instead. Similarly, ALTRO may convert the model to an InfeasibleModel
to leverage an initial state trajectory. Therefore, once the solver is created, the problem is solved by simply calling solve!(solver)
, which then runs the optimization.
Defining a New Solver
For creating a new solver, e.g. NewSolver
, the user must define two new types:
NewSolverOptions{T} <: AbstractSolverOptions{T}
NewSolver{T} <: AbstractSolver{T}
Below we list the methods needed to implement the different solver interfaces, along with a list of inherited methods (that can be overloaded as needed). The docstrings for these functions are listed in more detail down below.
Unconstrained Solvers
Needed methods:
model = get_model(::NewSolver)
obj = get_objective(::NewSolver)
Z = get_trajectory(::NewSolver)
n,m,N = Base.size(::NewSolver)
x0 = get_initial_state(::NewSolver)
solve!(::NewSolver)
Needed fields (these will likely be replaced by getters in the near future):
opts
- instance ofAbstractSolverOptions
stats
- mutable struct containing statistics on the solve
Inherited methods:
states(::NewSolver)
controls(::NewSolver)
initial_states!(::NewSolver, X0)
initial_controls!(::NewSolver, U0)
initial_trajectory!(::NewSolver, Z::Traj)
cost(::NewSolver, Z=get_trajectory(::NewSolver))
rollout!(::NewSolver)
Constrained Solvers
Needed methods (in addition to those for Unconstrained Solvers):
get_constraints(::NewSolver)
Inherited methods (in addition to those for Unconstrained Solvers):
num_constraints(::NewSolver)
max_violation(::NewSolver, Z=get_trajectory(::NewSolver))
update_constraints!(::NewSolver, Z=get_trajectory(::NewSolver))
update_active_set!(::NewSolver, Z=get_trajectory(::NewSolver))
Direct Solvers
Currently, direct solvers solve the problem by forming a large, sparse matrix by concatenating the states and controls for all time steps. They need to generate a list of indices that map the constraints to their location in the concatenated array of constraint values, which can be done using gen_con_inds
. This must be stored as the con_inds
field in the DirectSolver
(this will be replaced by a getter method in the near future). With this, all DirectSolver
s inherit the following methods for copying values to and from the large, concatenated vectors and matrices:
copy_constraints!(d, ::DirectSolver)
copy_active_set!(a, ::DirectSolver)
copy_jacobians!(D, ::DirectSolver)
Unconstrained Solvers
TrajectoryOptimization.AbstractSolver
— Typeabstract type AbstractSolver <: MathOptInterface.AbstractNLPEvaluator
Abstract solver for trajectory optimization problems
Any type that inherits from AbstractSolver
must define the following methods:
model = get_model(::AbstractSolver)::AbstractModel
obj = get_objective(::AbstractSolver)::AbstractObjective
Z = get_trajectory(::AbstractSolver)::Traj
n,m,N = Base.size(::AbstractSolver)
x0 = get_initial_state(::AbstractSolver)::SVector
solve!(::AbstractSolver)
TrajectoryOptimization.UnconstrainedSolver
— Typeabstract type UnconstrainedSolver <: TrajectoryOptimization.AbstractSolver{T}
Unconstrained optimization solver. Will ignore any constraints in the problem
TrajectoryOptimization.states
— Functionstates(::Problem)
states(::AbstractSolver)
states(::Traj)
Get the state trajectory
TrajectoryOptimization.controls
— Functioncontrols(::Problem)
controls(::AbstractSolver)
controls(::Traj)
Get the control trajectory
TrajectoryOptimization.initial_trajectory!
— Functioninitial_trajectory!(::Problem, Z)
initial_trajectory!(::AbstractSolver, Z)
Copy the trajectory
TrajectoryOptimization.initial_states!
— Functioninitial_states!(::Union{Problem,AbstractSolver}, X0::Vector{<:AbstractVector})
initial_states!(::Union{Problem,AbstractSolver}, X0::AbstractMatrix)
Copy the state trajectory
TrajectoryOptimization.initial_controls!
— Functioninitial_controls!(::Union{Problem,AbstractSolver}, U0::Vector{<:AbstractVector})
initial_controls!(::Union{Problem,AbstractSolver}, U0::AbstractMatrx)
Copy the control trajectory
TrajectoryOptimization.cost
— Functioncost(obj::Objective, Z::Traj)::Float64
cost(obj::Objective, dyn_con::DynamicsConstraint{Q}, Z::Traj)
Evaluate the cost for a trajectory. Calculate the cost gradient for an entire trajectory. If a dynamics constraint is given, use the appropriate integration rule, if defined.
cost(::Problem)
cost(::AbstractSolver)
Compute the cost for the current trajectory
TrajectoryOptimization.cost_expansion!
— Functioncost_expansion!(E::Any, obj::Objective, Z::AbstractArray{#s63,1} where #s63<:KnotPoint)
Expand cost for entire trajectory
Constrained Solvers
TrajectoryOptimization.ConstrainedSolver
— Typeabstract type ConstrainedSolver <: TrajectoryOptimization.AbstractSolver{T}
Abstract solver for constrained trajectory optimization problems
In addition to the methods required for AbstractSolver
, all ConstrainedSolver
s must define the following method
get_constraints(::ConstrainedSolver)::ConstrainSet
TrajectoryOptimization.update_constraints!
— Functionupdate_constraints!(solver)
update_constraints!(solver, Z)
Calculate all the constraint values given the trajectory Z
TrajectoryOptimization.update_active_set!
— Functionupdate_active_set!(conSet::ConstraintSet, Z::Traj, ::Val{tol})
Compute the active set for the current constraint values, with tolerance tol. Uses a value type to avoid an allocation down the line.
TrajectoryOptimization.constraint_jacobian!
— Functionconstraint_jacobian!(solver)
constraint_jacobian!(solver, Z)
Calculate all the constraint Jacobians given the trajectory Z
Direct Solvers
Direct solvers often perform similar operations, so the following methods are provided that should work with any direct solver
TrajectoryOptimization.DirectSolver
— Typeabstract type DirectSolver <: ConstrainedSolver{T}
Solve the trajectory optimization problem by computing search directions using the joint state vector, often solving the KKT system directly.
TrajectoryOptimization.remove_bounds!
— Functionremove_bounds!(conSet)
Remove bounds constraints from constraint set
TrajectoryOptimization.remove_constraint_type!
— Functionremove_constraint_type!(conSet, ?)
Remove a type of constraint from constraint set
TrajectoryOptimization.get_bounds
— Functionget_bounds(conSet)
Remove bounds from constraint set and return them as vectors
TrajectoryOptimization.add_dynamics_constraints!
— Functionadd_dynamics_constraints!(prob::Problem)
Add dynamics constraints to the constraint set
TrajectoryOptimization.gen_con_inds
— Functiongen_con_inds(conSet)
gen_con_inds(conSet, structure)
Generate the indices into the concatenated constraint vector for each constraint. Determines the bandedness of the Jacobian
TrajectoryOptimization.constraint_jacobian_structure
— Functionconstraint_jacobian_structure(solver)
constraint_jacobian_structure(solver, structure)
Get the constraint Jacobian structure as a sparse array, and fill in the linear indices used for filling a vector of the non-zero elements of the Jacobian
TrajectoryOptimization.copy_constraints!
— FunctionCopy constraints to a single concatenated vector
TrajectoryOptimization.copy_active_set!
— FunctionCopy active set to a single concatenated vector
TrajectoryOptimization.copy_jacobian!
— FunctionCopy constraint Jacobians to given indices in a sparse array Dispatches on bandedness of the constraint
Copy constraint Jacobians to linear indices of a vector
TrajectoryOptimization.copy_jacobians!
— FunctionCopy all constraint Jacobians to a sparse matrix
Copy all constraint Jacobians to linear indices of a vector
The solver must also contain the following fields:
opts
: Solver options for the solver (e.g.opts::NewSolverOptions
)stats::Dict{Symbol,Any}
: Dictionary containing pertinent statistics for the solve, such as run time, final max constraint violation, final cost, optimality criteria, number of iterations, etc.