scs 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +1 -1
  4. data/lib/scs/ffi.rb +18 -0
  5. data/lib/scs/version.rb +1 -1
  6. data/vendor/scs/CITATION.cff +2 -3
  7. data/vendor/scs/CLAUDE.md +115 -0
  8. data/vendor/scs/CMakeLists.txt +330 -201
  9. data/vendor/scs/CONTRIBUTING.md +49 -0
  10. data/vendor/scs/Makefile +90 -43
  11. data/vendor/scs/README.md +96 -2
  12. data/vendor/scs/include/aa.h +59 -11
  13. data/vendor/scs/include/aa_stats.h +47 -0
  14. data/vendor/scs/include/cones.h +16 -7
  15. data/vendor/scs/include/glbopts.h +113 -26
  16. data/vendor/scs/include/linalg.h +5 -1
  17. data/vendor/scs/include/linsys.h +1 -2
  18. data/vendor/scs/include/normalize.h +6 -2
  19. data/vendor/scs/include/rw.h +9 -3
  20. data/vendor/scs/include/scs.h +17 -1
  21. data/vendor/scs/include/scs_blas.h +8 -0
  22. data/vendor/scs/include/scs_types.h +2 -2
  23. data/vendor/scs/include/scs_work.h +22 -1
  24. data/vendor/scs/include/util.h +3 -6
  25. data/vendor/scs/include/util_spectral_cones.h +3 -3
  26. data/vendor/scs/linsys/accelerate/direct/private.c +126 -0
  27. data/vendor/scs/linsys/accelerate/direct/private.h +34 -0
  28. data/vendor/scs/linsys/cpu/dense/private.c +250 -0
  29. data/vendor/scs/linsys/cpu/dense/private.h +31 -0
  30. data/vendor/scs/linsys/cpu/direct/private.c +86 -44
  31. data/vendor/scs/linsys/cpu/indirect/private.c +647 -113
  32. data/vendor/scs/linsys/cpu/indirect/private.h +28 -0
  33. data/vendor/scs/linsys/csparse.c +9 -3
  34. data/vendor/scs/linsys/csparse.h +4 -2
  35. data/vendor/scs/linsys/cudss/direct/private.c +77 -43
  36. data/vendor/scs/linsys/cudss/direct/private.h +33 -7
  37. data/vendor/scs/linsys/gpu/indirect/private.c +6 -2
  38. data/vendor/scs/linsys/mkl/direct/private.c +63 -31
  39. data/vendor/scs/linsys/mkl/direct/private.h +0 -1
  40. data/vendor/scs/linsys/scs_matrix.c +291 -165
  41. data/vendor/scs/linsys/scs_matrix.h +7 -9
  42. data/vendor/scs/scs.mk +35 -13
  43. data/vendor/scs/src/aa.c +673 -166
  44. data/vendor/scs/src/cones.c +545 -223
  45. data/vendor/scs/src/ctrlc.c +59 -16
  46. data/vendor/scs/src/exp_cone.c +70 -50
  47. data/vendor/scs/src/linalg.c +21 -2
  48. data/vendor/scs/src/normalize.c +24 -26
  49. data/vendor/scs/src/rw.c +596 -124
  50. data/vendor/scs/src/scs.c +990 -513
  51. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +240 -187
  52. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +108 -85
  53. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +62 -63
  54. data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +85 -78
  55. data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +92 -97
  56. data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +39 -28
  57. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +59 -40
  58. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +37 -29
  59. data/vendor/scs/src/spectral_cones/util_spectral_cones.c +12 -6
  60. data/vendor/scs/src/util.c +37 -7
  61. data/vendor/scs/test/mkl_interface_mismatch.c +97 -0
  62. data/vendor/scs/test/packaging/CMakeLists.txt +21 -0
  63. data/vendor/scs/test/packaging/consume.c +50 -0
  64. data/vendor/scs/test/problem_utils.h +9 -2
  65. data/vendor/scs/test/problems/dense_qp.h +64 -0
  66. data/vendor/scs/test/problems/hs21_tiny_qp.h +6 -2
  67. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +10 -2
  68. data/vendor/scs/test/problems/infeasible_lp.h +66 -0
  69. data/vendor/scs/test/problems/infeasible_socp.h +75 -0
  70. data/vendor/scs/test/problems/lp_update.h +110 -0
  71. data/vendor/scs/test/problems/qafiro_tiny_qp.h +3 -1
  72. data/vendor/scs/test/problems/rob_gauss_cov_est.h +6 -3
  73. data/vendor/scs/test/problems/small_qp.h +5 -0
  74. data/vendor/scs/test/problems/test_box_cone.h +92 -0
  75. data/vendor/scs/test/problems/test_dual_exp_cone.h +96 -0
  76. data/vendor/scs/test/problems/test_inaccurate.h +212 -0
  77. data/vendor/scs/test/problems/test_mixed_cones.h +113 -0
  78. data/vendor/scs/test/problems/test_normalize_roundtrip.h +279 -0
  79. data/vendor/scs/test/problems/test_power_cone.h +414 -0
  80. data/vendor/scs/test/problems/test_psd_metric.h +109 -0
  81. data/vendor/scs/test/problems/test_psd_n1.h +84 -0
  82. data/vendor/scs/test/problems/test_root_plus.h +191 -0
  83. data/vendor/scs/test/problems/test_rw_settings.h +112 -0
  84. data/vendor/scs/test/problems/test_soc_sizes.h +393 -0
  85. data/vendor/scs/test/problems/test_solver_options.h +558 -0
  86. data/vendor/scs/test/problems/test_validation.h +263 -24
  87. data/vendor/scs/test/problems/test_zero_cone.h +81 -0
  88. data/vendor/scs/test/problems/unbounded_lp.h +66 -0
  89. data/vendor/scs/test/problems/unbounded_socp.h +71 -0
  90. data/vendor/scs/test/run_from_file.c +6 -0
  91. data/vendor/scs/test/run_tests.c +71 -0
  92. data/vendor/scs/test/rw_settings.c +7 -0
  93. data/vendor/scs/test/spectral_cones_problems/test_ell1_and_nuc.h +128 -0
  94. data/vendor/scs/test/spectral_cones_problems/test_ell1_cone.h +115 -0
  95. metadata +35 -3
data/vendor/scs/src/scs.c CHANGED
@@ -1,5 +1,14 @@
1
+ /*
2
+ * Main SCS solver implementation.
3
+ *
4
+ * This file contains the ADMM iteration loop, workspace management,
5
+ * residual computation, convergence checking, and the public API
6
+ * functions (scs_init, scs_solve, scs_update, scs_finish, scs).
7
+ */
8
+
1
9
  #include "scs.h"
2
10
  #include "aa.h"
11
+ #include "cones.h"
3
12
  #include "ctrlc.h"
4
13
  #include "glbopts.h"
5
14
  #include "linalg.h"
@@ -10,67 +19,142 @@
10
19
  #include "scs_work.h"
11
20
  #include "util.h"
12
21
 
13
- /* printing header */
14
- static const char *HEADER[] = {
15
- " iter ", " pri res ", " dua res ", " gap ",
16
- " obj ", " scale ", " time (s)",
17
- };
18
- static const scs_int HSPACE = 9;
19
- static const scs_int HEADER_LEN = 7;
20
- static const scs_int LINE_LEN = 66;
22
+ #include <string.h>
23
+
24
+ #ifdef SCS_MKL
25
+ #define MKL_INTERFACE_LP64 0
26
+ #define MKL_INTERFACE_ILP64 1
27
+ /* MKL's interface-layer value is a bitmask: bit 0 selects the integer
28
+ * width (LP64/ILP64) and bit 1 is the GNU Fortran calling-convention
29
+ * flag, so MKL_Set_Interface_Layer can legitimately return 2 (LP64|GNU)
30
+ * or 3 (ILP64|GNU) -- e.g. when an MKL-backed NumPy initialized MKL
31
+ * first. Only the width bit affects integer-size safety. */
32
+ #define MKL_INTERFACE_GNU 2
33
+ /* bit 0 of the layer value selects the integer width (LP64/ILP64) */
34
+ #define MKL_INTERFACE_WIDTH_MASK MKL_INTERFACE_ILP64
35
+ /* Only libmkl_rt provides this; a static MKL link has nothing to negotiate,
36
+ * so the reference is weak and the check is skipped when the symbol is
37
+ * absent (MSVC links mkl_rt dynamically and keeps the strong reference). */
38
+ #if defined(__GNUC__)
39
+ int MKL_Set_Interface_Layer(int) __attribute__((weak));
40
+ #else
41
+ int MKL_Set_Interface_Layer(int);
42
+ #endif
21
43
 
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);
44
+ static const char *scs_mkl_interface_name(int layer) {
45
+ switch (layer) {
46
+ case MKL_INTERFACE_LP64:
47
+ return "LP64";
48
+ case MKL_INTERFACE_ILP64:
49
+ return "ILP64";
50
+ case MKL_INTERFACE_LP64 | MKL_INTERFACE_GNU:
51
+ return "LP64 (GNU)";
52
+ case MKL_INTERFACE_ILP64 | MKL_INTERFACE_GNU:
53
+ return "ILP64 (GNU)";
54
+ default:
55
+ return "unknown";
31
56
  }
32
57
  }
33
58
 
34
- static void free_work(ScsWork *w) {
35
- if (w) {
36
- scs_free(w->u);
37
- scs_free(w->u_t);
38
- scs_free(w->v);
39
- scs_free(w->v_prev);
40
- scs_free(w->rsk);
41
- scs_free(w->h);
42
- scs_free(w->g);
43
- scs_free(w->b_orig);
44
- scs_free(w->c_orig);
45
- scs_free(w->lin_sys_warm_start);
46
- scs_free(w->diag_r);
47
- SCS(free_sol)(w->xys_orig);
48
- if (w->scal) {
49
- scs_free(w->scal->D);
50
- scs_free(w->scal->E);
51
- scs_free(w->scal);
52
- }
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
- }
58
- if (w->stgs) {
59
- if (w->stgs->log_csv_filename)
60
- scs_free((char *)w->stgs->log_csv_filename);
61
- if (w->stgs->write_data_filename)
62
- scs_free((char *)w->stgs->write_data_filename);
63
- scs_free(w->stgs);
59
+ static scs_int scs_init_mkl_runtime(void) {
60
+ /* Enforce the correct MKL interface layer for BLAS/LAPACK calls before any
61
+ * common SCS code reaches MKL-backed BLAS routines.
62
+ *
63
+ * The interface layer must match what we linked against:
64
+ * BLAS64 defined -> ILP64 (64-bit BLAS integers, mkl-dynamic-ilp64-*)
65
+ * BLAS64 undefined -> LP64 (32-bit BLAS integers, mkl-dynamic-lp64-*)
66
+ *
67
+ * If another library in the process set the wrong layer, our BLAS calls
68
+ * would silently receive the wrong integer width, causing memory corruption.
69
+ *
70
+ * This only protects the BLAS layer. The pardiso_64 entry point is
71
+ * unaffected by the interface layer — it always uses 64-bit integers. */
72
+ {
73
+ #ifdef BLAS64
74
+ int expected = MKL_INTERFACE_ILP64;
75
+ #else
76
+ int expected = MKL_INTERFACE_LP64;
77
+ #endif
78
+ int actual;
79
+ #if defined(__GNUC__)
80
+ if (!MKL_Set_Interface_Layer) {
81
+ /* static MKL: no runtime interface layer exists to query */
82
+ return 0;
64
83
  }
65
- if (w->k) { /* deep copy */
66
- SCS(free_cone)(w->k);
84
+ #endif
85
+ actual = MKL_Set_Interface_Layer(expected);
86
+ /* MKL returns -1 for an invalid request; without this guard the width
87
+ * comparison below would misread it ((-1 & 1) == 1, i.e. ILP64) */
88
+ if (actual < 0) {
89
+ scs_printf("MKL_Set_Interface_Layer(%d) failed with error %d.\n",
90
+ expected, actual);
91
+ return -1;
67
92
  }
68
- if (w->d) { /* deep copy */
69
- SCS(free_data)(w->d);
93
+ /* compare only the integer-width bit: the GNU flag is a Fortran
94
+ * calling-convention variant with the same integer sizes */
95
+ if ((actual & MKL_INTERFACE_WIDTH_MASK) !=
96
+ (expected & MKL_INTERFACE_WIDTH_MASK)) {
97
+ scs_printf("MKL interface layer mismatch: expected %s, but MKL is using "
98
+ "%s (%d). Another library in this process likely "
99
+ "initialized MKL with an incompatible LP64/ILP64 setting.\n",
100
+ scs_mkl_interface_name(expected),
101
+ scs_mkl_interface_name(actual), actual);
102
+ return -1;
70
103
  }
71
- scs_free(w);
72
104
  }
105
+ return 0;
73
106
  }
107
+ #endif
108
+
109
+ /* ======================= Forward Declarations ====================== */
110
+
111
+ static void print_init_header(const ScsData *d, const ScsCone *k,
112
+ const ScsSettings *stgs);
113
+ static void print_header(ScsWork *w, const ScsCone *k);
114
+ static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer);
115
+ static void print_footer(ScsInfo *info);
116
+ static void free_residuals(ScsResiduals *r);
117
+ static ScsResiduals *init_residuals(const ScsData *d);
118
+ static void set_info_aa_stats(ScsInfo *info, const AaWork *accel);
119
+ static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
120
+ ScsInfo *info, scs_int status_val,
121
+ const char *msg);
122
+ static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
123
+ ScsInfo *info, scs_int stint, const char *msg,
124
+ const char *ststr);
125
+ static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n,
126
+ scs_float pd);
127
+ static void unnormalize_residuals(ScsWork *w);
128
+ static void populate_residual_struct(ScsWork *w, scs_int iter);
129
+ static scs_int has_converged(ScsWork *w, scs_int iter);
130
+ static void warm_start_vars(ScsWork *w, ScsSolution *sol);
131
+ static void cold_start_vars(ScsWork *w);
132
+ static scs_float root_plus(ScsWork *w, scs_float *p, scs_float *mu,
133
+ scs_float eta);
134
+ static scs_int project_lin_sys(ScsWork *w, scs_int iter);
135
+ static void compute_rsk(ScsWork *w);
136
+ static void update_dual_vars(ScsWork *w);
137
+ static scs_int project_cones(ScsWork *w, const ScsCone *k, scs_int iter);
138
+ static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
139
+ scs_int iter);
140
+ static void set_diag_r(ScsWork *w);
141
+ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
142
+ const ScsSettings *stgs);
143
+ static void update_work_cache(ScsWork *w);
144
+ static void reset_tracking(ScsWork *w);
145
+ static scs_int update_work(ScsWork *w, ScsSolution *sol);
146
+ static scs_int update_scale(ScsWork *w, const ScsCone *k, scs_int iter);
147
+ static inline void normalize_v(scs_float *v, scs_int len);
148
+
149
+ /* ======================== Printing / Output ======================== */
150
+
151
+ static const char *HEADER[] = {
152
+ " iter ", " pri res ", " dua res ", " gap ",
153
+ " obj ", " scale ", " time (s)",
154
+ };
155
+ static const scs_int HSPACE = 9;
156
+ static const scs_int HEADER_LEN = 7;
157
+ static const scs_int LINE_LEN = 66;
74
158
 
