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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/scs/ffi.rb +2 -0
  4. data/lib/scs/solver.rb +15 -7
  5. data/lib/scs/version.rb +1 -1
  6. data/vendor/scs/CITATION.cff +2 -2
  7. data/vendor/scs/CMakeLists.txt +136 -6
  8. data/vendor/scs/Makefile +53 -3
  9. data/vendor/scs/README.md +1 -1
  10. data/vendor/scs/include/cones.h +47 -2
  11. data/vendor/scs/include/glbopts.h +1 -1
  12. data/vendor/scs/include/scs.h +29 -0
  13. data/vendor/scs/include/scs_blas.h +4 -0
  14. data/vendor/scs/include/scs_types.h +3 -1
  15. data/vendor/scs/include/util_spectral_cones.h +45 -0
  16. data/vendor/scs/linsys/cpu/direct/private.c +3 -3
  17. data/vendor/scs/linsys/cpu/direct/private.h +2 -1
  18. data/vendor/scs/linsys/csparse.c +1 -1
  19. data/vendor/scs/linsys/cudss/direct/private.c +279 -0
  20. data/vendor/scs/linsys/cudss/direct/private.h +63 -0
  21. data/vendor/scs/linsys/external/qdldl/qdldl_types.h +1 -1
  22. data/vendor/scs/linsys/gpu/indirect/private.c +14 -21
  23. data/vendor/scs/scs.mk +17 -2
  24. data/vendor/scs/src/aa.c +8 -12
  25. data/vendor/scs/src/cones.c +783 -12
  26. data/vendor/scs/src/rw.c +15 -1
  27. data/vendor/scs/src/scs.c +4 -0
  28. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +660 -0
  29. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +279 -0
  30. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +205 -0
  31. data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +143 -0
  32. data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +221 -0
  33. data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +99 -0
  34. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +196 -0
  35. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +140 -0
  36. data/vendor/scs/src/spectral_cones/util_spectral_cones.c +52 -0
  37. data/vendor/scs/test/problems/complex_PSD.h +83 -0
  38. data/vendor/scs/test/rng.h +4 -4
  39. data/vendor/scs/test/run_tests.c +25 -0
  40. data/vendor/scs/test/spectral_cones_problems/exp_design.h +141 -0
  41. data/vendor/scs/test/spectral_cones_problems/graph_partitioning.h +275 -0
  42. data/vendor/scs/test/spectral_cones_problems/robust_pca.h +253 -0
  43. data/vendor/scs/test/spectral_cones_problems/several_logdet_cones.h +222 -0
  44. data/vendor/scs/test/spectral_cones_problems/several_nuc_cone.h +285 -0
  45. data/vendor/scs/test/spectral_cones_problems/several_sum_largest.h +420 -0
  46. 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
+ }