scs 0.2.0 → 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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/LICENSE.txt +18 -18
  4. data/README.md +28 -9
  5. data/ext/scs/extconf.rb +29 -0
  6. data/lib/scs/ffi.rb +30 -13
  7. data/lib/scs/solver.rb +32 -14
  8. data/lib/scs/version.rb +1 -1
  9. data/vendor/scs/CITATION.cff +39 -0
  10. data/vendor/scs/CMakeLists.txt +272 -0
  11. data/vendor/scs/Makefile +24 -15
  12. data/vendor/scs/README.md +8 -216
  13. data/vendor/scs/include/aa.h +67 -23
  14. data/vendor/scs/include/cones.h +17 -17
  15. data/vendor/scs/include/glbopts.h +98 -32
  16. data/vendor/scs/include/linalg.h +2 -4
  17. data/vendor/scs/include/linsys.h +58 -44
  18. data/vendor/scs/include/normalize.h +3 -3
  19. data/vendor/scs/include/rw.h +8 -2
  20. data/vendor/scs/include/scs.h +293 -133
  21. data/vendor/scs/include/util.h +3 -15
  22. data/vendor/scs/linsys/cpu/direct/private.c +220 -224
  23. data/vendor/scs/linsys/cpu/direct/private.h +13 -7
  24. data/vendor/scs/linsys/cpu/direct/private.o +0 -0
  25. data/vendor/scs/linsys/cpu/indirect/private.c +177 -110
  26. data/vendor/scs/linsys/cpu/indirect/private.h +8 -4
  27. data/vendor/scs/linsys/cpu/indirect/private.o +0 -0
  28. data/vendor/scs/linsys/csparse.c +87 -0
  29. data/vendor/scs/linsys/csparse.h +34 -0
  30. data/vendor/scs/linsys/csparse.o +0 -0
  31. data/vendor/scs/linsys/external/amd/SuiteSparse_config.c +1 -1
  32. data/vendor/scs/linsys/external/amd/SuiteSparse_config.o +0 -0
  33. data/vendor/scs/linsys/external/amd/amd_1.o +0 -0
  34. data/vendor/scs/linsys/external/amd/amd_2.o +0 -0
  35. data/vendor/scs/linsys/external/amd/amd_aat.o +0 -0
  36. data/vendor/scs/linsys/external/amd/amd_control.o +0 -0
  37. data/vendor/scs/linsys/external/amd/amd_defaults.o +0 -0
  38. data/vendor/scs/linsys/external/amd/amd_dump.o +0 -0
  39. data/vendor/scs/linsys/external/amd/amd_global.o +0 -0
  40. data/vendor/scs/linsys/external/amd/amd_info.o +0 -0
  41. data/vendor/scs/linsys/external/amd/amd_internal.h +1 -1
  42. data/vendor/scs/linsys/external/amd/amd_order.o +0 -0
  43. data/vendor/scs/linsys/external/amd/amd_post_tree.o +0 -0
  44. data/vendor/scs/linsys/external/amd/amd_postorder.o +0 -0
  45. data/vendor/scs/linsys/external/amd/amd_preprocess.o +0 -0
  46. data/vendor/scs/linsys/external/amd/amd_valid.o +0 -0
  47. data/vendor/scs/linsys/external/qdldl/changes +2 -0
  48. data/vendor/scs/linsys/external/qdldl/qdldl.c +29 -46
  49. data/vendor/scs/linsys/external/qdldl/qdldl.h +33 -41
  50. data/vendor/scs/linsys/external/qdldl/qdldl.o +0 -0
  51. data/vendor/scs/linsys/external/qdldl/qdldl_types.h +11 -3
  52. data/vendor/scs/linsys/gpu/gpu.c +58 -21
  53. data/vendor/scs/linsys/gpu/gpu.h +66 -28
  54. data/vendor/scs/linsys/gpu/indirect/private.c +368 -154
  55. data/vendor/scs/linsys/gpu/indirect/private.h +26 -12
  56. data/vendor/scs/linsys/scs_matrix.c +498 -0
  57. data/vendor/scs/linsys/scs_matrix.h +70 -0
  58. data/vendor/scs/linsys/scs_matrix.o +0 -0
  59. data/vendor/scs/scs.mk +13 -9
  60. data/vendor/scs/src/aa.c +384 -109
  61. data/vendor/scs/src/aa.o +0 -0
  62. data/vendor/scs/src/cones.c +440 -353
  63. data/vendor/scs/src/cones.o +0 -0
  64. data/vendor/scs/src/ctrlc.c +15 -5
  65. data/vendor/scs/src/ctrlc.o +0 -0
  66. data/vendor/scs/src/linalg.c +84 -28
  67. data/vendor/scs/src/linalg.o +0 -0
  68. data/vendor/scs/src/normalize.c +22 -64
  69. data/vendor/scs/src/normalize.o +0 -0
  70. data/vendor/scs/src/rw.c +161 -22
  71. data/vendor/scs/src/rw.o +0 -0
  72. data/vendor/scs/src/scs.c +768 -561
  73. data/vendor/scs/src/scs.o +0 -0
  74. data/vendor/scs/src/scs_indir.o +0 -0
  75. data/vendor/scs/src/scs_version.c +9 -3
  76. data/vendor/scs/src/scs_version.o +0 -0
  77. data/vendor/scs/src/util.c +37 -106
  78. data/vendor/scs/src/util.o +0 -0
  79. data/vendor/scs/test/minunit.h +17 -8
  80. data/vendor/scs/test/problem_utils.h +176 -14
  81. data/vendor/scs/test/problems/degenerate.h +130 -0
  82. data/vendor/scs/test/problems/hs21_tiny_qp.h +124 -0
  83. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +116 -0
  84. data/vendor/scs/test/problems/infeasible_tiny_qp.h +100 -0
  85. data/vendor/scs/test/problems/qafiro_tiny_qp.h +199 -0
  86. data/vendor/scs/test/problems/random_prob +0 -0
  87. data/vendor/scs/test/problems/random_prob.h +45 -0
  88. data/vendor/scs/test/problems/rob_gauss_cov_est.h +188 -31
  89. data/vendor/scs/test/problems/small_lp.h +13 -14
  90. data/vendor/scs/test/problems/test_fails.h +43 -0
  91. data/vendor/scs/test/problems/unbounded_tiny_qp.h +82 -0
  92. data/vendor/scs/test/random_socp_prob.c +54 -53
  93. data/vendor/scs/test/rng.h +109 -0
  94. data/vendor/scs/test/run_from_file.c +19 -10
  95. data/vendor/scs/test/run_tests.c +27 -3
  96. metadata +30 -73
  97. data/ext/scs/Rakefile +0 -11
  98. data/vendor/scs/linsys/amatrix.c +0 -305
  99. data/vendor/scs/linsys/amatrix.h +0 -36
  100. data/vendor/scs/linsys/amatrix.o +0 -0
  101. data/vendor/scs/test/data/small_random_socp +0 -0
  102. data/vendor/scs/test/problems/small_random_socp.h +0 -33
  103. 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));
