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
@@ -0,0 +1,558 @@
1
+ #include <string.h>
2
+
3
+ #include "glbopts.h"
4
+ #include "minunit.h"
5
+ #include "problem_utils.h"
6
+ #include "scs.h"
7
+ #include "util.h"
8
+
9
+ /*
10
+ * Shared setup for solver-options tests.
11
+ * Problem: min x s.t. x >= 2 (n=1, m=1, l=1)
12
+ * Optimal obj = 2.
13
+ */
14
+ #define _OPTS_SETUP(bval, cval) \
15
+ do { \
16
+ scs_float _Ax[] = {-1.0}; \
17
+ scs_int _Ai[] = {0}; \
18
+ scs_int _Ap[] = {0, 1}; \
19
+ k = (ScsCone *)scs_calloc(1, sizeof(ScsCone)); \
20
+ d = (ScsData *)scs_calloc(1, sizeof(ScsData)); \
21
+ stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings)); \
22
+ sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution)); \
23
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix)); \
24
+ d->b = (scs_float *)scs_calloc(1, sizeof(scs_float)); \
25
+ d->c = (scs_float *)scs_calloc(1, sizeof(scs_float)); \
26
+ d->m = 1; d->n = 1; \
27
+ d->b[0] = (bval); d->c[0] = (cval); \
28
+ d->A->m = 1; d->A->n = 1; \
29
+ d->A->x = (scs_float *)scs_calloc(1, sizeof(scs_float)); \
30
+ d->A->i = (scs_int *)scs_calloc(1, sizeof(scs_int)); \
31
+ d->A->p = (scs_int *)scs_calloc(2, sizeof(scs_int)); \
32
+ d->A->x[0] = _Ax[0]; d->A->i[0] = _Ai[0]; \
33
+ d->A->p[0] = _Ap[0]; d->A->p[1] = _Ap[1]; \
34
+ k->l = 1; \
35
+ scs_set_default_settings(stgs); \
36
+ stgs->eps_abs = 1e-6; \
37
+ stgs->eps_rel = 1e-6; \
38
+ stgs->verbose = 1; \
39
+ } while (0)
40
+
41
+ #define _OPTS_CLEANUP() \
42
+ do { \
43
+ SCS(free_sol)(sol); \
44
+ scs_free(d->A->x); \
45
+ scs_free(d->A->i); \
46
+ scs_free(d->A->p); \
47
+ scs_free(d->A); \
48
+ scs_free(d->b); \
49
+ scs_free(d->c); \
50
+ scs_free(d); \
51
+ scs_free(k); \
52
+ scs_free(stgs); \
53
+ } while (0)
54
+
55
+ /*
56
+ * Test adaptive_scale=1: verify the solver converges to the correct answer.
57
+ * Default settings already have adaptive_scale=1, but most problem tests
58
+ * override it to 0 to get reproducible iteration counts. This test uses a
59
+ * random LP large enough (m=60, n=20) for the scale update heuristic to fire
60
+ * at least once during the solve.
61
+ */
62
+ static const char *test_adaptive_scale(void) {
63
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
64
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
65
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
66
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
67
+ ScsSolution *opt_sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
68
+ ScsInfo info = {0};
69
+ scs_int exitflag;
70
+ scs_float perr;
71
+ scs_int n = 20, m = 60;
72
+ scs_int col_nnz = 4, nnz = n * col_nnz;
73
+ const char *fail;
74
+
75
+ k->z = 6;
76
+ k->l = m - k->z;
77
+ d->m = m;
78
+ d->n = n;
79
+ gen_random_prob_data(nnz, col_nnz, d, k, opt_sol, 4321);
80
+
81
+ scs_set_default_settings(stgs);
82
+ stgs->eps_abs = 1e-5;
83
+ stgs->eps_rel = 1e-5;
84
+ stgs->adaptive_scale = 1;
85
+ /* deliberately bad initial scale so the update heuristic must fire,
86
+ * independent of how well the equilibration balances the problem */
87
+ stgs->scale = 100.;
88
+ stgs->verbose = 0;
89
+
90
+ exitflag = scs(d, k, stgs, sol, &info);
91
+
92
+ perr = SCS(dot)(d->c, sol->x, d->n) - SCS(dot)(d->c, opt_sol->x, d->n);
93
+
94
+ mu_assert("test_adaptive_scale: expected SCS_SOLVED", exitflag == SCS_SOLVED);
95
+ mu_assert("test_adaptive_scale: primal obj error too large", ABS(perr) < 1e-3);
96
+ mu_assert("test_adaptive_scale: expected scale updates > 0",
97
+ info.scale_updates > 0);
98
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
99
+
100
+ SCS(free_data)(d);
101
+ SCS(free_cone)(k);
102
+ SCS(free_sol)(sol);
103
+ SCS(free_sol)(opt_sol);
104
+ scs_free(stgs);
105
+ return fail;
106
+ }
107
+
108
+ /*
109
+ * Test acceleration_lookback=0: disables Anderson acceleration (w->accel=NULL
110
+ * in init_work). Verify the solver still converges to the correct answer.
111
+ */
112
+ static const char *test_no_acceleration(void) {
113
+ ScsCone *k;
114
+ ScsData *d;
115
+ ScsSettings *stgs;
116
+ ScsSolution *sol;
117
+ ScsInfo info = {0};
118
+ scs_int exitflag;
119
+ const char *fail;
120
+
121
+ _OPTS_SETUP(-2.0, 1.0);
122
+ stgs->acceleration_lookback = 0;
123
+
124
+ exitflag = scs(d, k, stgs, sol, &info);
125
+
126
+ mu_assert("test_no_acceleration: expected SCS_SOLVED", exitflag == SCS_SOLVED);
127
+ mu_assert("test_no_acceleration: no AA steps should be accepted",
128
+ info.accepted_accel_steps == 0);
129
+ mu_assert("test_no_acceleration: AA stats should be zeroed",
130
+ info.aa_stats.iter == 0 && info.aa_stats.n_accept == 0 &&
131
+ info.aa_stats.n_safeguard_reject == 0);
132
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
133
+
134
+ _OPTS_CLEANUP();
135
+ return fail;
136
+ }
137
+
138
+ /*
139
+ * Test Type-II Anderson acceleration via the explicit acceleration_type_1=0
140
+ * setting. Lower the regularization to type-II's preferred regime since the
141
+ * default is tuned for type-I. Verify convergence on a simple LP.
142
+ */
143
+ static const char *test_type2_acceleration(void) {
144
+ ScsCone *k;
145
+ ScsData *d;
146
+ ScsSettings *stgs;
147
+ ScsSolution *sol;
148
+ ScsInfo info = {0};
149
+ scs_int exitflag;
150
+ const char *fail;
151
+
152
+ _OPTS_SETUP(-2.0, 1.0);
153
+ stgs->acceleration_lookback = 10;
154
+ stgs->acceleration_type_1 = 0;
155
+ stgs->acceleration_regularization = 1e-12;
156
+
157
+ exitflag = scs(d, k, stgs, sol, &info);
158
+
159
+ mu_assert("test_type2_acceleration: expected SCS_SOLVED",
160
+ exitflag == SCS_SOLVED);
161
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
162
+
163
+ _OPTS_CLEANUP();
164
+ return fail;
165
+ }
166
+
167
+ /*
168
+ * Test that a negative acceleration_lookback is rejected by validate().
169
+ * Previously this was overloaded to mean type-II AA; that hack is gone now,
170
+ * so the only valid signal for type-II is acceleration_type_1=0.
171
+ */
172
+ static const char *test_negative_lookback_rejected(void) {
173
+ ScsCone *k;
174
+ ScsData *d;
175
+ ScsSettings *stgs;
176
+ ScsSolution *sol;
177
+ ScsInfo info = {0};
178
+ scs_int exitflag;
179
+
180
+ _OPTS_SETUP(-2.0, 1.0);
181
+ stgs->verbose = 0;
182
+ stgs->acceleration_lookback = -10;
183
+
184
+ exitflag = scs(d, k, stgs, sol, &info);
185
+
186
+ mu_assert("test_negative_lookback_rejected: expected SCS_FAILED",
187
+ exitflag == SCS_FAILED);
188
+
189
+ _OPTS_CLEANUP();
190
+ return 0;
191
+ }
192
+
193
+ /*
194
+ * Test that non-default acceleration_relaxation values still solve.
195
+ * Sweeps three points in [0, 2]: 0.5 (under-relaxed), 1.0 (vanilla), and 1.7
196
+ * (over-relaxed). All three should converge on the simple LP.
197
+ */
198
+ static const char *test_aa_relaxation_sweep(void) {
199
+ scs_float relaxations[] = {0.5, 1.0, 1.7};
200
+ scs_int n_relax = sizeof(relaxations) / sizeof(relaxations[0]);
201
+ scs_int j;
202
+ for (j = 0; j < n_relax; ++j) {
203
+ ScsCone *k;
204
+ ScsData *d;
205
+ ScsSettings *stgs;
206
+ ScsSolution *sol;
207
+ ScsInfo info = {0};
208
+ scs_int exitflag;
209
+ const char *fail;
210
+
211
+ _OPTS_SETUP(-2.0, 1.0);
212
+ stgs->verbose = 0;
213
+ stgs->acceleration_relaxation = relaxations[j];
214
+
215
+ exitflag = scs(d, k, stgs, sol, &info);
216
+ mu_assert("test_aa_relaxation_sweep: expected SCS_SOLVED",
217
+ exitflag == SCS_SOLVED);
218
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
219
+
220
+ _OPTS_CLEANUP();
221
+ if (fail) return fail;
222
+ }
223
+ return 0;
224
+ }
225
+
226
+ /*
227
+ * Test that non-default acceleration_regularization values still solve.
228
+ * Includes 0 (regularization disabled — valid per aa.h) and a value much
229
+ * larger than the default (should still produce a stable solve since AA
230
+ * just shrinks toward the un-accelerated update).
231
+ */
232
+ static const char *test_aa_regularization_sweep(void) {
233
+ scs_float regs[] = {0.0, 1e-12, 1e-4};
234
+ scs_int n_regs = sizeof(regs) / sizeof(regs[0]);
235
+ scs_int j;
236
+ for (j = 0; j < n_regs; ++j) {
237
+ ScsCone *k;
238
+ ScsData *d;
239
+ ScsSettings *stgs;
240
+ ScsSolution *sol;
241
+ ScsInfo info = {0};
242
+ scs_int exitflag;
243
+ const char *fail;
244
+
245
+ _OPTS_SETUP(-2.0, 1.0);
246
+ stgs->verbose = 0;
247
+ stgs->acceleration_regularization = regs[j];
248
+
249
+ exitflag = scs(d, k, stgs, sol, &info);
250
+ mu_assert("test_aa_regularization_sweep: expected SCS_SOLVED",
251
+ exitflag == SCS_SOLVED);
252
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
253
+
254
+ _OPTS_CLEANUP();
255
+ if (fail) return fail;
256
+ }
257
+ return 0;
258
+ }
259
+
260
+ /*
261
+ * Test that an invalid acceleration_relaxation (outside [0, 2]) is rejected.
262
+ */
263
+ static const char *test_invalid_aa_relaxation_rejected(void) {
264
+ ScsCone *k;
265
+ ScsData *d;
266
+ ScsSettings *stgs;
267
+ ScsSolution *sol;
268
+ ScsInfo info = {0};
269
+ scs_int exitflag;
270
+
271
+ _OPTS_SETUP(-2.0, 1.0);
272
+ stgs->verbose = 0;
273
+ stgs->acceleration_relaxation = 2.5;
274
+
275
+ exitflag = scs(d, k, stgs, sol, &info);
276
+ mu_assert("test_invalid_aa_relaxation_rejected: expected SCS_FAILED",
277
+ exitflag == SCS_FAILED);
278
+
279
+ _OPTS_CLEANUP();
280
+ return 0;
281
+ }
282
+
283
+ /*
284
+ * Test the sign-encoded acceleration_regularization contract from
285
+ * include/aa.h: negative means the absolute value is used as a pinned
286
+ * (unscaled) regularization -- a documented mode that must not merely be
287
+ * tolerated but must solve the problem.
288
+ */
289
+ static const char *test_pinned_negative_regularization_solves(void) {
290
+ ScsCone *k;
291
+ ScsData *d;
292
+ ScsSettings *stgs;
293
+ ScsSolution *sol;
294
+ ScsInfo info = {0};
295
+ scs_int exitflag;
296
+
297
+ _OPTS_SETUP(-2.0, 1.0);
298
+ stgs->verbose = 0;
299
+ stgs->acceleration_regularization = -1e-12;
300
+
301
+ exitflag = scs(d, k, stgs, sol, &info);
302
+ mu_assert("test_pinned_negative_regularization_solves: pinned-negative reg "
303
+ "must solve the LP",
304
+ exitflag == SCS_SOLVED);
305
+
306
+ _OPTS_CLEANUP();
307
+ return 0;
308
+ }
309
+
310
+ /*
311
+ * Non-finite acceleration_regularization values must be rejected by
312
+ * validate(); only the sign carries meaning, not NaN/inf.
313
+ */
314
+ static const char *test_invalid_aa_regularization_rejected(void) {
315
+ scs_float bad[] = {NAN, INFINITY, -INFINITY};
316
+ scs_int j;
317
+ for (j = 0; j < 3; ++j) {
318
+ ScsCone *k;
319
+ ScsData *d;
320
+ ScsSettings *stgs;
321
+ ScsSolution *sol;
322
+ ScsInfo info = {0};
323
+ scs_int exitflag;
324
+
325
+ _OPTS_SETUP(-2.0, 1.0);
326
+ stgs->verbose = 0;
327
+ stgs->acceleration_regularization = bad[j];
328
+
329
+ exitflag = scs(d, k, stgs, sol, &info);
330
+ mu_assert("test_invalid_aa_regularization_rejected: non-finite reg "
331
+ "should be SCS_FAILED",
332
+ exitflag == SCS_FAILED);
333
+
334
+ _OPTS_CLEANUP();
335
+ }
336
+ return 0;
337
+ }
338
+
339
+ /*
340
+ * Test normalize=0 vs normalize=1: both should converge to the same primal
341
+ * objective on the same LP (up to solver tolerance).
342
+ */
343
+ static const char *test_normalize_off(void) {
344
+ ScsCone *k;
345
+ ScsData *d;
346
+ ScsSettings *stgs;
347
+ ScsSolution *sol;
348
+ ScsInfo info0 = {0}, info1 = {0};
349
+ scs_int exitflag;
350
+ scs_float pobj0, pobj1;
351
+ const char *fail;
352
+
353
+ /* solve with normalize=1 */
354
+ _OPTS_SETUP(-2.0, 1.0);
355
+ stgs->normalize = 1;
356
+ exitflag = scs(d, k, stgs, sol, &info1);
357
+ mu_assert("test_normalize_off: normalize=1 failed", exitflag == SCS_SOLVED);
358
+ pobj1 = info1.pobj;
359
+ fail = verify_solution_correct(d, k, stgs, &info1, sol, exitflag);
360
+ _OPTS_CLEANUP();
361
+ if (fail) return fail;
362
+
363
+ /* solve with normalize=0 */
364
+ _OPTS_SETUP(-2.0, 1.0);
365
+ stgs->normalize = 0;
366
+ exitflag = scs(d, k, stgs, sol, &info0);
367
+ mu_assert("test_normalize_off: normalize=0 failed", exitflag == SCS_SOLVED);
368
+ pobj0 = info0.pobj;
369
+ fail = verify_solution_correct(d, k, stgs, &info0, sol, exitflag);
370
+ _OPTS_CLEANUP();
371
+ if (fail) return fail;
372
+
373
+ mu_assert("test_normalize_off: objectives differ between normalize=0 and 1",
374
+ ABS(pobj0 - pobj1) < 1e-4);
375
+ return 0;
376
+ }
377
+
378
+ /*
379
+ * Test normalize roundtrip: verify that un_normalize_sol correctly inverts
380
+ * the Ruiz equilibration by solving the same problem with normalize=0 and
381
+ * normalize=1 and comparing the full primal solution vectors.
382
+ *
383
+ * The problem uses A entries with different magnitudes (1, 10, 100) so that
384
+ * Ruiz scaling actually changes D/E significantly.
385
+ *
386
+ * Problem: min x1 + x2 + x3
387
+ * subject to x1 >= 1
388
+ * x2 >= 1
389
+ * x3 >= 1
390
+ * with A = diag(-1, -10, -100), b = [-1, -10, -100], c = [1, 1, 1].
391
+ * Solution: x1 = x2 = x3 = 1, obj = 3.
392
+ */
393
+ static const char *test_normalize_roundtrip(void) {
394
+ ScsSettings *stgs0, *stgs1;
395
+ ScsData *d0, *d1;
396
+ ScsCone *k0, *k1;
397
+ ScsSolution *sol0, *sol1;
398
+ ScsInfo info0 = {0}, info1 = {0};
399
+ scs_int exitflag0, exitflag1, i;
400
+ const char *fail;
401
+
402
+ scs_float Ax[] = {-1.0, -10.0, -100.0};
403
+ scs_int Ai[] = {0, 1, 2};
404
+ scs_int Ap[] = {0, 1, 2, 3};
405
+ scs_float b[] = {-1.0, -10.0, -100.0};
406
+ scs_float c[] = {1.0, 1.0, 1.0};
407
+ scs_int m = 3, n = 3;
408
+
409
+ /* helper macro to init data/settings/cone/sol */
410
+ #define _SETUP_NRM(d, k, stgs, sol) \
411
+ do { \
412
+ (d) = (ScsData *)scs_calloc(1, sizeof(ScsData)); \
413
+ (d)->m = m; (d)->n = n; (d)->b = b; (d)->c = c; \
414
+ (d)->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix)); \
415
+ (d)->A->m = m; (d)->A->n = n; \
416
+ (d)->A->x = Ax; (d)->A->i = Ai; (d)->A->p = Ap; \
417
+ (k) = (ScsCone *)scs_calloc(1, sizeof(ScsCone)); \
418
+ (k)->l = 3; \
419
+ (stgs) = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings)); \
420
+ scs_set_default_settings(stgs); \
421
+ (stgs)->eps_abs = 1e-7; (stgs)->eps_rel = 1e-7; \
422
+ (stgs)->verbose = 0; \
423
+ (sol) = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution)); \
424
+ } while (0)
425
+
426
+ #define _CLEANUP_NRM(d, k, stgs, sol) \
427
+ do { \
428
+ SCS(free_sol)(sol); \
429
+ scs_free((d)->A); \
430
+ scs_free(d); \
431
+ scs_free(k); \
432
+ scs_free(stgs); \
433
+ } while (0)
434
+
435
+ _SETUP_NRM(d1, k1, stgs1, sol1);
436
+ stgs1->normalize = 1;
437
+ exitflag1 = scs(d1, k1, stgs1, sol1, &info1);
438
+ mu_assert("test_normalize_roundtrip: normalize=1 failed", exitflag1 == SCS_SOLVED);
439
+ fail = verify_solution_correct(d1, k1, stgs1, &info1, sol1, exitflag1);
440
+ if (fail) { _CLEANUP_NRM(d1, k1, stgs1, sol1); return fail; }
441
+
442
+ _SETUP_NRM(d0, k0, stgs0, sol0);
443
+ stgs0->normalize = 0;
444
+ exitflag0 = scs(d0, k0, stgs0, sol0, &info0);
445
+ mu_assert("test_normalize_roundtrip: normalize=0 failed", exitflag0 == SCS_SOLVED);
446
+ fail = verify_solution_correct(d0, k0, stgs0, &info0, sol0, exitflag0);
447
+ if (fail) { _CLEANUP_NRM(d0, k0, stgs0, sol0); return fail; }
448
+
449
+ /* Compare primal solution vectors */
450
+ for (i = 0; i < n; ++i) {
451
+ mu_assert("test_normalize_roundtrip: x differs between normalize=0 and 1",
452
+ ABS(sol0->x[i] - sol1->x[i]) < 1e-3);
453
+ }
454
+ mu_assert("test_normalize_roundtrip: objectives differ",
455
+ ABS(info0.pobj - info1.pobj) < 1e-4);
456
+
457
+ _CLEANUP_NRM(d0, k0, stgs0, sol0);
458
+ _CLEANUP_NRM(d1, k1, stgs1, sol1);
459
+
460
+ #undef _SETUP_NRM
461
+ #undef _CLEANUP_NRM
462
+ return 0;
463
+ }
464
+
465
+ /*
466
+ * Test scs_version(): the public API function should return a non-NULL,
467
+ * non-empty version string.
468
+ */
469
+ static const char *test_scs_version(void) {
470
+ const char *ver = scs_version();
471
+ mu_assert("scs_version: should not return NULL", ver != SCS_NULL);
472
+ mu_assert("scs_version: version string should be non-empty", ver[0] != '\0');
473
+ return 0;
474
+ }
475
+
476
+ /*
477
+ * Test time_limit_secs: setting an extremely small time limit (1e-10 s)
478
+ * guarantees the timer fires before any iteration completes. The solver must
479
+ * exit with an inaccurate status code and append "(inaccurate - reached
480
+ * time_limit_secs)" to info.status.
481
+ */
482
+ static const char *test_time_limit_secs(void) {
483
+ ScsCone *k;
484
+ ScsData *d;
485
+ ScsSettings *stgs;
486
+ ScsSolution *sol;
487
+ ScsInfo info = {0};
488
+ scs_int exitflag;
489
+
490
+ _OPTS_SETUP(-2.0, 1.0);
491
+ stgs->time_limit_secs = 1e-10; /* fires before first iteration */
492
+ stgs->max_iters = 100000; /* large enough so max_iters is not the cause */
493
+
494
+ exitflag = scs(d, k, stgs, sol, &info);
495
+
496
+ /* Must return an inaccurate code (not SCS_FAILED) */
497
+ mu_assert("test_time_limit_secs: should not return SCS_FAILED",
498
+ exitflag != SCS_FAILED);
499
+ mu_assert("test_time_limit_secs: expected inaccurate exit code",
500
+ exitflag == SCS_SOLVED_INACCURATE ||
501
+ exitflag == SCS_INFEASIBLE_INACCURATE ||
502
+ exitflag == SCS_UNBOUNDED_INACCURATE);
503
+ mu_assert("test_time_limit_secs: status should mention time_limit_secs",
504
+ strstr(info.status, "time_limit_secs") != SCS_NULL);
505
+
506
+ _OPTS_CLEANUP();
507
+ return 0;
508
+ }
509
+
510
+ /*
511
+ * Test explicit warm start: manually set sol->x, sol->y, sol->s to values
512
+ * far from the optimum, then solve with warm_start=1. The solver should
513
+ * still converge to the correct answer.
514
+ *
515
+ * Uses the 3-step API: scs_init / scs_solve / scs_finish.
516
+ *
517
+ * Problem: min x s.t. x >= 2 (same LP as _OPTS_SETUP)
518
+ * Optimal: x=2, obj=2.
519
+ */
520
+ static const char *test_warm_start(void) {
521
+ ScsCone *k;
522
+ ScsData *d;
523
+ ScsSettings *stgs;
524
+ ScsSolution *sol;
525
+ ScsInfo info = {0};
526
+ scs_int exitflag;
527
+ const char *fail;
528
+
529
+ _OPTS_SETUP(-2.0, 1.0);
530
+
531
+ ScsWork *w = scs_init(d, k, stgs);
532
+ mu_assert("test_warm_start: scs_init failed", w != SCS_NULL);
533
+
534
+ /* Cold solve first to verify baseline */
535
+ exitflag = scs_solve(w, sol, &info, 0);
536
+ mu_assert("test_warm_start: cold solve failed", exitflag == SCS_SOLVED);
537
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
538
+ if (fail) { scs_finish(w); _OPTS_CLEANUP(); return fail; }
539
+
540
+ /* Now set sol to a bad initial guess: x=100, y=0, s=0 */
541
+ sol->x[0] = 100.0;
542
+ sol->y[0] = 0.0;
543
+ sol->s[0] = 0.0;
544
+
545
+ /* Warm-start solve with the bad initial guess */
546
+ exitflag = scs_solve(w, sol, &info, 1);
547
+ mu_assert("test_warm_start: warm solve failed", exitflag == SCS_SOLVED);
548
+ mu_assert("test_warm_start: wrong objective after warm start",
549
+ ABS(info.pobj - 2.0) < 1e-4);
550
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
551
+
552
+ scs_finish(w);
553
+ _OPTS_CLEANUP();
554
+ return fail;
555
+ }
556
+
557
+ #undef _OPTS_SETUP
558
+ #undef _OPTS_CLEANUP