scs 0.2.2 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  }