Linear Quadratic Problem

In our previous blog post, we discussed using Riccati recursion to solve linear-quadratic (LQ) problems. As an alternative, the KKT system associated with an LQ problem can be solved directly using a sparse linear solver, such as QDLDL. We first write the KKT system $(N = 3)$ as

\[\begin{aligned} \mathcal{A} z &= b \\ \left[ \begin{array}{ccccccccc} R_0 & B_0^\top \\ B_0 & & -I\\ \hline & -I & Q_1 & S_1^\top & A_1^\top \\ & & S_1 & R_1 & B_1^\top \\ & & A_1 & B_1 & & -I \\ \hline & & & & -I & Q_2 & S_2^\top & A_2^\top \\ & & & & & S_2 & R_2 & B_2^\top \\ & & & & & A_2 & B_2 & & -I \\ \hline & & & & & & & -I & P_3 \end{array} \right] \left[ \begin{array}{c} u_0 \\ \lambda_1 \\ \hline x_1 \\ u_1 \\ \lambda_2 \\ \hline x_2 \\ u_2 \\ \lambda_3 \\ \hline x_3 \end{array} \right] &= \left[ \begin{array}{c} -r_0 - S_0 x_0 \\ -A x_0 - c_0 \\ \hline -q_1 \\ -r_1 \\ -c_1 \\ \hline -q_2 \\ -r_2 \\ -c_2 \\ \hline -p_3 \end{array} \right] \end{aligned}.\]

The KKT matrix $\mathcal{A}$ exhibits sparsity, with a significant number of zero elements. For improved computational performance, we use a sparse linear solver to solve the KKT system above. Let us now take a closer look at how sparse matrices are represented.

Sparse matrix

As a tutorial example, we consider the following matrix

\[\begin{bmatrix} 0 & 3 & 0 & 0 & 0 \\ 22 & 0 & 0 & 0 & 17 \\ 7 & 5 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 14 & 0 & 8 \end{bmatrix}.\]

We store the nonzero elements in an array using column-major order.

const double x[] = { 22.0, 7.0, 3.0, 5.0, 14.0, 1.0, 17.0, 8.0 };

Two additional index arrays are used to indicate the positions of the nonzero elements in the original matrix.

  • row indices: stores the row indices of the nonzero elements.
  • column pointers: indicates where each column starts in the array x. i[k+1] - i[k] implies the number of the nonzero elements in a column.
const int p[] = { 1, 2, 0, 2, 4, 2, 1, 4 };
const int i[] = { 0, 2, 4, 5, 6, 8};

We pack these variables using a struct.

struct CscMatrix {
    int     m;     // number of rows
    int     n;     // number of columns 
    int    *p;     // column pointers (size n+1)
    int    *i;     // row indices 
    double *x;     // values
    int     nzmax; // maximum number of nonzero entries
};

QDLDL

We now have the data $(Q, R, S, q, r, A, B, c, N)$ required for an LQ problem. Using these dense matrices and vectors, we construct the sparse KKT matrix $\mathcal{A}$ and the corresponding right-hand side vector $b$. Then, we call the functions in QDLDL to solve the sparse linear system. For more information, please refer to the repository.