335
- }
336
-
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;
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;
356
472
  }
357
473
 
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,34 +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 (r->res_pri < eps && r->res_dual < eps && r->rel_gap < eps) {
629
- 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
+ }
630
683
  }
631
- /* Add iter > 0 to avoid strange edge case where infeasible point found
632
- * right at start of run `out/demo_SOCP_indirect 2 0.1 0.3 1506264403` */
633
- if (r->res_unbdd < eps && iter > 0) {
684
+ if (isless(r->res_unbdd_a, eps_infeas) &&
685
+ isless(r->res_unbdd_p, eps_infeas)) {
634
686
  return SCS_UNBOUNDED;
635
687
  }
636
- if (r->res_infeas < eps && iter > 0) {
688
+ if (isless(r->res_infeas, eps_infeas)) {
637
689
  return SCS_INFEASIBLE;
638
690
  }
639
691
  return 0;
640
692
  }
641
693
 
642
- static scs_int validate(const ScsData *d, const ScsCone *k) {
643
- ScsSettings *stgs = d->stgs;
694
+ #if NOVALIDATE == 0
695
+ static scs_int validate(const ScsData *d, const ScsCone *k,
696
+ const ScsSettings *stgs) {
644
697
  if (d->m <= 0 || d->n <= 0) {
645
698
  scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
646
699
  (long)d->m, (long)d->n);
647
700
  return -1;
648
701
  }
649
702
  if (d->m < d->n) {
650
- scs_printf("WARN: m less than n, problem likely degenerate\n");
703
+ /* scs_printf("WARN: m less than n, problem likely degenerate\n"); */
651
704
  /* return -1; */
652
705
  }
653
- if (SCS(validate_lin_sys)(d->A) < 0) {
706
+ if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
654
707
  scs_printf("invalid linear system input data\n");
655
708
  return -1;
656
709
  }
@@ -662,8 +715,16 @@ static scs_int validate(const ScsData *d, const ScsCone *k) {
662
715
  scs_printf("max_iters must be positive\n");
663
716
  return -1;
664
717
  }
665
- if (stgs->eps <= 0) {
666
- 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");
667
728
  return -1;
668
729
  }
669
730
  if (stgs->alpha <= 0 || stgs->alpha >= 2) {
@@ -680,222 +741,374 @@ static scs_int validate(const ScsData *d, const ScsCone *k) {
680
741
  }
681
742
  return 0;
682
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
+ }
683
757
 
684
- 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) {
685
760
  ScsWork *w = (ScsWork *)scs_calloc(1, sizeof(ScsWork));
686
761
  scs_int l = d->n + d->m + 1;
687
- if (d->stgs->verbose) {
688
- print_init_header(d, k);
762
+ if (stgs->verbose) {
763
+ print_init_header(d, k, stgs);
689
764
  }
690
765
  if (!w) {
691
766
  scs_printf("ERROR: allocating work failure\n");
692
767
  return SCS_NULL;
693
768
  }
694
769
  /* get settings and dims from data struct */
695
- 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 */
696
774
  w->m = d->m;
697
775
  w->n = d->n;
698
- 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;
699
781
  /* allocate workspace: */
700
- /* u* include v* values */
701
- w->u = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
702
- w->u_best = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
703
- w->u_t = (scs_float *)scs_malloc(l * sizeof(scs_float));
704
- w->u_prev = (scs_float *)scs_malloc(2 * l * sizeof(scs_float));
705
- w->h = (scs_float *)scs_malloc((l - 1) * sizeof(scs_float));
706
- w->g = (scs_float *)scs_malloc((l - 1) * sizeof(scs_float));
707
- w->pr = (scs_float *)scs_malloc(d->m * sizeof(scs_float));
708
- w->dr = (scs_float *)scs_malloc(d->n * sizeof(scs_float));
709
- w->b = (scs_float *)scs_malloc(d->m * sizeof(scs_float));
710
- w->c = (scs_float *)scs_malloc(d->n * sizeof(scs_float));
711
- if (!w->u || !w->u_t || !w->u_prev || !w->h || !w->g || !w->pr || !w->dr ||
712
- !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) {
713
810
  scs_printf("ERROR: work memory allocation failure\n");
714
811
  return SCS_NULL;
715
812
  }
716
- /* make u,v and u_prev,v_prev contiguous in memory */
717
- w->v = &(w->u[l]);
718
- w->v_best = &(w->u_best[l]);
719
- w->v_prev = &(w->u_prev[l]);
720
- w->A = d->A;
813
+
721
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
+
722
821
  #ifdef COPYAMATRIX
723
- if (!SCS(copy_a_matrix)(&(w->A), d->A)) {
822
+ if (!SCS(copy_matrix)(&(w->A), d->A)) {
724
823
  scs_printf("ERROR: copy A matrix failed\n");
725
824
  return SCS_NULL;
726
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
+ }
727
830
  #endif
728
- w->scal = (ScsScaling *)scs_malloc(sizeof(ScsScaling));
729
- SCS(normalize_a)(w->A, w->stgs, k, w->scal);
730
- #if EXTRA_VERBOSE > 0
731
- SCS(print_array)(w->scal->D, d->m, "D");
732
- scs_printf("SCS(norm) D = %4f\n", SCS(norm)(w->scal->D, d->m));
733
- SCS(print_array)(w->scal->E, d->n, "E");
734
- scs_printf("SCS(norm) E = %4f\n", SCS(norm)(w->scal->E, d->n));
735
- #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);
736
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;
737
842
  w->scal = SCS_NULL;
738
843
  }
739
- if (!(w->cone_work = SCS(init_cone)(k))) {
844
+ if (!(w->cone_work = SCS(init_cone)(k, w->scal, w->m))) {
740
845
  scs_printf("ERROR: init_cone failure\n");
741
846
  return SCS_NULL;
742
847
  }
743
- 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))) {
744
850
  scs_printf("ERROR: init_lin_sys_work failure\n");
745
851
  return SCS_NULL;
746
852
  }
747
- if (!(w->accel =
748
- aa_init(2 * (w->m + w->n + 1), ABS(w->stgs->acceleration_lookback),
749
- w->stgs->acceleration_lookback >= 0))) {
750
- 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
+ }
868
+ }
869
+ } else {
870
+ w->accel = SCS_NULL;
751
871
  }
