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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +1 -1
- data/lib/scs/ffi.rb +18 -0
- data/lib/scs/version.rb +1 -1
- data/vendor/scs/CITATION.cff +2 -3
- data/vendor/scs/CLAUDE.md +115 -0
- data/vendor/scs/CMakeLists.txt +330 -201
- data/vendor/scs/CONTRIBUTING.md +49 -0
- data/vendor/scs/Makefile +90 -43
- data/vendor/scs/README.md +96 -2
- data/vendor/scs/include/aa.h +59 -11
- data/vendor/scs/include/aa_stats.h +47 -0
- data/vendor/scs/include/cones.h +16 -7
- data/vendor/scs/include/glbopts.h +113 -26
- data/vendor/scs/include/linalg.h +5 -1
- data/vendor/scs/include/linsys.h +1 -2
- data/vendor/scs/include/normalize.h +6 -2
- data/vendor/scs/include/rw.h +9 -3
- data/vendor/scs/include/scs.h +17 -1
- data/vendor/scs/include/scs_blas.h +8 -0
- data/vendor/scs/include/scs_types.h +2 -2
- data/vendor/scs/include/scs_work.h +22 -1
- data/vendor/scs/include/util.h +3 -6
- data/vendor/scs/include/util_spectral_cones.h +3 -3
- data/vendor/scs/linsys/accelerate/direct/private.c +126 -0
- data/vendor/scs/linsys/accelerate/direct/private.h +34 -0
- data/vendor/scs/linsys/cpu/dense/private.c +250 -0
- data/vendor/scs/linsys/cpu/dense/private.h +31 -0
- data/vendor/scs/linsys/cpu/direct/private.c +86 -44
- data/vendor/scs/linsys/cpu/indirect/private.c +647 -113
- data/vendor/scs/linsys/cpu/indirect/private.h +28 -0
- data/vendor/scs/linsys/csparse.c +9 -3
- data/vendor/scs/linsys/csparse.h +4 -2
- data/vendor/scs/linsys/cudss/direct/private.c +77 -43
- data/vendor/scs/linsys/cudss/direct/private.h +33 -7
- data/vendor/scs/linsys/gpu/indirect/private.c +6 -2
- data/vendor/scs/linsys/mkl/direct/private.c +63 -31
- data/vendor/scs/linsys/mkl/direct/private.h +0 -1
- data/vendor/scs/linsys/scs_matrix.c +291 -165
- data/vendor/scs/linsys/scs_matrix.h +7 -9
- data/vendor/scs/scs.mk +35 -13
- data/vendor/scs/src/aa.c +673 -166
- data/vendor/scs/src/cones.c +545 -223
- data/vendor/scs/src/ctrlc.c +59 -16
- data/vendor/scs/src/exp_cone.c +70 -50
- data/vendor/scs/src/linalg.c +21 -2
- data/vendor/scs/src/normalize.c +24 -26
- data/vendor/scs/src/rw.c +596 -124
- data/vendor/scs/src/scs.c +990 -513
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +240 -187
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +108 -85
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +62 -63
- data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +85 -78
- data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +92 -97
- data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +39 -28
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +59 -40
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +37 -29
- data/vendor/scs/src/spectral_cones/util_spectral_cones.c +12 -6
- data/vendor/scs/src/util.c +37 -7
- data/vendor/scs/test/mkl_interface_mismatch.c +97 -0
- data/vendor/scs/test/packaging/CMakeLists.txt +21 -0
- data/vendor/scs/test/packaging/consume.c +50 -0
- data/vendor/scs/test/problem_utils.h +9 -2
- data/vendor/scs/test/problems/dense_qp.h +64 -0
- data/vendor/scs/test/problems/hs21_tiny_qp.h +6 -2
- data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +10 -2
- data/vendor/scs/test/problems/infeasible_lp.h +66 -0
- data/vendor/scs/test/problems/infeasible_socp.h +75 -0
- data/vendor/scs/test/problems/lp_update.h +110 -0
- data/vendor/scs/test/problems/qafiro_tiny_qp.h +3 -1
- data/vendor/scs/test/problems/rob_gauss_cov_est.h +6 -3
- data/vendor/scs/test/problems/small_qp.h +5 -0
- data/vendor/scs/test/problems/test_box_cone.h +92 -0
- data/vendor/scs/test/problems/test_dual_exp_cone.h +96 -0
- data/vendor/scs/test/problems/test_inaccurate.h +212 -0
- data/vendor/scs/test/problems/test_mixed_cones.h +113 -0
- data/vendor/scs/test/problems/test_normalize_roundtrip.h +279 -0
- data/vendor/scs/test/problems/test_power_cone.h +414 -0
- data/vendor/scs/test/problems/test_psd_metric.h +109 -0
- data/vendor/scs/test/problems/test_psd_n1.h +84 -0
- data/vendor/scs/test/problems/test_root_plus.h +191 -0
- data/vendor/scs/test/problems/test_rw_settings.h +112 -0
- data/vendor/scs/test/problems/test_soc_sizes.h +393 -0
- data/vendor/scs/test/problems/test_solver_options.h +558 -0
- data/vendor/scs/test/problems/test_validation.h +263 -24
- data/vendor/scs/test/problems/test_zero_cone.h +81 -0
- data/vendor/scs/test/problems/unbounded_lp.h +66 -0
- data/vendor/scs/test/problems/unbounded_socp.h +71 -0
- data/vendor/scs/test/run_from_file.c +6 -0
- data/vendor/scs/test/run_tests.c +71 -0
- data/vendor/scs/test/rw_settings.c +7 -0
- data/vendor/scs/test/spectral_cones_problems/test_ell1_and_nuc.h +128 -0
- data/vendor/scs/test/spectral_cones_problems/test_ell1_cone.h +115 -0
- 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
|