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,212 @@
1
+ #include "glbopts.h"
2
+ #include "minunit.h"
3
+ #include "scs.h"
4
+ #include "util.h"
5
+
6
+ /*
7
+ * Shared tiny LP used for all inaccurate termination tests.
8
+ * min x s.t. x >= 2
9
+ * n=1, m=1, l=1, A=[[-1]], b=[-2], c=[1], optimal obj=2.
10
+ */
11
+ #define _INACCURATE_LP_VARS \
12
+ scs_float Ax[] = {-1.0}; \
13
+ scs_int Ai[] = {0}; \
14
+ scs_int Ap[] = {0, 1}; \
15
+ scs_float b_feas[] = {-2.0}; \
16
+ scs_float c_feas[] = {1.0}
17
+
18
+ /*
19
+ * Shared tiny infeasible LP.
20
+ * x >= 1 AND x <= 0 (infeasible).
21
+ * n=1, m=2, l=2, A=[[-1],[1]], b=[-1,0], c=[1]
22
+ */
23
+ #define _INACCURATE_INFEAS_VARS \
24
+ scs_float Ax_i[] = {-1.0, 1.0}; \
25
+ scs_int Ai_i[] = {0, 1}; \
26
+ scs_int Ap_i[] = {0, 2}; \
27
+ scs_float b_i[] = {-1.0, 0.0}; \
28
+ scs_float c_i[] = {1.0}
29
+
30
+ /*
31
+ * Shared tiny unbounded LP.
32
+ * min -x s.t. x >= 0 (unbounded below).
33
+ * n=1, m=1, l=1, A=[[-1]], b=[0], c=[-1]
34
+ */
35
+ #define _INACCURATE_UNBDD_VARS \
36
+ scs_float Ax_u[] = {-1.0}; \
37
+ scs_int Ai_u[] = {0}; \
38
+ scs_int Ap_u[] = {0, 1}; \
39
+ scs_float b_u[] = {0.0}; \
40
+ scs_float c_u[] = {-1.0}
41
+
42
+ /*
43
+ * Test SCS_SOLVED_INACCURATE: run a feasible, bounded LP with max_iters=2.
44
+ * After only 2 iterations tau > 0 and kap/tau is small, so set_unfinished
45
+ * returns SCS_SOLVED_INACCURATE.
46
+ */
47
+ static const char *test_solved_inaccurate(void) {
48
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
49
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
50
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
51
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
52
+ ScsInfo info = {0};
53
+ scs_int exitflag;
54
+ _INACCURATE_LP_VARS;
55
+ scs_int m = 1, n = 1;
56
+
57
+ d->m = m; d->n = n; d->b = b_feas; d->c = c_feas;
58
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
59
+ d->A->m = m; d->A->n = n;
60
+ d->A->x = Ax; d->A->i = Ai; d->A->p = Ap;
61
+ k->l = 1;
62
+
63
+ scs_set_default_settings(stgs);
64
+ stgs->max_iters = 2;
65
+ stgs->verbose = 1;
66
+
67
+ exitflag = scs(d, k, stgs, sol, &info);
68
+
69
+ mu_assert("test_solved_inaccurate: expected SCS_SOLVED_INACCURATE",
70
+ exitflag == SCS_SOLVED_INACCURATE);
71
+ mu_assert("test_solved_inaccurate: expected max_iters exhausted",
72
+ info.iter == stgs->max_iters);
73
+
74
+ SCS(free_sol)(sol);
75
+ scs_free(d->A);
76
+ scs_free(k);
77
+ scs_free(stgs);
78
+ scs_free(d);
79
+ return 0;
80
+ }
81
+
82
+ /*
83
+ * Test SCS_INFEASIBLE_INACCURATE: run an infeasible LP with eps_infeas=0
84
+ * (disables the infeasibility termination criterion entirely), so the solver
85
+ * runs until max_iters. set_unfinished then detects kap > tau with the
86
+ * infeasibility direction (bty_tau < 0) and returns SCS_INFEASIBLE_INACCURATE.
87
+ */
88
+ static const char *test_infeasible_inaccurate(void) {
89
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
90
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
91
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
92
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
93
+ ScsInfo info = {0};
94
+ scs_int exitflag;
95
+ _INACCURATE_INFEAS_VARS;
96
+ scs_int m = 2, n = 1;
97
+
98
+ d->m = m; d->n = n; d->b = b_i; d->c = c_i;
99
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
100
+ d->A->m = m; d->A->n = n;
101
+ d->A->x = Ax_i; d->A->i = Ai_i; d->A->p = Ap_i;
102
+ k->l = 2;
103
+
104
+ scs_set_default_settings(stgs);
105
+ stgs->eps_infeas = 0.0; /* disabled: res < 0 never true, forces max_iters */
106
+ /* Use enough iterations so kap >> tau is clearly established across all
107
+ * linear solver backends (numerical trajectory is backend-dependent). */
108
+ stgs->max_iters = 100;
109
+ stgs->verbose = 1;
110
+
111
+ exitflag = scs(d, k, stgs, sol, &info);
112
+
113
+ mu_assert("test_infeasible_inaccurate: expected SCS_INFEASIBLE_INACCURATE",
114
+ exitflag == SCS_INFEASIBLE_INACCURATE);
115
+ mu_assert("test_infeasible_inaccurate: expected max_iters exhausted",
116
+ info.iter == stgs->max_iters);
117
+
118
+ SCS(free_sol)(sol);
119
+ scs_free(d->A);
120
+ scs_free(k);
121
+ scs_free(stgs);
122
+ scs_free(d);
123
+ return 0;
124
+ }
125
+
126
+ /*
127
+ * Test SCS_UNBOUNDED_INACCURATE: run an unbounded LP with eps_infeas=0
128
+ * (disables the unboundedness termination criterion entirely), so the solver
129
+ * runs until max_iters. set_unfinished then detects kap > tau with the
130
+ * unboundedness direction (ctx_tau < 0) and returns SCS_UNBOUNDED_INACCURATE.
131
+ */
132
+ static const char *test_unbounded_inaccurate(void) {
133
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
134
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
135
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
136
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
137
+ ScsInfo info = {0};
138
+ scs_int exitflag;
139
+ _INACCURATE_UNBDD_VARS;
140
+ scs_int m = 1, n = 1;
141
+
142
+ d->m = m; d->n = n; d->b = b_u; d->c = c_u;
143
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
144
+ d->A->m = m; d->A->n = n;
145
+ d->A->x = Ax_u; d->A->i = Ai_u; d->A->p = Ap_u;
146
+ k->l = 1;
147
+
148
+ scs_set_default_settings(stgs);
149
+ stgs->eps_infeas = 0.0; /* disabled: res < 0 never true, forces max_iters */
150
+ /* Use enough iterations so kap >> tau is clearly established across all
151
+ * linear solver backends (numerical trajectory is backend-dependent). */
152
+ stgs->max_iters = 100;
153
+ stgs->verbose = 1;
154
+
155
+ exitflag = scs(d, k, stgs, sol, &info);
156
+
157
+ mu_assert("test_unbounded_inaccurate: expected SCS_UNBOUNDED_INACCURATE",
158
+ exitflag == SCS_UNBOUNDED_INACCURATE);
159
+ mu_assert("test_unbounded_inaccurate: expected max_iters exhausted",
160
+ info.iter == stgs->max_iters);
161
+
162
+ SCS(free_sol)(sol);
163
+ scs_free(d->A);
164
+ scs_free(k);
165
+ scs_free(stgs);
166
+ scs_free(d);
167
+ return 0;
168
+ }
169
+
170
+ /*
171
+ * Test SCS_SOLVED_INACCURATE with max_iters=1: the solver exits after a single
172
+ * iteration with tau > 0 (kap/tau small), so set_unfinished returns
173
+ * SCS_SOLVED_INACCURATE. This exercises the minimum-iteration code path.
174
+ */
175
+ static const char *test_max_iters_1(void) {
176
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
177
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
178
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
179
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
180
+ ScsInfo info = {0};
181
+ scs_int exitflag;
182
+ _INACCURATE_LP_VARS;
183
+ scs_int m = 1, n = 1;
184
+
185
+ d->m = m; d->n = n; d->b = b_feas; d->c = c_feas;
186
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
187
+ d->A->m = m; d->A->n = n;
188
+ d->A->x = Ax; d->A->i = Ai; d->A->p = Ap;
189
+ k->l = 1;
190
+
191
+ scs_set_default_settings(stgs);
192
+ stgs->max_iters = 1;
193
+ stgs->verbose = 1;
194
+
195
+ exitflag = scs(d, k, stgs, sol, &info);
196
+
197
+ mu_assert("test_max_iters_1: expected SCS_SOLVED_INACCURATE",
198
+ exitflag == SCS_SOLVED_INACCURATE);
199
+ mu_assert("test_max_iters_1: expected exactly 1 iteration",
200
+ info.iter == 1);
201
+
202
+ SCS(free_sol)(sol);
203
+ scs_free(d->A);
204
+ scs_free(k);
205
+ scs_free(stgs);
206
+ scs_free(d);
207
+ return 0;
208
+ }
209
+
210
+ #undef _INACCURATE_LP_VARS
211
+ #undef _INACCURATE_INFEAS_VARS
212
+ #undef _INACCURATE_UNBDD_VARS
@@ -0,0 +1,113 @@
1
+ #include "glbopts.h"
2
+ #include "linalg.h"
3
+ #include "minunit.h"
4
+ #include "problem_utils.h"
5
+ #include "scs.h"
6
+ #include "util.h"
7
+
8
+ /*
9
+ * Mixed-cone test: exercises the full cone ordering path (z, l, q, ep)
10
+ * in a single problem to verify that set_cone_boundaries and proj_cone
11
+ * dispatch correctly across multiple different cone types.
12
+ *
13
+ * Problem (5 variables, 8 constraints):
14
+ * minimize x1 + x2 + x3 + x4 + x5
15
+ * subject to
16
+ * x1 + x2 = 3 (zero cone, 1 row)
17
+ * x3 >= 1 (non-neg cone, 1 row)
18
+ * (x4, x5) in SOC_2 (SOC, 2 rows)
19
+ * (x1, x2, x3) in K_exp (exp cone, 3 rows)
20
+ * x4 >= 1 (duplicate of above, extra l row)
21
+ *
22
+ * Cone ordering: z=1, l=2, q=[2], ep=1 → total m=1+2+2+3=8.
23
+ *
24
+ * But this is over-constrained. Let me use a simpler formulation:
25
+ *
26
+ * Variables: [x1, x2, x3] (n=3)
27
+ * m=6, cone order: z=1, l=1, q=[2], ep=0 (skipping exp for simpler math)
28
+ *
29
+ * Actually let's just use z + l + q:
30
+ * minimize x1 + x2 + x3
31
+ * subject to
32
+ * x1 = 2 (zero cone, 1 row: z=1)
33
+ * x2 >= 1 (non-neg cone, 1 row: l=1)
34
+ * (x3, x2) in SOC_2 (SOC, 2 rows: q=[2])
35
+ * Optimal: x1=2, and (x3, x2) in SOC means x3 >= |x2|.
36
+ * To minimize x2+x3 with x2>=1: x2=1, x3>=1, so x3=1.
37
+ * obj = 2+1+1 = 4.
38
+ *
39
+ * SCS formulation: Ax + s = b, s in K
40
+ * Row 0 (z): -x1 + s0 = -2 => x1 = 2
41
+ * Row 1 (l): -x2 + s1 = -1 => x2 >= 1
42
+ * Row 2 (q0): -x3 + s2 = 0 => s2 = x3 (t component of SOC)
43
+ * Row 3 (q1): -x2 + s3 = 0 => s3 = x2 (body of SOC)
44
+ * SOC: |s3| <= s2, i.e., |x2| <= x3
45
+ *
46
+ * A (4x3 CSC):
47
+ * col 0 (x1): row 0 -> -1
48
+ * col 1 (x2): row 1 -> -1, row 3 -> -1
49
+ * col 2 (x3): row 2 -> -1
50
+ */
51
+ static const char *test_mixed_cones(void) {
52
+ ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
53
+ ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData));
54
+ ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
55
+ ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
56
+ ScsInfo info = {0};
57
+ scs_int exitflag;
58
+ scs_float perr, derr;
59
+ scs_int success;
60
+ const char *fail;
61
+
62
+ scs_float opt = 4.0;
63
+
64
+ scs_float Ax[] = {-1.0, -1.0, -1.0, -1.0};
65
+ scs_int Ai[] = {0, 1, 3, 2};
66
+ scs_int Ap[] = {0, 1, 3, 4};
67
+
68
+ scs_float b[] = {-2.0, -1.0, 0.0, 0.0};
69
+ scs_float c[] = {1.0, 1.0, 1.0};
70
+
71
+ scs_int q_arr[] = {2};
72
+ scs_int m = 4, n = 3;
73
+
74
+ d->m = m;
75
+ d->n = n;
76
+ d->b = b;
77
+ d->c = c;
78
+
79
+ d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
80
+ d->A->m = m;
81
+ d->A->n = n;
82
+ d->A->x = Ax;
83
+ d->A->i = Ai;
84
+ d->A->p = Ap;
85
+
86
+ k->z = 1;
87
+ k->l = 1;
88
+ k->qsize = 1;
89
+ k->q = q_arr;
90
+
91
+ scs_set_default_settings(stgs);
92
+ stgs->eps_abs = 1e-6;
93
+ stgs->eps_rel = 1e-6;
94
+
95
+ exitflag = scs(d, k, stgs, sol, &info);
96
+
97
+ perr = info.pobj - opt;
98
+ derr = info.dobj - opt;
99
+
100
+ scs_printf("primal obj error %4e\n", perr);
101
+ scs_printf("dual obj error %4e\n", derr);
102
+
103
+ success = ABS(perr) < 1e-4 && ABS(derr) < 1e-4 && exitflag == SCS_SOLVED;
104
+ mu_assert("test_mixed_cones: SCS failed to produce SCS_SOLVED", success);
105
+ fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
106
+
107
+ SCS(free_sol)(sol);
108
+ scs_free(d->A);
109
+ scs_free(k);
110
+ scs_free(stgs);
111
+ scs_free(d);
112
+ return fail;
113
+ }
@@ -0,0 +1,279 @@
1
+ #include "glbopts.h"
2
+ #include "linalg.h"
3
+ #include "minunit.h"
4
+ #include "problem_utils.h"
5
+ #include "scs.h"
6
+ #include "util.h"
7
+
8
+ /*
9
+ * Test that normalization is consistent:
10
+ * 1. Solve the same problem with normalize=0 and normalize=1 and verify
11
+ * that the returned (un-normalized) solutions agree.
12
+ * 2. Solve a problem where A entries span several orders of magnitude to
13
+ * exercise the Ruiz equilibration more aggressively.
14
+ *
15
+ * Problem (LP):
16
+ * minimize x1 + x2 + x3
17
+ * subject to x1 >= 1 (l cone)
18
+ * x2 >= 10 (l cone)
19
+ * x3 >= 100 (l cone)
20
+ * A entries: -1, -1, -1 (diagonal), b = [-1, -10, -100]
21
+ * Optimal: x = [1, 10, 100], obj = 111.
22
+ *
23
+ * The varying magnitudes in b (and correspondingly in optimal x) ensure
24
+ * that D scaling factors are non-trivial.
25
+ */
26
+ static const char *test_normalize_roundtrip_lp(void) {
27
+ ScsCone *k0, *k1;
28
+ ScsData *d0, *d1;
29
+ ScsSettings *stgs0, *stgs1;
30
+ ScsSolution *sol0, *sol1;
31
+ ScsInfo info0 = {0}, info1 = {0};
32
+ scs_int exitflag0, exitflag1;
33
+ scs_float err_x, err_y, err_s, err_obj;
34
+ scs_int i;
35
+
36
+ scs_float opt = 111.0;
37
+
38
+ scs_int Ai[] = {0, 1, 2};
39
+ scs_int Ap[] = {0, 1, 2, 3};
40
+ scs_int m = 3, n = 3;
41
+
42
+ /* Separate copies needed because normalization modifies A, b, c in place */
43
+ scs_float Ax0[] = {-1.0, -1.0, -1.0};
44
+ scs_float Ax1[] = {-1.0, -1.0, -1.0};
45
+ scs_float b0[] = {-1.0, -10.0, -100.0};
46
+ scs_float b1[] = {-1.0, -10.0, -100.0};
47
+ scs_float c0[] = {1.0, 1.0, 1.0};
48
+ scs_float c1[] = {1.0, 1.0, 1.0};
49
+
50
+ /* --- Setup problem 0: normalize OFF --- */
51
+ k0 = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
52
+ d0 = (ScsData *)scs_calloc(1, sizeof(ScsData));
53
+ stgs0 = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
54
+ sol0 = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
55
+
56
+ d0->m = m; d0->n = n;
57
+ d0->b = b0; d0->c = c0;
58
+ d0->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
59
+ d0->A->m = m; d0->A->n = n;
60
+ d0->A->x = Ax0; d0->A->i = Ai; d0->A->p = Ap;
61
+ k0->l = m;
62
+ scs_set_default_settings(stgs0);
63
+ stgs0->normalize = 0;
64
+ stgs0->eps_abs = 1e-9;
65
+ stgs0->eps_rel = 1e-9;
66
+
67
+ /* --- Setup problem 1: normalize ON --- */
68
+ k1 = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
69
+ d1 = (ScsData *)scs_calloc(1, sizeof(ScsData));
70
+ stgs1 = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
71
+ sol1 = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
72
+
73
+ d1->m = m; d1->n = n;
74
+ d1->b = b1; d1->c = c1;
75
+ d1->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
76
+ d1->A->m = m; d1->A->n = n;
77
+ d1->A->x = Ax1; d1->A->i = Ai; d1->A->p = Ap;
78
+ k1->l = m;
79
+ scs_set_default_settings(stgs1);
80
+ stgs1->normalize = 1;
81
+ stgs1->eps_abs = 1e-9;
82
+ stgs1->eps_rel = 1e-9;
83
+
84
+ /* --- Solve both --- */
85
+ exitflag0 = scs(d0, k0, stgs0, sol0, &info0);
86
+ exitflag1 = scs(d1, k1, stgs1, sol1, &info1);
87
+
88
+ mu_assert("normalize_roundtrip: unnormalized solve failed",
89
+ exitflag0 == SCS_SOLVED);
90
+ mu_assert("normalize_roundtrip: normalized solve failed",
91
+ exitflag1 == SCS_SOLVED);
92
+
93
+ /* Both should match the known optimal */
94
+ mu_assert("normalize_roundtrip: unnormalized obj wrong",
95
+ ABS(info0.pobj - opt) < 1e-4);
96
+ mu_assert("normalize_roundtrip: normalized obj wrong",
97
+ ABS(info1.pobj - opt) < 1e-4);
98
+
99
+ /* Solutions should agree (un-normalization must be correct) */
100
+ err_x = 0; err_y = 0; err_s = 0;
101
+ for (i = 0; i < n; ++i) {
102
+ scs_float d = ABS(sol0->x[i] - sol1->x[i]);
103
+ if (d > err_x) err_x = d;
104
+ }
105
+ for (i = 0; i < m; ++i) {
106
+ scs_float dy = ABS(sol0->y[i] - sol1->y[i]);
107
+ scs_float ds = ABS(sol0->s[i] - sol1->s[i]);
108
+ if (dy > err_y) err_y = dy;
109
+ if (ds > err_s) err_s = ds;
110
+ }
111
+ err_obj = ABS(info0.pobj - info1.pobj);
112
+
113
+ scs_printf("normalize_roundtrip: x_err=%.2e y_err=%.2e s_err=%.2e "
114
+ "obj_err=%.2e\n", err_x, err_y, err_s, err_obj);
115
+
116
+ mu_assert("normalize_roundtrip: x vectors differ too much", err_x < 1e-4);
117
+ mu_assert("normalize_roundtrip: y vectors differ too much", err_y < 1e-4);
118
+ mu_assert("normalize_roundtrip: s vectors differ too much", err_s < 1e-4);
119
+ mu_assert("normalize_roundtrip: obj values differ too much", err_obj < 1e-5);
120
+
121
+ /* Cleanup */
122
+ SCS(free_sol)(sol0);
123
+ scs_free(d0->A);
124
+ scs_free(k0);
125
+ scs_free(stgs0);
126
+ scs_free(d0);
127
+
128
+ SCS(free_sol)(sol1);
129
+ scs_free(d1->A);
130
+ scs_free(k1);
131
+ scs_free(stgs1);
132
+ scs_free(d1);
133
+
134
+ return 0;
135
+ }
136
+
137
+ /*
138
+ * Test normalization roundtrip with a QP where P is present,
139
+ * ensuring the E scaling (column scaling for P) is also correct.
140
+ *
141
+ * Problem (QP):
142
+ * minimize 0.5 * (x1^2 + 100*x2^2) + x1 + x2
143
+ * subject to x1 >= 0, x2 >= 0
144
+ *
145
+ * P = [[1, 0], [0, 100]] (upper triangular)
146
+ * c = [1, 1]
147
+ * A = [[-1, 0], [0, -1]], b = [0, 0], l = 2
148
+ *
149
+ * The 100x difference in P eigenvalues stresses normalization.
150
+ * Optimal: x1 = 0 (since the unconstrained min of x1 + 0.5*x1^2 is at
151
+ * x1 = -1 which is infeasible), x2 = 0 (unconstrained min at
152
+ * x2 = -0.01 which is infeasible).
153
+ * Actually: x1 >= 0 binds, x2 >= 0 binds => x = [0, 0], obj = 0.
154
+ *
155
+ * Wait, let me redo: min 0.5*x'Px + c'x s.t. x >= 0.
156
+ * Gradient at x=0: c = [1, 1] > 0, so x=0 is optimal. obj=0.
157
+ */
158
+ static const char *test_normalize_roundtrip_qp(void) {
159
+ ScsCone *k0, *k1;
160
+ ScsData *d0, *d1;
161
+ ScsSettings *stgs0, *stgs1;
162
+ ScsSolution *sol0, *sol1;
163
+ ScsInfo info0 = {0}, info1 = {0};
164
+ scs_int exitflag0, exitflag1;
165
+ scs_float err_x, err_y, err_s, err_obj;
166
+ scs_int i;
167
+
168
+ scs_float opt = 0.0;
169
+
170
+ /* A (2x2 diagonal) */
171
+ scs_float Ax0[] = {-1.0, -1.0};
172
+ scs_float Ax1[] = {-1.0, -1.0};
173
+ scs_int Ai[] = {0, 1};
174
+ scs_int Ap[] = {0, 1, 2};
175
+
176
+ /* P (2x2 upper triangular, diagonal: [1, 100]) */
177
+ scs_float Px0[] = {1.0, 100.0};
178
+ scs_float Px1[] = {1.0, 100.0};
179
+ scs_int Pi[] = {0, 1};
180
+ scs_int Pp[] = {0, 1, 2};
181
+
182
+ scs_float b0[] = {0.0, 0.0};
183
+ scs_float b1[] = {0.0, 0.0};
184
+ scs_float c0[] = {1.0, 1.0};
185
+ scs_float c1[] = {1.0, 1.0};
186
+ scs_int m = 2, n = 2;
187
+
188
+ /* --- normalize OFF --- */
189
+ k0 = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
190
+ d0 = (ScsData *)scs_calloc(1, sizeof(ScsData));
191
+ stgs0 = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
192
+ sol0 = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
193
+
194
+ d0->m = m; d0->n = n;
195
+ d0->b = b0; d0->c = c0;
196
+ d0->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
197
+ d0->A->m = m; d0->A->n = n;
198
+ d0->A->x = Ax0; d0->A->i = Ai; d0->A->p = Ap;
199
+ d0->P = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
200
+ d0->P->m = n; d0->P->n = n;
201
+ d0->P->x = Px0; d0->P->i = Pi; d0->P->p = Pp;
202
+ k0->l = m;
203
+ scs_set_default_settings(stgs0);
204
+ stgs0->normalize = 0;
205
+ stgs0->eps_abs = 1e-9;
206
+ stgs0->eps_rel = 1e-9;
207
+
208
+ /* --- normalize ON --- */
209
+ k1 = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
210
+ d1 = (ScsData *)scs_calloc(1, sizeof(ScsData));
211
+ stgs1 = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
212
+ sol1 = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
213
+
214
+ d1->m = m; d1->n = n;
215
+ d1->b = b1; d1->c = c1;
216
+ d1->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
217
+ d1->A->m = m; d1->A->n = n;
218
+ d1->A->x = Ax1; d1->A->i = Ai; d1->A->p = Ap;
219
+ d1->P = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
220
+ d1->P->m = n; d1->P->n = n;
221
+ d1->P->x = Px1; d1->P->i = Pi; d1->P->p = Pp;
222
+ k1->l = m;
223
+ scs_set_default_settings(stgs1);
224
+ stgs1->normalize = 1;
225
+ stgs1->eps_abs = 1e-9;
226
+ stgs1->eps_rel = 1e-9;
227
+
228
+ /* --- Solve both --- */
229
+ exitflag0 = scs(d0, k0, stgs0, sol0, &info0);
230
+ exitflag1 = scs(d1, k1, stgs1, sol1, &info1);
231
+
232
+ mu_assert("normalize_qp: unnormalized solve failed",
233
+ exitflag0 == SCS_SOLVED);
234
+ mu_assert("normalize_qp: normalized solve failed",
235
+ exitflag1 == SCS_SOLVED);
236
+
237
+ mu_assert("normalize_qp: unnormalized obj wrong",
238
+ ABS(info0.pobj - opt) < 1e-4);
239
+ mu_assert("normalize_qp: normalized obj wrong",
240
+ ABS(info1.pobj - opt) < 1e-4);
241
+
242
+ err_x = 0; err_y = 0; err_s = 0;
243
+ for (i = 0; i < n; ++i) {
244
+ scs_float d = ABS(sol0->x[i] - sol1->x[i]);
245
+ if (d > err_x) err_x = d;
246
+ }
247
+ for (i = 0; i < m; ++i) {
248
+ scs_float dy = ABS(sol0->y[i] - sol1->y[i]);
249
+ scs_float ds = ABS(sol0->s[i] - sol1->s[i]);
250
+ if (dy > err_y) err_y = dy;
251
+ if (ds > err_s) err_s = ds;
252
+ }
253
+ err_obj = ABS(info0.pobj - info1.pobj);
254
+
255
+ scs_printf("normalize_qp: x_err=%.2e y_err=%.2e s_err=%.2e "
256
+ "obj_err=%.2e\n", err_x, err_y, err_s, err_obj);
257
+
258
+ mu_assert("normalize_qp: x vectors differ too much", err_x < 1e-4);
259
+ mu_assert("normalize_qp: y vectors differ too much", err_y < 1e-4);
260
+ mu_assert("normalize_qp: s vectors differ too much", err_s < 1e-4);
261
+ mu_assert("normalize_qp: obj values differ too much", err_obj < 1e-5);
262
+
263
+ /* Cleanup */
264
+ SCS(free_sol)(sol0);
265
+ scs_free(d0->A);
266
+ scs_free(d0->P);
267
+ scs_free(k0);
268
+ scs_free(stgs0);
269
+ scs_free(d0);
270
+
271
+ SCS(free_sol)(sol1);
272
+ scs_free(d1->A);
273
+ scs_free(d1->P);
274
+ scs_free(k1);
275
+ scs_free(stgs1);
276
+ scs_free(d1);
277
+
278
+ return 0;
279
+ }