752
872
  return w;
753
873
  }
754
874
 
755
- static scs_int update_work(const ScsData *d, ScsWork *w,
756
- 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) {
757
884
  /* before normalization */
758
885
  scs_int n = d->n;
759
886
  scs_int m = d->m;
760
-
761
- w->nm_b = SCS(norm)(d->b, m);
762
- w->nm_c = SCS(norm)(d->c, n);
763
- memcpy(w->b, d->b, d->m * sizeof(scs_float));
764
- memcpy(w->c, d->c, d->n * sizeof(scs_float));
765
-
766
- #if EXTRA_VERBOSE > 0
767
- SCS(print_array)(w->b, m, "b");
768
- scs_printf("pre-normalized norm b = %4f\n", SCS(norm)(w->b, m));
769
- SCS(print_array)(w->c, n, "c");
770
- scs_printf("pre-normalized norm c = %4f\n", SCS(norm)(w->c, n));
771
- #endif
772
- if (w->stgs->normalize) {
773
- SCS(normalize_b_c)(w);
774
- #if EXTRA_VERBOSE > 0
775
- SCS(print_array)(w->b, m, "bn");
776
- scs_printf("sc_b = %4f\n", w->sc_b);
777
- scs_printf("post-normalized norm b = %4f\n", SCS(norm)(w->b, m));
778
- SCS(print_array)(w->c, n, "cn");
779
- scs_printf("sc_c = %4f\n", w->sc_c);
780
- scs_printf("post-normalized norm c = %4f\n", SCS(norm)(w->c, n));
781
- #endif
782
- }
783
887
  if (w->stgs->warm_start) {
784
888
  warm_start_vars(w, sol);
785
889
  } else {
786
890
  cold_start_vars(w);
787
891
  }
788
- memcpy(w->h, w->c, n * sizeof(scs_float));
789
- memcpy(&(w->h[n]), w->b, m * sizeof(scs_float));
790
- memcpy(w->g, w->h, (n + m) * sizeof(scs_float));
791
- SCS(solve_lin_sys)(w->A, w->stgs, w->p, w->g, SCS_NULL, -1);
792
- SCS(scale_array)(&(w->g[n]), -1, m);
793
- 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);
794
897
  return 0;