75
159
  static void print_init_header(const ScsData *d, const ScsCone *k,
76
160
  const ScsSettings *stgs) {
@@ -96,15 +180,18 @@ static void print_init_header(const ScsData *d, const ScsCone *k,
96
180
  scs_printf("\n");
97
181
  scs_printf("problem: variables n: %i, constraints m: %i\n", (int)d->n,
98
182
  (int)d->m);
99
- scs_printf("%s", cone_str);
100
- scs_free(cone_str);
183
+ if (cone_str) {
184
+ scs_printf("%s", cone_str);
185
+ scs_free(cone_str);
186
+ } else {
187
+ scs_printf("cones: <unavailable>\n");
188
+ }
101
189
  scs_printf("settings: eps_abs: %.1e, eps_rel: %.1e, eps_infeas: %.1e\n"
102
190
  "\t alpha: %.2f, scale: %.2e, adaptive_scale: %i\n"
103
191
  "\t max_iters: %i, normalize: %i, rho_x: %.2e\n",
104
192
  stgs->eps_abs, stgs->eps_rel, stgs->eps_infeas, stgs->alpha,
105
193
  stgs->scale, (int)stgs->adaptive_scale, (int)stgs->max_iters,
106
194
  (int)stgs->normalize, stgs->rho_x);
107
- /* (int)stgs->warm_start); */
108
195
  if (stgs->acceleration_lookback != 0) {
109
196
  scs_printf("\t acceleration_lookback: %i, acceleration_interval: %i\n",
110
197
  (int)acceleration_lookback, (int)acceleration_interval);
@@ -125,6 +212,148 @@ static void print_init_header(const ScsData *d, const ScsCone *k,
125
212
  #endif
126
213
  }
127
214
 
215
+ static void print_header(ScsWork *w, const ScsCone *k) {
216
+ scs_int i;
217
+ for (i = 0; i < LINE_LEN; ++i) {
218
+ scs_printf("-");
219
+ }
220
+ scs_printf("\n");
221
+ for (i = 0; i < HEADER_LEN - 1; ++i) {
222
+ scs_printf("%s|", HEADER[i]);
223
+ }
224
+ scs_printf("%s\n", HEADER[HEADER_LEN - 1]);
225
+ for (i = 0; i < LINE_LEN; ++i) {
226
+ scs_printf("-");
227
+ }
228
+ scs_printf("\n");
229
+ #ifdef MATLAB_MEX_FILE
230
+ mexEvalString("drawnow;");
231
+ #endif
232
+ }
233
+
234
+ static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer) {
235
+ ScsResiduals *r = w->r_orig;
236
+ scs_printf("%*i|", (int)strlen(HEADER[0]), (int)i);
237
+ scs_printf("%*.2e ", (int)HSPACE, r->res_pri);
238
+ scs_printf("%*.2e ", (int)HSPACE, r->res_dual);
239
+ scs_printf("%*.2e ", (int)HSPACE, r->gap);
240
+ /* report mid point of primal and dual objective values */
241
+ scs_printf("%*.2e ", (int)HSPACE, 0.5 * (r->pobj + r->dobj));
242
+ scs_printf("%*.2e ", (int)HSPACE, w->stgs->scale);
243
+ /* Report TOTAL time, including setup */
244
+ scs_printf("%*.2e ", (int)HSPACE,
245
+ (SCS(tocq)(solve_timer) + w->setup_time) / 1e3);
246
+ scs_printf("\n");
247
+
248
+ #if VERBOSITY > 0
249
+ scs_printf("Norm u = %1.6e, ", SCS(norm_2)(w->u, w->d->n + w->d->m + 1));
250
+ scs_printf("Norm u_t = %1.6e, ", SCS(norm_2)(w->u_t, w->d->n + w->d->m + 1));
251
+ scs_printf("Norm v = %1.6e, ", SCS(norm_2)(w->v, w->d->n + w->d->m + 1));
252
+ scs_printf("Norm rsk = %1.6e, ", SCS(norm_2)(w->rsk, w->d->n + w->d->m + 1));
253
+ scs_printf("Norm x = %1.6e, ", SCS(norm_2)(w->xys_orig->x, w->d->n));
254
+ scs_printf("Norm y = %1.6e, ", SCS(norm_2)(w->xys_orig->y, w->d->m));
255
+ scs_printf("Norm s = %1.6e, ", SCS(norm_2)(w->xys_orig->s, w->d->m));
256
+ scs_printf("Norm |Ax + s| = %1.6e, ", SCS(norm_2)(r->ax_s, w->d->m));
257
+ scs_printf("tau = %1.6e, ", w->u[w->d->n + w->d->m]);
258
+ scs_printf("kappa = %1.6e, ", w->rsk[w->d->n + w->d->m]);
259
+ scs_printf("|u - u_t| = %1.6e, ",
260
+ SCS(norm_diff)(w->u, w->u_t, w->d->n + w->d->m + 1));
261
+ scs_printf("res_infeas = %1.6e, ", r->res_infeas);
262
+ scs_printf("res_unbdd_a = %1.6e, ", r->res_unbdd_a);
263
+ scs_printf("res_unbdd_p = %1.6e, ", r->res_unbdd_p);
264
+ scs_printf("ctx_tau = %1.6e, ", r->ctx_tau);
265
+ scs_printf("bty_tau = %1.2e\n", r->bty_tau);
266
+ #endif
267
+
268
+ #ifdef MATLAB_MEX_FILE
269
+ mexEvalString("drawnow;");
270
+ #endif
271
+ }
272
+
273
+ static void print_footer(ScsInfo *info) {
274
+ scs_int i;
275
+
276
+ for (i = 0; i < LINE_LEN; ++i) {
277
+ scs_printf("-");
278
+ }
279
+ scs_printf("\n");
280
+ scs_printf("status: %s\n", info->status);
281
+ scs_printf("timings: total: %1.2es = setup: %1.2es + solve: %1.2es\n",
282
+ (info->setup_time + info->solve_time) / 1e3,
283
+ info->setup_time / 1e3, info->solve_time / 1e3);
284
+ scs_printf("\t lin-sys: %1.2es, cones: %1.2es, accel: %1.2es\n",
285
+ info->lin_sys_time / 1e3, info->cone_time / 1e3,
286
+ info->accel_time / 1e3);
287
+
288
+ for (i = 0; i < LINE_LEN; ++i) {
289
+ scs_printf("-");
290
+ }
291
+ scs_printf("\n");
292
+ /* report mid point of primal and dual objective values */
293
+ scs_printf("objective = %.6f", 0.5 * (info->pobj + info->dobj));
294
+ switch (info->status_val) {
295
+ case SCS_SOLVED_INACCURATE:
296
+ case SCS_UNBOUNDED_INACCURATE:
297
+ case SCS_INFEASIBLE_INACCURATE:
298
+ scs_printf(" (inaccurate)");
299
+ /* fallthrough */
300
+ default:
301
+ scs_printf("\n");
302
+ }
303
+ for (i = 0; i < LINE_LEN; ++i) {
304
+ scs_printf("-");
305
+ }
306
+ scs_printf("\n");
307
+ #ifdef MATLAB_MEX_FILE
308
+ mexEvalString("drawnow;");
309
+ #endif
310
+ }
311
+
312
+ /* ======================== Memory Management ======================== */
313
+
314
+ static void free_residuals(ScsResiduals *r) {
315
+ if (r) {
316
+ scs_free(r->ax);
317
+ scs_free(r->ax_s);
318
+ scs_free(r->px);
319
+ scs_free(r->aty);
320
+ scs_free(r->ax_s_btau);
321
+ scs_free(r->px_aty_ctau);
322
+ scs_free(r);
323
+ }
324
+ }
325
+
326
+ static ScsResiduals *init_residuals(const ScsData *d) {
327
+ ScsResiduals *r = (ScsResiduals *)scs_calloc(1, sizeof(ScsResiduals));
328
+ if (!r)
329
+ return SCS_NULL;
330
+ r->ax = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
331
+ r->ax_s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
332
+ r->ax_s_btau = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
333
+ r->px = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
334
+ r->aty = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
335
+ r->px_aty_ctau = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
336
+ if (!r->ax || !r->ax_s || !r->ax_s_btau || !r->px || !r->aty ||
337
+ !r->px_aty_ctau) {
338
+ free_residuals(r);
339
+ return SCS_NULL;
340
+ }
341
+ return r;
342
+ }
343
+
344
+ static void set_info_aa_stats(ScsInfo *info, const AaWork *accel) {
345
+ if (!info) {
346
+ return;
347
+ }
348
+ memset(&info->aa_stats, 0, sizeof(info->aa_stats));
349
+ info->aa_stats.last_aa_norm = NAN;
350
+ if (accel) {
351
+ info->aa_stats = aa_get_stats(accel);
352
+ }
353
+ }
354
+
355
+ /* ==================== Error / Failure Handling ===================== */
356
+
128
357
  static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
129
358
  ScsInfo *info, scs_int status_val,
130
359
  const char *msg) {
@@ -144,17 +373,23 @@ static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
144
373
  if (!sol->x) {
145
374
  sol->x = (scs_float *)scs_calloc(n, sizeof(scs_float));
146
375
  }
147
- SCS(scale_array)(sol->x, NAN, n);
376
+ if (sol->x) {
377
+ SCS(scale_array)(sol->x, NAN, n);
378
+ }
148
379
  }
149
380
  if (m > 0) {
150
381
  if (!sol->y) {
151
382
  sol->y = (scs_float *)scs_calloc(m, sizeof(scs_float));
152
383
  }
153
- SCS(scale_array)(sol->y, NAN, m);
384
+ if (sol->y) {
385
+ SCS(scale_array)(sol->y, NAN, m);
386
+ }
154
387
  if (!sol->s) {
155
388
  sol->s = (scs_float *)scs_calloc(m, sizeof(scs_float));
156
389
  }
157
- SCS(scale_array)(sol->s, NAN, m);
390
+ if (sol->s) {
391
+ SCS(scale_array)(sol->s, NAN, m);
392
+ }
158
393
  }
159
394
  }
