QPALM 1.1.1
Proximal Augmented Lagrangian method for Quadratic Programs
solver_interface.c
Go to the documentation of this file.
1/**
2 * @file solver_interface.c
3 * @author Ben Hermans
4 * @brief Interface and wrapper to matrix/factorization (ladel) functions
5 * @details This file includes all calls to ladel functions apart from scaling in scaling.c and memory
6 * allocation/deallocation in the main functions in qpalm.c. It includes all matrix operations, such as
7 * matrix vector products, row- and columnwise norms, cholesky factorizations, factorization updates and
8 * solving the linear system.
9 *
10 */
11
12#include "lin_alg.h"
13#include "solver_interface.h"
14#include <stdio.h>
15
16#include "ladel.h"
17
19{
21 {
22 ladel_int col, index, nnz_kkt, nnz_schur, n = work->data->n, m = work->data->m;
23 solver_sparse *Q = work->data->Q, *A = work->data->A;
24 /* Compute nnz in KKT */
25 nnz_kkt = Q->nzmax + n + A->nzmax + m;
26 /* Compensate for diagonal entries present in Q */
27 for (col = 1; col <= n; col++)
28 {
29 index = Q->p[col]-1;
30 if (index >= 0 && Q->i[index] == col-1) nnz_kkt--;
31 }
32
33 /* Compute nnz in SCHUR (approximately) */
34 nnz_schur = nnz_kkt - A->nzmax - m; /* Q + diagonal */
35
36 /* (over)estimate the nnz introduced by AtA */
37 solver_sparse *At;
38 c->array_int_ncol1 = work->index_L; /* Avoid allocating full workspace */
39 At = ladel_transpose(work->data->A, FALSE, c);
40 c->array_int_ncol1 = NULL;
41
42 ladel_int Atnz_col, Atnz_col_max = 0;
43 for (col = 0; col < m; col++) Atnz_col_max = c_max(At->p[col+1] - At->p[col], Atnz_col_max);
44 for (col = 0; col < m; col++)
45 {
46 Atnz_col = At->p[col+1] - At->p[col];
47 if (Atnz_col + Atnz_col_max <= n)
48 nnz_schur += (Atnz_col*(Atnz_col-1))/2;
49 else
50 nnz_schur += (Atnz_col*(n - Atnz_col_max) - ((n - Atnz_col_max)*(n - Atnz_col_max + 1)) / 2);
51 }
52 if (2 * Atnz_col_max > n) nnz_schur += (Atnz_col_max*(Atnz_col_max - 1)) / 2 - (Atnz_col_max*(n - Atnz_col_max) - ((n - Atnz_col_max)*(n - Atnz_col_max + 1)) / 2);
53 nnz_schur = c_max(c_min(nnz_schur, (n*(n-1))/2), 1);
54 /* NB: If nnz(KKT) == nnz(Q+AtA) << n^2, then KKT will perform a bit better due to the
55 ordering. */;
56 At = ladel_sparse_free(At);
57
58 /* Switching criterion */
59 if ((nnz_kkt*nnz_kkt)/(nnz_schur*nnz_schur)*n/(n+m) < 2)
61 else
63
64 } else
65 {
67 }
68
69}
70
71
72
74{
75 (void)c;
76 ladel_int n = A->ncol;
77 if (x!=y) {
78 if (A->symmetry == UNSYMMETRIC)
79 ladel_matvec(A, x, y, TRUE);
80 else
81 ladel_symmetric_matvec(A, x, y, TRUE);
82 } else {
83 ladel_double* x2 = ladel_malloc(n, sizeof(c_float));
84 ladel_double_vector_copy(x, n, x2);
85 if (A->symmetry == UNSYMMETRIC)
86 ladel_matvec(A, x2, y, TRUE);
87 else
88 ladel_symmetric_matvec(A, x2, y, TRUE);
89 ladel_free(x2);
90 }
91}
92
94{
95 (void)c;
96 ladel_int m = A->nrow;
97 if (x!=y) {
98 if (A->symmetry == UNSYMMETRIC)
99 ladel_tpose_matvec(A, x, y, TRUE);
100 else
101 ladel_symmetric_matvec(A, x, y, TRUE);
102 } else {
103 ladel_double* x2 = ladel_malloc(m, sizeof(c_float));
104 ladel_double_vector_copy(x, m, x2);
105 if (A->symmetry == UNSYMMETRIC)
106 ladel_tpose_matvec(A, x2, y, TRUE);
107 else
108 ladel_symmetric_matvec(A, x2, y, TRUE);
109 ladel_free(x2);
110 }
111}
112
113// TODO: incorporate the factorizations here also, and write a version of FACTORIZE_KKT using cholmod
115{
116 solver_sparse *Q = work->data->Q, *kkt = work->solver->kkt, *kkt_full = work->solver->kkt_full, *At = work->solver->At;
117 ladel_int col, index, index_kkt, n = work->data->n, m = work->data->m, Qnz = Q->nzmax;
118 c_float *sigma_inv = work->sigma_inv, *first_elem_A = work->solver->first_elem_A;
119 c_int *first_row_A = work->solver->first_row_A;
120 /* copy Q */
121 for (col = 0; col < n; col++)
122 {
123 kkt->p[col] = kkt_full->p[col] = Q->p[col];
124 kkt->nz[col] = Q->p[col+1] - Q->p[col];
125 }
126 kkt->p[col] = kkt_full->p[col] = Q->p[col];
127 for (index = 0; index < Qnz; index++)
128 {
129 kkt->i[index] = kkt_full->i[index] = Q->i[index];
130 kkt->x[index] = kkt_full->x[index] = Q->x[index];
131 }
132
133 /* copy [At; -\Sigma^{-1}] */
134 index_kkt = Qnz;
135 for (; col < m+n; col++)
136 {
137 kkt_full->i[index_kkt] = first_row_A[col-n] = At->i[At->p[col-n]];
138 kkt_full->x[index_kkt] = first_elem_A[col-n] = At->x[At->p[col-n]];
139
140 if (work->solver->active_constraints[col-n])
141 {
142 kkt->nz[col] = At->p[col-n+1] - At->p[col-n] + 1;
143 kkt->i[index_kkt] = At->i[At->p[col-n]];
144 kkt->x[index_kkt] = At->x[At->p[col-n]];
145 }
146 else
147 {
148 kkt->nz[col] = 1;
149 kkt->i[index_kkt] = col;
150 kkt->x[index_kkt] = 1;
151 }
152
153 if (At->p[col-n+1]-At->p[col-n] != 0) index_kkt++;
154
155 for (index = At->p[col-n]+1; index < At->p[col-n+1]; index++)
156 {
157 kkt->i[index_kkt] = kkt_full->i[index_kkt] = At->i[index];
158 kkt->x[index_kkt] = kkt_full->x[index_kkt] = At->x[index];
159 index_kkt++;
160 }
161
162 kkt->i[index_kkt] = kkt_full->i[index_kkt] = col;
163 kkt->x[index_kkt] = kkt_full->x[index_kkt] = -sigma_inv[col-n];
164 if (At->p[col-n+1]-At->p[col-n] == 0) kkt->x[index_kkt] = 1;
165 index_kkt++;
166
167 kkt->p[col+1] = kkt_full->p[col+1] = Qnz + At->p[col+1-n] + 1 + col - n;
168 }
169}
170
171
173{
174 solver_sparse *kkt = work->solver->kkt, *At = work->solver->At;
175 ladel_int col, n = work->data->n, m = work->data->m;
176 ladel_int *first_row_A = work->solver->first_row_A;
177 ladel_double *sigma_inv = work->sigma_inv, *first_elem_A = work->solver->first_elem_A;
178
179 for (col = n; col < n+m; col++)
180 {
181 if (work->solver->active_constraints[col-n])
182 {
183 kkt->nz[col] = At->p[col-n+1] - At->p[col-n] + 1;
184 kkt->i[kkt->p[col]] = first_row_A[col-n];
185 kkt->x[kkt->p[col]] = first_elem_A[col-n];
186 kkt->x[kkt->p[col+1]-1] = -sigma_inv[col-n]; /* row index should be correct already */
187 kkt->i[kkt->p[col+1]-1] = col;
188 } else
189 {
190 kkt->nz[col] = 1;
191 kkt->i[kkt->p[col]] = col;
192 kkt->x[kkt->p[col]] = 1;
193 }
194 }
195}
196
198{
199 solver_sparse *kkt = work->solver->kkt, *At = work->solver->At;
200 ladel_int col, index, n = work->data->n;
201 ladel_int *first_row_A = work->solver->first_row_A;
202 ladel_double *sigma_inv = work->sigma_inv, *first_elem_A = work->solver->first_elem_A;
203
204 for (index = 0; index < work->solver->nb_enter; index++)
205 {
206 col = work->solver->enter[index] + n;
207 kkt->nz[col] = At->p[col-n+1] - At->p[col-n] + 1;
208 kkt->i[kkt->p[col]] = first_row_A[col-n];
209 kkt->x[kkt->p[col]] = first_elem_A[col-n];
210 kkt->x[kkt->p[col+1]-1] = -sigma_inv[col-n]; /* row index should be correct already */
211 ladel_row_add(work->solver->LD, work->solver->sym, col, kkt, col, -sigma_inv[col-n], c);
212 }
213}
214
216{
217 ladel_int col, index, n = work->data->n;
218 ladel_double *sigma_inv = work->sigma_inv;
219 solver_sparse *kkt = work->solver->kkt;
220
221 for (index = 0; index < work->solver->nb_leave; index++)
222 {
223 col = work->solver->leave[index] + n;
224 ladel_row_del(work->solver->LD, work->solver->sym, col, c);
225
226 /* no need to update the kkt system here, except for iterative refinement */
227 kkt->nz[col] = 1;
228 kkt->i[kkt->p[col]] = col;
229 kkt->x[kkt->p[col]] = -sigma_inv[col-n];
230 }
231}
232
234{
235 c_int n = work->data->n, m = work->data->m;
236 prea_vec_copy(work->dphi, work->solver->rhs_kkt, n);
237 vec_self_mult_scalar(work->solver->rhs_kkt, -1, n);
238 vec_set_scalar(work->solver->rhs_kkt + n, 0, m);
239
240 ladel_dense_solve(work->solver->LD, work->solver->rhs_kkt, work->solver->sol_kkt, c);
241 prea_vec_copy(work->solver->sol_kkt, work->d, n);
242}
243
244
245
247 ladel_diag d;
248 d.diag_elem = 1.0/work->gamma;
249 if (work->settings->proximal) d.diag_size = work->data->n;
250 else d.diag_size = 0;
251
252 if (work->solver->first_factorization)
253 {
254 work->solver->LD = ladel_factor_free(work->solver->LD);
255 /* Compute the pattern of Q+A^T*A to allocate L */
256 solver_sparse *AtA, *QAtA;
257 AtA = ladel_mat_mat_transpose_pattern(work->solver->At_sqrt_sigma, work->data->A, c);
258 QAtA = ladel_add_matrices_pattern(work->data->Q, AtA, c);
259 QAtA->symmetry = UPPER;
260
261 /* TODO: consider SCHUR method also with ordering */
262 ladel_factorize_advanced_with_diag(M, d, work->solver->sym, NO_ORDERING, &work->solver->LD, QAtA, c);
263
264 ladel_sparse_free(AtA);
265 ladel_sparse_free(QAtA);
267 }
268 else
269 {
270 ladel_factorize_with_prior_basis_with_diag(M, d, work->solver->sym, work->solver->LD, c);
271 }
272}
273
275 solver_sparse *AtsigmaA;
276 solver_sparse *QAtsigmaA;
277 size_t nb_active = 0;
278 for (size_t i = 0; i < work->data->m; i++) {
279 if (work->solver->active_constraints[i]){
280 work->solver->enter[nb_active] = (c_int)i;
281 nb_active++;
282 }
283 }
284 solver_sparse *At_sqrt_sigma = ladel_column_submatrix(work->solver->At_sqrt_sigma, work->solver->enter, nb_active);
285 solver_sparse *A_sqrt_sigma = ladel_transpose(At_sqrt_sigma, TRUE, c);
286 AtsigmaA = ladel_mat_mat_transpose(At_sqrt_sigma, A_sqrt_sigma, c);
287 QAtsigmaA = ladel_add_matrices(1.0, work->data->Q, 1.0, AtsigmaA, c);
288 QAtsigmaA->symmetry = UPPER;
289 ldlchol(QAtsigmaA, work, c);
290
291 AtsigmaA = ladel_sparse_free(AtsigmaA);
292 QAtsigmaA = ladel_sparse_free(QAtsigmaA);
293 At_sqrt_sigma = ladel_sparse_free(At_sqrt_sigma);
294 A_sqrt_sigma = ladel_sparse_free(A_sqrt_sigma);
295}
296
298 ladel_int index;
299 for (index = 0; index < work->solver->nb_enter; index++)
300 {
301 ladel_rank1_update(work->solver->LD, work->solver->sym, work->solver->At_sqrt_sigma,
302 work->solver->enter[index], 1.0, UPDATE, c);
303 }
304}
305
307 ladel_int index;
308 for (index = 0; index < work->solver->nb_leave; index++)
309 {
310 ladel_rank1_update(work->solver->LD, work->solver->sym, work->solver->At_sqrt_sigma,
311 work->solver->leave[index], 1.0, DOWNDATE, c);
312 }
313}
314
316 c_int *sigma_changed = work->solver->enter;
317 c_int row;
318 size_t k, nb_sigma_changed = (size_t) work->nb_sigma_changed;
319
320 c_float *At_scalex = work->solver->At_scale;
321
322 for (k = 0; k < nb_sigma_changed; k++) {
323 row = sigma_changed[k];
324 At_scalex[row] = At_scalex[row]*At_scalex[row];
326 At_scalex[row] = c_sqrt(1-1/At_scalex[row]);
327 }
328
330 {
331 ladel_sparse_matrix *W = ladel_sparse_alloc(work->data->n + work->data->m, 1, 1, UNSYMMETRIC, TRUE, FALSE);
332 W->p[0] = 0;
333 W->p[1] = 1;
334 W->x[0] = 1.0;
335 c_int row;
336 c_float factor;
337 for (k = 0; k < nb_sigma_changed; k++)
338 {
339 row = sigma_changed[k];
340 W->i[0] = (work->solver->LD->pinv) ? work->solver->LD->pinv[row] : row;
341 // ladel_print("Row: %d\n", row);
342 factor = work->sigma_inv[row]*(At_scalex[row] - 1.0);
343 // ladel_print("Factor: %f\n", factor);
344 ladel_rank1_update(work->solver->LD, work->solver->sym, W, 0, factor, UPDATE, c);
345 }
346 ladel_sparse_free(W);
347 work->solver->reset_newton = TRUE;
348 } else
349 {
350 for (k = 0; k < nb_sigma_changed; k++)
351 {
352 ladel_rank1_update(work->solver->LD, work->solver->sym, work->solver->At_sqrt_sigma,
353 sigma_changed[k], At_scalex[sigma_changed[k]], UPDATE, c);
354 }
355 }
356
357}
358
360 //set -dphi
361 prea_vec_copy(work->dphi, work->neg_dphi, work->data->n);
362 vec_self_mult_scalar(work->neg_dphi, -1, work->data->n);
363 ladel_dense_solve(work->solver->LD, work->neg_dphi, work->d, c);
364}
365
#define FACTORIZE_SCHUR
factorize the Schur complement
Definition: constants.h:107
#define TRUE
Definition: constants.h:18
#define FALSE
Definition: constants.h:19
#define FACTORIZE_KKT_OR_SCHUR
select automatically between kkt system and schur complement
Definition: constants.h:108
#define FACTORIZE_KKT
factorize the kkt system
Definition: constants.h:106
#define c_sqrt
square root
Definition: global_opts.h:109
ladel_int c_int
type for integer numbers
Definition: global_opts.h:42
ladel_double c_float
type for floating point numbers
Definition: global_opts.h:41
#define c_max(a, b)
maximum of two values
Definition: global_opts.h:92
#define c_min(a, b)
minimum of two values
Definition: global_opts.h:96
void vec_set_scalar(c_float *a, c_float sc, size_t n)
Fill float vector with a scalar value.
Definition: lin_alg.c:40
void vec_self_mult_scalar(c_float *a, c_float sc, size_t n)
Mulitply vector with a constant scale factor.
Definition: lin_alg.c:56
void prea_vec_copy(const c_float *a, c_float *b, size_t n)
Copy vector a into preallocated vector b.
Definition: lin_alg.c:24
Linear algebra with vectors.
void ldlsolveLD_neg_dphi(QPALMWorkspace *work, solver_common *c)
Solve the linear system .
void kkt_update_leaving_constraints(QPALMWorkspace *work, solver_common *c)
Perform a factorization update for the leaving constraints.
void qpalm_form_kkt(QPALMWorkspace *work)
Form the KKT system .
void mat_vec(solver_sparse *A, solver_dense *x, solver_dense *y, solver_common *c)
Matrix-vector multiplication.
void qpalm_set_factorization_method(QPALMWorkspace *work, solver_common *c)
Choose the linear systems solver method based on the problem data sizes.
void ldldowndate_leaving_constraints(QPALMWorkspace *work, solver_common *c)
Downdate the factorization given a set of leaving constraints.
void ldlupdate_sigma_changed(QPALMWorkspace *work, solver_common *c)
Update the factorization given a set of indexes where has been updated.
void kkt_update_entering_constraints(QPALMWorkspace *work, solver_common *c)
Perform a factorization update for the entering constraints.
void ldlcholQAtsigmaA(QPALMWorkspace *work, solver_common *c)
Calculate factorization of , with and the set of active constraints.
void mat_tpose_vec(solver_sparse *A, solver_dense *x, solver_dense *y, solver_common *c)
Matrix-transpose-vector multiplication.
void ldlchol(solver_sparse *M, QPALMWorkspace *work, solver_common *c)
Calculate factorization of a matrix .
void kkt_solve(QPALMWorkspace *work, solver_common *c)
Solve the KKT system .
void qpalm_reform_kkt(QPALMWorkspace *work)
Reform the KKT system (i.e.
void ldlupdate_entering_constraints(QPALMWorkspace *work, solver_common *c)
Update the factorization given a set of entering constraints.
Interface and wrapper to matrix/factorization (ladel) functions.
size_t m
number of constraints m
Definition: types.h:104
size_t n
number of variables n
Definition: types.h:103
solver_sparse * A
sparse linear constraints matrix A (size m x n)
Definition: types.h:106
solver_sparse * Q
sparse quadratic part of the cost Q (size n x n)
Definition: types.h:105
c_int proximal
boolean, use proximal method of multipliers or not
Definition: types.h:131
c_int factorization_method
factorize KKT or Schur complement
Definition: types.h:145
solver_sparse * At_sqrt_sigma
A' * sqrt(sigma)
Definition: types.h:186
c_int * first_row_A
row index of the first element in each column of A'
Definition: types.h:160
c_int * active_constraints
index set of active constraints
Definition: types.h:178
c_int reset_newton
boolean, after sigma is updated perform a new factorization
Definition: types.h:177
c_int * leave
index set of leaving constraints
Definition: types.h:183
solver_dense * rhs_kkt
[-dphi; zeros(m,1)]
Definition: types.h:169
solver_factor * LD
LD factor (part of LDL' factorization)
Definition: types.h:162
c_int factorization_method
factorize KKT or Schur complement
Definition: types.h:156
c_int * enter
index set of entering constraints
Definition: types.h:181
solver_sparse * At
A'.
Definition: types.h:159
c_int first_factorization
boolean, indicate we have not factorized previously
Definition: types.h:176
solver_dense * At_scale
running vector of sqrt(sigma), used to scale At_sqrt_sigma
Definition: types.h:185
solver_dense * sol_kkt
sol_kkt = kkt \ rhs_kkt
Definition: types.h:170
c_float * first_elem_A
value of the first element in each column of A'
Definition: types.h:161
c_int nb_leave
number of leaving constraints
Definition: types.h:184
solver_symbolics * sym
symbolics for kkt (only used in LADEL)
Definition: types.h:163
solver_sparse * kkt_full
KKT matrix if all constraints would be active.
Definition: types.h:158
solver_sparse * kkt
KKT matrix.
Definition: types.h:157
c_int nb_enter
number of entering constraints
Definition: types.h:182
QPALM Workspace.
Definition: types.h:197
c_int nb_sigma_changed
number of sigma-components that changed in an outer iteration (relevant for factorization update)
Definition: types.h:222
c_float * neg_dphi
-dphi, required as the rhs in SCHUR
Definition: types.h:235
QPALMSettings * settings
problem settings
Definition: types.h:305
c_float * sigma_inv
1./sigma
Definition: types.h:220
c_float gamma
proximal penalty factor
Definition: types.h:223
c_float * dphi
gradient of the Lagrangian
Definition: types.h:234
QPALMSolver * solver
linsys variables
Definition: types.h:304
QPALMData * data
problem data to work on (possibly scaled)
Definition: types.h:198
c_int * index_L
index set L (where s>0)
Definition: types.h:258
c_float * d
primal update step
Definition: types.h:237
ladel_work solver_common
Definition: types.h:18
ladel_double solver_dense
Definition: types.h:20
ladel_sparse_matrix solver_sparse
Definition: types.h:19