795
898
  }
796
899
 
797
- static scs_float iterate_norm_diff(ScsWork *w) {
798
- scs_int l = w->m + w->n + 1;
799
- scs_float u_norm_difference = SCS(norm_diff)(w->u, w->u_prev, l);
800
- scs_float v_norm_difference = SCS(norm_diff)(w->v, w->v_prev, l);
801
- scs_float norm = SQRTF(SCS(norm_sq)(w->u, l) + SCS(norm_sq)(w->v, l));
802
- scs_float norm_diff = SQRTF(u_norm_difference * u_norm_difference +
803
- v_norm_difference * v_norm_difference);
804
- 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.));
805
903
  }
806
904
 
807
- static void update_best_iterate(ScsWork *w, ScsResiduals *r) {
808
- scs_float max_residual = get_max_residual(r);
809
- if (w->best_max_residual > max_residual) {
810
- w->best_max_residual = max_residual;
811
- memcpy(w->u_best, w->u, (w->m + w->n + 1) * sizeof(scs_float));
812
- 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;
813
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);
814
971
  }
815
972
 
816
- scs_int SCS(solve)(ScsWork *w, const ScsData *d, const ScsCone *k,
817
- ScsSolution *sol, ScsInfo *info) {
973
+ scs_int SCS(solve)(ScsWork *w, ScsSolution *sol, ScsInfo *info) {
818
974
  scs_int i;
819
- SCS(timer) solve_timer, accel_timer;
820
- scs_float total_accel_time = 0.0, total_norm;
821
- ScsResiduals r;
822
- scs_int l = w->m + w->n + 1;
823
- if (!d || !k || !sol || !info || !w || !d->b || !d->c) {
824
- 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");
825
980
  return SCS_FAILED;
826
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;
827
986
  /* initialize ctrl-c support */
828
987
  scs_start_interrupt_listener();
829
988
  SCS(tic)(&solve_timer);
830
989
  info->status_val = SCS_UNFINISHED; /* not yet converged */
831
- r.last_iter = -1;
832
990
  update_work(d, w, sol);
833
991
 
834
992
  if (w->stgs->verbose) {
835
993
  print_header(w, k);
836
994
  }
837
- /* scs: */
995
+
996
+ /* SCS */
838
997
  for (i = 0; i < w->stgs->max_iters; ++i) {
839
- /* accelerate here so that last step always projection onto cone */
998
+ /* Accelerate here so that last step always projection onto cone */
840
999
  /* this ensures the returned iterates always satisfy conic constraints */
841
- /* this relies on the fact that u and v are contiguous in memory */
842
- SCS(tic)(&accel_timer);
843
- if (i > 0 && aa_apply(w->u, w->u_prev, w->accel) != 0) {
844
- /*
845
- return failure(w, w->m, w->n, sol, info, SCS_FAILED,
846
- "error in accelerate", "Failure");
847
- */
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);
848
1007
  }
849
- total_accel_time += SCS(tocq)(&accel_timer);
850
1008
 
851
- /* scs is homogeneous so scale the iterates to keep norm reasonable */
852
- total_norm = SQRTF(SCS(norm_sq)(w->u, l) + SCS(norm_sq)(w->v, l));
853
- SCS(scale_array)(w->u, SQRTF((scs_float)l) * ITERATE_NORM / total_norm, l);
854
- 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
+ }
855
1014
 
856
- memcpy(w->u_prev, w->u, l * sizeof(scs_float));
1015
+ /* store v_prev = v, *after* normalizing */
857
1016
  memcpy(w->v_prev, w->v, l * sizeof(scs_float));
858
1017
 
1018
+ /* linear system solve */
1019
+ SCS(tic)(&lin_sys_timer);
859
1020
  if (project_lin_sys(w, i) < 0) {
860
1021
  return failure(w, w->m, w->n, sol, info, SCS_FAILED,
861
- "error in project_lin_sys", "Failure");
1022
+ "error in project_lin_sys", "failure");
862
1023
  }
1024
+ total_lin_sys_time += SCS(tocq)(&lin_sys_timer);
1025
+
1026
+ /* project onto the cones */
1027
+ SCS(tic)(&cone_timer);
863
1028
  if (project_cones(w, k, i) < 0) {
864
1029
  return failure(w, w->m, w->n, sol, info, SCS_FAILED,
865
- "error in project_cones", "Failure");
1030
+ "error in project_cones", "failure");
866
1031
  }
