Practical NLP Implementation of LGR Optimal Control
The previous sessions developed the mathematical foundations of Legendre pseudospectral transcription and costate recovery. This section explains how to implement an LGR transcription inside a general nonlinear programming solver.
The central practical issue is that an optimal-control transcription is naturally expressed using matrices of state and control values, whereas an NLP solver expects a single decision vector. Therefore, the implementation must provide a systematic mapping between:
the continuous optimal-control problem;
the LGR-discretized state and control arrays;
the NLP decision vector;
the scalar objective function;
the stacked equality and inequality constraints; and
the initial guess supplied to the solver.
Standard Nonlinear Programming Form¶
A general nonlinear program may be written as
Here:
is the NLP decision vector;
is the scalar objective;
contains equality constraints;
contains inequality constraints; and
and are variable bounds.
For implementation, the equality and inequality constraints may be stacked into one vector,
with corresponding lower and upper bounds.
LGR-Discretized Bolza Problem¶
Consider a Bolza problem on :
After mapping the interval to and applying LGR collocation, the objective is approximated by
The defect equations are
Endpoint constraints are
Path constraints are imposed at the LGR points:
Dimensions of the Discrete Variables¶
Let:
be the number of states;
be the number of controls;
be the number of LGR collocation points.
Then
because the state is stored at the LGR points plus the noncollocated terminal point.
Similarly,
because the control is represented only at the collocation points.
If both and are optimization variables, the total number of NLP variables is
Packing the NLP Decision Vector¶
The NLP solver expects a single column vector. A standard column-major packing is
The state block is
and the control block is
The time variables occupy the final two entries.
Index Ranges¶
The state portion is
The control portion is
The time variables are
and
Unpacking and Reshaping¶
Inside the objective and constraint functions, reshape the vector blocks back into matrices:
and
In MATLAB:
X = reshape(zx, N+1, nx);
U = reshape(zu, N, nu);The state values at the collocation points are
The endpoint state values are
Physical Time at the LGR Points¶
The affine time transformation gives
In vector form,
These time values are used to evaluate the dynamics, Lagrange integrand, and path constraints.
Evaluating the Dynamics¶
Evaluate the dynamics at the LGR points:
Stacking these rows produces
The dynamics evaluation is problem dependent.
A MATLAB-style implementation is:
Xlgr = X(1:N,:);
F = dynamics(Xlgr, U, t);Defect Constraints¶
The LGR defect matrix is
Its dimension is
An NLP solver requires a vector, so vectorize the matrix:
In MATLAB:
defect = D*X - 0.5*(tf-t0)*F;
defect_col = defect(:);The expression
A(:)stacks the columns of a matrix into one column vector.
Evaluating the Objective¶
Separate the objective into Mayer and Lagrange terms.
Mayer Term¶
Evaluate
Lagrange Term¶
Evaluate the integrand at all LGR points:
Let
Then
The total cost is
A MATLAB-style implementation is:
mayer = endpointCost(X(1,:), t0, X(end,:), tf);
ell = runningCost(X(1:N,:), U, t);
cost = mayer + 0.5*(tf-t0)*(w.'*ell);Boundary Constraints¶
Suppose the endpoint constraints are described by
Evaluate
These constraints are usually already returned as a vector and do not require additional reshaping.
Path Constraints¶
Suppose there are path-constraint functions. Evaluating them at all LGR points produces
The matrix is vectorized as
In MATLAB:
C = pathConstraints(X(1:N,:), U, t);
Ccol = C(:);Stacking the Complete Constraint Vector¶
The full NLP constraint vector can be assembled as
The corresponding lower and upper bounds are stacked in the same order:
and
This arrangement is convenient for solvers such as SNOPT that permit general lower and upper bounds on constraint functions.
Objective Function Template¶
A practical MATLAB-style objective function is:
function J = objective(z, data)
N = data.N;
nx = data.nx;
nu = data.nu;
w = data.w;
tau = data.tau;
nX = (N+1)*nx;
nU = N*nu;
zx = z(1:nX);
zu = z(nX+1:nX+nU);
t0 = z(nX+nU+1);
tf = z(nX+nU+2);
X = reshape(zx, N+1, nx);
U = reshape(zu, N, nu);
t = 0.5*(tf-t0)*tau + 0.5*(tf+t0);
JM = endpointCost(X(1,:), t0, X(end,:), tf);
L = runningCost(X(1:N,:), U, t);
J = JM + 0.5*(tf-t0)*(w.'*L);
endConstraint Function Template¶
A practical constraint function is:
function K = constraints(z, data)
N = data.N;
nx = data.nx;
nu = data.nu;
D = data.D;
tau = data.tau;
nX = (N+1)*nx;
nU = N*nu;
zx = z(1:nX);
zu = z(nX+1:nX+nU);
t0 = z(nX+nU+1);
tf = z(nX+nU+2);
X = reshape(zx, N+1, nx);
U = reshape(zu, N, nu);
Xlgr = X(1:N,:);
t = 0.5*(tf-t0)*tau + 0.5*(tf+t0);
F = dynamics(Xlgr, U, t);
defect = D*X - 0.5*(tf-t0)*F;
defect_col = defect(:);
BC = endpointConstraints(X(1,:), t0, X(end,:), tf);
C = pathConstraints(Xlgr, U, t);
Ccol = C(:);
K = [defect_col; Ccol; BC];
endSolver-Specific Treatment of Inequalities¶
Different NLP solvers accept constraints in different formats.
Bounded Constraint Form¶
Some solvers allow
This is natural for path constraints of the form
MATLAB fmincon Form¶
The standard fmincon interface uses
A two-sided bound
must be converted to
and
Thus:
c = [Ccol - Cmax_col;
Cmin_col - Ccol];
ceq = [defect_col;
BC];Constructing the Initial Guess¶
An NLP solver requires an initial guess
A simple and widely used procedure is:
guess the initial and final state;
linearly interpolate the state over the LGR grid plus the terminal point;
guess the control at the first and last collocation points;
linearly interpolate the control over the LGR points;
guess and ;
vectorize and stack all quantities.
State Guess¶
Given guesses
construct
by straight-line interpolation at
Control Guess¶
Given endpoint or representative control guesses, interpolate over the LGR points.
Packing the Guess¶
Finally,
A MATLAB-style implementation is:
Xguess = interp1([-1; 1], [X0guess; Xfguess], ...
[tau; 1], 'linear');
Uguess = interp1([-1; 1], [U0guess; Ufguess], ...
tau, 'linear');
zguess = [Xguess(:);
Uguess(:);
t0guess;
tfguess];Variable and Constraint Bounds¶
Bounds must be packed in exactly the same order as the decision vector:
and similarly for .
The defect constraints have zero lower and upper bounds:
Endpoint equality constraints also receive identical lower and upper bounds.
Recovering the Solution¶
After the NLP solver returns
unpack it using the same indexing convention:
Then reconstruct the physical time vector and inspect:
state trajectories;
control trajectories;
endpoint residuals;
defect residuals;
path-constraint residuals;
solver optimality measures;
KKT multipliers and costate estimates.
Common Implementation Errors¶
Using inconsistent row-major and column-major packing.
Reshaping the state to instead of .
Evaluating the control or dynamics at the noncollocated terminal point.
Omitting the time-scaling factor .
Using instead of as the terminal state.
Stacking constraint values in an order inconsistent with their bounds.
Using the wrong sign convention for the defects.
Providing a differentiation matrix of the wrong size.
Forgetting to remove the row associated with a noncollocated point when a general differentiation routine returns a square matrix.
Starting directly with a complex research problem before validating the implementation on a known problem.
Computing LGR Points, Weights, and Differentiation Matrices¶
The implementation requires:
the LGR nodes ;
the quadrature weights ;
the rectangular differentiation matrix .
A general differentiation routine may return a square matrix on the grid
For left-LGR collocation, the final point is noncollocated. Therefore, the row corresponding to must be removed so that
The columns must not be removed because all state values participate in the interpolation.
Recommended Validation Workflow¶
A robust development sequence is:
Implement a scalar problem with a known analytical solution.
Verify variable packing and unpacking.
Verify dimensions of every matrix and vector.
Check the objective independently.
Evaluate the constraints at a known feasible trajectory.
Solve the problem at low polynomial order.
Increase and verify convergence.
Compare the state and control against the known solution.
Recover and compare the costate when available.
Only then adapt the implementation to the research problem.
Minimal End-to-End Workflow¶
The complete computational process is:
Choose .
Compute LGR points, weights, and .
Define state, control, and time bounds.
Construct .
Call the NLP solver.
Inside the objective function:
unpack ;
reshape and ;
compute physical time;
evaluate the Mayer and Lagrange costs.
Inside the constraint function:
unpack and reshape;
evaluate the dynamics;
form the defect matrix;
vectorize defects;
evaluate and vectorize path constraints;
evaluate endpoint constraints;
stack all constraints.
Recover the optimized trajectories.
Check residuals and convergence.
Extract multipliers and estimate costates if needed.
Summary¶
The NLP solver operates on one decision vector, not state and control matrices.
The discrete state and control arrays must be vectorized and stacked.
Inside each NLP function, the vector is unpacked and reshaped.
The LGR defect matrix is
Defect and path-constraint matrices must be converted into column vectors.
The objective consists of a Mayer term plus an LGR quadrature approximation.
Endpoint and path constraints are stacked with the defects.
A straight-line state and control profile is often sufficient as an initial guess for simple problems.
Solver interfaces differ mainly in how they accept equality and inequality constraints.
Implementation should first be validated on simple benchmark problems.