scs 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +11 -6
  4. data/lib/scs/ffi.rb +30 -13
  5. data/lib/scs/solver.rb +32 -9
  6. data/lib/scs/version.rb +1 -1
  7. data/vendor/scs/CITATION.cff +39 -0
  8. data/vendor/scs/CMakeLists.txt +7 -8
  9. data/vendor/scs/Makefile +24 -15
  10. data/vendor/scs/README.md +5 -263
  11. data/vendor/scs/include/aa.h +67 -23
  12. data/vendor/scs/include/cones.h +17 -17
  13. data/vendor/scs/include/glbopts.h +98 -32
  14. data/vendor/scs/include/linalg.h +2 -4
  15. data/vendor/scs/include/linsys.h +58 -44
  16. data/vendor/scs/include/normalize.h +3 -3
  17. data/vendor/scs/include/rw.h +8 -2
  18. data/vendor/scs/include/scs.h +293 -133
  19. data/vendor/scs/include/util.h +3 -15
  20. data/vendor/scs/linsys/cpu/direct/private.c +220 -224
  21. data/vendor/scs/linsys/cpu/direct/private.h +13 -7
  22. data/vendor/scs/linsys/cpu/direct/private.o +0 -0
  23. data/vendor/scs/linsys/cpu/indirect/private.c +177 -110
  24. data/vendor/scs/linsys/cpu/indirect/private.h +8 -4
  25. data/vendor/scs/linsys/cpu/indirect/private.o +0 -0
  26. data/vendor/scs/linsys/csparse.c +87 -0
  27. data/vendor/scs/linsys/csparse.h +34 -0
  28. data/vendor/scs/linsys/csparse.o +0 -0
  29. data/vendor/scs/linsys/external/amd/SuiteSparse_config.c +1 -1
  30. data/vendor/scs/linsys/external/amd/SuiteSparse_config.o +0 -0
  31. data/vendor/scs/linsys/external/amd/amd_1.o +0 -0
  32. data/vendor/scs/linsys/external/amd/amd_2.o +0 -0
  33. data/vendor/scs/linsys/external/amd/amd_aat.o +0 -0
  34. data/vendor/scs/linsys/external/amd/amd_control.o +0 -0
  35. data/vendor/scs/linsys/external/amd/amd_defaults.o +0 -0
  36. data/vendor/scs/linsys/external/amd/amd_dump.o +0 -0
  37. data/vendor/scs/linsys/external/amd/amd_global.o +0 -0
  38. data/vendor/scs/linsys/external/amd/amd_info.o +0 -0
  39. data/vendor/scs/linsys/external/amd/amd_internal.h +1 -1
  40. data/vendor/scs/linsys/external/amd/amd_order.o +0 -0
  41. data/vendor/scs/linsys/external/amd/amd_post_tree.o +0 -0
  42. data/vendor/scs/linsys/external/amd/amd_postorder.o +0 -0
  43. data/vendor/scs/linsys/external/amd/amd_preprocess.o +0 -0
  44. data/vendor/scs/linsys/external/amd/amd_valid.o +0 -0
  45. data/vendor/scs/linsys/external/qdldl/changes +2 -0
  46. data/vendor/scs/linsys/external/qdldl/qdldl.c +29 -46
  47. data/vendor/scs/linsys/external/qdldl/qdldl.h +33 -41
  48. data/vendor/scs/linsys/external/qdldl/qdldl.o +0 -0
  49. data/vendor/scs/linsys/external/qdldl/qdldl_types.h +11 -3
  50. data/vendor/scs/linsys/gpu/gpu.c +31 -33
  51. data/vendor/scs/linsys/gpu/gpu.h +48 -31
  52. data/vendor/scs/linsys/gpu/indirect/private.c +338 -232
  53. data/vendor/scs/linsys/gpu/indirect/private.h +23 -14
  54. data/vendor/scs/linsys/scs_matrix.c +498 -0
  55. data/vendor/scs/linsys/scs_matrix.h +70 -0
  56. data/vendor/scs/linsys/scs_matrix.o +0 -0
  57. data/vendor/scs/scs.mk +13 -9
  58. data/vendor/scs/src/aa.c +384 -109
  59. data/vendor/scs/src/aa.o +0 -0
  60. data/vendor/scs/src/cones.c +440 -353
  61. data/vendor/scs/src/cones.o +0 -0
  62. data/vendor/scs/src/ctrlc.c +15 -5
  63. data/vendor/scs/src/ctrlc.o +0 -0
  64. data/vendor/scs/src/linalg.c +84 -28
  65. data/vendor/scs/src/linalg.o +0 -0
  66. data/vendor/scs/src/normalize.c +22 -64
  67. data/vendor/scs/src/normalize.o +0 -0
  68. data/vendor/scs/src/rw.c +160 -21
  69. data/vendor/scs/src/rw.o +0 -0
  70. data/vendor/scs/src/scs.c +767 -563
  71. data/vendor/scs/src/scs.o +0 -0
  72. data/vendor/scs/src/scs_indir.o +0 -0
  73. data/vendor/scs/src/scs_version.c +9 -3
  74. data/vendor/scs/src/scs_version.o +0 -0
  75. data/vendor/scs/src/util.c +37 -106
  76. data/vendor/scs/src/util.o +0 -0
  77. data/vendor/scs/test/minunit.h +17 -8
  78. data/vendor/scs/test/problem_utils.h +176 -14
  79. data/vendor/scs/test/problems/degenerate.h +130 -0
  80. data/vendor/scs/test/problems/hs21_tiny_qp.h +124 -0
  81. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +116 -0
  82. data/vendor/scs/test/problems/infeasible_tiny_qp.h +100 -0
  83. data/vendor/scs/test/problems/qafiro_tiny_qp.h +199 -0
  84. data/vendor/scs/test/problems/random_prob +0 -0
  85. data/vendor/scs/test/problems/random_prob.h +45 -0
  86. data/vendor/scs/test/problems/rob_gauss_cov_est.h +188 -31
  87. data/vendor/scs/test/problems/small_lp.h +13 -14
  88. data/vendor/scs/test/problems/test_fails.h +43 -0
  89. data/vendor/scs/test/problems/unbounded_tiny_qp.h +82 -0
  90. data/vendor/scs/test/random_socp_prob.c +54 -53
  91. data/vendor/scs/test/rng.h +109 -0
  92. data/vendor/scs/test/run_from_file.c +19 -10
  93. data/vendor/scs/test/run_tests.c +27 -3
  94. metadata +20 -8
  95. data/vendor/scs/linsys/amatrix.c +0 -305
  96. data/vendor/scs/linsys/amatrix.h +0 -36
  97. data/vendor/scs/linsys/amatrix.o +0 -0
  98. data/vendor/scs/test/data/small_random_socp +0 -0
  99. data/vendor/scs/test/problems/small_random_socp.h +0 -33
  100. data/vendor/scs/test/run_tests +0 -2
data/vendor/scs/src/scs.c CHANGED
@@ -1,5 +1,4 @@
1
1
  #include "scs.h"
2
-
3
2
  #include "aa.h"
4
3
  #include "ctrlc.h"
5
4
  #include "glbopts.h"
@@ -7,88 +6,107 @@
7
6
  #include "linsys.h"
8
7
  #include "normalize.h"
9
8
  #include "rw.h"
9
+ #include "scs_matrix.h"
10
10
  #include "util.h"
11
11
 
12
- SCS(timer) global_timer;
13
-
14
12
  /* printing header */
15
13
  static const char *HEADER[] = {
16
- " Iter ", " pri res ", " dua res ", " rel gap ",
17
- " pri obj ", " dua obj ", " kap/tau ", " time (s)",
14
+ " iter ", " pri res ", " dua res ", " gap ",
15
+ " obj ", " scale ", " time (s)",
18
16
  };
19
17
  static const scs_int HSPACE = 9;
20
- static const scs_int HEADER_LEN = 8;
21
- static const scs_int LINE_LEN = 76;
22
-
23
- static scs_int scs_isnan(scs_float x) { return (x == NAN || x != x); }
18
+ static const scs_int HEADER_LEN = 7;
19
+ static const scs_int LINE_LEN = 66;
20
+
21
+ static void free_residuals(ScsResiduals *r) {
22
+ if (r) {
23
+ scs_free(r->ax);
24
+ scs_free(r->ax_s);
25
+ scs_free(r->px);
26
+ scs_free(r->aty);
27
+ scs_free(r->ax_s_btau);
28
+ scs_free(r->px_aty_ctau);
29
+ scs_free(r);
30
+ }
31
+ }
24
32
 
25
33
  static void free_work(ScsWork *w) {
26
34
  if (w) {
27
35
  scs_free(w->u);
28
- scs_free(w->u_best);
29
36
  scs_free(w->u_t);
30
- scs_free(w->u_prev);
31
- /* Don't need these because u*, v* are contiguous in mem
32
- scs_free(w->v);
33
- scs_free(w->v_best);
34
- scs_free(w->v_prev);
35
- */
37
+ scs_free(w->v);
38
+ scs_free(w->v_prev);
39
+ scs_free(w->rsk);
36
40
  scs_free(w->h);
37
41
  scs_free(w->g);
38
- scs_free(w->b);
39
- scs_free(w->c);
40
- scs_free(w->pr);
41
- scs_free(w->dr);
42
+ scs_free(w->b_normalized);
43
+ scs_free(w->c_normalized);
44
+ scs_free(w->rho_y_vec);
45
+ scs_free(w->lin_sys_warm_start);
46
+ if (w->cone_boundaries) {
47
+ scs_free(w->cone_boundaries);
48
+ }
42
49
  if (w->scal) {
43
50
  scs_free(w->scal->D);
44
51
  scs_free(w->scal->E);
45
52
  scs_free(w->scal);
46
53
  }
54
+ SCS(free_sol)(w->xys_orig);
55
+ free_residuals(w->r_orig);
56
+ if (w->stgs->normalize) {
57
+ SCS(free_sol)(w->xys_normalized);
58
+ free_residuals(w->r_normalized);
59
+ }
47
60
  scs_free(w);
48
61
  }
49
62
  }
50
63
 