1032
+ total_cone_time += SCS(tocq)(&cone_timer);
867
1033
 
1034
+ /* dual variable step */
868
1035
  update_dual_vars(w);
869
1036
 
870
- if (scs_is_interrupted()) {
871
- return failure(w, w->m, w->n, sol, info, SCS_SIGINT, "Interrupted",
872
- "Interrupted");
873
- }
874
- if (i % CONVERGED_INTERVAL == 0 || iterate_norm_diff(w) < 1e-10) {
875
- calc_residuals(w, &r, i);
876
- 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) {
877
1044
  break;
878
1045
  }
879
- 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
+ }
880
1052
  }
881
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. */
882
1070
  if (w->stgs->verbose && i % PRINT_INTERVAL == 0) {
883
- calc_residuals(w, &r, i);
884
- update_best_iterate(w, &r);
885
- 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);
886
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);
887
1092
  }
1093
+
888
1094
  if (w->stgs->verbose) {
889
- calc_residuals(w, &r, i);
890
- print_summary(w, i, &r, &solve_timer);
1095
+ populate_residual_struct(w, i);
1096
+ print_summary(w, i, &solve_timer);
891
1097
  }
1098
+
892
1099
  /* populate solution vectors (unnormalized) and info */
893
- get_solution(w, sol, info, &r, i);
1100
+ finalize(w, sol, info, i);
1101
+
1102
+ /* populate timings */
894
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;
895
1107
 
896
1108
  if (w->stgs->verbose) {
897
- print_footer(d, k, sol, w, info, total_accel_time);
1109
+ print_footer(info);
898
1110
  }
