scs 0.2.3 → 0.3.0

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 (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;