scs 0.2.2 → 0.3.2

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