1111
+
899
1112
  scs_end_interrupt_listener();
900
1113
  return info->status_val;
901
1114
  }
@@ -905,9 +1118,10 @@ void SCS(finish)(ScsWork *w) {
905
1118
  SCS(finish_cone)(w->cone_work);
906
1119
  if (w->stgs && w->stgs->normalize) {
907
1120
  #ifndef COPYAMATRIX
908
- SCS(un_normalize_a)(w->A, w->stgs, w->scal);
1121
+ SCS(un_normalize)(w->A, w->P, w->scal);
909
1122
  #else
910
- SCS(free_a_matrix)(w->A);
1123
+ SCS(free_scs_matrix)(w->A);
1124
+ SCS(free_scs_matrix)(w->P);
911
1125
  #endif
912
1126
  }
913
1127
  if (w->p) {
@@ -920,55 +1134,48 @@ void SCS(finish)(ScsWork *w) {
920
1134
  }
921
1135
  }
922
1136
 
923
- ScsWork *SCS(init)(const ScsData *d, const ScsCone *k, ScsInfo *info) {
924
- #if EXTRA_VERBOSE > 1
925
- SCS(tic)(&global_timer);
926
- #endif
1137
+ ScsWork *SCS(init)(const ScsData *d, const ScsCone *k,
1138
+ const ScsSettings *stgs) {
927
1139
  ScsWork *w;
928
1140
  SCS(timer) init_timer;
929
1141
  scs_start_interrupt_listener();
930
- if (!d || !k || !info) {
931
- scs_printf("ERROR: Missing ScsData, ScsCone or ScsInfo input\n");
1142
+ if (!d || !k) {
1143
+ scs_printf("ERROR: Missing ScsData or ScsCone input\n");
932
1144
  return SCS_NULL;
933
1145
  }
934
- #if EXTRA_VERBOSE > 0
935
- SCS(print_data)(d);
936
- SCS(print_cone_data)(k);
937
- #endif
938
- #ifndef NOVALIDATE
939
- if (validate(d, k) < 0) {
1146
+ #if NOVALIDATE == 0
1147
+ if (validate(d, k, stgs) < 0) {
940
1148
  scs_printf("ERROR: Validation returned failure\n");
941
1149
  return SCS_NULL;
942
1150
  }
943
1151
  #endif
944
1152
  SCS(tic)(&init_timer);
945
- if (d->stgs->write_data_filename) {
946
- SCS(write_data)(d, k);
1153
+ if (stgs->write_data_filename) {
1154
+ SCS(write_data)(d, k, stgs);
947
1155
  }
948
- w = init_work(d, k);
949
- info->setup_time = SCS(tocq)(&init_timer);
950
- if (d->stgs->verbose) {
951
- 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);
952
1159
  }
953
1160
  scs_end_interrupt_listener();
954
1161
  return w;
955
1162
  }
956
1163
 
957
1164
  /* this just calls SCS(init), SCS(solve), and SCS(finish) */
958
- scs_int scs(const ScsData *d, const ScsCone *k, ScsSolution *sol,
959
- ScsInfo *info) {
1165
+ scs_int scs(const ScsData *d, const ScsCone *k, const ScsSettings *stgs,
1166
+ ScsSolution *sol, ScsInfo *info) {
960
1167
  scs_int status;
961
- ScsWork *w = SCS(init)(d, k, info);
962
- #if EXTRA_VERBOSE > 0
1168
+ ScsWork *w = SCS(init)(d, k, stgs);
1169
+ #if VERBOSITY > 0
963
1170
  scs_printf("size of scs_int = %lu, size of scs_float = %lu\n",
964
1171
  sizeof(scs_int), sizeof(scs_float));
965
1172
  #endif
966
1173
  if (w) {
967
- SCS(solve)(w, d, k, sol, info);
1174
+ SCS(solve)(w, sol, info);
968
1175
  status = info->status_val;
969
1176
  } else {
970
1177
  status = failure(SCS_NULL, d ? d->m : -1, d ? d->n : -1, sol, info,
971
- SCS_FAILED, "could not initialize work", "Failure");
1178
+ SCS_FAILED, "could not initialize work", "failure");
972
1179
  }
973
1180
  SCS(finish)(w);
974
1181
  return status;