160
395
  }
@@ -163,57 +398,127 @@ static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
163
398
  ScsInfo *info, scs_int stint, const char *msg,
164
399
  const char *ststr) {
165
400
  scs_int status = stint;
401
+ SCS(close_csv_log_file)(w);
166
402
  populate_on_failure(m, n, sol, info, status, ststr);
403
+ set_info_aa_stats(info, w ? w->accel : SCS_NULL);
167
404
  scs_printf("Failure:%s\n", msg);
168
405
  scs_end_interrupt_listener();
169
406
  return status;
170
407
  }
171
408
 
172
- static inline scs_int _is_nan(scs_float x) {
173
- return x != x;
174
- }
409
+ /* ========================= Validation ============================== */
175
410
 
176
- /* given x,y,s warm start, set v = [x; s / R + y; 1]
177
- * check for nans and set to zero if present
178
- */
179
- static void warm_start_vars(ScsWork *w, ScsSolution *sol) {
180
- scs_int n = w->d->n, m = w->d->m, i;
181
- scs_float *v = w->v;
182
- /* normalize the warm-start */
183
- if (w->stgs->normalize) {
184
- SCS(normalize_sol)(w->scal, sol);
411
+ #if NO_VALIDATE == 0
412
+ static scs_int validate(const ScsData *d, const ScsCone *k,
413
+ const ScsSettings *stgs) {
414
+ if (d->m <= 0 || d->n <= 0) {
415
+ scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
416
+ (long)d->m, (long)d->n);
417
+ return -1;
185
418
  }
186
- for (i = 0; i < n; ++i) {
187
- v[i] = _is_nan(sol->x[i]) ? 0. : sol->x[i];
419
+ if (d->A && (d->A->m != d->m || d->A->n != d->n)) {
420
+ scs_printf("A dimensions = (%li, %li), inconsistent with m = %li, "
421
+ "n = %li\n",
422
+ (long)d->A->m, (long)d->A->n, (long)d->m, (long)d->n);
423
+ return -1;
188
424
  }
189
- for (i = 0; i < m; ++i) {
190
- v[i + n] = sol->y[i] + sol->s[i] / w->diag_r[i + n];
191
- v[i + n] = _is_nan(v[i + n]) ? 0. : v[i + n];
425
+ if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
426
+ scs_printf("invalid linear system input data\n");
427
+ return -1;
192
428
  }
193
- v[n + m] = 1.0; /* tau = 1 */
194
- /* un-normalize so sol unchanged */
195
- if (w->stgs->normalize) {
196
- SCS(un_normalize_sol)(w->scal, sol);
429
+ if (SCS(validate_cones)(d, k) < 0) {
430
+ scs_printf("cone validation error\n");
431
+ return -1;
197
432
  }
198
- }
199
-
200
- static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n) {
201
- scs_float nm_ax_s, nm_px, nm_aty;
202
- scs_float nm_ax_s_btau = NORM(r->ax_s_btau, m);
203
- scs_float nm_px_aty_ctau = NORM(r->px_aty_ctau, n);
204
-
205
- r->res_pri = SAFEDIV_POS(nm_ax_s_btau, r->tau);
433
+ if (stgs->max_iters <= 0) {
434
+ scs_printf("max_iters must be positive\n");
435
+ return -1;
436
+ }
437
+ if (!isfinite(stgs->eps_abs) || stgs->eps_abs < 0) {
438
+ scs_printf("eps_abs tolerance must be a nonnegative finite number\n");
439
+ return -1;
440
+ }
441
+ if (!isfinite(stgs->eps_rel) || stgs->eps_rel < 0) {
442
+ scs_printf("eps_rel tolerance must be a nonnegative finite number\n");
443
+ return -1;
444
+ }
445
+ if (!isfinite(stgs->eps_infeas) || stgs->eps_infeas < 0) {
446
+ scs_printf("eps_infeas tolerance must be a nonnegative finite number\n");
447
+ return -1;
448
+ }
449
+ if (!isfinite(stgs->alpha) || stgs->alpha <= 0 || stgs->alpha >= 2) {
450
+ scs_printf("alpha must be in (0,2)\n");
451
+ return -1;
452
+ }
453
+ if (!isfinite(stgs->rho_x) || stgs->rho_x <= 0) {
454
+ scs_printf("rho_x must be a positive finite number (1e-3 works well).\n");
455
+ return -1;
456
+ }
457
+ if (stgs->adaptive_diag_scale < 0 || stgs->adaptive_diag_scale > 1) {
458
+ scs_printf("adaptive_diag_scale must be 0 (off) or 1 (on).\n");
459
+ return -1;
460
+ }
461
+ if (!isfinite(stgs->scale) || stgs->scale <= 0) {
462
+ scs_printf("scale must be a positive finite number (1 works well).\n");
463
+ return -1;
464
+ }
465
+ if (!isfinite(stgs->time_limit_secs) || stgs->time_limit_secs < 0) {
466
+ scs_printf("time_limit_secs must be a nonnegative finite number.\n");
467
+ return -1;
468
+ }
469
+ if (stgs->acceleration_interval <= 0) {
470
+ scs_printf("acceleration_interval must be positive (10 works well).\n");
471
+ return -1;
472
+ }
473
+ if (stgs->acceleration_lookback < 0) {
474
+ scs_printf("acceleration_lookback must be nonnegative "
475
+ "(use acceleration_type_1=0 for type-II AA).\n");
476
+ return -1;
477
+ }
478
+ if (!isfinite(stgs->acceleration_regularization)) {
479
+ /* Sign-encoded modes per include/aa.h: positive = scaled by
480
+ * ||A||_F ||Y||_F, negative = pinned absolute value, zero = off. */
481
+ scs_printf("acceleration_regularization must be a finite number.\n");
482
+ return -1;
483
+ }
484
+ if (!isfinite(stgs->acceleration_relaxation) ||
485
+ stgs->acceleration_relaxation < 0 ||
486
+ stgs->acceleration_relaxation > 2) {
487
+ scs_printf("acceleration_relaxation must be in [0, 2].\n");
488
+ return -1;
489
+ }
490
+ return 0;
491
+ }
492
+ #endif
493
+
494
+ /* ==================== Residual Computation ========================= */
495
+
496
+ /* pd = primal_scale * dual_scale = sigma^2 from the Ruiz equilibration.
497
+ * Pass pd = 1 when operating on normalised residuals (no amplification).
498
+ *
499
+ * After unnormalization bty_tau and ctx_tau are divided by pd, so
500
+ * floating-point noise is amplified by 1/pd. INFEAS_NEGATIVITY_TOL is
501
+ * calibrated for normalised scale, so the correct threshold in the
502
+ * un-normalised space is INFEAS_NEGATIVITY_TOL/pd (issue #350). */
503
+ static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n,
504
+ scs_float pd) {
505
+ scs_float nm_ax_s, nm_px, nm_aty;
506
+ scs_float nm_ax_s_btau = NORM(r->ax_s_btau, m);
507
+ scs_float nm_px_aty_ctau = NORM(r->px_aty_ctau, n);
508
+ scs_float tol = INFEAS_NEGATIVITY_TOL / pd;
509
+
510
+ r->res_pri = SAFEDIV_POS(nm_ax_s_btau, r->tau);
206
511
  r->res_dual = SAFEDIV_POS(nm_px_aty_ctau, r->tau);
207
512
  r->res_unbdd_a = NAN;
208
513
  r->res_unbdd_p = NAN;
209
514
  r->res_infeas = NAN;
210
- if (r->ctx_tau < -INFEAS_NEGATIVITY_TOL) {
515
+ if (r->ctx_tau < -tol) {
211
516
  nm_ax_s = NORM(r->ax_s, m);
212
517
  nm_px = NORM(r->px, n);
213
518
  r->res_unbdd_a = SAFEDIV_POS(nm_ax_s, -r->ctx_tau);
214
519
  r->res_unbdd_p = SAFEDIV_POS(nm_px, -r->ctx_tau);
215
520
  }
216
- if (r->bty_tau < -INFEAS_NEGATIVITY_TOL) {
521
+ if (r->bty_tau < -tol) {
217
522
  nm_aty = NORM(r->aty, n);
218
523
  r->res_infeas = SAFEDIV_POS(nm_aty, -r->bty_tau);
219
524
  }
@@ -228,14 +533,6 @@ static void unnormalize_residuals(ScsWork *w) {
228
533
  r->last_iter = r_n->last_iter;
229
534
  r->tau = r_n->tau;
230
535
 
231
- /* mem copy arrays */
232
- memcpy(r->ax, r_n->ax, w->d->m * sizeof(scs_float));
233
- memcpy(r->ax_s, r_n->ax_s, w->d->m * sizeof(scs_float));
234
- memcpy(r->ax_s_btau, r_n->ax_s_btau, w->d->m * sizeof(scs_float));
235
- memcpy(r->aty, r_n->aty, w->d->n * sizeof(scs_float));
236
- memcpy(r->px, r_n->px, w->d->n * sizeof(scs_float));
237
- memcpy(r->px_aty_ctau, r_n->px_aty_ctau, w->d->n * sizeof(scs_float));
238
-
239
536
  /* unnormalize */
240
537
  r->kap = r_n->kap / pd;
241
538
  r->bty_tau = r_n->bty_tau / pd;
@@ -248,20 +545,35 @@ static void unnormalize_residuals(ScsWork *w) {
248
545
  r->dobj = r_n->dobj / pd;
249
546
  r->gap = r_n->gap / pd;
250
547
 
251
- SCS(un_normalize_primal)(w->scal, r->ax);
252
- SCS(un_normalize_primal)(w->scal, r->ax_s);
253
- SCS(un_normalize_primal)(w->scal, r->ax_s_btau);
254
- SCS(un_normalize_dual)(w->scal, r->aty);
255
- SCS(un_normalize_dual)(w->scal, r->px);
256
- SCS(un_normalize_dual)(w->scal, r->px_aty_ctau);
548
+ /* Fuse the six memcpy+un_normalize calls into two loops.
549
+ * Primal: divide by D[i]*dual_scale. Dual: divide by E[i]*primal_scale.
550
+ * This reduces 6 memcpy + 6 scale passes to 2 passes. */
551
+ {
552
+ scs_int i;
553
+ const scs_float *D = w->scal->D, *E = w->scal->E;
554
+ scs_float inv_ds = 1.0 / (w->scal->dual_scale);
555
+ scs_float inv_ps = 1.0 / (w->scal->primal_scale);
556
+ for (i = 0; i < w->d->m; ++i) {
557
+ scs_float f = inv_ds / D[i];
558
+ r->ax[i] = r_n->ax[i] * f;
559
+ r->ax_s[i] = r_n->ax_s[i] * f;
560
+ r->ax_s_btau[i] = r_n->ax_s_btau[i] * f;
561
+ }
562
+ for (i = 0; i < w->d->n; ++i) {
563
+ scs_float f = inv_ps / E[i];
564
+ r->aty[i] = r_n->aty[i] * f;
565
+ r->px[i] = r_n->px[i] * f;
566
+ r->px_aty_ctau[i] = r_n->px_aty_ctau[i] * f;
567
+ }
568
+ }
257
569
 
258
- compute_residuals(r, w->d->m, w->d->n);
570
+ compute_residuals(r, w->d->m, w->d->n, pd);
259
571
  }
260
572
 
261
573
  /* calculates un-normalized residual quantities */
262
574
  /* this is somewhat slow but not a bottleneck */
263
575
  static void populate_residual_struct(ScsWork *w, scs_int iter) {
264
- scs_int n = w->d->n, m = w->d->m;
576
+ scs_int i, n = w->d->n, m = w->d->m;
265
577
  /* normalized x,y,s terms */
266
578
  scs_float *x = w->xys_normalized->x;
267
579
  scs_float *y = w->xys_normalized->y;
@@ -286,13 +598,11 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
286
598
  /* ax = Ax */
287
599
  SCS(accum_by_a)(w->d->A, x, r->ax);
288
600
 
289
- memcpy(r->ax_s, r->ax, m * sizeof(scs_float));
290
- /* ax_s = Ax + s */
291
- SCS(add_scaled_array)(r->ax_s, s, m, 1.);
292
-
293
- memcpy(r->ax_s_btau, r->ax_s, m * sizeof(scs_float));
294
- /* ax_s_btau = Ax + s - b * tau */
295
- SCS(add_scaled_array)(r->ax_s_btau, w->d->b, m, -r->tau);
601
+ /* Build ax_s and ax_s_btau in one fused pass (saves 2 memcpy + 2 axpy). */
602
+ for (i = 0; i < m; ++i) {
603
+ r->ax_s[i] = r->ax[i] + s[i];
604
+ r->ax_s_btau[i] = r->ax_s[i] - r->tau * w->d->b[i];
605
+ }
296
606
 
297
607
  /**************** DUAL *********************/
298
608
  memset(r->px, 0, n * sizeof(scs_float));
@@ -308,12 +618,10 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
308
618
  /* aty = A'y */
309
619
  SCS(accum_by_atrans)(w->d->A, y, r->aty);
310
620
 
311
- /* r->px_aty_ctau = Px */
312
- memcpy(r->px_aty_ctau, r->px, n * sizeof(scs_float));
313
- /* r->px_aty_ctau = Px + A'y */
314
- SCS(add_scaled_array)(r->px_aty_ctau, r->aty, n, 1.);
315
- /* r->px_aty_ctau = Px + A'y + c * tau */
316
- SCS(add_scaled_array)(r->px_aty_ctau, w->d->c, n, r->tau);
621
+ /* Build px_aty_ctau in one fused pass (saves 1 memcpy + 2 axpy). */
622
+ for (i = 0; i < n; ++i) {
623
+ r->px_aty_ctau[i] = r->px[i] + r->aty[i] + r->tau * w->d->c[i];
624
+ }
317
625
 
318
626
  /**************** OTHERS *****************/
319
627
  r->bty_tau = SCS(dot)(y, w->d->b, m);
@@ -327,7 +635,7 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
327
635
  r->pobj = r->xt_p_x / 2. + r->ctx;
328
636
  r->dobj = -r->xt_p_x / 2. - r->bty;
329
637
 
330
- compute_residuals(r, m, n);
638
+ compute_residuals(r, m, n, 1.0);
331
639
 
332
640
  if (w->stgs->normalize) {
333
641
  memcpy(w->xys_orig->x, w->xys_normalized->x, n * sizeof(scs_float));
@@ -338,31 +646,149 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
338
646
  }
339
647
  }
340
648
 
649
+ /* ==================== Convergence Checking ========================= */
650
+
651
+ static scs_int has_converged(ScsWork *w, scs_int iter) {
652
+ scs_float abs_xt_p_x, abs_ctx, abs_bty;
653
+ scs_float nm_s, nm_px, nm_aty, nm_ax;
654
+ scs_float grl, prl, drl;
655
+ scs_float eps_abs = w->stgs->eps_abs;
656
+ scs_float eps_rel = w->stgs->eps_rel;
657
+ scs_float eps_infeas = w->stgs->eps_infeas;
658
+
659
+ ScsResiduals *r = w->r_orig;
660
+
661
+ if (r->tau > 0.) {
662
+ abs_xt_p_x = ABS(r->xt_p_x);
663
+ abs_ctx = ABS(r->ctx);
664
+ abs_bty = ABS(r->bty);
665
+
666
+ nm_s = NORM(w->xys_orig->s, w->d->m);
667
+ nm_px = NORM(r->px, w->d->n);
668
+ nm_aty = NORM(r->aty, w->d->n);
669
+ nm_ax = NORM(r->ax, w->d->m);
670
+ /* xt_p_x, ctx, bty already have tau divided out */
671
+ grl = MAX(MAX(abs_xt_p_x, abs_ctx), abs_bty);
672
+ /* s, ax, px, aty do *not* have tau divided out, so need to divide */
673
+ prl = MAX(MAX(w->nm_b_orig * r->tau, nm_s), nm_ax) / r->tau;
674
+ drl = MAX(MAX(w->nm_c_orig * r->tau, nm_px), nm_aty) / r->tau;
675
+ if (isless(r->res_pri, eps_abs + eps_rel * prl) &&
676
+ isless(r->res_dual, eps_abs + eps_rel * drl) &&
677
+ isless(r->gap, eps_abs + eps_rel * grl)) {
678
+ return SCS_SOLVED;
679
+ }
680
+ }
681
+ /* Certificate residuals divide the violation by the certificate's
682
+ * objective value (-c'x or -b'y), which bakes the data magnitudes into
683
+ * the effective tolerance: with e.g. ||c|| ~ 1e6 the unboundedness test
684
+ * is six orders looser than intended and produces false certificates
685
+ * (netlib agg/grow families). Tighten by the data-norm ratio so the
686
+ * test is invariant to rescaling of c (resp. b) vs A; only ever
687
+ * tighten. Additionally require the certificate to hold on consecutive
688
+ * checks to reject transient (e.g. accelerated) iterates. */
689
+ {
690
+ scs_float tighten_unbdd =
691
+ MAX(1., SAFEDIV_POS(w->nm_c_orig, w->nm_a_orig));
692
+ scs_float tighten_infeas =
693
+ MAX(1., SAFEDIV_POS(w->nm_b_orig, w->nm_a_orig));
694
+ if (isless(r->res_unbdd_a * tighten_unbdd, eps_infeas) &&
695
+ isless(r->res_unbdd_p, eps_infeas)) {
696
+ if (++w->unbdd_cert_streak >= CERT_PERSISTENCE_CHECKS) {
697
+ return SCS_UNBOUNDED;
698
+ }
699
+ } else {
700
+ w->unbdd_cert_streak = 0;
701
+ }
702
+ if (isless(r->res_infeas * tighten_infeas, eps_infeas)) {
703
+ if (++w->infeas_cert_streak >= CERT_PERSISTENCE_CHECKS) {
704
+ return SCS_INFEASIBLE;
705
+ }
706
+ } else {
707
+ w->infeas_cert_streak = 0;
708
+ }
709
+ }
710
+ return 0;
711
+ }
712
+
713
+ /* =================== Warm / Cold Start Helpers ==================== */
714
+
715
+ static inline scs_int _is_nan(scs_float x) {
716
+ return x != x;
717
+ }
718
+
719
+ /* given x,y,s warm start, set v = [x; s / R + y; 1]
720
+ * check for nans and set to zero if present
721
+ */
722
+ static void warm_start_vars(ScsWork *w, ScsSolution *sol) {
723
+ scs_int n = w->d->n, m = w->d->m, i;
724
+ scs_float *v = w->v;
725
+ /* normalize the warm-start */
726
+ if (w->stgs->normalize) {
727
+ SCS(normalize_sol)(w->scal, sol);
728
+ }
729
+ for (i = 0; i < n; ++i) {
730
+ v[i] = _is_nan(sol->x[i]) ? 0. : sol->x[i];
731
+ }
732
+ for (i = 0; i < m; ++i) {
733
+ v[i + n] = sol->y[i] + sol->s[i] / w->diag_r[i + n];
734
+ v[i + n] = _is_nan(v[i + n]) ? 0. : v[i + n];
735
+ }
736
+ v[n + m] = 1.0; /* tau = 1 */
737
+ /* un-normalize so sol unchanged */
738
+ if (w->stgs->normalize) {
739
+ SCS(un_normalize_sol)(w->scal, sol);
740
+ }
741
+ }
742
+
341
743
  static void cold_start_vars(ScsWork *w) {
342
744
  scs_int l = w->d->n + w->d->m + 1;
343
745
  memset(w->v, 0, l * sizeof(scs_float));
344
746
  w->v[l - 1] = 1.;
345
747
  }
346
748
 
347
- /* utility function that computes x'Ry */
348
- static inline scs_float dot_r(ScsWork *w, const scs_float *x,
349
- const scs_float *y) {
350
- scs_int i;
351
- scs_float ip = 0.0;
352
- for (i = 0; i < w->d->n + w->d->m; ++i) {
353
- ip += x[i] * y[i] * w->diag_r[i];
749
+ /* ====================== ADMM Iteration ============================= */
750
+
751
+ static scs_float root_plus_from_coeffs(scs_float a, scs_float b, scs_float c) {
752
+ scs_float rad, sqrt_rad, q;
753
+ if (!isfinite(a) || !isfinite(b) || !isfinite(c) || a <= 0.) {
754
+ return NAN;
755
+ }
756
+ rad = b * b - 4 * a * c;
757
+ if (!isfinite(rad)) {
758
+ return NAN;
354
759
  }
355
- return ip;
760
+ if (rad < 0.) {
761
+ /* Preserve the historical repeated-root fallback for no-real-root cases. */
762
+ return -b / (2 * a);
763
+ }
764
+ sqrt_rad = SQRTF(rad);
765
+ if (b <= 0.) {
766
+ return (-b + sqrt_rad) / (2 * a);
767
+ }
768
+ q = -0.5 * (b + sqrt_rad);
769
+ return q != 0. ? c / q : 0.;
356
770
  }
357
771
 
358
772
  static scs_float root_plus(ScsWork *w, scs_float *p, scs_float *mu,
359
773
  scs_float eta) {
360
- scs_float a, b, c, rad, tau_scale = w->diag_r[w->d->n + w->d->m];
361
- a = tau_scale + dot_r(w, w->g, w->g);
362
- b = dot_r(w, mu, w->g) - 2 * dot_r(w, p, w->g) - eta * tau_scale;
363
- c = dot_r(w, p, p) - dot_r(w, p, mu);
364
- rad = b * b - 4 * a * c;
365
- return (-b + SQRTF(MAX(rad, 0.))) / (2 * a);
774
+ /* Compute all five weighted dot products (g'Rg, mu'Rg, p'Rg, p'Rp, p'Rmu)
775
+ * in a single pass over diag_r to minimise memory traffic. */
776
+ scs_int i, nm = w->d->n + w->d->m;
777
+ scs_float gg = 0., mug = 0., pg = 0., pp = 0., pmu = 0.;
778
+ scs_float a, b, c, tau_scale = w->diag_r[nm];
779
+ const scs_float *g = w->g, *r = w->diag_r;
780
+ for (i = 0; i < nm; ++i) {
781
+ scs_float ri = r[i], gi = g[i], pi = p[i], mui = mu[i];
782
+ gg += gi * gi * ri;
783
+ mug += mui * gi * ri;
784
+ pg += pi * gi * ri;
785
+ pp += pi * pi * ri;
786
+ pmu += pi * mui * ri;
787
+ }
788
+ a = tau_scale + gg;
789
+ b = mug - 2 * pg - eta * tau_scale;
790
+ c = pp - pmu;
791
+ return root_plus_from_coeffs(a, b, c);
366
792
  }
367
793
 
368
794
  /* status != 0 indicates failure */
@@ -370,10 +796,14 @@ static scs_int project_lin_sys(ScsWork *w, scs_int iter) {
370
796
  scs_int n = w->d->n, m = w->d->m, l = n + m + 1, status, i;
371
797
  scs_float *warm_start = SCS_NULL;
372
798
  scs_float tol = -1.0; /* only used for indirect methods, overridden later */
373
- memcpy(w->u_t, w->v, l * sizeof(scs_float));
374
- for (i = 0; i < l - 1; ++i) {
375
- w->u_t[i] *= (i < n ? 1 : -1) * w->diag_r[i];
799
+ /* Copy and scale in one pass, eliminating the intermediate memcpy. */
800
+ for (i = 0; i < n; ++i) {
801
+ w->u_t[i] = w->v[i] * w->diag_r[i];
802
+ }
803
+ for (i = n; i < l - 1; ++i) {
804
+ w->u_t[i] = -w->v[i] * w->diag_r[i];
376
805
  }
806
+ w->u_t[l - 1] = w->v[l - 1];
377
807
  #if INDIRECT > 0
378
808
  scs_float nm_ax_s_btau, nm_px_aty_ctau, nm_ws;
379
809
  /* compute warm start using the cone projection output */
@@ -441,6 +871,19 @@ static scs_int project_cones(ScsWork *w, const ScsCone *k, scs_int iter) {
441
871
  return status;
442
872
  }
443
873
 
874
+ /* scs is homogeneous so scale the iterate to keep norm reasonable */
875
+ static inline void normalize_v(scs_float *v, scs_int len) {
876
+ scs_float v_norm = SCS(norm_2)(v, len); /* always l2 norm */
877
+ if (v_norm == 0.) {
878
+ scs_printf("WARNING: normalize_v called with zero-norm iterate; this is "
879
+ "highly pathological (e.g., strong duality may not hold).\n");
880
+ return;
881
+ }
882
+ SCS(scale_array)(v, SQRTF((scs_float)len) * ITERATE_NORM / v_norm, len);
883
+ }
884
+
885
+ /* ================== Solution Extraction / Finalization ============== */
886
+
444
887
  static void sety(const ScsWork *w, ScsSolution *sol) {
445
888
  if (!sol->y) {
446
889
  sol->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
@@ -502,324 +945,108 @@ static void set_unbounded(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
502
945
  info->status_val = SCS_UNBOUNDED;
503
946
  }
504
947
 
505
- /* not yet converged, take best guess */
506
- static void set_unfinished(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
507
- if (w->r_orig->tau > w->r_orig->kap) {
508
- set_solved(w, sol, info);
509
- info->status_val = SCS_SOLVED_INACCURATE;
510
- } else if (w->r_orig->bty_tau < w->r_orig->ctx_tau) {
511
- set_infeasible(w, sol, info);
512
- info->status_val = SCS_INFEASIBLE_INACCURATE;
513
- } else {
514
- set_unbounded(w, sol, info);
515
- info->status_val = SCS_UNBOUNDED_INACCURATE;
516
- }
517
- /* Append inaccurate to the status string */
518
- if (w->time_limit_reached) {
519
- strcat(info->status, " (inaccurate - reached time_limit_secs)");
520
- } else if (info->iter >= w->stgs->max_iters) {
521
- strcat(info->status, " (inaccurate - reached max_iters)");
522
- } else {
523
- scs_printf("ERROR: should not be in this state (1).\n");
524
- }
525
- }
526
-
527
- /* sets solutions, re-scales by inner prods if infeasible or unbounded */
528
- static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
529
- scs_int iter) {
530
- scs_float nm_s, nm_y, sty;
531
- setx(w, sol);
532
- sety(w, sol);
533
- sets(w, sol);
534
- if (w->stgs->normalize) {
535
- SCS(un_normalize_sol)(w->scal, sol);
536
- }
537
- populate_residual_struct(w, iter);
538
-
539
- nm_s = SCS(norm_inf)(sol->s, w->d->m);
540
- nm_y = SCS(norm_inf)(sol->y, w->d->m);
541
- sty = SCS(dot)(sol->s, sol->y, w->d->m);
542
-
543
- info->setup_time = w->setup_time;
544
- info->iter = iter;
545
- info->res_infeas = w->r_orig->res_infeas;
546
- info->res_unbdd_a = w->r_orig->res_unbdd_a;
547
- info->res_unbdd_p = w->r_orig->res_unbdd_p;
548
- info->scale = w->stgs->scale;
549
- info->scale_updates = w->scale_updates;
550
- info->rejected_accel_steps = w->rejected_accel_steps;
551
- info->accepted_accel_steps = w->accepted_accel_steps;
552
- info->comp_slack = ABS(sty);
553
- #ifdef SPECTRAL_TIMING_FLAG
554
- info->ave_time_matrix_cone_proj = w->cone_work->tot_time_mat_cone_proj / iter;
555
- info->ave_time_vector_cone_proj = w->cone_work->tot_time_vec_cone_proj / iter;
556
- #endif
557
- if (info->comp_slack > 1e-5 * MAX(nm_s, nm_y)) {
558
- scs_printf("WARNING - large complementary slackness residual: %f\n",
559
- info->comp_slack);
560
- }
561
- switch (info->status_val) {
562
- case SCS_SOLVED:
563
- set_solved(w, sol, info);
564
- break;
565
- case SCS_INFEASIBLE:
566
- set_infeasible(w, sol, info);
567
- break;
568
- case SCS_UNBOUNDED:
569
- set_unbounded(w, sol, info);
570
- break;
571
- case SCS_UNFINISHED: /* When SCS reaches max_iters or time_limit_secs */
572
- set_unfinished(w, sol, info);
573
- break;
574
- default:
575
- scs_printf("ERROR: should not be in this state (2).\n");
576
- }
577
- }
578
-
579
- static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer) {
580
- ScsResiduals *r = w->r_orig;
581
- scs_printf("%*i|", (int)strlen(HEADER[0]), (int)i);
582
- scs_printf("%*.2e ", (int)HSPACE, r->res_pri);
583
- scs_printf("%*.2e ", (int)HSPACE, r->res_dual);
584
- scs_printf("%*.2e ", (int)HSPACE, r->gap);
585
- /* report mid point of primal and dual objective values */
586
- scs_printf("%*.2e ", (int)HSPACE, 0.5 * (r->pobj + r->dobj));
587
- scs_printf("%*.2e ", (int)HSPACE, w->stgs->scale);
588
- /* Report TOTAL time, including setup */
589
- scs_printf("%*.2e ", (int)HSPACE,
590
- (SCS(tocq)(solve_timer) + w->setup_time) / 1e3);
591
- scs_printf("\n");
592
-
593
- #if VERBOSITY > 0
594
- scs_printf("Norm u = %1.6e, ", SCS(norm_2)(w->u, w->d->n + w->d->m + 1));
595
- scs_printf("Norm u_t = %1.6e, ", SCS(norm_2)(w->u_t, w->d->n + w->d->m + 1));
596
- scs_printf("Norm v = %1.6e, ", SCS(norm_2)(w->v, w->d->n + w->d->m + 1));
597
- scs_printf("Norm rsk = %1.6e, ", SCS(norm_2)(w->rsk, w->d->n + w->d->m + 1));
598
- scs_printf("Norm x = %1.6e, ", SCS(norm_2)(w->xys_orig->x, w->d->n));
599
- scs_printf("Norm y = %1.6e, ", SCS(norm_2)(w->xys_orig->y, w->d->m));
600
- scs_printf("Norm s = %1.6e, ", SCS(norm_2)(w->xys_orig->s, w->d->m));
601
- scs_printf("Norm |Ax + s| = %1.6e, ", SCS(norm_2)(r->ax_s, w->d->m));
602
- scs_printf("tau = %1.6e, ", w->u[w->d->n + w->d->m]);
603
- scs_printf("kappa = %1.6e, ", w->rsk[w->d->n + w->d->m]);
604
- scs_printf("|u - u_t| = %1.6e, ",
605
- SCS(norm_diff)(w->u, w->u_t, w->d->n + w->d->m + 1));
606
- scs_printf("res_infeas = %1.6e, ", r->res_infeas);
607
- scs_printf("res_unbdd_a = %1.6e, ", r->res_unbdd_a);
608
- scs_printf("res_unbdd_p = %1.6e, ", r->res_unbdd_p);
609
- scs_printf("ctx_tau = %1.6e, ", r->ctx_tau);
610
- scs_printf("bty_tau = %1.2e\n", r->bty_tau);
611
- #endif
612
-
613
- #ifdef MATLAB_MEX_FILE
614
- mexEvalString("drawnow;");
615
- #endif
616
- }
617
-
618
- static void print_header(ScsWork *w, const ScsCone *k) {
619
- scs_int i;
620
- for (i = 0; i < LINE_LEN; ++i) {
621
- scs_printf("-");
622
- }
623
- scs_printf("\n");
624
- for (i = 0; i < HEADER_LEN - 1; ++i) {
625
- scs_printf("%s|", HEADER[i]);
626
- }
627
- scs_printf("%s\n", HEADER[HEADER_LEN - 1]);
628
- for (i = 0; i < LINE_LEN; ++i) {
629
- scs_printf("-");
630
- }
631
- scs_printf("\n");
632
- #ifdef MATLAB_MEX_FILE
633
- mexEvalString("drawnow;");
634
- #endif
635
- }
636
-
637
- static void print_footer(ScsInfo *info) {
638
- scs_int i;
639
-
640
- for (i = 0; i < LINE_LEN; ++i) {
641
- scs_printf("-");
642
- }
643
- scs_printf("\n");
644
- scs_printf("status: %s\n", info->status);
645
- scs_printf("timings: total: %1.2es = setup: %1.2es + solve: %1.2es\n",
646
- (info->setup_time + info->solve_time) / 1e3,
647
- info->setup_time / 1e3, info->solve_time / 1e3);
648
- scs_printf("\t lin-sys: %1.2es, cones: %1.2es, accel: %1.2es\n",
649
- info->lin_sys_time / 1e3, info->cone_time / 1e3,
650
- info->accel_time / 1e3);
651
-
652
- for (i = 0; i < LINE_LEN; ++i) {
653
- scs_printf("-");
654
- }
655
- scs_printf("\n");
656
- /* report mid point of primal and dual objective values */
657
- scs_printf("objective = %.6f", 0.5 * (info->pobj + info->dobj));
658
- switch (info->status_val) {
659
- case SCS_SOLVED_INACCURATE:
660
- case SCS_UNBOUNDED_INACCURATE:
661
- case SCS_INFEASIBLE_INACCURATE:
662
- scs_printf(" (inaccurate)");
663
- default:
664
- scs_printf("\n");
665
- }
666
- for (i = 0; i < LINE_LEN; ++i) {
667
- scs_printf("-");
668
- }
669
- scs_printf("\n");
670
- #ifdef MATLAB_MEX_FILE
671
- mexEvalString("drawnow;");
672
- #endif
673
- }
674
-
675
- static scs_int has_converged(ScsWork *w, scs_int iter) {
676
- scs_float abs_xt_p_x, abs_ctx, abs_bty;
677
- scs_float nm_s, nm_px, nm_aty, nm_ax;
678
- scs_float grl, prl, drl;
679
- scs_float eps_abs = w->stgs->eps_abs;
680
- scs_float eps_rel = w->stgs->eps_rel;
681
- scs_float eps_infeas = w->stgs->eps_infeas;
682
-
683
- ScsResiduals *r = w->r_orig;
684
-
685
- if (r->tau > 0.) {
686
- abs_xt_p_x = ABS(r->xt_p_x);
687
- abs_ctx = ABS(r->ctx);
688
- abs_bty = ABS(r->bty);
689
-
690
- nm_s = NORM(w->xys_orig->s, w->d->m);
691
- nm_px = NORM(r->px, w->d->n);
692
- nm_aty = NORM(r->aty, w->d->n);
693
- nm_ax = NORM(r->ax, w->d->m);
694
- /* xt_p_x, ctx, bty already have tau divided out */
695
- grl = MAX(MAX(abs_xt_p_x, abs_ctx), abs_bty);
696
- /* s, ax, px, aty do *not* have tau divided out, so need to divide */
697
- prl = MAX(MAX(w->nm_b_orig * r->tau, nm_s), nm_ax) / r->tau;
698
- drl = MAX(MAX(w->nm_c_orig * r->tau, nm_px), nm_aty) / r->tau;
699
- if (isless(r->res_pri, eps_abs + eps_rel * prl) &&
700
- isless(r->res_dual, eps_abs + eps_rel * drl) &&
701
- isless(r->gap, eps_abs + eps_rel * grl)) {
702
- return SCS_SOLVED;
703
- }
704
- }
705
- if (isless(r->res_unbdd_a, eps_infeas) &&
706
- isless(r->res_unbdd_p, eps_infeas)) {
707
- return SCS_UNBOUNDED;
708
- }
709
- if (isless(r->res_infeas, eps_infeas)) {
710
- return SCS_INFEASIBLE;
711
- }
712
- return 0;
713
- }
714
-
715
- #if NO_VALIDATE == 0
716
- static scs_int validate(const ScsData *d, const ScsCone *k,
717
- const ScsSettings *stgs) {
718
- if (d->m <= 0 || d->n <= 0) {
719
- scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
720
- (long)d->m, (long)d->n);
721
- return -1;
722
- }
723
- if (d->m < d->n) {
724
- /* scs_printf("WARN: m less than n, problem likely degenerate\n"); */
725
- /* return -1; */
726
- }
727
- if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
728
- scs_printf("invalid linear system input data\n");
729
- return -1;
730
- }
731
- if (SCS(validate_cones)(d, k) < 0) {
732
- scs_printf("cone validation error\n");
733
- return -1;
734
- }
735
- if (stgs->max_iters <= 0) {
736
- scs_printf("max_iters must be positive\n");
737
- return -1;
738
- }
739
- if (stgs->eps_abs < 0) {
740
- scs_printf("eps_abs tolerance must be positive\n");
741
- return -1;
742
- }
743
- if (stgs->eps_rel < 0) {
744
- scs_printf("eps_rel tolerance must be positive\n");
745
- return -1;
746
- }
747
- if (stgs->eps_infeas < 0) {
748
- scs_printf("eps_infeas tolerance must be positive\n");
749
- return -1;
750
- }
751
- if (stgs->alpha <= 0 || stgs->alpha >= 2) {
752
- scs_printf("alpha must be in (0,2)\n");
753
- return -1;
754
- }
755
- if (stgs->rho_x <= 0) {
756
- scs_printf("rho_x must be positive (1e-3 works well).\n");
757
- return -1;
758
- }
759
- if (stgs->scale <= 0) {
760
- scs_printf("scale must be positive (1 works well).\n");
761
- return -1;
762
- }
763
- if (stgs->acceleration_interval <= 0) {
764
- scs_printf("acceleration_interval must be positive (10 works well).\n");
765
- return -1;
766
- }
767
- return 0;
768
- }
769
- #endif
770
-
771
- static ScsResiduals *init_residuals(const ScsData *d) {
772
- ScsResiduals *r = (ScsResiduals *)scs_calloc(1, sizeof(ScsResiduals));
773
- r->ax = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
774
- r->ax_s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
775
- r->ax_s_btau = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
776
- r->px = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
777
- r->aty = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
778
- r->px_aty_ctau = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
779
- return r;
780
- }
781
-
782
- scs_int scs_update(ScsWork *w, scs_float *b, scs_float *c) {
783
- SCS(timer) update_timer;
784
- SCS(tic)(&update_timer);
785
-
786
- if (b) {
787
- memcpy(w->b_orig, b, w->d->m * sizeof(scs_float));
788
- memcpy(w->d->b, b, w->d->m * sizeof(scs_float));
948
+ /* not yet converged, take best guess */
949
+ static void set_unfinished(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
950
+ if (w->r_orig->kap > w->r_orig->tau &&
951
+ (w->r_orig->bty_tau < 0 || w->r_orig->ctx_tau < 0)) {
952
+ if (w->r_orig->bty_tau < 0 &&
953
+ w->r_orig->bty_tau < w->r_orig->ctx_tau) {
954
+ set_infeasible(w, sol, info);
955
+ info->status_val = SCS_INFEASIBLE_INACCURATE;
956
+ } else {
957
+ set_unbounded(w, sol, info);
958
+ info->status_val = SCS_UNBOUNDED_INACCURATE;
959
+ }
960
+ } else if (w->r_orig->tau > 0) {
961
+ set_solved(w, sol, info);
962
+ info->status_val = SCS_SOLVED_INACCURATE;
789
963
  } else {
790
- memcpy(w->d->b, w->b_orig, w->d->m * sizeof(scs_float));
964
+ scs_printf("ERROR: could not determine problem status.\n");
965
+ info->status_val = SCS_FAILED;
791
966
  }
792
- w->nm_b_orig = NORM(w->b_orig, w->d->m);
793
-
794
- if (c) {
795
- memcpy(w->c_orig, c, w->d->n * sizeof(scs_float));
796
- memcpy(w->d->c, c, w->d->n * sizeof(scs_float));
967
+ /* Append inaccurate to the status string */
968
+ if (w->time_limit_reached) {
969
+ strcat(info->status, " (inaccurate - reached time_limit_secs)");
970
+ } else if (info->iter >= w->stgs->max_iters) {
971
+ strcat(info->status, " (inaccurate - reached max_iters)");
797
972
  } else {
798
- memcpy(w->d->c, w->c_orig, w->d->n * sizeof(scs_float));
973
+ scs_printf("ERROR: should not be in this state (1).\n");
799
974
  }
800
- w->nm_c_orig = NORM(w->c_orig, w->d->n);
975
+ }
801
976
 
802
- /* normalize */
803
- if (w->scal) {
804
- SCS(normalize_b_c)(w->scal, w->d->b, w->d->c);
977
+ /* sets solutions, re-scales by inner prods if infeasible or unbounded */
978
+ static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
979
+ scs_int iter) {
980
+ scs_float nm_s, nm_y, sty;
981
+ setx(w, sol);
982
+ sety(w, sol);
983
+ sets(w, sol);
984
+ if (w->stgs->normalize) {
985
+ SCS(un_normalize_sol)(w->scal, sol);
805
986
  }
987
+ populate_residual_struct(w, iter);
806
988
 
807
- /* override setup time with update time, since the update is the 'setup' */
808
- w->setup_time = SCS(tocq)(&update_timer);
809
- return 0;
989
+ nm_s = SCS(norm_inf)(sol->s, w->d->m);
990
+ nm_y = SCS(norm_inf)(sol->y, w->d->m);
991
+ sty = SCS(dot)(sol->s, sol->y, w->d->m);
992
+
993
+ info->setup_time = w->setup_time;
994
+ info->iter = iter;
995
+ info->res_infeas = w->r_orig->res_infeas;
996
+ info->res_unbdd_a = w->r_orig->res_unbdd_a;
997
+ info->res_unbdd_p = w->r_orig->res_unbdd_p;
998
+ info->scale = w->stgs->scale;
999
+ info->scale_updates = w->scale_updates;
1000
+ info->rejected_accel_steps = w->rejected_accel_steps;
1001
+ info->accepted_accel_steps = w->accepted_accel_steps;
1002
+ set_info_aa_stats(info, w->accel);
1003
+ info->comp_slack = ABS(sty);
1004
+ #ifdef SPECTRAL_TIMING_FLAG
1005
+ info->ave_time_matrix_cone_proj = w->cone_work->tot_time_mat_cone_proj / iter;
1006
+ info->ave_time_vector_cone_proj = w->cone_work->tot_time_vec_cone_proj / iter;
1007
+ #endif
1008
+ if (info->comp_slack > 1e-5 * MAX(nm_s, nm_y)) {
1009
+ scs_printf("WARNING - large complementary slackness residual: %f\n",
1010
+ info->comp_slack);
1011
+ }
1012
+ switch (info->status_val) {
1013
+ case SCS_SOLVED:
1014
+ set_solved(w, sol, info);
1015
+ break;
1016
+ case SCS_INFEASIBLE:
1017
+ set_infeasible(w, sol, info);
1018
+ break;
1019
+ case SCS_UNBOUNDED:
1020
+ set_unbounded(w, sol, info);
1021
+ break;
1022
+ case SCS_UNFINISHED: /* When SCS reaches max_iters or time_limit_secs */
1023
+ set_unfinished(w, sol, info);
1024
+ break;
1025
+ default:
1026
+ scs_printf("ERROR: should not be in this state (2).\n");
1027
+ }
810
1028
  }
811
1029
 
1030
+ /* ================ Workspace Init / Scale Updating ================== */
1031
+
812
1032
  /* Sets the diag_r vector, given the scale parameters in work */
813
1033
  static void set_diag_r(ScsWork *w) {
814
1034
  scs_int i;
815
1035
  for (i = 0; i < w->d->n; ++i) {
816
1036
  w->diag_r[i] = w->stgs->rho_x;
817
1037
  }
1038
+
818
1039
  /* use cone information to set R_y */
819
1040
  SCS(set_r_y)(w->cone_work, w->stgs->scale, &(w->diag_r[w->d->n]));
820
- /* if modified need to SCS(enforce_cone_boundaries)(...) */
821
- w->diag_r[w->d->n + w->d->m] =
822
- TAU_FACTOR; /* TODO: is this the best choice? */
1041
+ if (w->scale_mults) {
1042
+ /* per-row multipliers act like a per-row scale: R_y_i = base_i / f_i.
1043
+ * Multipliers are kept uniform within cone blocks (enforced at update
1044
+ * time) so no SCS(enforce_cone_boundaries) needed here. */
1045
+ for (i = 0; i < w->d->m; ++i) {
1046
+ w->diag_r[w->d->n + i] /= w->scale_mults[i];
1047
+ }
1048
+ }
1049
+ w->diag_r[w->d->n + w->d->m] = TAU_FACTOR;
823
1050
  }
824
1051
 
825
1052
  static ScsWork *init_work(const ScsData *d, const ScsCone *k,
@@ -835,19 +1062,35 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
835
1062
  }
836
1063
  /* deep copy data */
837
1064
  w->d = (ScsData *)scs_calloc(1, sizeof(ScsData));
838
- SCS(deep_copy_data)(w->d, d);
1065
+ if (!w->d || !SCS(deep_copy_data)(w->d, d)) {
1066
+ scs_printf("ERROR: data copy failure\n");
1067
+ scs_finish(w);
1068
+ return SCS_NULL;
1069
+ }
839
1070
  d = SCS_NULL; /* for safety */
840
1071
 
841
1072
  /* deep copy cone */
842
1073
  w->k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
843
- SCS(deep_copy_cone)(w->k, k);
1074
+ if (!w->k || !SCS(deep_copy_cone)(w->k, k)) {
1075
+ scs_printf("ERROR: cone copy failure\n");
1076
+ scs_finish(w);
1077
+ return SCS_NULL;
1078
+ }
844
1079
  k = SCS_NULL; /* for safety */
845
1080
 
846
1081
  /* deep copy settings */
847
1082
  w->stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
848
- SCS(deep_copy_stgs)(w->stgs, stgs);
1083
+ if (!w->stgs || !SCS(deep_copy_stgs)(w->stgs, stgs)) {
1084
+ scs_printf("ERROR: settings copy failure\n");
1085
+ scs_finish(w);
1086
+ return SCS_NULL;
1087
+ }
849
1088
  stgs = SCS_NULL; /* for safety */
850
1089
 
1090
+ /* record the original (pre-normalization) magnitude of A for the
1091
+ * data-scale-invariant certificate checks */
1092
+ w->nm_a_orig = SCS(norm_inf)(w->d->A->x, w->d->A->p[w->d->A->n]);
1093
+
851
1094
  /* allocate workspace: */
852
1095
  w->u = (scs_float *)scs_calloc(l, sizeof(scs_float));
853
1096
  w->u_t = (scs_float *)scs_calloc(l, sizeof(scs_float));
@@ -860,32 +1103,89 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
860
1103
  w->diag_r = (scs_float *)scs_calloc(l, sizeof(scs_float));
861
1104
  /* x,y,s struct */
862
1105
  w->xys_orig = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
1106
+ if (!w->xys_orig) {
1107
+ scs_printf("ERROR: work memory allocation failure\n");
1108
+ scs_finish(w);
1109
+ return SCS_NULL;
1110
+ }
863
1111
  w->xys_orig->x = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
864
1112
  w->xys_orig->s = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
865
1113
  w->xys_orig->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
866
1114
  w->r_orig = init_residuals(w->d);
867
1115
  w->b_orig = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
868
1116
  w->c_orig = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
1117
+ #ifdef USE_SPECTRAL_CONES
1118
+ if (w->stgs->adaptive_diag_scale &&
1119
+ (w->k->dsize || w->k->nucsize || w->k->ell1_size || w->k->sl_size)) {
1120
+ /* The spectral-cone projections are iterative inner solvers with
1121
+ * warm-start state that does not currently tolerate mid-solve metric
1122
+ * changes (observed as dual iterates leaving the cone). Disable
1123
+ * dynamic diagonal rescaling on such problems until the inner
1124
+ * solvers are made metric-change aware. */
1125
+ w->stgs->adaptive_diag_scale = 0;
1126
+ }
1127
+ #endif
1128
+ if (w->stgs->adaptive_diag_scale) {
1129
+ if (!w->stgs->adaptive_scale) {
1130
+ /* silently disable: diag scaling rides the adaptive-scale update
1131
+ * machinery, and since it defaults on, users who only turn off
1132
+ * adaptive_scale should not see a warning */
1133
+ w->stgs->adaptive_diag_scale = 0;
1134
+ } else {
1135
+ scs_int j;
1136
+ w->scale_mults = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
1137
+ if (!w->scale_mults) {
1138
+ scs_printf("ERROR: work memory allocation failure\n");
1139
+ scs_finish(w);
1140
+ return SCS_NULL;
1141
+ }
1142
+ for (j = 0; j < w->d->m; ++j) {
1143
+ w->scale_mults[j] = 1.0;
1144
+ }
1145
+ }
1146
+ }
869
1147
 
870
- if (!w->c_orig) {
1148
+ if (!w->u || !w->u_t || !w->v || !w->v_prev || !w->rsk || !w->h || !w->g ||
1149
+ !w->lin_sys_warm_start || !w->diag_r || !w->xys_orig ||
1150
+ !w->xys_orig->x || !w->xys_orig->s || !w->xys_orig->y || !w->r_orig ||
1151
+ !w->b_orig || !w->c_orig) {
871
1152
  scs_printf("ERROR: work memory allocation failure\n");
1153
+ scs_finish(w);
872
1154
  return SCS_NULL;
873
1155
  }
874
1156
 
875
1157
  if (!(w->cone_work = SCS(init_cone)(w->k, w->d->m))) {
876
1158
  scs_printf("ERROR: init_cone failure\n");
1159
+ scs_finish(w);
877
1160
  return SCS_NULL;
878
1161
  }
879
1162
  set_diag_r(w);
880
1163
 
881
1164
  if (w->stgs->normalize) {
882
1165
  w->xys_normalized = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
1166
+ if (!w->xys_normalized) {
1167
+ scs_printf("ERROR: normalized work memory allocation failure\n");
1168
+ scs_finish(w);
1169
+ return SCS_NULL;
1170
+ }
883
1171
  w->xys_normalized->x = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
884
1172
  w->xys_normalized->s = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
885
1173
  w->xys_normalized->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
886
1174
  w->r_normalized = init_residuals(w->d);
1175
+ if (!w->xys_normalized || !w->xys_normalized->x || !w->xys_normalized->s ||
1176
+ !w->xys_normalized->y || !w->r_normalized) {
1177
+ scs_printf("ERROR: normalized work memory allocation failure\n");
1178
+ scs_finish(w);
1179
+ return SCS_NULL;
1180
+ }
887
1181
  /* this allocates memory that must be freed */
888
- w->scal = SCS(normalize_a_p)(w->d->P, w->d->A, w->cone_work);
1182
+ w->scal = SCS(normalize_a_p)(w->d->P, w->d->A, w->d->b, w->d->c,
1183
+ w->cone_work);
1184
+ if (!w->scal) {
1185
+ scs_printf("ERROR: normalize_a_p failure\n");
1186
+ scs_finish(w);
1187
+ return SCS_NULL;
1188
+ }
889
1189
  } else {
890
1190
  w->xys_normalized = w->xys_orig;
891
1191
  w->r_normalized = w->r_orig;
@@ -896,17 +1196,20 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
896
1196
 
897
1197
  if (!(w->p = scs_init_lin_sys_work(w->d->A, w->d->P, w->diag_r))) {
898
1198
  scs_printf("ERROR: init_lin_sys_work failure\n");
1199
+ scs_finish(w);
899
1200
  return SCS_NULL;
900
1201
  }
901
1202
  if (w->stgs->acceleration_lookback) {
902
- /* TODO(HACK!) negative acceleration_lookback interpreted as type-II */
903
- if (!(w->accel = aa_init(l, IABS(w->stgs->acceleration_lookback),
904
- w->stgs->acceleration_lookback > 0,
905
- w->stgs->acceleration_lookback > 0
906
- ? AA_REGULARIZATION_TYPE_1
907
- : AA_REGULARIZATION_TYPE_2,
908
- AA_RELAXATION, AA_SAFEGUARD_FACTOR,
909
- AA_MAX_WEIGHT_NORM, VERBOSITY))) {
1203
+ /* min_len = mem preserves the previous FILL_MEMORY_BEFORE_SOLVE
1204
+ gate — AA holds off until the sliding window is full. */
1205
+ if (!(w->accel = aa_init(l, w->stgs->acceleration_lookback,
1206
+ w->stgs->acceleration_lookback,
1207
+ w->stgs->acceleration_type_1,
1208
+ w->stgs->acceleration_regularization,
1209
+ w->stgs->acceleration_relaxation,
1210
+ AA_SAFEGUARD_FACTOR,
1211
+ AA_MAX_WEIGHT_NORM, AA_IR_MAX_STEPS,
1212
+ VERBOSITY))) {
910
1213
  if (w->stgs->verbose) {
911
1214
  scs_printf("WARN: aa_init returned NULL, no acceleration applied.\n");
912
1215
  }
@@ -918,11 +1221,15 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
918
1221
  }
919
1222
 
920
1223
  static void update_work_cache(ScsWork *w) {
921
- /* g = (I + M)^{-1} h */
922
- memcpy(w->g, w->h, (w->d->n + w->d->m) * sizeof(scs_float));
923
- SCS(scale_array)(&(w->g[w->d->n]), -1., w->d->m);
1224
+ /* g = (I + M)^{-1} [c; -b]
1225
+ * Build g = [c; -b] directly in one pass, avoiding a separate memcpy of h
1226
+ * followed by a negate pass over g[n:]. */
1227
+ scs_int i, n = w->d->n, m = w->d->m;
1228
+ memcpy(w->g, w->d->c, n * sizeof(scs_float));
1229
+ for (i = 0; i < m; ++i) {
1230
+ w->g[n + i] = -w->d->b[i];
1231
+ }
924
1232
  scs_solve_lin_sys(w->p, w->g, SCS_NULL, CG_BEST_TOL);
925
- return;
926
1233
  }
927
1234
 
928
1235
  /* Reset quantities specific to current solve */
@@ -932,6 +1239,9 @@ static void reset_tracking(ScsWork *w) {
932
1239
  w->n_log_scale_factor = 0;
933
1240
  w->scale_updates = 0;
934
1241
  w->time_limit_reached = 0;
1242
+ /* certificate persistence */
1243
+ w->infeas_cert_streak = 0;
1244
+ w->unbdd_cert_streak = 0;
935
1245
  /* Acceleration */
936
1246
  w->rejected_accel_steps = 0;
937
1247
  w->accepted_accel_steps = 0;
@@ -950,19 +1260,49 @@ static scs_int update_work(ScsWork *w, ScsSolution *sol) {
950
1260
  cold_start_vars(w);
951
1261
  }
952
1262
 
953
- /* h = [c;b] */
954
- memcpy(w->h, w->d->c, w->d->n * sizeof(scs_float));
955
- memcpy(&(w->h[w->d->n]), w->d->b, w->d->m * sizeof(scs_float));
956
1263
  update_work_cache(w);
957
1264
  return 0;
958
1265
  }
959
1266
 
960
1267
  /* will update if the factor is outside of range */
961
- scs_int should_update_r(scs_float factor, scs_int iter) {
1268
+ static scs_int should_update_r(scs_float factor) {
962
1269
  return (factor > SQRTF(10.) || factor < 1. / SQRTF(10.));
963
1270
  }
964
1271
 
965
- static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
1272
+ /* Relative primal residual of constraint row i in the normalized space
1273
+ * (where diag_r acts). All quantities carry tau consistently. */
1274
+ /* Relative primal residual of row i, used as the per-row profile.
1275
+ *
1276
+ * The denominator carries a floor at DEN_FLOOR_FRAC of the block's rms
1277
+ * denominator. Without it, a row whose b_i is zero divides only by
1278
+ * max(|(Ax)_i|, |s_i|) -- both of which shrink as the iterates converge
1279
+ * -- so its ratio inflates even when the row is converging perfectly
1280
+ * well, and the profile ends up reporting which terms a row happens to
1281
+ * contain rather than how it is converging. Rows with denominators of
1282
+ * ordinary size are unaffected; only the collapsing ones are floored. */
1283
+ static scs_float row_rel_res(const ScsWork *w, scs_int i) {
1284
+ const ScsResiduals *r = w->r_normalized;
1285
+ scs_float den = MAX(ABS(r->ax[i]), ABS(w->xys_normalized->s[i]));
1286
+ den = MAX(den, ABS(w->d->b[i]) * r->tau);
1287
+ den = MAX(den, w->den_floor);
1288
+ den = MAX(den, _DIV_EPS_TOL);
1289
+ return MAX(ABS(r->ax_s_btau[i]), _DIV_EPS_TOL) / den;
1290
+ }
1291
+
1292
+ /* Recompute the row-profile denominator floor from the current iterate. */
1293
+ static void set_den_floor(ScsWork *w) {
1294
+ const ScsResiduals *r = w->r_normalized;
1295
+ scs_int i, m = w->d->m;
1296
+ scs_float s2 = 0.;
1297
+ for (i = 0; i < m; ++i) {
1298
+ scs_float d = MAX(ABS(r->ax[i]), ABS(w->xys_normalized->s[i]));
1299
+ d = MAX(d, ABS(w->d->b[i]) * r->tau);
1300
+ s2 += d * d;
1301
+ }
1302
+ w->den_floor = DEN_FLOOR_FRAC * SQRTF(s2 / (scs_float)MAX(m, 1));
1303
+ }
1304
+
1305
+ static scs_int update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
966
1306
  scs_int i;
967
1307
  scs_float factor, new_scale, relative_res_pri, relative_res_dual;
968
1308
  scs_float denom_pri, denom_dual;
@@ -977,6 +1317,9 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
977
1317
  scs_float nm_ax_s_btau = SCALE_NORM(r->ax_s_btau, w->d->m);
978
1318
 
979
1319
  scs_int iters_since_last_update = iter - w->last_scale_update_iter;
1320
+ if (w->scale_mults) {
1321
+ set_den_floor(w);
1322
+ }
980
1323
  /* ||Ax + s - b * tau|| */
981
1324
  denom_pri = MAX(nm_ax, nm_s);
982
1325
  denom_pri = MAX(denom_pri, w->nm_b_orig * r->tau);
@@ -987,6 +1330,9 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
987
1330
  relative_res_dual = SAFEDIV_POS(nm_px_aty_ctau, denom_dual);
988
1331
 
989
1332
  /* higher scale makes res_pri go down faster, so increase if res_pri larger */
1333
+ /* clamp to avoid log(0) which would NaN-poison sum_log_scale_factor */
1334
+ relative_res_pri = MAX(relative_res_pri, _DIV_EPS_TOL);
1335
+ relative_res_dual = MAX(relative_res_dual, _DIV_EPS_TOL);
990
1336
  w->sum_log_scale_factor += log(relative_res_pri) - log(relative_res_dual);
991
1337
  w->n_log_scale_factor++;
992
1338
 
@@ -996,25 +1342,66 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
996
1342
 
997
1343
  /* need at least RESCALING_MIN_ITERS since last update */
998
1344
  if (iters_since_last_update < RESCALING_MIN_ITERS) {
999
- return;
1345
+ return 0;
1000
1346
  }
1001
1347
  new_scale =
1002
1348
  MIN(MAX(w->stgs->scale * factor, MIN_SCALE_VALUE), MAX_SCALE_VALUE);
1003
- if (new_scale == w->stgs->scale) {
1004
- return;
1349
+ scs_int apply_scalar =
1350
+ (new_scale != w->stgs->scale) && should_update_r(factor);
1351
+ scs_int apply_diag = 0;
1352
+ scs_float log_g = 0., g, step, newf, change;
1353
+ if (w->stgs->adaptive_diag_scale) {
1354
+ /* residual profiles: adapt when the scalar updates, or when some
1355
+ * row's/column's damped *clamped* step exceeds the update threshold
1356
+ * (a railed scalar must not freeze the diagonal; a railed multiplier
1357
+ * must not keep triggering updates it cannot take). */
1358
+ scs_float drift = 1.0;
1359
+ for (i = 0; i < w->d->m; ++i) {
1360
+ log_g += log(row_rel_res(w, i));
1361
+ }
1362
+ log_g /= (scs_float)w->d->m;
1363
+ g = exp(log_g);
1364
+ for (i = 0; i < w->d->m; ++i) {
1365
+ step = POWF(row_rel_res(w, i) / g, DIAG_SCALE_DAMP);
1366
+ newf = MIN(MAX(w->scale_mults[i] * step, DIAG_SCALE_MULT_MIN),
1367
+ DIAG_SCALE_MULT_MAX);
1368
+ change = newf / w->scale_mults[i];
1369
+ drift = MAX(drift, MAX(change, 1. / change));
1370
+ }
1371
+ apply_diag = apply_scalar || should_update_r(drift);
1005
1372
  }
1006
- if (should_update_r(factor, iters_since_last_update)) {
1373
+ if (!apply_scalar && !apply_diag) {
1374
+ return 0;
1375
+ }
1376
+ {
1377
+ scs_int linsys_status;
1007
1378
  w->scale_updates++;
1008
1379
  w->sum_log_scale_factor = 0;
1009
1380
  w->n_log_scale_factor = 0;
1010
1381
  w->last_scale_update_iter = iter;
1011
- w->stgs->scale = new_scale;
1382
+ if (apply_scalar) {
1383
+ w->stgs->scale = new_scale;
1384
+ }
1385
+ if (apply_diag) {
1386
+ g = exp(log_g);
1387
+ for (i = 0; i < w->d->m; ++i) {
1388
+ step = POWF(row_rel_res(w, i) / g, DIAG_SCALE_DAMP);
1389
+ w->scale_mults[i] = MIN(
1390
+ MAX(w->scale_mults[i] * step, DIAG_SCALE_MULT_MIN),
1391
+ DIAG_SCALE_MULT_MAX);
1392
+ }
1393
+ /* non-polyhedral cone blocks must share a single metric entry */
1394
+ SCS(enforce_cone_boundaries)(w->cone_work, w->scale_mults, &SCS(mean), 1);
1395
+ }
1012
1396
 
1013
1397
  /* update diag r vector */
1014
1398
  set_diag_r(w);
1015
1399
 
1016
1400
  /* update linear systems */
1017
- scs_update_lin_sys_diag_r(w->p, w->diag_r);
1401
+ linsys_status = scs_update_lin_sys_diag_r(w->p, w->diag_r);
1402
+ if (linsys_status < 0) {
1403
+ return linsys_status;
1404
+ }
1018
1405
 
1019
1406
  /* update pre-solved quantities */
1020
1407
  update_work_cache(w);
@@ -1031,12 +1418,91 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
1031
1418
  w->v[i] = w->rsk[i] / w->diag_r[i] + 2 * w->u_t[i] - w->u[i];
1032
1419
  }
1033
1420
  }
1421
+ return 0;
1034
1422
  }
1035
1423
 
1036
- /* scs is homogeneous so scale the iterate to keep norm reasonable */
1037
- static inline void normalize_v(scs_float *v, scs_int len) {
1038
- scs_float v_norm = SCS(norm_2)(v, len); /* always l2 norm */
1039
- SCS(scale_array)(v, SQRTF((scs_float)len) * ITERATE_NORM / v_norm, len);
1424
+ /* ========================== Public API ============================= */
1425
+
1426
+ ScsWork *scs_init(const ScsData *d, const ScsCone *k, const ScsSettings *stgs) {
1427
+ ScsWork *w;
1428
+ SCS(timer) init_timer;
1429
+ if (!d || !k || !stgs) {
1430
+ scs_printf("ERROR: Missing ScsData, ScsCone, or ScsSettings input\n");
1431
+ return SCS_NULL;
1432
+ }
1433
+ #ifdef SCS_MKL
1434
+ if (scs_init_mkl_runtime() != 0) {
1435
+ scs_printf("ERROR: MKL runtime initialization failed before solver setup. "
1436
+ "See the MKL message above for the specific cause.\n");
1437
+ return SCS_NULL;
1438
+ }
1439
+ #endif
1440
+ #if NO_VALIDATE == 0
1441
+ if (validate(d, k, stgs) < 0) {
1442
+ scs_printf("ERROR: Validation returned failure\n");
1443
+ return SCS_NULL;
1444
+ }
1445
+ #endif
1446
+ scs_start_interrupt_listener();
1447
+ #if VERBOSITY > 0
1448
+ scs_printf("size of scs_int = %lu, size of scs_float = %lu\n",
1449
+ (unsigned long)sizeof(scs_int), (unsigned long)sizeof(scs_float));
1450
+ #endif
1451
+ SCS(tic)(&init_timer);
1452
+ if (stgs->write_data_filename) {
1453
+ scs_printf("Writing raw problem data to %s\n", stgs->write_data_filename);
1454
+ SCS(write_data)(d, k, stgs);
1455
+ }
1456
+ if (stgs->log_csv_filename) {
1457
+ scs_printf("Logging run data to %s\n", stgs->log_csv_filename);
1458
+ /* logging done every iteration */
1459
+ }
1460
+ w = init_work(d, k, stgs);
1461
+ if (w) {
1462
+ w->setup_time = SCS(tocq)(&init_timer);
1463
+ }
1464
+ scs_end_interrupt_listener();
1465
+ return w;
1466
+ }
1467
+
1468
+ scs_int scs_update(ScsWork *w, scs_float *b, scs_float *c) {
1469
+ SCS(timer) update_timer;
1470
+ SCS(tic)(&update_timer);
1471
+
1472
+ if (b) {
1473
+ if (w->b_orig != b) {
1474
+ memcpy(w->b_orig, b, w->d->m * sizeof(scs_float));
1475
+ }
1476
+ if (w->d->b != b) {
1477
+ memcpy(w->d->b, b, w->d->m * sizeof(scs_float));
1478
+ }
1479
+ w->nm_b_orig = NORM(w->b_orig, w->d->m);
1480
+ } else {
1481
+ /* b_orig unchanged so no need to recompute the norm */
1482
+ memcpy(w->d->b, w->b_orig, w->d->m * sizeof(scs_float));
1483
+ }
1484
+
1485
+ if (c) {
1486
+ if (w->c_orig != c) {
1487
+ memcpy(w->c_orig, c, w->d->n * sizeof(scs_float));
1488
+ }
1489
+ if (w->d->c != c) {
1490
+ memcpy(w->d->c, c, w->d->n * sizeof(scs_float));
1491
+ }
1492
+ w->nm_c_orig = NORM(w->c_orig, w->d->n);
1493
+ } else {
1494
+ /* c_orig unchanged so no need to recompute the norm */
1495
+ memcpy(w->d->c, w->c_orig, w->d->n * sizeof(scs_float));
1496
+ }
1497
+
1498
+ /* normalize */
1499
+ if (w->scal) {
1500
+ SCS(normalize_b_c)(w->scal, w->d->b, w->d->c);
1501
+ }
1502
+
1503
+ /* override setup time with update time, since the update is the 'setup' */
1504
+ w->setup_time = SCS(tocq)(&update_timer);
1505
+ return 0;
1040
1506
  }
1041
1507
 
1042
1508
  scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
@@ -1061,6 +1527,7 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
1061
1527
  strcpy(info->lin_sys_solver, scs_get_lin_sys_method());
1062
1528
  info->status_val = SCS_UNFINISHED; /* not yet converged */
1063
1529
  update_work(w, sol);
1530
+ SCS(open_csv_log_file)(w);
1064
1531
 
1065
1532
  if (w->stgs->verbose) {
1066
1533
  print_header(w, k);
@@ -1085,8 +1552,10 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
1085
1552
  normalize_v(w->v, l);
1086
1553
  }
1087
1554
 
1088
- /* store v_prev = v, *after* normalizing */
1089
- memcpy(w->v_prev, w->v, l * sizeof(scs_float));
1555
+ /* store v_prev = v for AA safeguard; skip when acceleration is off */
1556
+ if (w->accel) {
1557
+ memcpy(w->v_prev, w->v, l * sizeof(scs_float));
1558
+ }
1090
1559
 
1091
1560
  /******************* linear system solve ********************/
1092
1561
  SCS(tic)(&lin_sys_timer);
@@ -1133,7 +1602,10 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
1133
1602
 
1134
1603
  /* If residuals are fresh then maybe compute new scale. */
1135
1604
  if (w->stgs->adaptive_scale && i == w->r_orig->last_iter) {
1136
- update_scale(w, k, i);
1605
+ if (update_scale(w, k, i) < 0) {
1606
+ return failure(w, w->d->m, w->d->n, sol, info, SCS_FAILED,
1607
+ "error in update_scale", "failure");
1608
+ }
1137
1609
  }
1138
1610
 
1139
1611
  /****************** dual variable step **********************/
@@ -1156,17 +1628,17 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
1156
1628
  }
1157
1629
 
1158
1630
  /* Log *after* updating scale so residual recalc does not affect alg */
1159
- if (w->stgs->log_csv_filename) {
1631
+ if (w->log_csv_fout) {
1160
1632
  /* calc residuals every iter if logging to csv */
1161
1633
  populate_residual_struct(w, i);
1162
- SCS(log_data_to_csv)(k, stgs, w, i, &solve_timer);
1634
+ SCS(log_data_to_csv)(k, w, i, &solve_timer);
1163
1635
  }
1164
1636
  }
1165
1637
 
1166
1638
  /* Final logging after full run */
1167
- if (w->stgs->log_csv_filename) {
1639
+ if (w->log_csv_fout) {
1168
1640
  populate_residual_struct(w, i);
1169
- SCS(log_data_to_csv)(k, stgs, w, i, &solve_timer);
1641
+ SCS(log_data_to_csv)(k, w, i, &solve_timer);
1170
1642
  }
1171
1643
 
1172
1644
  if (w->stgs->verbose) {
@@ -1187,56 +1659,61 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
1187
1659
  print_footer(info);
1188
1660
  }
1189
1661
 
1662
+ SCS(close_csv_log_file)(w);
1190
1663
  scs_end_interrupt_listener();
1191
1664
  return info->status_val;
1192
1665
  }
1193
1666
 
1194
1667
  void scs_finish(ScsWork *w) {
1195
1668
  if (w) {
1196
- SCS(finish_cone)(w->cone_work);
1669
+ SCS(close_csv_log_file)(w);
1670
+ if (w->cone_work) {
1671
+ SCS(finish_cone)(w->cone_work);
1672
+ }
1197
1673
  if (w->p) {
1198
1674
  scs_free_lin_sys_work(w->p);
1199
1675
  }
1200
1676
  if (w->accel) {
1201
1677
  aa_finish(w->accel);
1202
1678
  }
1203
- free_work(w);
1204
- }
1205
- }
1206
-
1207
- ScsWork *scs_init(const ScsData *d, const ScsCone *k, const ScsSettings *stgs) {
1208
- ScsWork *w;
1209
- SCS(timer) init_timer;
1210
- scs_start_interrupt_listener();
1211
- if (!d || !k) {
1212
- scs_printf("ERROR: Missing ScsData or ScsCone input\n");
1213
- return SCS_NULL;
1214
- }
1215
- #if NO_VALIDATE == 0
1216
- if (validate(d, k, stgs) < 0) {
1217
- scs_printf("ERROR: Validation returned failure\n");
1218
- return SCS_NULL;
1219
- }
1220
- #endif
1221
- #if VERBOSITY > 0
1222
- scs_printf("size of scs_int = %lu, size of scs_float = %lu\n",
1223
- sizeof(scs_int), sizeof(scs_float));
1224
- #endif
1225
- SCS(tic)(&init_timer);
1226
- if (stgs->write_data_filename) {
1227
- scs_printf("Writing raw problem data to %s\n", stgs->write_data_filename);
1228
- SCS(write_data)(d, k, stgs);
1229
- }
1230
- if (stgs->log_csv_filename) {
1231
- scs_printf("Logging run data to %s\n", stgs->log_csv_filename);
1232
- /* logging done every iteration */
1233
- }
1234
- w = init_work(d, k, stgs);
1235
- if (w) {
1236
- w->setup_time = SCS(tocq)(&init_timer);
1679
+ scs_free(w->u);
1680
+ scs_free(w->u_t);
1681
+ scs_free(w->v);
1682
+ scs_free(w->v_prev);
1683
+ scs_free(w->rsk);
1684
+ scs_free(w->h);
1685
+ scs_free(w->g);
1686
+ scs_free(w->b_orig);
1687
+ scs_free(w->c_orig);
1688
+ scs_free(w->lin_sys_warm_start);
1689
+ scs_free(w->diag_r);
1690
+ scs_free(w->scale_mults);
1691
+ SCS(free_sol)(w->xys_orig);
1692
+ if (w->scal) {
1693
+ scs_free(w->scal->D);
1694
+ scs_free(w->scal->E);
1695
+ scs_free(w->scal);
1696
+ }
1697
+ free_residuals(w->r_orig);
1698
+ if (w->stgs && w->stgs->normalize) {
1699
+ SCS(free_sol)(w->xys_normalized);
1700
+ free_residuals(w->r_normalized);
1701
+ }
1702
+ if (w->stgs) {
1703
+ if (w->stgs->log_csv_filename)
1704
+ scs_free((char *)w->stgs->log_csv_filename);
1705
+ if (w->stgs->write_data_filename)
1706
+ scs_free((char *)w->stgs->write_data_filename);
1707
+ scs_free(w->stgs);
1708
+ }
1709
+ if (w->k) { /* deep copy */
1710
+ SCS(free_cone)(w->k);
1711
+ }
1712
+ if (w->d) { /* deep copy */
1713
+ SCS(free_data)(w->d);
1714
+ }
1715
+ scs_free(w);
1237
1716
  }
1238
- scs_end_interrupt_listener();
1239
- return w;
1240
1717
  }
1241
1718
 
1242
1719
  /* this just calls scs_init, scs_solve, and scs_finish */