51
- static void print_init_header(const ScsData *d, const ScsCone *k) {
64
+ static void print_init_header(const ScsData *d, const ScsCone *k,
65
+ const ScsSettings *stgs) {
52
66
  scs_int i;
53
- ScsSettings *stgs = d->stgs;
54
67
  char *cone_str = SCS(get_cone_header)(k);
55
- char *lin_sys_method = SCS(get_lin_sys_method)(d->A, d->stgs);
68
+ const char *lin_sys_method = SCS(get_lin_sys_method)();
56
69
  #ifdef USE_LAPACK
57
70
  scs_int acceleration_lookback = stgs->acceleration_lookback;
71
+ scs_int acceleration_interval = stgs->acceleration_interval;
58
72
  #else
59
73
  scs_int acceleration_lookback = 0;
74
+ scs_int acceleration_interval = 0;
60
75
  #endif
61
76
  for (i = 0; i < LINE_LEN; ++i) {
62
77
  scs_printf("-");
63
78
  }
64
- scs_printf(
65
- "\n\tSCS v%s - Splitting Conic Solver\n\t(c) Brendan "
66
- "O'Donoghue, Stanford University, 2012\n",
67
- SCS(version)());
79
+ scs_printf("\n\t SCS v%s - Splitting Conic Solver\n\t(c) Brendan "
80
+ "O'Donoghue, Stanford University, 2012\n",
81
+ SCS(version)());
68
82
  for (i = 0; i < LINE_LEN; ++i) {
69
83
  scs_printf("-");
70
84
  }
71
85
  scs_printf("\n");
72
- if (lin_sys_method) {
73
- scs_printf("Lin-sys: %s\n", lin_sys_method);
74
- scs_free(lin_sys_method);
75
- }
76
- if (stgs->normalize) {
77
- scs_printf(
78
- "eps = %.2e, alpha = %.2f, max_iters = %i, normalize = %i, "
79
- "scale = %2.2f\nacceleration_lookback = %i, rho_x = %.2e\n",
80
- stgs->eps, stgs->alpha, (int)stgs->max_iters, (int)stgs->normalize,
81
- stgs->scale, (int)acceleration_lookback, stgs->rho_x);
82
- } else {
83
- scs_printf(
84
- "eps = %.2e, alpha = %.2f, max_iters = %i, normalize = %i\n"
85
- "acceleration_lookback = %i, rho_x = %.2e\n",
86
- stgs->eps, stgs->alpha, (int)stgs->max_iters, (int)stgs->normalize,
87
- (int)acceleration_lookback, stgs->rho_x);
88
- }
89
- scs_printf("Variables n = %i, constraints m = %i\n", (int)d->n, (int)d->m);
86
+ scs_printf("problem: variables n: %i, constraints m: %i\n", (int)d->n,
87
+ (int)d->m);
90
88
  scs_printf("%s", cone_str);
91
89
  scs_free(cone_str);
90
+ scs_printf("settings: eps_abs: %.1e, eps_rel: %.1e, eps_infeas: %.1e\n"
91
+ "\t alpha: %.2f, scale: %.2e, adaptive_scale: %i\n"
92
+ "\t max_iters: %i, normalize: %i, warm_start: %i\n",
93
+ /*, rho_x: %.2e\n", */
94
+ stgs->eps_abs, stgs->eps_rel, stgs->eps_infeas, stgs->alpha,
95
+ stgs->scale, (int)stgs->adaptive_scale, (int)stgs->max_iters,
96
+ (int)stgs->normalize, (int)stgs->warm_start);
97
+ /* , stgs->rho_x); */
98
+ if (stgs->acceleration_lookback != 0) {
99
+ scs_printf("\t acceleration_lookback: %i, acceleration_interval: %i\n",
100
+ (int)acceleration_lookback, (int)acceleration_interval);
101
+ }
102
+ if (stgs->time_limit_secs) {
103
+ scs_printf("\t time_limit_secs: %.2e\n", stgs->time_limit_secs);
104
+ }
105
+ if (lin_sys_method) {
106
+ scs_printf("lin-sys: %s\n\t nnz(A): %li, nnz(P): %li\n", lin_sys_method,
107
+ (long)d->A->p[d->A->n], d->P ? (long)d->P->p[d->P->n] : 0l);
108
+ }
109
+
92
110
  #ifdef MATLAB_MEX_FILE
93
111
  mexEvalString("drawnow;");
94
112
  #endif
@@ -98,7 +116,7 @@ static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
98
116
  ScsInfo *info, scs_int status_val,
99
117
  const char *msg) {
100
118
  if (info) {
101
- info->rel_gap = NAN;
119
+ info->gap = NAN;
102
120
  info->res_pri = NAN;
103
121
  info->res_dual = NAN;
104
122
  info->pobj = NAN;
@@ -111,17 +129,17 @@ static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
111
129
  if (sol) {
112
130
  if (n > 0) {
113
131
  if (!sol->x) {
114
- sol->x = (scs_float *)scs_malloc(sizeof(scs_float) * n);
132
+ sol->x = (scs_float *)scs_calloc(n, sizeof(scs_float));
115
133
  }
116
134
  SCS(scale_array)(sol->x, NAN, n);
117
135
  }
118
136
  if (m > 0) {
119
137
  if (!sol->y) {
120
- sol->y = (scs_float *)scs_malloc(sizeof(scs_float) * m);
138
+ sol->y = (scs_float *)scs_calloc(m, sizeof(scs_float));
121
139
  }
122
140
  SCS(scale_array)(sol->y, NAN, m);
123
141
  if (!sol->s) {
124
- sol->s = (scs_float *)scs_malloc(sizeof(scs_float) * m);
142
+ sol->s = (scs_float *)scs_calloc(m, sizeof(scs_float));
125
143
  }
126
144
  SCS(scale_array)(sol->s, NAN, m);
127
145
  }
@@ -138,70 +156,90 @@ static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
138
156
  return status;
139
157
  }
140
158
 
141
- static void warm_start_vars(ScsWork *w, const ScsSolution *sol) {
142
- scs_int i, n = w->n, m = w->m;
143
- memset(w->v, 0, n * sizeof(scs_float));
144
- memcpy(w->u, sol->x, n * sizeof(scs_float));
145
- memcpy(&(w->u[n]), sol->y, m * sizeof(scs_float));
146
- memcpy(&(w->v[n]), sol->s, m * sizeof(scs_float));
147
- w->u[n + m] = 1.0;
148
- w->v[n + m] = 0.0;
149
- #ifndef NOVALIDATE
150
- for (i = 0; i < n + m + 1; ++i) {
151
- if (scs_isnan(w->u[i])) {
152
- w->u[i] = 0;
153
- }
154
- if (scs_isnan(w->v[i])) {
155
- w->v[i] = 0;
156
- }
159
+ /* given x,y,s warm start, set v = [x; s / R + y; 1]
160
+ * where R = diag(w->rho_y_vec).
161
+ */
162
+ static void warm_start_vars(ScsWork *w, ScsSolution *sol) {
163
+ scs_int n = w->n, m = w->m, i;
164
+ scs_float *v = w->v;
165
+ /* normalize the warm-start */
166
+ if (w->stgs->normalize) {
167
+ SCS(normalize_sol)(w, sol);
157
168
  }
158
- #endif
169
+ memcpy(v, sol->x, n * sizeof(scs_float));
170
+ for (i = 0; i < m; ++i) {
171
+ v[i + n] = sol->y[i] + sol->s[i] / w->rho_y_vec[i];
172
+ }
173
+ v[n + m] = 1.0; /* tau = 1 */
174
+ /* un-normalize so sol unchanged */
159
175
  if (w->stgs->normalize) {
160
- SCS(normalize_warm_start)(w);
176
+ SCS(un_normalize_sol)(w, sol);
161
177
  }
162
178
  }
163
179
 
164
- static scs_float calc_primal_resid(ScsWork *w, const scs_float *x,
165
- const scs_float *s, const scs_float tau,
166
- scs_float *nm_axs) {
167
- scs_int i;
168
- scs_float pres = 0, scale, *pr = w->pr;
169
- *nm_axs = 0;
170
- memset(pr, 0, w->m * sizeof(scs_float));
171
- SCS(accum_by_a)(w->A, w->p, x, pr);
172
- SCS(add_scaled_array)(pr, s, w->m, 1.0); /* pr = Ax + s */
173
- for (i = 0; i < w->m; ++i) {
174
- scale = w->stgs->normalize ? w->scal->D[i] / (w->sc_b * w->stgs->scale) : 1;
175
- scale = scale * scale;
176
- *nm_axs += (pr[i] * pr[i]) * scale;
177
- pres += (pr[i] - w->b[i] * tau) * (pr[i] - w->b[i] * tau) * scale;
178
- }
179
- *nm_axs = SQRTF(*nm_axs);
180
- return SQRTF(pres); /* SCS(norm)(Ax + s - b * tau) */
181
- }
182
-
183
- static scs_float calc_dual_resid(ScsWork *w, const scs_float *y,
184
- const scs_float tau, scs_float *nm_a_ty) {
185
- scs_int i;
186
- scs_float dres = 0, scale, *dr = w->dr;
187
- *nm_a_ty = 0;
188
- memset(dr, 0, w->n * sizeof(scs_float));
189
- SCS(accum_by_atrans)(w->A, w->p, y, dr); /* dr = A'y */
190
- for (i = 0; i < w->n; ++i) {
191
- scale = w->stgs->normalize ? w->scal->E[i] / (w->sc_c * w->stgs->scale) : 1;
192
- scale = scale * scale;
193
- *nm_a_ty += (dr[i] * dr[i]) * scale;
194
- dres += (dr[i] + w->c[i] * tau) * (dr[i] + w->c[i] * tau) * scale;
180
+ static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n) {
181
+ r->res_pri = SAFEDIV_POS(NORM(r->ax_s_btau, m), r->tau);
182
+ r->res_dual = SAFEDIV_POS(NORM(r->px_aty_ctau, n), r->tau);
183
+ r->res_unbdd_a = NAN;
184
+ r->res_unbdd_p = NAN;
185
+ r->res_infeas = NAN;
186
+ if (r->ctx_tau < 0) {
187
+ r->res_unbdd_a = SAFEDIV_POS(NORM(r->ax_s, m), -r->ctx_tau);
188
+ r->res_unbdd_p = SAFEDIV_POS(NORM(r->px, n), -r->ctx_tau);
195
189
  }
196
- *nm_a_ty = SQRTF(*nm_a_ty);
197
- return SQRTF(dres); /* SCS(norm)(A'y + c * tau) */
190
+ if (r->bty_tau < 0) {
191
+ r->res_infeas = SAFEDIV_POS(NORM(r->aty, n), -r->bty_tau);
192
+ }
193
+ }
194
+
195
+ static void unnormalize_residuals(ScsWork *w) {
196
+ ScsResiduals *r_n = w->r_normalized; /* normalized residuals */
197
+ ScsResiduals *r = w->r_orig; /* original problem residuals */
198
+ scs_float pd = w->scal->primal_scale * w->scal->dual_scale;
199
+
200
+ /* copy vars */
201
+ r->last_iter = r_n->last_iter;
202
+ r->tau = r_n->tau;
203
+
204
+ /* mem copy arrays */
205
+ memcpy(r->ax, r_n->ax, w->m * sizeof(scs_float));
206
+ memcpy(r->ax_s, r_n->ax_s, w->m * sizeof(scs_float));
207
+ memcpy(r->ax_s_btau, r_n->ax_s_btau, w->m * sizeof(scs_float));
208
+ memcpy(r->aty, r_n->aty, w->n * sizeof(scs_float));
209
+ memcpy(r->px, r_n->px, w->n * sizeof(scs_float));
210
+ memcpy(r->px_aty_ctau, r_n->px_aty_ctau, w->n * sizeof(scs_float));
211
+
212
+ /* unnormalize */
213
+ r->kap = r_n->kap / pd;
214
+ r->bty_tau = r_n->bty_tau / pd;
215
+ r->ctx_tau = r_n->ctx_tau / pd;
216
+ r->xt_p_x_tau = r_n->xt_p_x_tau / pd;
217
+ r->xt_p_x = r_n->xt_p_x / pd;
218
+ r->ctx = r_n->ctx / pd;
219
+ r->bty = r_n->bty / pd;
220
+ r->pobj = r_n->pobj / pd;
221
+ r->dobj = r_n->dobj / pd;
222
+ r->gap = r_n->gap / pd;
223
+
224
+ SCS(un_normalize_primal)(w, r->ax);
225
+ SCS(un_normalize_primal)(w, r->ax_s);
226
+ SCS(un_normalize_primal)(w, r->ax_s_btau);
227
+ SCS(un_normalize_dual)(w, r->aty);
228
+ SCS(un_normalize_dual)(w, r->px);
229
+ SCS(un_normalize_dual)(w, r->px_aty_ctau);
230
+
231
+ compute_residuals(r, w->m, w->n);
198
232
  }
199
233
 
200
- /* calculates un-normalized quantities */
201
- static void calc_residuals(ScsWork *w, ScsResiduals *r, scs_int iter) {
202
- scs_float *x = w->u, *y = &(w->u[w->n]), *s = &(w->v[w->n]);
203
- scs_float nmpr_tau, nmdr_tau, nm_axs_tau, nm_a_ty_tau, ct_x, bt_y;
234
+ /* calculates un-normalized residual quantities */
235
+ /* this is somewhat slow but not a bottleneck */
236
+ static void populate_residual_struct(ScsWork *w, scs_int iter) {
204
237
  scs_int n = w->n, m = w->m;
238
+ /* normalized x,y,s terms */
239
+ scs_float *x = w->xys_normalized->x;
240
+ scs_float *y = w->xys_normalized->y;
241
+ scs_float *s = w->xys_normalized->s;
242
+ ScsResiduals *r = w->r_normalized; /* normalized residuals */
205
243
 
206
244
  /* checks if the residuals are unchanged by checking iteration */
207
245
  if (r->last_iter == iter) {
@@ -209,272 +247,350 @@ static void calc_residuals(ScsWork *w, ScsResiduals *r, scs_int iter) {
209
247
  }
210
248
  r->last_iter = iter;
211
249
 
250
+ memcpy(x, w->u, n * sizeof(scs_float));
251
+ memcpy(y, &(w->u[n]), m * sizeof(scs_float));
252
+ memcpy(s, &(w->rsk[n]), m * sizeof(scs_float));
253
+
212
254
  r->tau = ABS(w->u[n + m]);
213
- r->kap = ABS(w->v[n + m]) /
214
- (w->stgs->normalize ? (w->stgs->scale * w->sc_c * w->sc_b) : 1);
255
+ r->kap = ABS(w->rsk[n + m]);
256
+
257
+ /**************** PRIMAL *********************/
258
+ memset(r->ax, 0, m * sizeof(scs_float));
259
+ /* ax = Ax */
260
+ SCS(accum_by_a)(w->A, x, r->ax);
261
+
262
+ memcpy(r->ax_s, r->ax, m * sizeof(scs_float));
263
+ /* ax_s = Ax + s */
264
+ SCS(add_scaled_array)(r->ax_s, s, m, 1.);
265
+
266
+ memcpy(r->ax_s_btau, r->ax_s, m * sizeof(scs_float));
267
+ /* ax_s_btau = Ax + s - b * tau */
268
+ SCS(add_scaled_array)(r->ax_s_btau, w->b_normalized, m, -r->tau);
269
+
270
+ /**************** DUAL *********************/
271
+ memset(r->px, 0, n * sizeof(scs_float));
272
+ if (w->P) {
273
+ /* px = Px */
274
+ SCS(accum_by_p)(w->P, x, r->px);
275
+ r->xt_p_x_tau = SCS(dot)(r->px, x, n);
276
+ } else {
277
+ r->xt_p_x_tau = 0.;
278
+ }
215
279
 
216
- nmpr_tau = calc_primal_resid(w, x, s, r->tau, &nm_axs_tau);
217
- nmdr_tau = calc_dual_resid(w, y, r->tau, &nm_a_ty_tau);
280
+ memset(r->aty, 0, n * sizeof(scs_float));
281
+ /* aty = A'y */
282
+ SCS(accum_by_atrans)(w->A, y, r->aty);
218
283
 
219
- r->bt_y_by_tau =
220
- SCS(dot)(y, w->b, m) /
221
- (w->stgs->normalize ? (w->stgs->scale * w->sc_c * w->sc_b) : 1);
222
- r->ct_x_by_tau =
223
- SCS(dot)(x, w->c, n) /
224
- (w->stgs->normalize ? (w->stgs->scale * w->sc_c * w->sc_b) : 1);
284
+ /* r->px_aty_ctau = Px */
285
+ memcpy(r->px_aty_ctau, r->px, n * sizeof(scs_float));
286
+ /* r->px_aty_ctau = Px + A'y */
287
+ SCS(add_scaled_array)(r->px_aty_ctau, r->aty, n, 1.);
288
+ /* r->px_aty_ctau = Px + A'y + c * tau */
289
+ SCS(add_scaled_array)(r->px_aty_ctau, w->c_normalized, n, r->tau);
225
290
 
226
- r->res_infeas =
227
- r->bt_y_by_tau < 0 ? w->nm_b * nm_a_ty_tau / -r->bt_y_by_tau : NAN;
228
- r->res_unbdd =
229
- r->ct_x_by_tau < 0 ? w->nm_c * nm_axs_tau / -r->ct_x_by_tau : NAN;
291
+ /**************** OTHERS *****************/
292
+ r->bty_tau = SCS(dot)(y, w->b_normalized, m);
293
+ r->ctx_tau = SCS(dot)(x, w->c_normalized, n);
230
294
 
231
- bt_y = SAFEDIV_POS(r->bt_y_by_tau, r->tau);
232
- ct_x = SAFEDIV_POS(r->ct_x_by_tau, r->tau);
295
+ r->bty = SAFEDIV_POS(r->bty_tau, r->tau);
296
+ r->ctx = SAFEDIV_POS(r->ctx_tau, r->tau);
297
+ r->xt_p_x = SAFEDIV_POS(r->xt_p_x_tau, r->tau * r->tau);
233
298
 
234
- r->res_pri = SAFEDIV_POS(nmpr_tau / (1 + w->nm_b), r->tau);
235
- r->res_dual = SAFEDIV_POS(nmdr_tau / (1 + w->nm_c), r->tau);
236
- r->rel_gap = ABS(ct_x + bt_y) / (1 + ABS(ct_x) + ABS(bt_y));
299
+ r->gap = ABS(r->xt_p_x + r->ctx + r->bty);
300
+ r->pobj = r->xt_p_x / 2. + r->ctx;
301
+ r->dobj = -r->xt_p_x / 2. - r->bty;
302
+
303
+ compute_residuals(r, m, n);
304
+
305
+ if (w->stgs->normalize) {
306
+ memcpy(w->xys_orig->x, w->xys_normalized->x, n * sizeof(scs_float));
307
+ memcpy(w->xys_orig->y, w->xys_normalized->y, m * sizeof(scs_float));
308
+ memcpy(w->xys_orig->s, w->xys_normalized->s, m * sizeof(scs_float));
309
+ SCS(un_normalize_sol)(w, w->xys_orig);
310
+ unnormalize_residuals(w);
311
+ }
237
312
  }
238
313
 
239
314
  static void cold_start_vars(ScsWork *w) {
240
315
  scs_int l = w->n + w->m + 1;
241
- memset(w->u, 0, l * sizeof(scs_float));
242
316
  memset(w->v, 0, l * sizeof(scs_float));
243
- w->u[l - 1] = SQRTF((scs_float)l);
244
- w->v[l - 1] = SQRTF((scs_float)l);
317
+ w->v[l - 1] = 1.;
245
318
  }
246
319
 
247
- /* status < 0 indicates failure */
248
- static scs_int project_lin_sys(ScsWork *w, scs_int iter) {
249
- /* ut = u + v */
250
-
251
- scs_int n = w->n, m = w->m, l = n + m + 1, status;
252
- memcpy(w->u_t, w->u, l * sizeof(scs_float));
253
- SCS(add_scaled_array)(w->u_t, w->v, l, 1.0);
254
-
255
- SCS(scale_array)(w->u_t, w->stgs->rho_x, n);
256
-
257
- SCS(add_scaled_array)(w->u_t, w->h, l - 1, -w->u_t[l - 1]);
258
- SCS(add_scaled_array)
259
- (w->u_t, w->h, l - 1, -SCS(dot)(w->u_t, w->g, l - 1) / (w->g_th + 1));
260
- SCS(scale_array)(&(w->u_t[n]), -1, m);
320
+ /* utility function that scales first n entries in inner prod by rho_x */
321
+ /* and last m entries by 1 / rho_y_vec, assumes length of array is n + m */
322
+ /* See .note_on_scale in repo for explanation */
323
+ static scs_float dot_with_diag_scaling(ScsWork *w, const scs_float *x,
324
+ const scs_float *y) {
325
+ scs_int i, n = w->n, len = w->n + w->m;
326
+ scs_float ip = 0.0;
327
+ for (i = 0; i < n; ++i) {
328
+ ip += w->stgs->rho_x * x[i] * y[i];
329
+ }
330
+ for (i = n; i < len; ++i) {
331
+ ip += x[i] * y[i] * w->rho_y_vec[i - n];
332
+ }
333
+ return ip;
334
+ }
261
335
 
262
- status = SCS(solve_lin_sys)(w->A, w->stgs, w->p, w->u_t, w->u, iter);
336
+ static inline scs_float get_tau_scale(ScsWork *w) {
337
+ return TAU_FACTOR; /* TAU_FACTOR * w->scale; */
338
+ }
263
339
 
264
- w->u_t[l - 1] += SCS(dot)(w->u_t, w->h, l - 1);
340
+ static scs_float root_plus(ScsWork *w, scs_float *p, scs_float *mu,
341
+ scs_float eta) {
342
+ scs_float b, c, tau, a, tau_scale;
343
+ tau_scale = get_tau_scale(w);
344
+ a = tau_scale + dot_with_diag_scaling(w, w->g, w->g);
345
+ b = (dot_with_diag_scaling(w, mu, w->g) -
346
+ 2 * dot_with_diag_scaling(w, p, w->g) - eta * tau_scale);
347
+ c = dot_with_diag_scaling(w, p, p) - dot_with_diag_scaling(w, p, mu);
348
+ tau = (-b + SQRTF(MAX(b * b - 4 * a * c, 0.))) / (2 * a);
349
+ return tau;
350
+ }
265
351
 
352
+ /* status < 0 indicates failure */
353
+ static scs_int project_lin_sys(ScsWork *w, scs_int iter) {
354
+ scs_int n = w->n, m = w->m, l = n + m + 1, status, i;
355
+ scs_float *warm_start = SCS_NULL;
356
+ scs_float tol = -1.0; /* only used for indirect methods, overriden later */
357
+ memcpy(w->u_t, w->v, l * sizeof(scs_float));
358
+ SCS(scale_array)(w->u_t, w->stgs->rho_x, n);
359
+ for (i = n; i < l - 1; ++i) {
360
+ w->u_t[i] *= -w->rho_y_vec[i - n];
361
+ }
362
+ #if INDIRECT > 0
363
+ /* compute warm start using the cone projection output */
364
+ warm_start = w->lin_sys_warm_start;
365
+ memcpy(warm_start, w->u, (l - 1) * sizeof(scs_float));
366
+ /* warm_start = u[:n] + tau * g[:n] */
367
+ SCS(add_scaled_array)(warm_start, w->g, l - 1, w->u[l - 1]);
368
+ /* use normalized residuals to compute tolerance */
369
+ tol = MIN(CG_NORM(w->r_normalized->ax_s_btau, w->m),
370
+ CG_NORM(w->r_normalized->px_aty_ctau, w->n));
371
+ /* tol ~ O(1/k^(1+eps)) guarantees convergence */
372
+ /* use warm-start to calculate tolerance rather than w->u_t, since warm_start
373
+ * should be approximately equal to the true solution */
374
+ tol = CG_TOL_FACTOR * MIN(tol, CG_NORM(warm_start, w->n) /
375
+ POWF((scs_float)iter + 1, CG_RATE));
376
+ tol = MAX(CG_BEST_TOL, tol);
377
+ #endif
378
+ status = SCS(solve_lin_sys)(w->p, w->u_t, warm_start, tol);
379
+ if (iter < FEASIBLE_ITERS) {
380
+ w->u_t[l - 1] = 1.;
381
+ } else {
382
+ w->u_t[l - 1] = root_plus(w, w->u_t, w->v, w->v[l - 1]);
383
+ }
384
+ SCS(add_scaled_array)(w->u_t, w->g, l - 1, -w->u_t[l - 1]);
266
385
  return status;
267
386
  }
268
387
 
388
+ /* Compute the [r;s;kappa] iterate
389
+ *
390
+ * rsk^{k+1} = R ( u^{k+1} + v^k - 2 * u_t^{k+1} )
391
+ *
392
+ * uses Moreau decomposition to get projection onto dual cone
393
+ * since it depends on v^k MUST be called before update_dual_vars is done
394
+ * (no effect of w->stgs->alpha here).
395
+ */
396
+ static void compute_rsk(ScsWork *w) {
397
+ scs_int i, l = w->m + w->n + 1;
398
+ /* r, should = 0 so skip */
399
+ /*
400
+ for (i = 0; i < w->n; ++i) {
401
+ w->rsk[i] = w->stgs->rho_x * (w->v[i] + w->u[i] - 2 * w->u_t[i]);
402
+ }
403
+ */
404
+ /* s */
405
+ for (i = w->n; i < l - 1; ++i) {
406
+ w->rsk[i] = (w->v[i] + w->u[i] - 2 * w->u_t[i]) * w->rho_y_vec[i - w->n];
407
+ }
408
+ /* kappa, incorporates tau scaling parameter */
409
+ w->rsk[l - 1] =
410
+ get_tau_scale(w) * (w->v[l - 1] + w->u[l - 1] - 2 * w->u_t[l - 1]);
411
+ }
412
+
269
413
  static void update_dual_vars(ScsWork *w) {
270
- scs_int i, n = w->n, l = n + w->m + 1;
271
- /* this does not relax 'x' variable */
272
- for (i = n; i < l; ++i) {
273
- w->v[i] += (w->u[i] - w->stgs->alpha * w->u_t[i] -
274
- (1.0 - w->stgs->alpha) * w->u_prev[i]);
414
+ scs_int i, l = w->n + w->m + 1;
415
+ scs_float a = w->stgs->alpha;
416
+ /* compute and store [r;s;kappa] */
417
+ compute_rsk(w);
418
+ for (i = 0; i < l; ++i) {
419
+ w->v[i] += a * (w->u[i] - w->u_t[i]);
275
420
  }
276
421
  }
277
422
 
278
423
  /* status < 0 indicates failure */
279
424
  static scs_int project_cones(ScsWork *w, const ScsCone *k, scs_int iter) {
280
- scs_int i, n = w->n, l = n + w->m + 1, status;
281
- /* this does not relax 'x' variable */
282
- for (i = 0; i < n; ++i) {
283
- w->u[i] = w->u_t[i] - w->v[i];
284
- }
285
- for (i = n; i < l; ++i) {
286
- w->u[i] = w->stgs->alpha * w->u_t[i] + (1 - w->stgs->alpha) * w->u_prev[i] -
287
- w->v[i];
425
+ scs_int i, n = w->n, l = w->n + w->m + 1, status;
426
+ for (i = 0; i < l; ++i) {
427
+ w->u[i] = 2 * w->u_t[i] - w->v[i];
288
428
  }
289
429
  /* u = [x;y;tau] */
290
- status =
291
- SCS(proj_dual_cone)(&(w->u[n]), k, w->cone_work, &(w->u_prev[n]), iter);
292
- if (w->u[l - 1] < 0.0) {
293
- w->u[l - 1] = 0.0;
430
+ status = SCS(proj_dual_cone)(&(w->u[n]), k, w->cone_work, w->stgs->normalize);
431
+ if (iter < FEASIBLE_ITERS) {
432
+ w->u[l - 1] = 1.0;
433
+ } else {
434
+ w->u[l - 1] = MAX(w->u[l - 1], 0.);
294
435
  }
295
-
296
436
  return status;
297
437
  }
298
438
 
299
- static scs_int indeterminate(ScsWork *w, ScsSolution *sol, ScsInfo *info) {
300
- strcpy(info->status, "Indeterminate");
301
- SCS(scale_array)(sol->x, NAN, w->n);
302
- SCS(scale_array)(sol->y, NAN, w->m);
303
- SCS(scale_array)(sol->s, NAN, w->m);
304
- return SCS_INDETERMINATE;
305
- }
306
-
307
- static void sety(ScsWork *w, ScsSolution *sol) {
439
+ static void sety(const ScsWork *w, ScsSolution *sol) {
308
440
  if (!sol->y) {
309
- sol->y = (scs_float *)scs_malloc(sizeof(scs_float) * w->m);
441
+ sol->y = (scs_float *)scs_calloc(w->m, sizeof(scs_float));
310
442
  }
311
443
  memcpy(sol->y, &(w->u[w->n]), w->m * sizeof(scs_float));
312
444
  }
313
445
 
314
- static void sets(ScsWork *w, ScsSolution *sol) {
446
+ /* s is contained in rsk */
447
+ static void sets(const ScsWork *w, ScsSolution *sol) {
315
448
  if (!sol->s) {
316
- sol->s = (scs_float *)scs_malloc(sizeof(scs_float) * w->m);
449
+ sol->s = (scs_float *)scs_calloc(w->m, sizeof(scs_float));
317
450
  }
318
- memcpy(sol->s, &(w->v[w->n]), w->m * sizeof(scs_float));
451
+ memcpy(sol->s, &(w->rsk[w->n]), w->m * sizeof(scs_float));
319
452
  }
320
453
 
321
- static void setx(ScsWork *w, ScsSolution *sol) {
454
+ static void setx(const ScsWork *w, ScsSolution *sol) {
322
455
  if (!sol->x) {
323
- sol->x = (scs_float *)scs_malloc(sizeof(scs_float) * w->n);
456
+ sol->x = (scs_float *)scs_calloc(w->n, sizeof(scs_float));
324
457
  }
325
458
  memcpy(sol->x, w->u, w->n * sizeof(scs_float));
326
459
  }
327
460
 
328
- static scs_float get_max_residual(ScsResiduals *r) {
329
- return MAX(r->rel_gap, MAX(r->res_pri, r->res_dual));
330
- }
331
-
332
- static void copy_from_best_iterate(ScsWork *w) {
333
- memcpy(w->u, w->u_best, (w->m + w->n + 1) * sizeof(scs_float));
334
- memcpy(w->v, w->v_best, (w->m + w->n + 1) * sizeof(scs_float));
461
+ static void set_solved(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
462
+ SCS(scale_array)(sol->x, SAFEDIV_POS(1.0, w->r_orig->tau), w->n);
463
+ SCS(scale_array)(sol->y, SAFEDIV_POS(1.0, w->r_orig->tau), w->m);
464
+ SCS(scale_array)(sol->s, SAFEDIV_POS(1.0, w->r_orig->tau), w->m);
465
+ info->gap = w->r_orig->gap;
466
+ info->res_pri = w->r_orig->res_pri;
467
+ info->res_dual = w->r_orig->res_dual;
468
+ info->pobj = w->r_orig->xt_p_x / 2. + w->r_orig->ctx;
469
+ info->dobj = -w->r_orig->xt_p_x / 2. - w->r_orig->bty;
470
+ strcpy(info->status, "solved");
471
+ info->status_val = SCS_SOLVED;
335
472
  }
336
473
 
337
- static scs_int solved(ScsWork *w, ScsSolution *sol, ScsInfo *info,
338
- ScsResiduals *r, scs_int iter) {
339
- if (w->best_max_residual < get_max_residual(r)) {
340
- r->last_iter = -1; /* Forces residual recomputation. */
341
- copy_from_best_iterate(w);
342
- calc_residuals(w, r, iter);
343
- setx(w, sol);
344
- sety(w, sol);
345
- sets(w, sol);
346
- }
347
- SCS(scale_array)(sol->x, SAFEDIV_POS(1.0, r->tau), w->n);
348
- SCS(scale_array)(sol->y, SAFEDIV_POS(1.0, r->tau), w->m);
349
- SCS(scale_array)(sol->s, SAFEDIV_POS(1.0, r->tau), w->m);
350
- if (info->status_val == 0) {
351
- strcpy(info->status, "Solved/Inaccurate");
352
- return SCS_SOLVED_INACCURATE;
353
- }
354
- strcpy(info->status, "Solved");
355
- return SCS_SOLVED;
356
- }
357
-
358
- static scs_int infeasible(ScsWork *w, ScsSolution *sol, ScsInfo *info,
359
- scs_float bt_y) {
360
- SCS(scale_array)(sol->y, -1 / bt_y, w->m);
474
+ static void set_infeasible(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
475
+ SCS(scale_array)(sol->y, -1 / w->r_orig->bty_tau, w->m);
361
476
  SCS(scale_array)(sol->x, NAN, w->n);
362
477
  SCS(scale_array)(sol->s, NAN, w->m);
363
- if (info->status_val == 0) {
364
- strcpy(info->status, "Infeasible/Inaccurate");
365
- return SCS_INFEASIBLE_INACCURATE;
366
- }
367
- strcpy(info->status, "Infeasible");
368
- return SCS_INFEASIBLE;
478
+ info->gap = NAN;
479
+ info->res_pri = NAN;
480
+ info->res_dual = NAN;
481
+ info->pobj = INFINITY;
482
+ info->dobj = INFINITY;
483
+ strcpy(info->status, "infeasible");
484
+ info->status_val = SCS_INFEASIBLE;
369
485
  }
370
486
 
371
- static scs_int unbounded(ScsWork *w, ScsSolution *sol, ScsInfo *info,
372
- scs_float ct_x) {
373
- SCS(scale_array)(sol->x, -1 / ct_x, w->n);
374
- SCS(scale_array)(sol->s, -1 / ct_x, w->m);
487
+ static void set_unbounded(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
488
+ SCS(scale_array)(sol->x, -1 / w->r_orig->ctx_tau, w->n);
489
+ SCS(scale_array)(sol->s, -1 / w->r_orig->ctx_tau, w->m);
375
490
  SCS(scale_array)(sol->y, NAN, w->m);
376
- if (info->status_val == 0) {
377
- strcpy(info->status, "Unbounded/Inaccurate");
378
- return SCS_UNBOUNDED_INACCURATE;
379
- }
380
- strcpy(info->status, "Unbounded");
381
- return SCS_UNBOUNDED;
382
- }
383
-
384
- static scs_int is_solved_status(scs_int status) {
385
- return status == SCS_SOLVED || status == SCS_SOLVED_INACCURATE;
491
+ info->gap = NAN;
492
+ info->res_pri = NAN;
493
+ info->res_dual = NAN;
494
+ info->pobj = -INFINITY;
495
+ info->dobj = -INFINITY;
496
+ strcpy(info->status, "unbounded");
497
+ info->status_val = SCS_UNBOUNDED;
386
498
  }
387
499
 
388
- static scs_int is_infeasible_status(scs_int status) {
389
- return status == SCS_INFEASIBLE || status == SCS_INFEASIBLE_INACCURATE;
390
- }
391
-
392
- static scs_int is_unbounded_status(scs_int status) {
393
- return status == SCS_UNBOUNDED || status == SCS_UNBOUNDED_INACCURATE;
394
- }
395
-
396
- static void get_info(ScsWork *w, ScsSolution *sol, ScsInfo *info,
397
- ScsResiduals *r, scs_int iter) {
398
- info->iter = iter;
399
- info->res_infeas = r->res_infeas;
400
- info->res_unbdd = r->res_unbdd;
401
- if (is_solved_status(info->status_val)) {
402
- info->rel_gap = r->rel_gap;
403
- info->res_pri = r->res_pri;
404
- info->res_dual = r->res_dual;
405
- info->pobj = r->ct_x_by_tau / r->tau;
406
- info->dobj = -r->bt_y_by_tau / r->tau;
407
- } else if (is_unbounded_status(info->status_val)) {
408
- info->rel_gap = NAN;
409
- info->res_pri = NAN;
410
- info->res_dual = NAN;
411
- info->pobj = -INFINITY;
412
- info->dobj = -INFINITY;
413
- } else if (is_infeasible_status(info->status_val)) {
414
- info->rel_gap = NAN;
415
- info->res_pri = NAN;
416
- info->res_dual = NAN;
417
- info->pobj = INFINITY;
418
- info->dobj = INFINITY;
500
+ /* not yet converged, take best guess */
501
+ static void set_unfinished(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
502
+ if (w->r_orig->tau > w->r_orig->kap) {
503
+ set_solved(w, sol, info);
504
+ info->status_val = SCS_SOLVED_INACCURATE;
505
+ } else if (w->r_orig->bty_tau < w->r_orig->ctx_tau) {
506
+ set_infeasible(w, sol, info);
507
+ info->status_val = SCS_INFEASIBLE_INACCURATE;
508
+ } else {
509
+ set_unbounded(w, sol, info);
510
+ info->status_val = SCS_UNBOUNDED_INACCURATE;
511
+ }
512
+ /* Append inaccurate to the status string */
513
+ if (w->time_limit_reached) {
514
+ strcat(info->status, " (inaccurate - reached time_limit_secs)");
515
+ } else if (info->iter >= w->stgs->max_iters) {
516
+ strcat(info->status, " (inaccurate - reached max_iters)");
517
+ } else {
518
+ scs_printf("ERROR: should not be in this state (1).\n");
419
519
  }
420
520
  }
421
521
 
422
522
  /* sets solutions, re-scales by inner prods if infeasible or unbounded */
423
- static void get_solution(ScsWork *w, ScsSolution *sol, ScsInfo *info,
424
- ScsResiduals *r, scs_int iter) {
425
- scs_int l = w->n + w->m + 1;
426
- calc_residuals(w, r, iter);
523
+ static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
524
+ scs_int iter) {
427
525
  setx(w, sol);
428
526
  sety(w, sol);
429
527
  sets(w, sol);
430
- if (info->status_val == SCS_UNFINISHED) {
431
- /* not yet converged, take best guess */
432
- if (r->tau > INDETERMINATE_TOL && r->tau > r->kap) {
433
- info->status_val = solved(w, sol, info, r, iter);
434
- } else if (SCS(norm)(w->u, l) < INDETERMINATE_TOL * SQRTF((scs_float)l)) {
435
- info->status_val = indeterminate(w, sol, info);
436
- } else if (r->bt_y_by_tau < r->ct_x_by_tau) {
437
- info->status_val = infeasible(w, sol, info, r->bt_y_by_tau);
438
- } else {
439
- info->status_val = unbounded(w, sol, info, r->ct_x_by_tau);
440
- }
441
- } else if (is_solved_status(info->status_val)) {
442
- info->status_val = solved(w, sol, info, r, iter);
443
- } else if (is_infeasible_status(info->status_val)) {
444
- info->status_val = infeasible(w, sol, info, r->bt_y_by_tau);
445
- } else {
446
- info->status_val = unbounded(w, sol, info, r->ct_x_by_tau);
447
- }
448
528
  if (w->stgs->normalize) {
449
529
  SCS(un_normalize_sol)(w, sol);
450
530
  }
451
- get_info(w, sol, info, r, iter);
531
+ populate_residual_struct(w, iter);
532
+ info->setup_time = w->setup_time;
533
+ info->iter = iter;
534
+ info->res_infeas = w->r_orig->res_infeas;
535
+ info->res_unbdd_a = w->r_orig->res_unbdd_a;
536
+ info->res_unbdd_p = w->r_orig->res_unbdd_p;
537
+ info->scale = w->scale;
538
+ info->scale_updates = w->scale_updates;
539
+ info->rejected_accel_steps = w->rejected_accel_steps;
540
+ info->accepted_accel_steps = w->accepted_accel_steps;
541
+ info->comp_slack = ABS(SCS(dot)(sol->s, sol->y, w->m));
542
+ if (info->comp_slack >
543
+ 1e-5 * MAX(SCS(norm_inf)(sol->s, w->m), SCS(norm_inf)(sol->y, w->m))) {
544
+ scs_printf("WARNING - large complementary slackness residual: %f\n",
545
+ info->comp_slack);
546
+ }
547
+ switch (info->status_val) {
548
+ case SCS_SOLVED:
549
+ set_solved(w, sol, info);
550
+ break;
551
+ case SCS_INFEASIBLE:
552
+ set_infeasible(w, sol, info);
553
+ break;
554
+ case SCS_UNBOUNDED:
555
+ set_unbounded(w, sol, info);
556
+ break;
557
+ case SCS_UNFINISHED: /* When SCS reaches max_iters or time_limit_secs */
558
+ set_unfinished(w, sol, info);
559
+ break;
560
+ default:
561
+ scs_printf("ERROR: should not be in this state (2).\n");
562
+ }
452
563
  }
453
564
 
454
- static void print_summary(ScsWork *w, scs_int i, ScsResiduals *r,
455
- SCS(timer) * solve_timer) {
565
+ static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer) {
566
+ ScsResiduals *r = w->r_orig;
456
567
  scs_printf("%*i|", (int)strlen(HEADER[0]), (int)i);
457
568
  scs_printf("%*.2e ", (int)HSPACE, r->res_pri);
458
569
  scs_printf("%*.2e ", (int)HSPACE, r->res_dual);
459
- scs_printf("%*.2e ", (int)HSPACE, r->rel_gap);
460
- scs_printf("%*.2e ", (int)HSPACE, SAFEDIV_POS(r->ct_x_by_tau, r->tau));
461
- scs_printf("%*.2e ", (int)HSPACE, SAFEDIV_POS(-r->bt_y_by_tau, r->tau));
462
- scs_printf("%*.2e ", (int)HSPACE, SAFEDIV_POS(r->kap, r->tau));
570
+ scs_printf("%*.2e ", (int)HSPACE, r->gap);
571
+ /* report mid point of primal and dual objective values */
572
+ scs_printf("%*.2e ", (int)HSPACE, 0.5 * (r->pobj + r->dobj));
573
+ scs_printf("%*.2e ", (int)HSPACE, w->scale);
463
574
  scs_printf("%*.2e ", (int)HSPACE, SCS(tocq)(solve_timer) / 1e3);
464
575
  scs_printf("\n");
465
576
 
466
- #if EXTRA_VERBOSE > 0
467
- scs_printf("Norm u = %4f, ", SCS(norm)(w->u, w->n + w->m + 1));
468
- scs_printf("Norm u_t = %4f, ", SCS(norm)(w->u_t, w->n + w->m + 1));
469
- scs_printf("Norm v = %4f, ", SCS(norm)(w->v, w->n + w->m + 1));
577
+ #if VERBOSITY > 0
578
+ scs_printf("Norm u = %4f, ", SCS(norm_2)(w->u, w->n + w->m + 1));
579
+ scs_printf("Norm u_t = %4f, ", SCS(norm_2)(w->u_t, w->n + w->m + 1));
580
+ scs_printf("Norm v = %4f, ", SCS(norm_2)(w->v, w->n + w->m + 1));
581
+ scs_printf("Norm x = %4f, ", SCS(norm_2)(w->xys_orig->x, w->n));
582
+ scs_printf("Norm y = %4f, ", SCS(norm_2)(w->xys_orig->y, w->m));
583
+ scs_printf("Norm s = %4f, ", SCS(norm_2)(w->xys_orig->s, w->m));
584
+ scs_printf("Norm |Ax + s| = %1.2e, ", SCS(norm_2)(r->ax_s, w->m));
470
585
  scs_printf("tau = %4f, ", w->u[w->n + w->m]);
471
- scs_printf("kappa = %4f, ", w->v[w->n + w->m]);
472
- scs_printf("|u - u_prev| = %1.2e, ",
473
- SCS(norm_diff)(w->u, w->u_prev, w->n + w->m + 1));
586
+ scs_printf("kappa = %4f, ", w->rsk[w->n + w->m]);
474
587
  scs_printf("|u - u_t| = %1.2e, ",
475
588
  SCS(norm_diff)(w->u, w->u_t, w->n + w->m + 1));
476
589
  scs_printf("res_infeas = %1.2e, ", r->res_infeas);
477
- scs_printf("res_unbdd = %1.2e\n", r->res_unbdd);
590
+ scs_printf("res_unbdd_a = %1.2e, ", r->res_unbdd_a);
591
+ scs_printf("res_unbdd_p = %1.2e, ", r->res_unbdd_p);
592
+ scs_printf("ctx_tau = %1.2e, ", r->ctx_tau);
593
+ scs_printf("bty_tau = %1.2e\n", r->bty_tau);
478
594
  #endif
479
595
 
480
596
  #ifdef MATLAB_MEX_FILE
@@ -484,9 +600,6 @@ static void print_summary(ScsWork *w, scs_int i, ScsResiduals *r,
484
600
 
485
601
  static void print_header(ScsWork *w, const ScsCone *k) {
486
602
  scs_int i;
487
- if (w->stgs->warm_start) {
488
- scs_printf("SCS using variable warm-starting\n");
489
- }
490
603
  for (i = 0; i < LINE_LEN; ++i) {
491
604
  scs_printf("-");
492
605
  }
@@ -504,118 +617,37 @@ static void print_header(ScsWork *w, const ScsCone *k) {
504
617
  #endif
505
618
  }
506
619
 
507
- static scs_float get_dual_cone_dist(const scs_float *y, const ScsCone *k,
508
- ScsConeWork *c, scs_int m) {
509
- scs_float dist;
510
- scs_float *t = (scs_float *)scs_malloc(sizeof(scs_float) * m);
511
- memcpy(t, y, m * sizeof(scs_float));
512
- SCS(proj_dual_cone)(t, k, c, SCS_NULL, -1);
513
- dist = SCS(norm_inf_diff)(t, y, m);
514
- #if EXTRA_VERBOSE > 0
515
- SCS(print_array)(y, m, "y");
516
- SCS(print_array)(t, m, "proj_y");
517
- scs_printf("dist = %4f\n", dist);
518
- #endif
519
- scs_free(t);
520
- return dist;
521
- }
522
-
523
- /* via moreau */
524
- static scs_float get_pri_cone_dist(const scs_float *s, const ScsCone *k,
525
- ScsConeWork *c, scs_int m) {
526
- scs_float dist;
527
- scs_float *t = (scs_float *)scs_malloc(sizeof(scs_float) * m);
528
- memcpy(t, s, m * sizeof(scs_float));
529
- SCS(scale_array)(t, -1.0, m);
530
- SCS(proj_dual_cone)(t, k, c, SCS_NULL, -1);
531
- dist = SCS(norm_inf)(t, m); /* ||s - Pi_c(s)|| = ||Pi_c*(-s)|| */
532
- #if EXTRA_VERBOSE > 0
533
- SCS(print_array)(s, m, "s");
534
- SCS(print_array)(t, m, "(s - proj_s)");
535
- scs_printf("dist = %4f\n", dist);
536
- #endif
537
- scs_free(t);
538
- return dist;
539
- }
540
-
541
- static char *get_accel_summary(ScsInfo *info, scs_float total_accel_time) {
542
- char *str = (char *)scs_malloc(sizeof(char) * 64);
543
- sprintf(str, "\tAcceleration: avg step time: %1.2es\n",
544
- total_accel_time / (info->iter + 1) / 1e3);
545
- return str;
546
- }
547
-
548
- static void print_footer(const ScsData *d, const ScsCone *k, ScsSolution *sol,
549
- ScsWork *w, ScsInfo *info,
550
- scs_float total_accel_time) {
620
+ static void print_footer(ScsInfo *info) {
551
621
  scs_int i;
552
- char *lin_sys_str = SCS(get_lin_sys_summary)(w->p, info);
553
- char *cone_str = SCS(get_cone_summary)(info, w->cone_work);
554
- char *accel_str = get_accel_summary(info, total_accel_time);
622
+
555
623
  for (i = 0; i < LINE_LEN; ++i) {
556
624
  scs_printf("-");
557
625
  }
558
- scs_printf("\nStatus: %s\n", info->status);
559
- if (info->iter == w->stgs->max_iters) {
560
- scs_printf(
561
- "Hit max_iters, solution may be inaccurate, returning best found "
562
- "solution.\n");
563
- }
564
- scs_printf("Timing: Solve time: %1.2es\n", info->solve_time / 1e3);
565
-
566
- if (lin_sys_str) {
567
- scs_printf("%s", lin_sys_str);
568
- scs_free(lin_sys_str);
569
- }
570
-
571
- if (cone_str) {
572
- scs_printf("%s", cone_str);
573
- scs_free(cone_str);
574
- }
575
-
576
- if (accel_str) {
577
- scs_printf("%s", accel_str);
578
- scs_free(accel_str);
579
- }
626
+ scs_printf("\n");
627
+ scs_printf("status: %s\n", info->status);
628
+ scs_printf("timings: total: %1.2es = setup: %1.2es + solve: %1.2es\n",
629
+ (info->setup_time + info->solve_time) / 1e3,
630
+ info->setup_time / 1e3, info->solve_time / 1e3);
631
+ scs_printf("\t lin-sys: %1.2es, cones: %1.2es, accel: %1.2es\n",
632
+ info->lin_sys_time / 1e3, info->cone_time / 1e3,
633
+ info->accel_time / 1e3);
580
634
 
581
635
  for (i = 0; i < LINE_LEN; ++i) {
582
636
  scs_printf("-");
583
637
  }
584
638
  scs_printf("\n");
585
-
586
- if (is_infeasible_status(info->status_val)) {
587
- scs_printf("Certificate of primal infeasibility:\n");
588
- scs_printf("dist(y, K*) = %.4e\n",
589
- get_dual_cone_dist(sol->y, k, w->cone_work, d->m));
590
- scs_printf("|A'y|_2 * |b|_2 = %.4e\n", info->res_infeas);
591
- scs_printf("b'y = %.4f\n", SCS(dot)(d->b, sol->y, d->m));
592
- } else if (is_unbounded_status(info->status_val)) {
593
- scs_printf("Certificate of dual infeasibility:\n");
594
- scs_printf("dist(s, K) = %.4e\n",
595
- get_pri_cone_dist(sol->s, k, w->cone_work, d->m));
596
- scs_printf("|Ax + s|_2 * |c|_2 = %.4e\n", info->res_unbdd);
597
- scs_printf("c'x = %.4f\n", SCS(dot)(d->c, sol->x, d->n));
598
- } else {
599
- scs_printf("Error metrics:\n");
600
- scs_printf("dist(s, K) = %.4e, dist(y, K*) = %.4e, s'y/|s||y| = %.4e\n",
601
- get_pri_cone_dist(sol->s, k, w->cone_work, d->m),
602
- get_dual_cone_dist(sol->y, k, w->cone_work, d->m),
603
- SCS(dot)(sol->s, sol->y, d->m) / SCS(norm)(sol->s, d->m) /
604
- SCS(norm)(sol->y, d->m));
605
- scs_printf("primal res: |Ax + s - b|_2 / (1 + |b|_2) = %.4e\n",
606
- info->res_pri);
607
- scs_printf("dual res: |A'y + c|_2 / (1 + |c|_2) = %.4e\n",
608
- info->res_dual);
609
- scs_printf("rel gap: |c'x + b'y| / (1 + |c'x| + |b'y|) = %.4e\n",
610
- info->rel_gap);
611
- for (i = 0; i < LINE_LEN; ++i) {
612
- scs_printf("-");
613
- }
639
+ /* report mid point of primal and dual objective values */
640
+ scs_printf("objective = %.6f", 0.5 * (info->pobj + info->dobj));
641
+ switch (info->status_val) {
642
+ case SCS_SOLVED_INACCURATE:
643
+ case SCS_UNBOUNDED_INACCURATE:
644
+ case SCS_INFEASIBLE_INACCURATE:
645
+ scs_printf(" (inaccurate)");
646
+ default:
614
647
  scs_printf("\n");
615
- scs_printf("c'x = %.4f, -b'y = %.4f\n", info->pobj, info->dobj);
616
648
  }
617
649
  for (i = 0; i < LINE_LEN; ++i) {
618
- scs_printf("=");
650
+ scs_printf("-");
619
651
  }
620
652
  scs_printf("\n");
621
653
  #ifdef MATLAB_MEX_FILE
@@ -623,35 +655,55 @@ static void print_footer(const ScsData *d, const ScsCone *k, ScsSolution *sol,
623
655
  #endif
624
656
  }
625
657
 
626
- static scs_int has_converged(ScsWork *w, ScsResiduals *r, scs_int iter) {
627
- scs_float eps = w->stgs->eps;
628
- if (isless(r->res_pri, eps) && isless(r->res_dual, eps) &&
629
- isless(r->rel_gap, eps)) {
630
- return SCS_SOLVED;
658
+ static scs_int has_converged(ScsWork *w, scs_int iter) {
659
+ scs_float eps_abs = w->stgs->eps_abs;
660
+ scs_float eps_rel = w->stgs->eps_rel;
661
+ scs_float eps_infeas = w->stgs->eps_infeas;
662
+ scs_float grl, prl, drl;
663
+
664
+ ScsResiduals *r = w->r_orig;
665
+ scs_float *b = w->b_orig;
666
+ scs_float *c = w->c_orig;
667
+ scs_float *s = w->xys_orig->s;
668
+
669
+ if (r->tau > 0.) {
670
+ /* xt_p_x, ctx, bty already have tau divided out */
671
+ grl = MAX(MAX(ABS(r->xt_p_x), ABS(r->ctx)), ABS(r->bty));
672
+ /* s, ax, px, aty do *not* have tau divided out, so need to divide */
673
+ prl = MAX(MAX(NORM(b, w->m) * r->tau, NORM(s, w->m)), NORM(r->ax, w->m)) /
674
+ r->tau;
675
+ drl = MAX(MAX(NORM(c, w->n) * r->tau, NORM(r->px, w->n)),
676
+ NORM(r->aty, w->n)) /
677
+ r->tau;
678
+ if (isless(r->res_pri, eps_abs + eps_rel * prl) &&
679
+ isless(r->res_dual, eps_abs + eps_rel * drl) &&
680
+ isless(r->gap, eps_abs + eps_rel * grl)) {
681
+ return SCS_SOLVED;
682
+ }
631
683
  }
632
- /* Add iter > 0 to avoid strange edge case where infeasible point found
633
- * right at start of run `out/demo_SOCP_indirect 2 0.1 0.3 1506264403` */
634
- if (isless(r->res_unbdd, eps) && iter > 0) {
684
+ if (isless(r->res_unbdd_a, eps_infeas) &&
685
+ isless(r->res_unbdd_p, eps_infeas)) {
635
686
  return SCS_UNBOUNDED;
636
687
  }
637
- if (isless(r->res_infeas, eps) && iter > 0) {
688
+ if (isless(r->res_infeas, eps_infeas)) {
638
689
  return SCS_INFEASIBLE;
639
690
  }
640
691
  return 0;
641
692
  }
642
693
 
643
- static scs_int validate(const ScsData *d, const ScsCone *k) {
644
- ScsSettings *stgs = d->stgs;
694
+ #if NOVALIDATE == 0
695
+ static scs_int validate(const ScsData *d, const ScsCone *k,
696
+ const ScsSettings *stgs) {
645
697
  if (d->m <= 0 || d->n <= 0) {
646
698
  scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
647
699
  (long)d->m, (long)d->n);
648
700
  return -1;
649
701
  }
650
702
  if (d->m < d->n) {
651
- scs_printf("WARN: m less than n, problem likely degenerate\n");
703
+ /* scs_printf("WARN: m less than n, problem likely degenerate\n"); */
652
704
  /* return -1; */
653
705
  }
654
- if (SCS(validate_lin_sys)(d->A) < 0) {
706
+ if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
655
707
  scs_printf("invalid linear system input data\n");
656
708
  return -1;
657
709
  }
@@ -663,8 +715,16 @@ static scs_int validate(const ScsData *d, const ScsCone *k) {
663
715
  scs_printf("max_iters must be positive\n");
664
716
  return -1;
665
717
  }
666
- if (stgs->eps <= 0) {
667
- scs_printf("eps tolerance must be positive\n");
718
+ if (stgs->eps_abs < 0) {
719
+ scs_printf("eps_abs tolerance must be positive\n");
720
+ return -1;
721
+ }
722
+ if (stgs->eps_rel < 0) {
723
+ scs_printf("eps_rel tolerance must be positive\n");
724
+ return -1;
725
+ }
726
+ if (stgs->eps_infeas < 0) {
727
+ scs_printf("eps_infeas tolerance must be positive\n");
668
728
  return -1;
669
729
  }
670
730
  if (stgs->alpha <= 0 || stgs->alpha >= 2) {
@@ -681,224 +741,374 @@ static scs_int validate(const ScsData *d, const ScsCone *k) {
681
741
  }
682
742
  return 0;
683
743
  }
744
+ #endif
745
+
746
+ static ScsResiduals *init_residuals(const ScsData *d) {
747
+ ScsResiduals *r = (ScsResiduals *)scs_calloc(1, sizeof(ScsResiduals));
748
+ r->last_iter = -1;
749
+ r->ax = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
750
+ r->ax_s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
751
+ r->ax_s_btau = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
752
+ r->px = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
753
+ r->aty = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
754
+ r->px_aty_ctau = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
755
+ return r;
756
+ }
684
757
 
685
- static ScsWork *init_work(const ScsData *d, const ScsCone *k) {
758
+ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
759
+ const ScsSettings *stgs) {
686
760
  ScsWork *w = (ScsWork *)scs_calloc(1, sizeof(ScsWork));
687
761
  scs_int l = d->n + d->m + 1;
688
- if (d->stgs->verbose) {
689
- print_init_header(d, k);
762
+ if (stgs->verbose) {
763
+ print_init_header(d, k, stgs);
690
764
  }
691
765
  if (!w) {
692
766
  scs_printf("ERROR: allocating work failure\n");
693
767
  return SCS_NULL;
694
768
  }
695
769
  /* get settings and dims from data struct */
696
- w->stgs = d->stgs;
770
+ w->d = d;
771
+ w->k = k;
772
+ w->stgs = stgs;
773
+ w->scale = stgs->scale; /* initial scale, may be updated */
697
774
  w->m = d->m;
698
775
  w->n = d->n;
699
- w->best_max_residual = INFINITY;
776
+ w->last_scale_update_iter = 0;
777
+ w->sum_log_scale_factor = 0.;
778
+ w->n_log_scale_factor = 0;
779
+ w->scale_updates = 0;
780
+ w->time_limit_reached = 0;
700
781
  /* allocate workspace: */
701
- /* u* include v* values */
702
- w->u = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
703
- w->u_best = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
704
- w->u_t = (scs_float *)scs_malloc(l * sizeof(scs_float));
705
- w->u_prev = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
706
- w->h = (scs_float *)scs_malloc((l - 1) * sizeof(scs_float));
707
- w->g = (scs_float *)scs_malloc((l - 1) * sizeof(scs_float));
708
- w->pr = (scs_float *)scs_malloc(d->m * sizeof(scs_float));
709
- w->dr = (scs_float *)scs_malloc(d->n * sizeof(scs_float));
710
- w->b = (scs_float *)scs_malloc(d->m * sizeof(scs_float));
711
- w->c = (scs_float *)scs_malloc(d->n * sizeof(scs_float));
712
- if (!w->u || !w->u_t || !w->u_prev || !w->h || !w->g || !w->pr || !w->dr ||
713
- !w->b || !w->c) {
782
+ w->u = (scs_float *)scs_calloc(l, sizeof(scs_float));
783
+ w->u_t = (scs_float *)scs_calloc(l, sizeof(scs_float));
784
+ w->v = (scs_float *)scs_calloc(l, sizeof(scs_float));
785
+ w->v_prev = (scs_float *)scs_calloc(l, sizeof(scs_float));
786
+ w->rsk = (scs_float *)scs_calloc(l, sizeof(scs_float));
787
+ w->h = (scs_float *)scs_calloc((l - 1), sizeof(scs_float));
788
+ w->g = (scs_float *)scs_calloc((l - 1), sizeof(scs_float));
789
+ w->lin_sys_warm_start = (scs_float *)scs_calloc((l - 1), sizeof(scs_float));
790
+ w->rho_y_vec = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
791
+ /* x,y,s struct */
792
+ w->xys_orig = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
793
+ w->xys_orig->x = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
794
+ w->xys_orig->s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
795
+ w->xys_orig->y = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
796
+ w->r_orig = init_residuals(d);
797
+
798
+ w->A = d->A;
799
+ w->P = d->P;
800
+
801
+ w->b_orig = d->b;
802
+ w->c_orig = d->c;
803
+ w->b_normalized = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
804
+ w->c_normalized = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
805
+ memcpy(w->b_normalized, w->b_orig, w->m * sizeof(scs_float));
806
+ memcpy(w->c_normalized, w->c_orig, w->n * sizeof(scs_float));
807
+ SCS(set_rho_y_vec)(k, w->scale, w->rho_y_vec, w->m);
808
+
809
+ if (!w->c_normalized) {
714
810
  scs_printf("ERROR: work memory allocation failure\n");
715
811
  return SCS_NULL;
716
812
  }
717
- /* make u,v and u_prev,v_prev contiguous in memory */
718
- w->v = &(w->u[l]);
719
- w->v_best = &(w->u_best[l]);
720
- w->v_prev = &(w->u_prev[l]);
721
- w->A = d->A;
813
+
722
814
  if (w->stgs->normalize) {
815
+ w->xys_normalized = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
816
+ w->xys_normalized->x = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
817
+ w->xys_normalized->s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
818
+ w->xys_normalized->y = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
819
+ w->r_normalized = init_residuals(d);
820
+
723
821
  #ifdef COPYAMATRIX
724
- if (!SCS(copy_a_matrix)(&(w->A), d->A)) {
822
+ if (!SCS(copy_matrix)(&(w->A), d->A)) {
725
823
  scs_printf("ERROR: copy A matrix failed\n");
726
824
  return SCS_NULL;
727
825
  }
826
+ if (w->P && !SCS(copy_matrix)(&(w->P), d->P)) {
827
+ scs_printf("ERROR: copy P matrix failed\n");
828
+ return SCS_NULL;
829
+ }
728
830
  #endif
729
- w->scal = (ScsScaling *)scs_malloc(sizeof(ScsScaling));
730
- SCS(normalize_a)(w->A, w->stgs, k, w->scal);
731
- #if EXTRA_VERBOSE > 0
732
- SCS(print_array)(w->scal->D, d->m, "D");
733
- scs_printf("SCS(norm) D = %4f\n", SCS(norm)(w->scal->D, d->m));
734
- SCS(print_array)(w->scal->E, d->n, "E");
735
- scs_printf("SCS(norm) E = %4f\n", SCS(norm)(w->scal->E, d->n));
736
- #endif
831
+ /* this allocates memory that must be freed */
832
+ w->cone_boundaries_len = SCS(set_cone_boundaries)(k, &w->cone_boundaries);
833
+ w->scal = (ScsScaling *)scs_calloc(1, sizeof(ScsScaling));
834
+ SCS(normalize)
835
+ (w->P, w->A, w->b_normalized, w->c_normalized, w->scal, w->cone_boundaries,
836
+ w->cone_boundaries_len);
737
837
  } else {
838
+ w->xys_normalized = w->xys_orig;
839
+ w->r_normalized = w->r_orig;
840
+ w->cone_boundaries_len = 0;
841
+ w->cone_boundaries = SCS_NULL;
738
842
  w->scal = SCS_NULL;
739
843
  }
740
- if (!(w->cone_work = SCS(init_cone)(k))) {
844
+ if (!(w->cone_work = SCS(init_cone)(k, w->scal, w->m))) {
741
845
  scs_printf("ERROR: init_cone failure\n");
742
846
  return SCS_NULL;
743
847
  }
744
- if (!(w->p = SCS(init_lin_sys_work)(w->A, w->stgs))) {
848
+ if (!(w->p =
849
+ SCS(init_lin_sys_work)(w->A, w->P, w->rho_y_vec, w->stgs->rho_x))) {
745
850
  scs_printf("ERROR: init_lin_sys_work failure\n");
746
851
  return SCS_NULL;
747
852
  }
748
- if (!(w->accel =
749
- aa_init(2 * (w->m + w->n + 1), ABS(w->stgs->acceleration_lookback),
750
- w->stgs->acceleration_lookback >= 0))) {
751
- if (w->stgs->verbose) {
752
- scs_printf("WARN: aa_init returned NULL, no acceleration applied.\n");
853
+ /* Acceleration */
854
+ w->rejected_accel_steps = 0;
855
+ w->accepted_accel_steps = 0;
856
+ if (w->stgs->acceleration_lookback) {
857
+ /* TODO(HACK!) negative acceleration_lookback interpreted as type-II */
858
+ if (!(w->accel = aa_init(l, ABS(w->stgs->acceleration_lookback),
859
+ w->stgs->acceleration_lookback > 0,
860
+ w->stgs->acceleration_lookback > 0
861
+ ? AA_REGULARIZATION_TYPE_1
862
+ : AA_REGULARIZATION_TYPE_2,
863
+ AA_RELAXATION, AA_SAFEGUARD_FACTOR,
864
+ AA_MAX_WEIGHT_NORM, VERBOSITY))) {
865
+ if (w->stgs->verbose) {
866
+ scs_printf("WARN: aa_init returned NULL, no acceleration applied.\n");
867
+ }
753
868
  }
869
+ } else {
870
+ w->accel = SCS_NULL;
754
871
  }
755
872
  return w;
756
873
  }
757
874
 
758
- static scs_int update_work(const ScsData *d, ScsWork *w,
759
- const ScsSolution *sol) {
875
+ static void update_work_cache(ScsWork *w) {
876
+ /* g = (I + M)^{-1} h */
877
+ memcpy(w->g, w->h, (w->n + w->m) * sizeof(scs_float));
878
+ SCS(scale_array)(&(w->g[w->n]), -1., w->m);
879
+ SCS(solve_lin_sys)(w->p, w->g, SCS_NULL, CG_BEST_TOL);
880
+ return;
881
+ }
882
+
883
+ static scs_int update_work(const ScsData *d, ScsWork *w, ScsSolution *sol) {
760
884
  /* before normalization */
761
885
  scs_int n = d->n;
762
886
  scs_int m = d->m;
763
-
764
- w->nm_b = SCS(norm)(d->b, m);
765
- w->nm_c = SCS(norm)(d->c, n);
766
- memcpy(w->b, d->b, d->m * sizeof(scs_float));
767
- memcpy(w->c, d->c, d->n * sizeof(scs_float));
768
-
769
- #if EXTRA_VERBOSE > 0
770
- SCS(print_array)(w->b, m, "b");
771
- scs_printf("pre-normalized norm b = %4f\n", SCS(norm)(w->b, m));
772
- SCS(print_array)(w->c, n, "c");
773
- scs_printf("pre-normalized norm c = %4f\n", SCS(norm)(w->c, n));
774
- #endif
775
- if (w->stgs->normalize) {
776
- SCS(normalize_b_c)(w);
777
- #if EXTRA_VERBOSE > 0
778
- SCS(print_array)(w->b, m, "bn");
779
- scs_printf("sc_b = %4f\n", w->sc_b);
780
- scs_printf("post-normalized norm b = %4f\n", SCS(norm)(w->b, m));
781
- SCS(print_array)(w->c, n, "cn");
782
- scs_printf("sc_c = %4f\n", w->sc_c);
783
- scs_printf("post-normalized norm c = %4f\n", SCS(norm)(w->c, n));
784
- #endif
785
- }
786
887
  if (w->stgs->warm_start) {
787
888
  warm_start_vars(w, sol);
788
889
  } else {
789
890
  cold_start_vars(w);
790
891
  }
791
- memcpy(w->h, w->c, n * sizeof(scs_float));
792
- memcpy(&(w->h[n]), w->b, m * sizeof(scs_float));
793
- memcpy(w->g, w->h, (n + m) * sizeof(scs_float));
794
- SCS(solve_lin_sys)(w->A, w->stgs, w->p, w->g, SCS_NULL, -1);
795
- SCS(scale_array)(&(w->g[n]), -1, m);
796
- w->g_th = SCS(dot)(w->h, w->g, n + m);
892
+
893
+ /* h = [c;b] */
894
+ memcpy(w->h, w->c_normalized, n * sizeof(scs_float));
895
+ memcpy(&(w->h[n]), w->b_normalized, m * sizeof(scs_float));
896
+ update_work_cache(w);
797
897
  return 0;
798
898
  }
799
899
 
800
- static scs_float iterate_norm_diff(ScsWork *w) {
801
- scs_int l = w->m + w->n + 1;
802
- scs_float u_norm_difference = SCS(norm_diff)(w->u, w->u_prev, l);
803
- scs_float v_norm_difference = SCS(norm_diff)(w->v, w->v_prev, l);
804
- scs_float norm = SQRTF(SCS(norm_sq)(w->u, l) + SCS(norm_sq)(w->v, l));
805
- scs_float norm_diff = SQRTF(u_norm_difference * u_norm_difference +
806
- v_norm_difference * v_norm_difference);
807
- return norm_diff / norm;
900
+ /* will update if the factor is outside of range */
901
+ scs_int should_update_rho_y_vec(scs_float factor, scs_int iter) {
902
+ return (factor > SQRTF(10.) || factor < 1. / SQRTF(10.));
808
903
  }
809
904
 
810
- static void update_best_iterate(ScsWork *w, ScsResiduals *r) {
811
- scs_float max_residual = get_max_residual(r);
812
- if (w->best_max_residual > max_residual) {
813
- w->best_max_residual = max_residual;
814
- memcpy(w->u_best, w->u, (w->m + w->n + 1) * sizeof(scs_float));
815
- memcpy(w->v_best, w->v, (w->m + w->n + 1) * sizeof(scs_float));
905
+ static void maybe_update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
906
+ scs_int i;
907
+ scs_float factor, new_scale;
908
+
909
+ ScsResiduals *r = w->r_orig;
910
+ ScsSolution *xys = w->xys_orig;
911
+ scs_float *b = w->b_orig;
912
+ scs_float *c = w->c_orig;
913
+
914
+ scs_int iters_since_last_update = iter - w->last_scale_update_iter;
915
+ /* ||Ax + s - b * tau|| */
916
+ scs_float relative_res_pri =
917
+ SAFEDIV_POS(SCALE_NORM(r->ax_s_btau, w->m),
918
+ MAX(MAX(SCALE_NORM(r->ax, w->m), SCALE_NORM(xys->s, w->m)),
919
+ SCALE_NORM(b, w->m) * r->tau));
920
+ /* ||Px + A'y + c * tau|| */
921
+ scs_float relative_res_dual =
922
+ SAFEDIV_POS(SCALE_NORM(r->px_aty_ctau, w->n),
923
+ MAX(MAX(SCALE_NORM(r->px, w->n), SCALE_NORM(r->aty, w->n)),
924
+ SCALE_NORM(c, w->n) * r->tau));
925
+
926
+ /* higher scale makes res_pri go down faster, so increase if res_pri larger */
927
+ w->sum_log_scale_factor += log(relative_res_pri) - log(relative_res_dual);
928
+ w->n_log_scale_factor++;
929
+
930
+ /* geometric mean */
931
+ factor =
932
+ SQRTF(exp(w->sum_log_scale_factor / (scs_float)(w->n_log_scale_factor)));
933
+
934
+ /* need at least RESCALING_MIN_ITERS since last update */
935
+ if (iters_since_last_update < RESCALING_MIN_ITERS) {
936
+ return;
937
+ }
938
+ new_scale = MIN(MAX(w->scale * factor, MIN_SCALE_VALUE), MAX_SCALE_VALUE);
939
+ if (new_scale == w->scale) {
940
+ return;
816
941
  }
942
+ if (should_update_rho_y_vec(factor, iters_since_last_update)) {
943
+ w->scale_updates++;
944
+ w->sum_log_scale_factor = 0;
945
+ w->n_log_scale_factor = 0;
946
+ w->last_scale_update_iter = iter;
947
+ w->scale = new_scale;
948
+ SCS(set_rho_y_vec)(k, w->scale, w->rho_y_vec, w->m);
949
+ SCS(update_lin_sys_rho_y_vec)(w->p, w->rho_y_vec);
950
+
951
+ /* update pre-solved quantities */
952
+ update_work_cache(w);
953
+
954
+ /* reset acceleration so that old iterates aren't affecting new values */
955
+ if (w->accel) {
956
+ aa_reset(w->accel);
957
+ }
958
+ /* update v, using fact that rsk, u, u_t vectors should be the same */
959
+ /* solve: R (v^+ + u - 2u_t) = rsk => v^+ = R^-1 rsk + 2u_t - u */
960
+ /* only elements with scale on diag */
961
+ for (i = w->n; i < w->n + w->m; i++) {
962
+ w->v[i] = w->rsk[i] / w->rho_y_vec[i - w->n] + 2 * w->u_t[i] - w->u[i];
963
+ }
964
+ }
965
+ }
966
+
967
+ /* scs is homogeneous so scale the iterate to keep norm reasonable */
968
+ static inline void normalize_v(scs_float *v, scs_int len) {
969
+ scs_float v_norm = SCS(norm_2)(v, len); /* always l2 norm */
970
+ SCS(scale_array)(v, SQRTF((scs_float)len) * ITERATE_NORM / v_norm, len);
817
971
  }
818
972
 
819
- scs_int SCS(solve)(ScsWork *w, const ScsData *d, const ScsCone *k,
820
- ScsSolution *sol, ScsInfo *info) {
973
+ scs_int SCS(solve)(ScsWork *w, ScsSolution *sol, ScsInfo *info) {
821
974
  scs_int i;
822
- SCS(timer) solve_timer, accel_timer;
823
- scs_float total_accel_time = 0.0, total_norm;
824
- ScsResiduals r;
825
- scs_int l = w->m + w->n + 1;
826
- if (!d || !k || !sol || !info || !w || !d->b || !d->c) {
827
- scs_printf("ERROR: SCS_NULL input\n");
975
+ SCS(timer) solve_timer, lin_sys_timer, cone_timer, accel_timer;
976
+ scs_float total_accel_time = 0.0, total_cone_time = 0.0,
977
+ total_lin_sys_time = 0.0;
978
+ if (!sol || !w || !info) {
979
+ scs_printf("ERROR: missing ScsWork, ScsSolution or ScsInfo input\n");
828
980
  return SCS_FAILED;
829
981
  }
982
+ scs_int l = w->m + w->n + 1;
983
+ const ScsData *d = w->d;
984
+ const ScsCone *k = w->k;
985
+ const ScsSettings *stgs = w->stgs;
830
986
  /* initialize ctrl-c support */
831
987
  scs_start_interrupt_listener();
832
988
  SCS(tic)(&solve_timer);
833
989
  info->status_val = SCS_UNFINISHED; /* not yet converged */
834
- r.last_iter = -1;
835
990
  update_work(d, w, sol);
836
991
 
837
992
  if (w->stgs->verbose) {
838
993
  print_header(w, k);
839
994
  }
840
- /* scs: */
995
+
996
+ /* SCS */
841
997
  for (i = 0; i < w->stgs->max_iters; ++i) {
842
- /* accelerate here so that last step always projection onto cone */
998
+ /* Accelerate here so that last step always projection onto cone */
843
999
  /* this ensures the returned iterates always satisfy conic constraints */
844
- /* this relies on the fact that u and v are contiguous in memory */
845
- SCS(tic)(&accel_timer);
846
- if (i > 0 && aa_apply(w->u, w->u_prev, w->accel) != 0) {
847
- /*
848
- return failure(w, w->m, w->n, sol, info, SCS_FAILED,
849
- "error in accelerate", "Failure");
850
- */
1000
+ if (w->accel) {
1001
+ SCS(tic)(&accel_timer);
1002
+ if (i > 0 && i % w->stgs->acceleration_interval == 0) {
1003
+ /* v overwritten with AA output here */
1004
+ w->aa_norm = aa_apply(w->v, w->v_prev, w->accel);
1005
+ }
1006
+ total_accel_time += SCS(tocq)(&accel_timer);
851
1007
  }
852
- total_accel_time += SCS(tocq)(&accel_timer);
853
1008
 
854
- /* scs is homogeneous so scale the iterates to keep norm reasonable */
855
- total_norm = SQRTF(SCS(norm_sq)(w->u, l) + SCS(norm_sq)(w->v, l));
856
- SCS(scale_array)(w->u, SQRTF((scs_float)l) * ITERATE_NORM / total_norm, l);
857
- SCS(scale_array)(w->v, SQRTF((scs_float)l) * ITERATE_NORM / total_norm, l);
1009
+ if (i >= FEASIBLE_ITERS) {
1010
+ /* normalize v *after* applying any acceleration */
1011
+ /* the input to the DR step should be normalized */
1012
+ normalize_v(w->v, l);
1013
+ }
858
1014
 
859
- memcpy(w->u_prev, w->u, l * sizeof(scs_float));
1015
+ /* store v_prev = v, *after* normalizing */
860
1016
  memcpy(w->v_prev, w->v, l * sizeof(scs_float));
861
1017
 
1018
+ /* linear system solve */
1019
+ SCS(tic)(&lin_sys_timer);
862
1020
  if (project_lin_sys(w, i) < 0) {
863
1021
  return failure(w, w->m, w->n, sol, info, SCS_FAILED,
864
- "error in project_lin_sys", "Failure");
1022
+ "error in project_lin_sys", "failure");
865
1023
  }
1024
+ total_lin_sys_time += SCS(tocq)(&lin_sys_timer);
1025
+
1026
+ /* project onto the cones */
1027
+ SCS(tic)(&cone_timer);
866
1028
  if (project_cones(w, k, i) < 0) {
867
1029
  return failure(w, w->m, w->n, sol, info, SCS_FAILED,
868
- "error in project_cones", "Failure");
1030
+ "error in project_cones", "failure");
869
1031
  }
1032
+ total_cone_time += SCS(tocq)(&cone_timer);
870
1033
 
1034
+ /* dual variable step */
871
1035
  update_dual_vars(w);
872
1036
 
873
- if (scs_is_interrupted()) {
874
- return failure(w, w->m, w->n, sol, info, SCS_SIGINT, "Interrupted",
875
- "Interrupted");
876
- }
877
- if (i % CONVERGED_INTERVAL == 0 || iterate_norm_diff(w) < 1e-10) {
878
- calc_residuals(w, &r, i);
879
- if ((info->status_val = has_converged(w, &r, i)) != 0) {
1037
+ if (i % CONVERGED_INTERVAL == 0) {
1038
+ if (scs_is_interrupted()) {
1039
+ return failure(w, w->m, w->n, sol, info, SCS_SIGINT, "interrupted",
1040
+ "interrupted");
1041
+ }
1042
+ populate_residual_struct(w, i);
1043
+ if ((info->status_val = has_converged(w, i)) != 0) {
880
1044
  break;
881
1045
  }
882
- update_best_iterate(w, &r);
1046
+ if (w->stgs->time_limit_secs) {
1047
+ if (SCS(tocq)(&solve_timer) > 1000. * w->stgs->time_limit_secs) {
1048
+ w->time_limit_reached = 1;
1049
+ break;
1050
+ }
1051
+ }
883
1052
  }
884
1053
 
1054
+ /* AA safeguard check.
1055
+ * Perform safeguarding *after* convergence check to prevent safeguard
1056
+ * overwriting converged iterate, since safeguard is on `v` and convergence
1057
+ * is on `u`.
1058
+ */
1059
+ if (w->accel && i % w->stgs->acceleration_interval == 0 && w->aa_norm > 0) {
1060
+ if (aa_safeguard(w->v, w->v_prev, w->accel) < 0) {
1061
+ /* TODO should we copy u from u_prev here too? Then move above, possibly
1062
+ * better residual calculation and scale updating. */
1063
+ w->rejected_accel_steps++;
1064
+ } else {
1065
+ w->accepted_accel_steps++;
1066
+ }
1067
+ }
1068
+
1069
+ /* Compute residuals. */
885
1070
  if (w->stgs->verbose && i % PRINT_INTERVAL == 0) {
886
- calc_residuals(w, &r, i);
887
- update_best_iterate(w, &r);
888
- print_summary(w, i, &r, &solve_timer);
1071
+ populate_residual_struct(w, i);
1072
+ print_summary(w, i, &solve_timer);
1073
+ }
1074
+
1075
+ /* If residuals are fresh then maybe compute new scale. */
1076
+ if (w->stgs->adaptive_scale && i == w->r_orig->last_iter) {
1077
+ maybe_update_scale(w, k, i);
889
1078
  }
1079
+
1080
+ /* Log *after* updating scale so residual recalc does not affect alg */
1081
+ if (w->stgs->log_csv_filename) {
1082
+ /* calc residuals every iter if logging to csv */
1083
+ populate_residual_struct(w, i);
1084
+ SCS(log_data_to_csv)(d, k, stgs, w, i, &solve_timer);
1085
+ }
1086
+ }
1087
+
1088
+ /* Final logging after full run */
1089
+ if (w->stgs->log_csv_filename) {
1090
+ populate_residual_struct(w, i);
1091
+ SCS(log_data_to_csv)(d, k, stgs, w, i, &solve_timer);
890
1092
  }
1093
+
891
1094
  if (w->stgs->verbose) {
892
- calc_residuals(w, &r, i);
893
- print_summary(w, i, &r, &solve_timer);
1095
+ populate_residual_struct(w, i);
1096
+ print_summary(w, i, &solve_timer);
894
1097
  }
1098
+
895
1099
  /* populate solution vectors (unnormalized) and info */
896
- get_solution(w, sol, info, &r, i);
1100
+ finalize(w, sol, info, i);
1101
+
1102
+ /* populate timings */
897
1103
  info->solve_time = SCS(tocq)(&solve_timer);
1104
+ info->lin_sys_time = total_lin_sys_time;
1105
+ info->cone_time = total_cone_time;
1106
+ info->accel_time = total_accel_time;
898
1107
 
899
1108
  if (w->stgs->verbose) {
900
- print_footer(d, k, sol, w, info, total_accel_time);
1109
+ print_footer(info);
901
1110
  }
1111
+
902
1112
  scs_end_interrupt_listener();
903
1113
  return info->status_val;
904
1114
  }
@@ -908,9 +1118,10 @@ void SCS(finish)(ScsWork *w) {
908
1118
  SCS(finish_cone)(w->cone_work);
909
1119
  if (w->stgs && w->stgs->normalize) {
910
1120
  #ifndef COPYAMATRIX
911
- SCS(un_normalize_a)(w->A, w->stgs, w->scal);
1121
+ SCS(un_normalize)(w->A, w->P, w->scal);
912
1122
  #else
913
- SCS(free_a_matrix)(w->A);
1123
+ SCS(free_scs_matrix)(w->A);
1124
+ SCS(free_scs_matrix)(w->P);
914
1125
  #endif
915
1126
  }
916
1127
  if (w->p) {
@@ -923,55 +1134,48 @@ void SCS(finish)(ScsWork *w) {
923
1134
  }
924
1135
  }
925
1136
 
926
- ScsWork *SCS(init)(const ScsData *d, const ScsCone *k, ScsInfo *info) {
927
- #if EXTRA_VERBOSE > 1
928
- SCS(tic)(&global_timer);
929
- #endif
1137
+ ScsWork *SCS(init)(const ScsData *d, const ScsCone *k,
1138
+ const ScsSettings *stgs) {
930
1139
  ScsWork *w;
931
1140
  SCS(timer) init_timer;
932
1141
  scs_start_interrupt_listener();
933
- if (!d || !k || !info) {
934
- scs_printf("ERROR: Missing ScsData, ScsCone or ScsInfo input\n");
1142
+ if (!d || !k) {
1143
+ scs_printf("ERROR: Missing ScsData or ScsCone input\n");
935
1144
  return SCS_NULL;
936
1145
  }
937
- #if EXTRA_VERBOSE > 0
938
- SCS(print_data)(d);
939
- SCS(print_cone_data)(k);
940
- #endif
941
- #ifndef NOVALIDATE
942
- if (validate(d, k) < 0) {
1146
+ #if NOVALIDATE == 0
1147
+ if (validate(d, k, stgs) < 0) {
943
1148
  scs_printf("ERROR: Validation returned failure\n");
944
1149
  return SCS_NULL;
945
1150
  }
946
1151
  #endif
947
1152
  SCS(tic)(&init_timer);
948
- if (d->stgs->write_data_filename) {
949
- SCS(write_data)(d, k);
1153
+ if (stgs->write_data_filename) {
1154
+ SCS(write_data)(d, k, stgs);
950
1155
  }
951
- w = init_work(d, k);
952
- info->setup_time = SCS(tocq)(&init_timer);
953
- if (d->stgs->verbose) {
954
- scs_printf("Setup time: %1.2es\n", info->setup_time / 1e3);
1156
+ w = init_work(d, k, stgs);
1157
+ if (w) {
1158
+ w->setup_time = SCS(tocq)(&init_timer);
955
1159
  }
956
1160
  scs_end_interrupt_listener();
957
1161
  return w;
958
1162
  }
959
1163
 
960
1164
  /* this just calls SCS(init), SCS(solve), and SCS(finish) */
961
- scs_int scs(const ScsData *d, const ScsCone *k, ScsSolution *sol,
962
- ScsInfo *info) {
1165
+ scs_int scs(const ScsData *d, const ScsCone *k, const ScsSettings *stgs,
1166
+ ScsSolution *sol, ScsInfo *info) {
963
1167
  scs_int status;
964
- ScsWork *w = SCS(init)(d, k, info);
965
- #if EXTRA_VERBOSE > 0
1168
+ ScsWork *w = SCS(init)(d, k, stgs);
1169
+ #if VERBOSITY > 0
966
1170
  scs_printf("size of scs_int = %lu, size of scs_float = %lu\n",
967
1171
  sizeof(scs_int), sizeof(scs_float));
968
1172
  #endif
969
1173
  if (w) {
970
- SCS(solve)(w, d, k, sol, info);
1174
+ SCS(solve)(w, sol, info);
971
1175
  status = info->status_val;
972
1176
  } else {
973
1177
  status = failure(SCS_NULL, d ? d->m : -1, d ? d->n : -1, sol, info,
974
- SCS_FAILED, "could not initialize work", "Failure");
1178
+ SCS_FAILED, "could not initialize work", "failure");
975
1179
  }
976
1180
  SCS(finish)(w);
977
1181
  return status;