scs 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/scs/ffi.rb +2 -0
- data/lib/scs/solver.rb +15 -7
- data/lib/scs/version.rb +1 -1
- data/vendor/scs/CITATION.cff +2 -2
- data/vendor/scs/CMakeLists.txt +136 -6
- data/vendor/scs/Makefile +53 -3
- data/vendor/scs/README.md +1 -1
- data/vendor/scs/include/cones.h +47 -2
- data/vendor/scs/include/glbopts.h +1 -1
- data/vendor/scs/include/scs.h +29 -0
- data/vendor/scs/include/scs_blas.h +4 -0
- data/vendor/scs/include/scs_types.h +3 -1
- data/vendor/scs/include/util_spectral_cones.h +45 -0
- data/vendor/scs/linsys/cpu/direct/private.c +3 -3
- data/vendor/scs/linsys/cpu/direct/private.h +2 -1
- data/vendor/scs/linsys/csparse.c +1 -1
- data/vendor/scs/linsys/cudss/direct/private.c +279 -0
- data/vendor/scs/linsys/cudss/direct/private.h +63 -0
- data/vendor/scs/linsys/external/qdldl/qdldl_types.h +1 -1
- data/vendor/scs/linsys/gpu/indirect/private.c +14 -21
- data/vendor/scs/scs.mk +17 -2
- data/vendor/scs/src/aa.c +8 -12
- data/vendor/scs/src/cones.c +783 -12
- data/vendor/scs/src/rw.c +15 -1
- data/vendor/scs/src/scs.c +4 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +660 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +279 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +205 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +143 -0
- data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +221 -0
- data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +99 -0
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +196 -0
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +140 -0
- data/vendor/scs/src/spectral_cones/util_spectral_cones.c +52 -0
- data/vendor/scs/test/problems/complex_PSD.h +83 -0
- data/vendor/scs/test/rng.h +4 -4
- data/vendor/scs/test/run_tests.c +25 -0
- data/vendor/scs/test/spectral_cones_problems/exp_design.h +141 -0
- data/vendor/scs/test/spectral_cones_problems/graph_partitioning.h +275 -0
- data/vendor/scs/test/spectral_cones_problems/robust_pca.h +253 -0
- data/vendor/scs/test/spectral_cones_problems/several_logdet_cones.h +222 -0
- data/vendor/scs/test/spectral_cones_problems/several_nuc_cone.h +285 -0
- data/vendor/scs/test/spectral_cones_problems/several_sum_largest.h +420 -0
- metadata +22 -7
@@ -0,0 +1,660 @@
|
|
1
|
+
#include "glbopts.h" // for scs_printf
|
2
|
+
#include "linalg.h"
|
3
|
+
#include "scs_blas.h"
|
4
|
+
#include "util_spectral_cones.h" // for Newton structs
|
5
|
+
#include <string.h> // for memcpy
|
6
|
+
|
7
|
+
/*
|
8
|
+
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
9
|
+
* Cones" by Daniel Cederberg and Stephen Boyd, 2024.
|
10
|
+
*
|
11
|
+
* If you have any questions on the code, please reach out to the code author
|
12
|
+
* Daniel Cederberg.
|
13
|
+
*
|
14
|
+
* This file implements a primal-dual IPM for projecting onto the logarithmic
|
15
|
+
* cone.
|
16
|
+
*
|
17
|
+
* Last modified: 25 August 2024.
|
18
|
+
*/
|
19
|
+
|
20
|
+
#define FEASTOL_IPM 1e-7
|
21
|
+
#define ABSTOL_IPM 1e-7
|
22
|
+
#define RELTOL_IPM 1e-6
|
23
|
+
#define MAX_ITER_IPM 100
|
24
|
+
#define BETA_IPM 0.5
|
25
|
+
#define STEP_IPM 0.99
|
26
|
+
#define ALPHA_IPM 0.01
|
27
|
+
#define MAX_RELAXED_ITERS 8
|
28
|
+
|
29
|
+
blas_int bi_one = 1;
|
30
|
+
scs_float d_one = 1.0;
|
31
|
+
scs_float d_minus_one = -1.0;
|
32
|
+
blas_int bi_three = 3;
|
33
|
+
|
34
|
+
#ifdef __cplusplus
|
35
|
+
extern "C" {
|
36
|
+
#endif
|
37
|
+
|
38
|
+
void BLAS(gemv)(const char *trans, const blas_int *m, const blas_int *n,
|
39
|
+
const scs_float *alpha, const scs_float *a, const blas_int *lda,
|
40
|
+
const scs_float *x, const blas_int *incx, const scs_float *beta,
|
41
|
+
scs_float *y, const blas_int *incy);
|
42
|
+
|
43
|
+
#ifdef __cplusplus
|
44
|
+
}
|
45
|
+
#endif
|
46
|
+
|
47
|
+
/* Evaluates the oracle f(u) = (f0(u), f1(u), f2(u)) where u = [t, v, x, r]
|
48
|
+
* and
|
49
|
+
* f0 = 0.5 * (t - t0)^2 + 0.5 * (v - v0)^2 + 0.5 * ||x - x0||^2 - r
|
50
|
+
* f1 = - v * sum(log(x/v)) - t
|
51
|
+
* f2 = -v
|
52
|
+
*
|
53
|
+
* Note that u1 = [t, v, x].
|
54
|
+
*/
|
55
|
+
static void f_oracle(const scs_float *u1, scs_float r, scs_float t0,
|
56
|
+
scs_float v0, const scs_float *x0, const scs_float *x_inv,
|
57
|
+
scs_int n, scs_float *f_u, scs_float *grad_f0,
|
58
|
+
scs_float *grad_f1) {
|
59
|
+
// compute grad_f0[0, 1] = t - t0, v - v0, grad_f1[2:-1] = x - x0
|
60
|
+
grad_f0[0] = u1[0] - t0;
|
61
|
+
grad_f0[1] = u1[1] - v0;
|
62
|
+
memcpy(grad_f0 + 2, u1 + 2, n * sizeof(*grad_f0));
|
63
|
+
SCS(add_scaled_array)(grad_f0 + 2, x0, n, -1.0);
|
64
|
+
grad_f0[n + 2] = -1.0;
|
65
|
+
|
66
|
+
// compute f_u
|
67
|
+
scs_float sum_log_xv = sum_log(u1 + 2, n) - n * log(u1[1]);
|
68
|
+
scs_float norm_2 = SCS(norm_2)(grad_f0 + 2, n);
|
69
|
+
f_u[0] = 0.5 * (grad_f0[0] * grad_f0[0] + grad_f0[1] * grad_f0[1] +
|
70
|
+
norm_2 * norm_2) -
|
71
|
+
r;
|
72
|
+
f_u[1] = -u1[1] * sum_log_xv - u1[0];
|
73
|
+
f_u[2] = -u1[1];
|
74
|
+
|
75
|
+
// compute grad_f1[0] = -1, grad_f1[1] = n - sum(log(x/v)),
|
76
|
+
// grad[2:-1] = - v / x, grad[-1] = 0
|
77
|
+
grad_f1[0] = -1.0;
|
78
|
+
grad_f1[1] = n - sum_log_xv;
|
79
|
+
memcpy(grad_f1 + 2, x_inv, n * sizeof(*x_inv));
|
80
|
+
SCS(scale_array)(grad_f1 + 2, -u1[1], n);
|
81
|
+
grad_f1[n + 2] = 0.0;
|
82
|
+
}
|
83
|
+
|
84
|
+
// u1 = [t, v, x]
|
85
|
+
static scs_float find_max_step_size(const scs_float *u1, const scs_float *z,
|
86
|
+
const scs_float *s, const scs_float *du1,
|
87
|
+
const scs_float *dz, const scs_float *ds,
|
88
|
+
scs_int n) {
|
89
|
+
scs_float step_max = 10.0;
|
90
|
+
scs_int i;
|
91
|
+
|
92
|
+
// find maximum step size with respect to z and s
|
93
|
+
for (i = 0; i < 3; ++i) {
|
94
|
+
if (dz[i] < 0) {
|
95
|
+
step_max = MIN(step_max, -z[i] / dz[i]);
|
96
|
+
}
|
97
|
+
|
98
|
+
if (ds[i] < 0) {
|
99
|
+
step_max = MIN(step_max, -s[i] / ds[i]);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
scs_float step_max_dom = 10;
|
104
|
+
|
105
|
+
// find maximum step size with respect to the domain of f
|
106
|
+
for (i = 1; i < n + 2; i++) {
|
107
|
+
if (du1[i] < 0) {
|
108
|
+
step_max_dom = MIN(step_max_dom, -u1[i] / du1[i]);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
scs_float step_size = MIN(1.0, STEP_IPM * step_max);
|
113
|
+
|
114
|
+
while (step_size > step_max_dom) {
|
115
|
+
step_size *= BETA_IPM;
|
116
|
+
}
|
117
|
+
|
118
|
+
return step_size;
|
119
|
+
}
|
120
|
+
|
121
|
+
typedef struct {
|
122
|
+
scs_float *temp1;
|
123
|
+
scs_float *temp2;
|
124
|
+
scs_float *GinvC;
|
125
|
+
scs_float *R;
|
126
|
+
scs_float *g0g1;
|
127
|
+
scs_float coeff;
|
128
|
+
scs_float *GinvRes;
|
129
|
+
scs_float *CTGinvRes;
|
130
|
+
scs_float *RinvCTGinvRes;
|
131
|
+
scs_float *residual;
|
132
|
+
scs_float *rhs_reduced;
|
133
|
+
scs_float *bnew;
|
134
|
+
scs_float *CCTdu;
|
135
|
+
|
136
|
+
} KKT_solver_workspace;
|
137
|
+
|
138
|
+
// Precomputations before solving the KKT system. You can think of this
|
139
|
+
// as factoring the KKT matrix.
|
140
|
+
static void KKT_precompute(scs_float v, const scs_float *x_inv,
|
141
|
+
const scs_float *z, const scs_float *w, scs_int n,
|
142
|
+
KKT_solver_workspace *KKT_work) {
|
143
|
+
scs_int u_dim = n + 3;
|
144
|
+
scs_float *temp1 = KKT_work->temp1;
|
145
|
+
scs_float *temp2 = KKT_work->temp2;
|
146
|
+
scs_float *GinvC = KKT_work->GinvC;
|
147
|
+
scs_float *g0g1 = KKT_work->g0g1;
|
148
|
+
scs_float *R = KKT_work->R;
|
149
|
+
|
150
|
+
scs_float a = z[0] + 1 / (w[2] * w[2]) + z[1] * n / v;
|
151
|
+
scs_float coeff = 0.0;
|
152
|
+
for (scs_int i = 0; i < n; i++) {
|
153
|
+
temp1[i] = z[0] + (z[1] * v) * (x_inv[i] * x_inv[i]);
|
154
|
+
temp2[i] = x_inv[i] / temp1[i];
|
155
|
+
coeff += (x_inv[i] * x_inv[i]) / temp1[i];
|
156
|
+
}
|
157
|
+
coeff = a - z[1] * z[1] * coeff;
|
158
|
+
KKT_work->coeff = coeff;
|
159
|
+
|
160
|
+
// --------------------------------------------------------------------
|
161
|
+
// compute G \ g0
|
162
|
+
// --------------------------------------------------------------------
|
163
|
+
GinvC[0] = g0g1[0] / z[0];
|
164
|
+
GinvC[1] = (g0g1[1] + z[1] * SCS(dot)(g0g1 + 2, temp2, n)) / coeff;
|
165
|
+
for (scs_int i = 2; i < n + 2; i++) {
|
166
|
+
GinvC[i] = (g0g1[i] + (z[1] * GinvC[1]) * x_inv[i - 2]) / temp1[i - 2];
|
167
|
+
}
|
168
|
+
GinvC[n + 2] = -g0g1[n + 2];
|
169
|
+
|
170
|
+
// --------------------------------------------------------------------
|
171
|
+
// compute G \ g1
|
172
|
+
// --------------------------------------------------------------------
|
173
|
+
GinvC[n + 3] = g0g1[n + 3] / z[0];
|
174
|
+
GinvC[n + 4] =
|
175
|
+
(g0g1[n + 4] + z[1] * SCS(dot)(g0g1 + n + 5, temp2, n)) / coeff;
|
176
|
+
for (scs_int i = n + 5; i < 2 * n + 5; i++) {
|
177
|
+
GinvC[i] =
|
178
|
+
(g0g1[i] + (z[1] * GinvC[n + 4]) * x_inv[i - n - 5]) / temp1[i - n - 5];
|
179
|
+
}
|
180
|
+
GinvC[2 * n + 5] = -g0g1[2 * n + 5];
|
181
|
+
|
182
|
+
// compute G \ ([0, 0, ..., 0, 1])
|
183
|
+
GinvC[3 * n + 8] = -1.0;
|
184
|
+
|
185
|
+
// ------------------------------------------------------------------------
|
186
|
+
// compute R = I + C.T @ GinvC. This matrix is always reverse triangular.
|
187
|
+
// ------------------------------------------------------------------------
|
188
|
+
R[0] = 1 + SCS(dot)(g0g1, GinvC, u_dim);
|
189
|
+
R[3] = SCS(dot)(g0g1, GinvC + u_dim, u_dim);
|
190
|
+
R[6] = SCS(dot)(g0g1, GinvC + 2 * u_dim, u_dim);
|
191
|
+
R[1] = SCS(dot)(g0g1 + u_dim, GinvC, u_dim);
|
192
|
+
R[4] = 1 + SCS(dot)(g0g1 + u_dim, GinvC + u_dim, u_dim);
|
193
|
+
R[2] = GinvC[n + 2];
|
194
|
+
}
|
195
|
+
|
196
|
+
static void KKT_solve(const scs_float *z, const scs_float *w,
|
197
|
+
const scs_float *x_inv, const scs_float v,
|
198
|
+
const scs_float *lmbda, scs_int n, const scs_float *rhs1,
|
199
|
+
const scs_float *rhs2, KKT_solver_workspace *KKT_work,
|
200
|
+
scs_float *du1, scs_float *dr, scs_float *dz,
|
201
|
+
scs_float *ds) {
|
202
|
+
blas_int u_dim = n + 3;
|
203
|
+
scs_int u1_dim = n + 2;
|
204
|
+
|
205
|
+
scs_float *temp1 = KKT_work->temp1;
|
206
|
+
scs_float *temp2 = KKT_work->temp2;
|
207
|
+
scs_float *GinvC = KKT_work->GinvC;
|
208
|
+
scs_float *R = KKT_work->R;
|
209
|
+
scs_float coeff = KKT_work->coeff;
|
210
|
+
scs_float *GinvRes = KKT_work->GinvRes;
|
211
|
+
scs_float *CTGinvRes = KKT_work->CTGinvRes;
|
212
|
+
scs_float *RinvCTGinvRes = KKT_work->RinvCTGinvRes;
|
213
|
+
scs_float *residual = KKT_work->residual;
|
214
|
+
scs_float *rhs_reduced = KKT_work->rhs_reduced;
|
215
|
+
scs_float *bnew = KKT_work->bnew;
|
216
|
+
scs_float *g0g1 = KKT_work->g0g1;
|
217
|
+
scs_float *CCTdu = KKT_work->CCTdu;
|
218
|
+
|
219
|
+
// -------------------------------------------------------------------------
|
220
|
+
// prepare RHS
|
221
|
+
// -------------------------------------------------------------------------
|
222
|
+
memcpy(rhs_reduced, rhs1, (n + 6) * sizeof(*rhs1));
|
223
|
+
rhs_reduced[u_dim] -= w[0] * (rhs2[0] / lmbda[0]);
|
224
|
+
rhs_reduced[u_dim + 1] -= w[1] * (rhs2[1] / lmbda[1]);
|
225
|
+
rhs_reduced[u_dim + 2] -= w[2] * (rhs2[2] / lmbda[2]);
|
226
|
+
memcpy(bnew, rhs_reduced, u_dim * sizeof(*rhs_reduced));
|
227
|
+
scs_float scale = rhs_reduced[n + 3] / w[0];
|
228
|
+
SCS(add_scaled_array)(bnew, g0g1, u_dim, scale);
|
229
|
+
scale = rhs_reduced[n + 4] / w[1];
|
230
|
+
SCS(add_scaled_array)(bnew, g0g1 + u_dim, u_dim, scale);
|
231
|
+
bnew[1] -= (rhs_reduced[n + 5] / (w[2] * w[2]));
|
232
|
+
|
233
|
+
// -------------------------------------------------------------------------
|
234
|
+
// solve system and apply iterative refinement
|
235
|
+
// -------------------------------------------------------------------------
|
236
|
+
memcpy(residual, bnew, u_dim * sizeof(*bnew));
|
237
|
+
memset(du1, 0, u1_dim * sizeof(*du1));
|
238
|
+
*dr = 0;
|
239
|
+
const scs_int num_ref = 3;
|
240
|
+
for (scs_int i = 0; i < num_ref; ++i) {
|
241
|
+
// --------------------------------
|
242
|
+
// solve (G + C @ C.T) d = residual
|
243
|
+
// --------------------------------
|
244
|
+
GinvRes[0] = residual[0] / z[0];
|
245
|
+
GinvRes[1] =
|
246
|
+
(residual[1] + z[1] * SCS(dot)(residual + 2, temp2, n)) / coeff;
|
247
|
+
for (scs_int i = 2; i < n + 2; ++i) {
|
248
|
+
GinvRes[i] =
|
249
|
+
(residual[i] + (z[1] * GinvRes[1]) * x_inv[i - 2]) / temp1[i - 2];
|
250
|
+
}
|
251
|
+
|
252
|
+
GinvRes[n + 2] = -residual[n + 2];
|
253
|
+
CTGinvRes[0] = SCS(dot)(g0g1, GinvRes, u_dim);
|
254
|
+
CTGinvRes[1] = SCS(dot)(g0g1 + u_dim, GinvRes, u_dim);
|
255
|
+
CTGinvRes[2] = GinvRes[n + 2];
|
256
|
+
RinvCTGinvRes[0] = CTGinvRes[2] / R[2];
|
257
|
+
RinvCTGinvRes[1] = (CTGinvRes[1] - R[1] * RinvCTGinvRes[0]) / R[4];
|
258
|
+
RinvCTGinvRes[2] =
|
259
|
+
(CTGinvRes[0] - R[0] * RinvCTGinvRes[0] - R[3] * RinvCTGinvRes[1]) /
|
260
|
+
R[6];
|
261
|
+
|
262
|
+
// --------------------------------------------------------------------
|
263
|
+
// Here we want to apply the correction d: du = du + d, where
|
264
|
+
// d = GinvRes - GinvC @ RinvCTGinvRes. Note that we expect the
|
265
|
+
// correction d to be very small, so we *must* compute d first and
|
266
|
+
// then add it to du, instead of computing it as
|
267
|
+
// du = du + GinvRes, du = du - GinvC @ RinvCTGinvRes.
|
268
|
+
// In other words, the following code will result in bugs due to
|
269
|
+
// numerics:
|
270
|
+
// daxpy_(&u_dim, &d_one, GinvRes, &bi_one, du, &bi_one);
|
271
|
+
// char trans = 'N';
|
272
|
+
// dgemv_(&trans, &u_dim, &i_three, &d_minus_one, GinvC, &u_dim,
|
273
|
+
// RinvCTGinvRes, &bi_one, &d_one, du, &bi_one);
|
274
|
+
// --------------------------------------------------------------------
|
275
|
+
|
276
|
+
char trans = 'N';
|
277
|
+
BLAS(gemv)(&trans, &u_dim, &bi_three, &d_minus_one, GinvC, &u_dim,
|
278
|
+
RinvCTGinvRes, &bi_one, &d_one, GinvRes, &bi_one);
|
279
|
+
SCS(add_scaled_array)(du1, GinvRes, u1_dim, 1.0);
|
280
|
+
*dr += GinvRes[n + 2];
|
281
|
+
|
282
|
+
// -----------------------------------
|
283
|
+
// compute new residual
|
284
|
+
// -----------------------------------
|
285
|
+
scs_float *Gdu = GinvRes;
|
286
|
+
if (i != num_ref - 1) {
|
287
|
+
Gdu[0] = z[0] * du1[0];
|
288
|
+
Gdu[1] = ((z[0] + 1 / (w[2] * w[2])) * du1[1] +
|
289
|
+
z[1] * (n / v * du1[1] - SCS(dot)(x_inv, du1 + 2, n)));
|
290
|
+
for (scs_int ii = 2; ii < n + 2; ++ii) {
|
291
|
+
Gdu[ii] = (z[0] * du1[ii] +
|
292
|
+
z[1] * (-du1[1] * x_inv[ii - 2] +
|
293
|
+
v * du1[ii] * x_inv[ii - 2] * x_inv[ii - 2]));
|
294
|
+
}
|
295
|
+
Gdu[n + 2] = -(*dr);
|
296
|
+
scs_float coeff0 = SCS(dot)(g0g1, du1, u1_dim) + g0g1[n + 2] * (*dr);
|
297
|
+
scs_float coeff1 =
|
298
|
+
SCS(dot)(g0g1 + u_dim, du1, u1_dim) + g0g1[2 * n + 5] * (*dr);
|
299
|
+
memcpy(CCTdu, g0g1, u_dim * sizeof(*g0g1));
|
300
|
+
SCS(scale_array)(CCTdu, coeff0, u_dim);
|
301
|
+
SCS(add_scaled_array)(CCTdu, g0g1 + u_dim, u_dim, coeff1);
|
302
|
+
CCTdu[n + 2] += (*dr);
|
303
|
+
memcpy(residual, bnew, u_dim * sizeof(*bnew));
|
304
|
+
|
305
|
+
// important to compute the residual as residual = bnew - (Gdu +
|
306
|
+
// CCTdu) and not as residual = (bnew - Gdu) - CCTdu
|
307
|
+
SCS(add_scaled_array)(Gdu, CCTdu, u_dim, 1.0);
|
308
|
+
SCS(add_scaled_array)(residual, Gdu, u_dim, -1.0);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
// -------------------------------------------------------------------------
|
313
|
+
// recover substituted variables
|
314
|
+
// -------------------------------------------------------------------------
|
315
|
+
memcpy(dz, rhs_reduced + u_dim, 3 * sizeof(*rhs_reduced));
|
316
|
+
dz[0] -= w[0] * (SCS(dot)(g0g1, du1, u1_dim) + g0g1[n + 2] * (*dr));
|
317
|
+
dz[1] -=
|
318
|
+
w[1] * (SCS(dot)(g0g1 + u_dim, du1, u1_dim) + g0g1[2 * n + 5] * (*dr));
|
319
|
+
dz[2] += du1[1];
|
320
|
+
for (scs_int ii = 0; ii < 3; ++ii) {
|
321
|
+
dz[ii] = -dz[ii] / (w[ii] * w[ii]);
|
322
|
+
ds[ii] = w[ii] * (rhs2[ii] / lmbda[ii] - w[ii] * dz[ii]);
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
/* A primal-dual interior point method for projecting the point (t0, v0, x0)
|
327
|
+
* onto the logarithm cone. Workspace must be of size 22n + 122, where n is
|
328
|
+
* the dimension of x0. The variable is u1 = [t, v, x], and epigraph
|
329
|
+
* variable r
|
330
|
+
*/
|
331
|
+
scs_int log_cone_IPM(scs_float t0, scs_float v0, scs_float *x0, scs_float *u1,
|
332
|
+
scs_int n, scs_float *workspace, Newton_stats *stats,
|
333
|
+
scs_int variant) {
|
334
|
+
scs_int m = 3;
|
335
|
+
scs_int u_dim = n + 3;
|
336
|
+
scs_int u1_dim = n + 2;
|
337
|
+
|
338
|
+
// -----------------------------------------------------------------
|
339
|
+
// scale (t0, v0, x0)
|
340
|
+
// -----------------------------------------------------------------
|
341
|
+
scs_float scale1 = SCS(norm_inf)(x0, n);
|
342
|
+
scale1 = MAX(t0, scale1);
|
343
|
+
scale1 = MAX(v0, scale1);
|
344
|
+
scale1 = 1 / scale1;
|
345
|
+
t0 = t0 * scale1;
|
346
|
+
v0 = v0 * scale1;
|
347
|
+
SCS(scale_array)(x0, scale1, n);
|
348
|
+
|
349
|
+
// ----------------------------------------------------------------------
|
350
|
+
// Parse workspace
|
351
|
+
// ----------------------------------------------------------------------
|
352
|
+
scs_float *x_inv = workspace;
|
353
|
+
scs_float *z = workspace + n;
|
354
|
+
scs_float *s = z + m;
|
355
|
+
scs_float *f_u = s + m;
|
356
|
+
scs_float *grad_f0 = f_u + m;
|
357
|
+
scs_float *grad_f1 = grad_f0 + u_dim;
|
358
|
+
scs_float *rx = grad_f1 + u_dim;
|
359
|
+
scs_float *rznl = rx + u_dim;
|
360
|
+
scs_float *w = rznl + m;
|
361
|
+
scs_float *lmbda = w + m;
|
362
|
+
scs_float *du1 = lmbda + m;
|
363
|
+
scs_float *dz = du1 + u1_dim;
|
364
|
+
scs_float *ds = dz + m;
|
365
|
+
|
366
|
+
// for evaluating the new residuals in the line search
|
367
|
+
scs_float *u1_new = ds + m;
|
368
|
+
scs_float *z_new = u1_new + u1_dim;
|
369
|
+
scs_float *s_new = z_new + m;
|
370
|
+
scs_float *x_inv_new = s_new + m;
|
371
|
+
scs_float *f_u_new = x_inv_new + n;
|
372
|
+
scs_float *grad_f0_new = f_u_new + m;
|
373
|
+
scs_float *grad_f1_new = grad_f0_new + u_dim;
|
374
|
+
scs_float *rx_new = grad_f1_new + u_dim;
|
375
|
+
scs_float *rznl_new = rx_new + u_dim;
|
376
|
+
|
377
|
+
// for storing the state for the line search
|
378
|
+
scs_float *u1_0 = rznl_new + m;
|
379
|
+
scs_float *du1_0 = u1_0 + u1_dim;
|
380
|
+
scs_float *z0 = du1_0 + u1_dim;
|
381
|
+
scs_float *dz0 = z0 + m;
|
382
|
+
scs_float *s0 = dz0 + m;
|
383
|
+
scs_float *ds0 = s0 + m;
|
384
|
+
scs_float r0 = 0.0;
|
385
|
+
scs_float dr0 = 0.0;
|
386
|
+
|
387
|
+
KKT_solver_workspace KKT_work;
|
388
|
+
KKT_work.g0g1 = grad_f0;
|
389
|
+
KKT_work.temp1 = ds0 + m;
|
390
|
+
KKT_work.temp2 = KKT_work.temp1 + u_dim;
|
391
|
+
KKT_work.GinvC = KKT_work.temp2 + u_dim;
|
392
|
+
KKT_work.R = KKT_work.GinvC + 3 * u_dim;
|
393
|
+
KKT_work.GinvRes = KKT_work.R + 9;
|
394
|
+
KKT_work.CTGinvRes = KKT_work.GinvRes + u_dim;
|
395
|
+
KKT_work.RinvCTGinvRes = KKT_work.CTGinvRes + 3;
|
396
|
+
KKT_work.residual = KKT_work.RinvCTGinvRes + 3;
|
397
|
+
KKT_work.rhs_reduced = KKT_work.residual + u_dim;
|
398
|
+
KKT_work.bnew = KKT_work.rhs_reduced + u_dim + 3;
|
399
|
+
KKT_work.CCTdu = KKT_work.bnew + u_dim;
|
400
|
+
|
401
|
+
// ---------------------------------------------------------------------
|
402
|
+
// initialize primal variable u1 = [t, v, x], epigraph variable r and
|
403
|
+
// primal slack variable s and Lagrange multipliers z
|
404
|
+
// ---------------------------------------------------------------------
|
405
|
+
for (scs_int i = 0; i < n + 2; ++i) {
|
406
|
+
u1[i] = 1.0;
|
407
|
+
}
|
408
|
+
|
409
|
+
scs_float r = 0.0;
|
410
|
+
scs_float dr = 0.0;
|
411
|
+
scs_float rnew = 0.0;
|
412
|
+
|
413
|
+
for (scs_int i = 0; i < m; ++i) {
|
414
|
+
z[i] = 1.0;
|
415
|
+
s[i] = 1.0;
|
416
|
+
}
|
417
|
+
|
418
|
+
scs_int relaxed_iters = 0;
|
419
|
+
size_t iter = 0;
|
420
|
+
scs_float theta1, theta2, theta3;
|
421
|
+
scs_float pres0, dres0;
|
422
|
+
scs_float phi0 = 0.0, dphi0 = 0.0, step_size0 = 0.0;
|
423
|
+
// scs_int small_consecutive_steps_counter = 0;
|
424
|
+
|
425
|
+
#ifdef SPECTRAL_DEBUG
|
426
|
+
printf("%-3s%-15s%-15s%-15s%-10s%-10s\n", "", "gap", "pres", "dres", "sig",
|
427
|
+
"step");
|
428
|
+
#endif
|
429
|
+
|
430
|
+
for (iter = 0; iter < MAX_ITER_IPM; ++iter) {
|
431
|
+
scs_float gap = z[0] * s[0] + z[1] * s[1] + z[2] * s[2];
|
432
|
+
scs_float mu = gap / m;
|
433
|
+
|
434
|
+
// --------------------------------------------------------
|
435
|
+
// evaluate oracle
|
436
|
+
// --------------------------------------------------------
|
437
|
+
for (scs_int i = 0; i < n; i++) {
|
438
|
+
x_inv[i] = 1 / u1[i + 2];
|
439
|
+
}
|
440
|
+
f_oracle(u1, r, t0, v0, x0, x_inv, n, f_u, grad_f0, grad_f1);
|
441
|
+
|
442
|
+
// --------------------------------------------------------
|
443
|
+
// compute residuals and check termination criteria
|
444
|
+
// --------------------------------------------------------
|
445
|
+
memcpy(rx, grad_f0, u_dim * sizeof(*grad_f0));
|
446
|
+
SCS(scale_array)(rx, z[0], u_dim);
|
447
|
+
SCS(add_scaled_array)(rx, grad_f1, u_dim, z[1]);
|
448
|
+
rx[1] -= z[2];
|
449
|
+
rx[n + 2] += 1;
|
450
|
+
rznl[0] = f_u[0] + s[0];
|
451
|
+
rznl[1] = f_u[1] + s[1];
|
452
|
+
rznl[2] = f_u[2] + s[2];
|
453
|
+
|
454
|
+
scs_float dres = SCS(norm_2)(rx, u_dim);
|
455
|
+
scs_float pres =
|
456
|
+
sqrt(rznl[0] * rznl[0] + rznl[1] * rznl[1] + rznl[2] * rznl[2]);
|
457
|
+
scs_float norm_rx = dres;
|
458
|
+
scs_float norm_rznl = pres;
|
459
|
+
|
460
|
+
if (iter == 0) {
|
461
|
+
pres0 = MAX(pres, 1.0);
|
462
|
+
dres0 = MAX(dres, 1.0);
|
463
|
+
theta1 = 1.0 / gap;
|
464
|
+
theta2 = 1.0 / dres0;
|
465
|
+
theta3 = 1.0 / pres0;
|
466
|
+
}
|
467
|
+
|
468
|
+
pres = pres / pres0;
|
469
|
+
dres = dres / dres0;
|
470
|
+
scs_float relgap = gap / MAX(r, 1.0);
|
471
|
+
|
472
|
+
if (dres < FEASTOL_IPM && pres < FEASTOL_IPM &&
|
473
|
+
(gap < ABSTOL_IPM || relgap <= RELTOL_IPM)) {
|
474
|
+
#ifdef SPECTRAL_DEBUG
|
475
|
+
printf("optimal solution found: \n");
|
476
|
+
printf("gap / pres / dres: %.7e, %.7e, %.7e \n", gap, pres, dres);
|
477
|
+
#endif
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
|
481
|
+
// ----------------------------------------------------
|
482
|
+
// compute scaling matrix and scaling point
|
483
|
+
// ----------------------------------------------------
|
484
|
+
for (scs_int i = 0; i < m; i++) {
|
485
|
+
w[i] = sqrt(s[i] / z[i]);
|
486
|
+
lmbda[i] = sqrt(s[i] * z[i]);
|
487
|
+
}
|
488
|
+
|
489
|
+
// -----------------------------------------------------------------
|
490
|
+
// precomputations for KKT system
|
491
|
+
// -----------------------------------------------------------------
|
492
|
+
scs_float scale2 = 1 / w[0];
|
493
|
+
SCS(scale_array)(grad_f0, scale2, u_dim);
|
494
|
+
scale2 = 1 / w[1];
|
495
|
+
SCS(scale_array)(grad_f1, scale2, u_dim);
|
496
|
+
KKT_precompute(u1[1], x_inv, z, w, n, &KKT_work);
|
497
|
+
|
498
|
+
scs_float rhs2_aff[] = {-lmbda[0] * lmbda[0], -lmbda[1] * lmbda[1],
|
499
|
+
-lmbda[2] * lmbda[2]};
|
500
|
+
|
501
|
+
// upper bound on loop is chosen so it also iterates over rznl
|
502
|
+
for (scs_int i = 0; i < u_dim + 3; ++i) {
|
503
|
+
rx[i] = -rx[i];
|
504
|
+
}
|
505
|
+
scs_float *rhs1_aff = rx;
|
506
|
+
|
507
|
+
scs_float sigma = 0.0;
|
508
|
+
scs_float phi = 0.0;
|
509
|
+
scs_float dphi = 0.0;
|
510
|
+
scs_float step_size = 0.0;
|
511
|
+
phi = theta1 * gap + theta2 * norm_rx + theta3 * norm_rznl;
|
512
|
+
dphi = -theta1 * (1 - sigma) * gap - theta2 * norm_rx - theta3 * norm_rznl;
|
513
|
+
|
514
|
+
for (scs_int i = 0; i < 2; ++i) {
|
515
|
+
// ------------------------------------------------------------
|
516
|
+
// For i = 0 we compute the affine-scaling direction.
|
517
|
+
// For i = 1 we compute the actual search direction.
|
518
|
+
// ------------------------------------------------------------
|
519
|
+
if (i == 1 && variant == 0) {
|
520
|
+
SCS(scale_array)(rhs1_aff, 1 - sigma, u_dim + 3);
|
521
|
+
rhs2_aff[0] += (sigma * mu - ds[0] * dz[0]);
|
522
|
+
rhs2_aff[1] += (sigma * mu - ds[1] * dz[1]);
|
523
|
+
rhs2_aff[2] += (sigma * mu - ds[2] * dz[2]);
|
524
|
+
}
|
525
|
+
|
526
|
+
KKT_solve(z, w, x_inv, u1[1], lmbda, n, rhs1_aff, rhs2_aff, &KKT_work,
|
527
|
+
du1, &dr, dz, ds);
|
528
|
+
|
529
|
+
// --------------------------------------------------------------
|
530
|
+
// For i = 0 we determine the centering parameter.
|
531
|
+
// For i = 1 we do a nonmonotone line search.
|
532
|
+
// --------------------------------------------------------------
|
533
|
+
step_size = find_max_step_size(u1, z, s, du1, dz, ds, n);
|
534
|
+
|
535
|
+
bool backtrack = true;
|
536
|
+
|
537
|
+
while (backtrack) {
|
538
|
+
// u_new = u + step * du, z_new = z + step * dz, s_new = s + step
|
539
|
+
memcpy(u1_new, u1, u1_dim * sizeof(*u1));
|
540
|
+
SCS(add_scaled_array)(u1_new, du1, u1_dim, step_size);
|
541
|
+
rnew = r + step_size * dr;
|
542
|
+
memcpy(z_new, z, 3 * sizeof(*z));
|
543
|
+
SCS(add_scaled_array)(z_new, dz, m, step_size);
|
544
|
+
memcpy(s_new, s, 3 * sizeof(*s));
|
545
|
+
SCS(add_scaled_array)(s_new, ds, m, step_size);
|
546
|
+
|
547
|
+
// evaluate oracle in u_new
|
548
|
+
for (scs_int i = 0; i < n; i++) {
|
549
|
+
x_inv_new[i] = 1 / u1_new[i + 2];
|
550
|
+
}
|
551
|
+
f_oracle(u1_new, rnew, t0, v0, x0, x_inv_new, n, f_u_new, grad_f0_new,
|
552
|
+
grad_f1_new);
|
553
|
+
|
554
|
+
// compute residuals and merit function
|
555
|
+
memcpy(rx_new, grad_f0_new, u_dim * sizeof(*grad_f0_new));
|
556
|
+
SCS(scale_array)(rx_new, z_new[0], u_dim);
|
557
|
+
SCS(add_scaled_array)(rx_new, grad_f1_new, u_dim, z_new[1]);
|
558
|
+
rx_new[1] -= z_new[2];
|
559
|
+
rx_new[n + 2] += 1;
|
560
|
+
rznl_new[0] = f_u_new[0] + s_new[0];
|
561
|
+
rznl_new[1] = f_u_new[1] + s_new[1];
|
562
|
+
rznl_new[2] = f_u_new[2] + s_new[2];
|
563
|
+
scs_float gap_new = SCS(dot)(z_new, s_new, m);
|
564
|
+
scs_float phi_new = theta1 * gap_new +
|
565
|
+
theta2 * SCS(norm_2)(rx_new, u_dim) +
|
566
|
+
theta3 * SCS(norm_2)(rznl_new, m);
|
567
|
+
// ----------------------------------------------------------
|
568
|
+
// For i == 0 we determine the centering parameter
|
569
|
+
// ----------------------------------------------------------
|
570
|
+
if (i == 0) {
|
571
|
+
if (phi_new <= (1 - ALPHA_IPM * step_size) * phi) {
|
572
|
+
backtrack = false;
|
573
|
+
sigma = (gap_new / gap);
|
574
|
+
if (sigma < 1) {
|
575
|
+
sigma *= (sigma * sigma);
|
576
|
+
}
|
577
|
+
} else {
|
578
|
+
step_size *= BETA_IPM;
|
579
|
+
}
|
580
|
+
}
|
581
|
+
// ------------------------------------------------------------
|
582
|
+
// For i == 1 we do a nonmonotone line search with the actual
|
583
|
+
// search direction
|
584
|
+
// ------------------------------------------------------------
|
585
|
+
else {
|
586
|
+
if (relaxed_iters == -1 || MAX_RELAXED_ITERS == 0) {
|
587
|
+
if (phi_new <= phi + ALPHA_IPM * step_size * dphi) {
|
588
|
+
// relaxed_iters = 0;
|
589
|
+
backtrack = false;
|
590
|
+
} else {
|
591
|
+
step_size *= BETA_IPM;
|
592
|
+
}
|
593
|
+
} else if (relaxed_iters == 0) {
|
594
|
+
if (phi_new <= phi + ALPHA_IPM * step_size * dphi) {
|
595
|
+
relaxed_iters = 0;
|
596
|
+
} else {
|
597
|
+
relaxed_iters = 1;
|
598
|
+
memcpy(u1_0, u1, u1_dim * sizeof(*u1));
|
599
|
+
r0 = r;
|
600
|
+
memcpy(z0, z, 3 * sizeof(*z));
|
601
|
+
memcpy(s0, s, 3 * sizeof(*s));
|
602
|
+
memcpy(du1_0, du1, u1_dim * sizeof(*du1));
|
603
|
+
dr0 = dr;
|
604
|
+
memcpy(dz0, dz, 3 * sizeof(*dz));
|
605
|
+
memcpy(ds0, ds, 3 * sizeof(*ds));
|
606
|
+
phi0 = phi;
|
607
|
+
dphi0 = dphi;
|
608
|
+
step_size0 = step_size;
|
609
|
+
}
|
610
|
+
|
611
|
+
backtrack = false;
|
612
|
+
} else if (relaxed_iters == MAX_RELAXED_ITERS) {
|
613
|
+
if (phi_new <= phi0 + ALPHA_IPM * step_size0 * dphi0) {
|
614
|
+
// relaxed_iters = 0;
|
615
|
+
backtrack = false;
|
616
|
+
} else {
|
617
|
+
relaxed_iters = -1;
|
618
|
+
memcpy(u1, u1_0, u1_dim * sizeof(*u1));
|
619
|
+
r = r0;
|
620
|
+
memcpy(z, z0, 3 * sizeof(*z));
|
621
|
+
memcpy(s, s0, 3 * sizeof(*s));
|
622
|
+
memcpy(du1, du1_0, u1_dim * sizeof(*du1));
|
623
|
+
dr = dr0;
|
624
|
+
memcpy(dz, dz0, 3 * sizeof(*dz));
|
625
|
+
memcpy(ds, ds0, 3 * sizeof(*ds));
|
626
|
+
phi = phi0;
|
627
|
+
dphi = dphi0;
|
628
|
+
step_size = step_size0;
|
629
|
+
}
|
630
|
+
} else if (relaxed_iters < MAX_RELAXED_ITERS) {
|
631
|
+
if (phi_new <= phi0 + ALPHA_IPM * step_size0 * dphi0) {
|
632
|
+
relaxed_iters = 0;
|
633
|
+
} else {
|
634
|
+
relaxed_iters += 1;
|
635
|
+
}
|
636
|
+
|
637
|
+
backtrack = false;
|
638
|
+
}
|
639
|
+
}
|
640
|
+
}
|
641
|
+
}
|
642
|
+
|
643
|
+
// update iterates
|
644
|
+
memcpy(u1, u1_new, u1_dim * sizeof(*u1));
|
645
|
+
r = rnew;
|
646
|
+
memcpy(z, z_new, 3 * sizeof(*z));
|
647
|
+
memcpy(s, s_new, 3 * sizeof(*s));
|
648
|
+
#ifdef SPECTRAL_DEBUG
|
649
|
+
printf("%ld: %.7e, %.7e, %.7e, %f, %.3f \n", iter, gap, pres, dres, sigma,
|
650
|
+
step_size);
|
651
|
+
#endif
|
652
|
+
}
|
653
|
+
|
654
|
+
// unscale solution
|
655
|
+
scale1 = 1 / scale1;
|
656
|
+
SCS(scale_array)(x0, scale1, n);
|
657
|
+
SCS(scale_array)(u1, scale1, u1_dim);
|
658
|
+
stats->iter = iter;
|
659
|
+
return 0;
|
660
|
+
}
|