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
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
/* contains routines common to direct and indirect sparse solvers */
|
|
2
|
+
|
|
3
|
+
/* ======================== Includes / Constants ======================== */
|
|
4
|
+
|
|
2
5
|
#include "scs_matrix.h"
|
|
6
|
+
#include "cones.h"
|
|
3
7
|
#include "linalg.h"
|
|
4
8
|
#include "linsys.h"
|
|
5
9
|
#include "util.h"
|
|
6
10
|
|
|
11
|
+
#include <string.h>
|
|
12
|
+
|
|
7
13
|
#define MIN_NORMALIZATION_FACTOR (1e-4)
|
|
8
14
|
#define MAX_NORMALIZATION_FACTOR (1e4)
|
|
9
15
|
#define NUM_RUIZ_PASSES (25) /* additional passes don't help much */
|
|
10
16
|
#define NUM_L2_PASSES (1) /* do one or zero, not more since not stable */
|
|
11
17
|
|
|
18
|
+
/* ======================== Matrix Copy / Free ======================== */
|
|
19
|
+
|
|
12
20
|
scs_int SCS(copy_matrix)(ScsMatrix **dstp, const ScsMatrix *src) {
|
|
21
|
+
scs_int Anz;
|
|
22
|
+
ScsMatrix *A;
|
|
13
23
|
if (!src) {
|
|
14
24
|
*dstp = SCS_NULL;
|
|
15
25
|
return 1;
|
|
16
26
|
}
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
Anz = src->p[src->n];
|
|
28
|
+
A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
|
|
19
29
|
if (!A) {
|
|
20
30
|
return 0;
|
|
21
31
|
}
|
|
@@ -28,6 +38,10 @@ scs_int SCS(copy_matrix)(ScsMatrix **dstp, const ScsMatrix *src) {
|
|
|
28
38
|
/* A column pointer, size: n+1 */
|
|
29
39
|
A->p = (scs_int *)scs_calloc(src->n + 1, sizeof(scs_int));
|
|
30
40
|
if (!A->x || !A->i || !A->p) {
|
|
41
|
+
scs_free(A->x);
|
|
42
|
+
scs_free(A->i);
|
|
43
|
+
scs_free(A->p);
|
|
44
|
+
scs_free(A);
|
|
31
45
|
return 0;
|
|
32
46
|
}
|
|
33
47
|
memcpy(A->x, src->x, sizeof(scs_float) * Anz);
|
|
@@ -37,45 +51,64 @@ scs_int SCS(copy_matrix)(ScsMatrix **dstp, const ScsMatrix *src) {
|
|
|
37
51
|
return 1;
|
|
38
52
|
}
|
|
39
53
|
|
|
54
|
+
void SCS(free_scs_matrix)(ScsMatrix *A) {
|
|
55
|
+
if (A) {
|
|
56
|
+
scs_free(A->x);
|
|
57
|
+
scs_free(A->i);
|
|
58
|
+
scs_free(A->p);
|
|
59
|
+
scs_free(A);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ======================== Validation ======================== */
|
|
64
|
+
|
|
40
65
|
scs_int SCS(validate_lin_sys)(const ScsMatrix *A, const ScsMatrix *P) {
|
|
41
|
-
scs_int i, j,
|
|
66
|
+
scs_int i, j, Anz, Pnz;
|
|
67
|
+
if (!A) {
|
|
68
|
+
scs_printf("A matrix missing\n");
|
|
69
|
+
return -1;
|
|
70
|
+
}
|
|
71
|
+
if (A->m <= 0 || A->n <= 0) {
|
|
72
|
+
scs_printf("A matrix dimensions must be positive\n");
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
42
75
|
if (!A->x || !A->i || !A->p) {
|
|
43
76
|
scs_printf("data incompletely specified\n");
|
|
44
77
|
return -1;
|
|
45
78
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
(long)i);
|
|
56
|
-
} else if (A->p[i] > A->p[i + 1]) {
|
|
57
|
-
scs_printf("ERROR: A->p (column pointers) decreasing\n");
|
|
58
|
-
return -1;
|
|
59
|
-
}
|
|
79
|
+
if (A->p[0] != 0) {
|
|
80
|
+
scs_printf("A->p[0] must equal 0\n");
|
|
81
|
+
return -1;
|
|
82
|
+
}
|
|
83
|
+
for (j = 0; j < A->n; ++j) {
|
|
84
|
+
if (A->p[j] < 0 || A->p[j] > A->p[j + 1]) {
|
|
85
|
+
scs_printf("A->p (column pointers) must be nonnegative and "
|
|
86
|
+
"nondecreasing\n");
|
|
87
|
+
return -1;
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
|
-
|
|
90
|
+
Anz = A->p[A->n];
|
|
63
91
|
if (((scs_float)Anz / A->m > A->n) || (Anz < 0)) {
|
|
64
92
|
scs_printf("Anz (nonzeros in A) = %li, outside of valid range\n",
|
|
65
93
|
(long)Anz);
|
|
66
94
|
return -1;
|
|
67
95
|
}
|
|
68
|
-
r_max = 0;
|
|
69
96
|
for (i = 0; i < Anz; ++i) {
|
|
70
|
-
if (A->i[i]
|
|
71
|
-
|
|
97
|
+
if (A->i[i] < 0 || A->i[i] >= A->m) {
|
|
98
|
+
scs_printf("A row index %li outside valid range [0, %li]\n",
|
|
99
|
+
(long)A->i[i], (long)A->m - 1);
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
if (!isfinite(A->x[i])) {
|
|
103
|
+
scs_printf("A contains a non-finite entry\n");
|
|
104
|
+
return -1;
|
|
72
105
|
}
|
|
73
|
-
}
|
|
74
|
-
if (r_max > A->m - 1) {
|
|
75
|
-
scs_printf("number of rows in A inconsistent with input dimension\n");
|
|
76
|
-
return -1;
|
|
77
106
|
}
|
|
78
107
|
if (P) {
|
|
108
|
+
if (!P->x || !P->i || !P->p) {
|
|
109
|
+
scs_printf("P matrix incompletely specified\n");
|
|
110
|
+
return -1;
|
|
111
|
+
}
|
|
79
112
|
if (P->n != A->n) {
|
|
80
113
|
scs_printf("P dimension = %li, inconsistent with n = %li\n", (long)P->n,
|
|
81
114
|
(long)A->n);
|
|
@@ -85,27 +118,114 @@ scs_int SCS(validate_lin_sys)(const ScsMatrix *A, const ScsMatrix *P) {
|
|
|
85
118
|
scs_printf("P is not square\n");
|
|
86
119
|
return -1;
|
|
87
120
|
}
|
|
121
|
+
if (P->p[0] != 0) {
|
|
122
|
+
scs_printf("P->p[0] must equal 0\n");
|
|
123
|
+
return -1;
|
|
124
|
+
}
|
|
125
|
+
for (j = 0; j < P->n; ++j) {
|
|
126
|
+
if (P->p[j] < 0 || P->p[j] > P->p[j + 1]) {
|
|
127
|
+
scs_printf("P->p (column pointers) must be nonnegative and "
|
|
128
|
+
"nondecreasing\n");
|
|
129
|
+
return -1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
Pnz = P->p[P->n];
|
|
133
|
+
if (((scs_float)Pnz / P->m > P->n) || (Pnz < 0)) {
|
|
134
|
+
scs_printf("Pnz (nonzeros in P) = %li, outside of valid range\n",
|
|
135
|
+
(long)Pnz);
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
88
138
|
for (j = 0; j < P->n; j++) { /* cols */
|
|
89
139
|
for (i = P->p[j]; i < P->p[j + 1]; i++) {
|
|
140
|
+
if (P->i[i] < 0 || P->i[i] >= P->n) {
|
|
141
|
+
scs_printf("P row index %li outside valid range [0, %li]\n",
|
|
142
|
+
(long)P->i[i], (long)P->n - 1);
|
|
143
|
+
return -1;
|
|
144
|
+
}
|
|
90
145
|
if (P->i[i] > j) { /* if row > */
|
|
91
146
|
scs_printf("P is not upper triangular\n");
|
|
92
147
|
return -1;
|
|
93
148
|
}
|
|
149
|
+
if (!isfinite(P->x[i])) {
|
|
150
|
+
scs_printf("P contains a non-finite entry\n");
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
94
153
|
}
|
|
95
154
|
}
|
|
96
155
|
}
|
|
97
156
|
return 0;
|
|
98
157
|
}
|
|
99
158
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
159
|
+
/* ======================== Matrix-Vector Products ======================== */
|
|
160
|
+
|
|
161
|
+
void SCS(accum_by_atrans)(const ScsMatrix *A, const scs_float *x,
|
|
162
|
+
scs_float *y) {
|
|
163
|
+
/* y += A'*x
|
|
164
|
+
A in column compressed format
|
|
165
|
+
parallelizes over columns (rows of A')
|
|
166
|
+
*/
|
|
167
|
+
scs_int p, j;
|
|
168
|
+
scs_int c1, c2;
|
|
169
|
+
scs_float yj;
|
|
170
|
+
scs_int n = A->n;
|
|
171
|
+
scs_int *Ap = A->p;
|
|
172
|
+
scs_int *Ai = A->i;
|
|
173
|
+
scs_float *Ax = A->x;
|
|
174
|
+
#ifdef _OPENMP
|
|
175
|
+
#pragma omp parallel for private(p, c1, c2, yj)
|
|
176
|
+
#endif
|
|
177
|
+
for (j = 0; j < n; j++) {
|
|
178
|
+
yj = y[j];
|
|
179
|
+
c1 = Ap[j];
|
|
180
|
+
c2 = Ap[j + 1];
|
|
181
|
+
for (p = c1; p < c2; p++) {
|
|
182
|
+
yj += Ax[p] * x[Ai[p]];
|
|
183
|
+
}
|
|
184
|
+
y[j] = yj;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
void SCS(accum_by_a)(const ScsMatrix *A, const scs_float *x, scs_float *y) {
|
|
189
|
+
/*y += A*x
|
|
190
|
+
A in column compressed format
|
|
191
|
+
*/
|
|
192
|
+
scs_int p, j, i;
|
|
193
|
+
scs_int n = A->n;
|
|
194
|
+
scs_int *Ap = A->p;
|
|
195
|
+
scs_int *Ai = A->i;
|
|
196
|
+
scs_float *Ax = A->x;
|
|
197
|
+
for (j = 0; j < n; j++) { /* col */
|
|
198
|
+
for (p = Ap[j]; p < Ap[j + 1]; p++) {
|
|
199
|
+
i = Ai[p]; /* row */
|
|
200
|
+
y[i] += Ax[p] * x[j];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Since P is upper triangular need to be clever here */
|
|
206
|
+
void SCS(accum_by_p)(const ScsMatrix *P, const scs_float *x, scs_float *y) {
|
|
207
|
+
/* returns y += P x where P is stored upper triangular (CSC).
|
|
208
|
+
* Single pass: each stored entry (i,j) contributes to both y[i] (upper)
|
|
209
|
+
* and y[j] (symmetric lower), halving NNZ traversals vs two-pass approach. */
|
|
210
|
+
scs_int p, j;
|
|
211
|
+
scs_int n = P->n;
|
|
212
|
+
scs_int *Pp = P->p;
|
|
213
|
+
scs_int *Pi = P->i;
|
|
214
|
+
scs_float *Px = P->x;
|
|
215
|
+
for (j = 0; j < n; j++) {
|
|
216
|
+
for (p = Pp[j]; p < Pp[j + 1]; p++) {
|
|
217
|
+
scs_int i = Pi[p];
|
|
218
|
+
scs_float val = Px[p];
|
|
219
|
+
y[i] += val * x[j]; /* upper triangle + diagonal */
|
|
220
|
+
if (i != j) {
|
|
221
|
+
y[j] += val * x[i]; /* symmetric lower triangle */
|
|
222
|
+
}
|
|
223
|
+
}
|
|
106
224
|
}
|
|
107
225
|
}
|
|
108
226
|
|
|
227
|
+
/* ======================== Normalization Internals ======================== */
|
|
228
|
+
|
|
109
229
|
static inline scs_float apply_limit(scs_float x) {
|
|
110
230
|
/* need to bound to 1 for cols/rows of all zeros, otherwise blows up */
|
|
111
231
|
x = x < MIN_NORMALIZATION_FACTOR ? 1.0 : x;
|
|
@@ -113,18 +233,27 @@ static inline scs_float apply_limit(scs_float x) {
|
|
|
113
233
|
return x;
|
|
114
234
|
}
|
|
115
235
|
|
|
116
|
-
|
|
117
|
-
|
|
236
|
+
/* Equilibrates the full homogeneous-embedding operator
|
|
237
|
+
* Q = [ 0 A' c ]
|
|
238
|
+
* [-A 0 b ]
|
|
239
|
+
* [-c' -b' 0 ]
|
|
240
|
+
* with the symmetric scaling diag(E, D, st): |Q| is symmetric so row and
|
|
241
|
+
* column norms coincide, b enters the row norms, c the column norms, and
|
|
242
|
+
* the scalar st on the tau slot replaces the old one-shot clamped sigma
|
|
243
|
+
* heuristic in normalize_b_c. bt/ct are the running scaled copies of b/c
|
|
244
|
+
* maintained by the caller; *st_out receives this pass's tau scaling. */
|
|
245
|
+
static void compute_ruiz_mats(ScsMatrix *P, ScsMatrix *A, const scs_float *bt,
|
|
246
|
+
const scs_float *ct, scs_float *Dt,
|
|
247
|
+
scs_float *Et, scs_float *st_out,
|
|
248
|
+
ScsConeWork *cone) {
|
|
118
249
|
scs_int i, j, kk;
|
|
119
250
|
scs_float wrk;
|
|
120
|
-
scs_float nm_a_col;
|
|
121
251
|
|
|
122
252
|
/**************************** D ****************************/
|
|
123
253
|
|
|
124
|
-
/* initialize D */
|
|
254
|
+
/* initialize D with the tau-column contribution */
|
|
125
255
|
for (i = 0; i < A->m; ++i) {
|
|
126
|
-
Dt[i] =
|
|
127
|
-
/* Dt[i] = ABS(b[i]); */
|
|
256
|
+
Dt[i] = ABS(bt[i]);
|
|
128
257
|
}
|
|
129
258
|
|
|
130
259
|
/* calculate row norms */
|
|
@@ -135,7 +264,7 @@ static void compute_ruiz_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
135
264
|
}
|
|
136
265
|
|
|
137
266
|
/* accumulate D across each cone */
|
|
138
|
-
SCS(enforce_cone_boundaries)(cone, Dt, &SCS(norm_inf));
|
|
267
|
+
SCS(enforce_cone_boundaries)(cone, Dt, &SCS(norm_inf), 0);
|
|
139
268
|
|
|
140
269
|
/* invert temporary vec to form D */
|
|
141
270
|
for (i = 0; i < A->m; ++i) {
|
|
@@ -145,10 +274,9 @@ static void compute_ruiz_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
145
274
|
|
|
146
275
|
/**************************** E ****************************/
|
|
147
276
|
|
|
148
|
-
/* initialize E */
|
|
277
|
+
/* initialize E with the tau-row contribution */
|
|
149
278
|
for (i = 0; i < A->n; ++i) {
|
|
150
|
-
Et[i] =
|
|
151
|
-
/* Et[i] = ABS(c[i]); */
|
|
279
|
+
Et[i] = ABS(ct[i]);
|
|
152
280
|
}
|
|
153
281
|
|
|
154
282
|
/* TODO: test not using P to determine scaling */
|
|
@@ -169,40 +297,76 @@ static void compute_ruiz_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
169
297
|
}
|
|
170
298
|
}
|
|
171
299
|
|
|
172
|
-
/* calculate col norms, E
|
|
300
|
+
/* calculate col norms, E — inline the norm_inf to avoid n BLAS call
|
|
301
|
+
* overheads (Fortran ABI, pointer args, 1-based return) for short cols. */
|
|
173
302
|
for (i = 0; i < A->n; ++i) {
|
|
174
|
-
nm_a_col =
|
|
303
|
+
scs_float nm_a_col = 0.0, tmp;
|
|
304
|
+
for (j = A->p[i]; j < A->p[i + 1]; ++j) {
|
|
305
|
+
tmp = ABS(A->x[j]);
|
|
306
|
+
if (tmp > nm_a_col) nm_a_col = tmp;
|
|
307
|
+
}
|
|
175
308
|
Et[i] = MAX(Et[i], nm_a_col);
|
|
176
309
|
Et[i] = SQRTF(apply_limit(Et[i]));
|
|
177
310
|
Et[i] = SAFEDIV_POS(1.0, Et[i]);
|
|
178
311
|
}
|
|
312
|
+
|
|
313
|
+
/************************** tau ****************************/
|
|
314
|
+
wrk = MAX(SCS(norm_inf)(bt, A->m), SCS(norm_inf)(ct, A->n));
|
|
315
|
+
*st_out = SAFEDIV_POS(1.0, SQRTF(apply_limit(wrk)));
|
|
179
316
|
}
|
|
180
317
|
|
|
181
|
-
|
|
182
|
-
|
|
318
|
+
/* The l2 pass normalizes by RMS (root-mean-square over stored entries)
|
|
319
|
+
* rather than the raw l2 norm. The raw l2 norm of a row/column grows like
|
|
320
|
+
* sqrt(nnz) times its typical entry magnitude, so dividing by it crushes
|
|
321
|
+
* dense or flat rows/columns by an extra nnz^(1/4) factor relative to
|
|
322
|
+
* sparse ones -- a density artifact, not a magnitude signal (the Ruiz
|
|
323
|
+
* linf passes are density-blind). RMS measures typical entry magnitude
|
|
324
|
+
* while keeping the l2 pass's averaging robustness. Under the stacked
|
|
325
|
+
* equilibration the b/c entries count toward their row/column averages,
|
|
326
|
+
* and the tau norms average over their own lengths.
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
static void compute_l2_mats(ScsMatrix *P, ScsMatrix *A, const scs_float *bt,
|
|
330
|
+
const scs_float *ct, scs_float *Dt, scs_float *Et,
|
|
331
|
+
scs_float *st_out, ScsConeWork *cone) {
|
|
183
332
|
scs_int i, j, kk;
|
|
333
|
+
scs_int rms = 1;
|
|
184
334
|
scs_float wrk;
|
|
335
|
+
scs_float *rcnt = SCS_NULL, *ecnt = SCS_NULL;
|
|
336
|
+
rcnt = (scs_float *)scs_calloc(A->m, sizeof(scs_float));
|
|
337
|
+
ecnt = (scs_float *)scs_calloc(A->n, sizeof(scs_float));
|
|
338
|
+
if (!rcnt || !ecnt) {
|
|
339
|
+
scs_free(rcnt);
|
|
340
|
+
scs_free(ecnt);
|
|
341
|
+
rcnt = ecnt = SCS_NULL;
|
|
342
|
+
rms = 0; /* fall back to plain l2 on alloc failure */
|
|
343
|
+
}
|
|
185
344
|
|
|
186
345
|
/**************************** D ****************************/
|
|
187
346
|
|
|
188
|
-
/* initialize D */
|
|
347
|
+
/* initialize D with the tau-column contribution */
|
|
189
348
|
for (i = 0; i < A->m; ++i) {
|
|
190
|
-
Dt[i] =
|
|
191
|
-
/* Dt[i] = b[i] * b[i]; */
|
|
349
|
+
Dt[i] = bt[i] * bt[i];
|
|
192
350
|
}
|
|
193
351
|
|
|
194
352
|
/* calculate row norms */
|
|
195
353
|
for (i = 0; i < A->n; ++i) {
|
|
196
354
|
for (j = A->p[i]; j < A->p[i + 1]; ++j) {
|
|
197
355
|
Dt[A->i[j]] += A->x[j] * A->x[j];
|
|
356
|
+
if (rms) {
|
|
357
|
+
rcnt[A->i[j]] += 1.;
|
|
358
|
+
}
|
|
198
359
|
}
|
|
199
360
|
}
|
|
200
361
|
for (i = 0; i < A->m; ++i) {
|
|
201
|
-
|
|
362
|
+
if (rms) {
|
|
363
|
+
Dt[i] /= (rcnt[i] + 1.); /* +1 for the bt (tau-column) entry */
|
|
364
|
+
}
|
|
365
|
+
Dt[i] = SQRTF(Dt[i]); /* l2 (or rms) norm of rows */
|
|
202
366
|
}
|
|
203
367
|
|
|
204
368
|
/* accumulate D across each cone */
|
|
205
|
-
SCS(enforce_cone_boundaries)(cone, Dt, &SCS(mean));
|
|
369
|
+
SCS(enforce_cone_boundaries)(cone, Dt, &SCS(mean), 0);
|
|
206
370
|
|
|
207
371
|
for (i = 0; i < A->m; ++i) {
|
|
208
372
|
Dt[i] = SQRTF(apply_limit(Dt[i]));
|
|
@@ -211,10 +375,9 @@ static void compute_l2_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
211
375
|
|
|
212
376
|
/**************************** E ****************************/
|
|
213
377
|
|
|
214
|
-
/* initialize E */
|
|
378
|
+
/* initialize E with the tau-row contribution */
|
|
215
379
|
for (i = 0; i < A->n; ++i) {
|
|
216
|
-
Et[i] =
|
|
217
|
-
/* Et[i] = c[i] * c[i]; */
|
|
380
|
+
Et[i] = ct[i] * ct[i];
|
|
218
381
|
}
|
|
219
382
|
|
|
220
383
|
/* TODO: test not using P to determine scaling */
|
|
@@ -228,8 +391,14 @@ static void compute_l2_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
228
391
|
i = P->i[kk]; /* row */
|
|
229
392
|
wrk = P->x[kk] * P->x[kk];
|
|
230
393
|
Et[j] += wrk;
|
|
394
|
+
if (rms) {
|
|
395
|
+
ecnt[j] += 1.;
|
|
396
|
+
}
|
|
231
397
|
if (i != j) {
|
|
232
398
|
Et[i] += wrk;
|
|
399
|
+
if (rms) {
|
|
400
|
+
ecnt[i] += 1.;
|
|
401
|
+
}
|
|
233
402
|
}
|
|
234
403
|
}
|
|
235
404
|
}
|
|
@@ -238,37 +407,56 @@ static void compute_l2_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
|
|
|
238
407
|
/* calculate col norms, E */
|
|
239
408
|
for (i = 0; i < A->n; ++i) {
|
|
240
409
|
Et[i] += SCS(norm_sq)(&(A->x[A->p[i]]), A->p[i + 1] - A->p[i]);
|
|
410
|
+
if (rms) {
|
|
411
|
+
Et[i] /= (ecnt[i] + (scs_float)(A->p[i + 1] - A->p[i]) + 1.);
|
|
412
|
+
}
|
|
241
413
|
Et[i] = SQRTF(apply_limit(SQRTF(Et[i])));
|
|
242
414
|
Et[i] = SAFEDIV_POS(1.0, Et[i]);
|
|
243
415
|
}
|
|
416
|
+
|
|
417
|
+
/************************** tau ****************************/
|
|
418
|
+
if (rms) {
|
|
419
|
+
wrk = MAX(SCS(norm_2)(bt, A->m) / SQRTF((scs_float)A->m),
|
|
420
|
+
SCS(norm_2)(ct, A->n) / SQRTF((scs_float)A->n));
|
|
421
|
+
} else {
|
|
422
|
+
wrk = MAX(SCS(norm_2)(bt, A->m), SCS(norm_2)(ct, A->n));
|
|
423
|
+
}
|
|
424
|
+
*st_out = SAFEDIV_POS(1.0, SQRTF(apply_limit(wrk)));
|
|
425
|
+
scs_free(rcnt);
|
|
426
|
+
scs_free(ecnt);
|
|
244
427
|
}
|
|
245
428
|
|
|
246
|
-
static void rescale(ScsMatrix *P, ScsMatrix *A, scs_float *
|
|
429
|
+
static void rescale(ScsMatrix *P, ScsMatrix *A, scs_float *bt, scs_float *ct,
|
|
430
|
+
scs_float st, scs_float *Dt, scs_float *Et,
|
|
247
431
|
ScsScaling *scal, ScsConeWork *cone) {
|
|
248
432
|
scs_int i, j;
|
|
249
|
-
/*
|
|
433
|
+
/* Fuse row and col scaling of A: A[i,j] *= Dt[i] * Et[j].
|
|
434
|
+
* Single NNZ pass replaces two separate passes. */
|
|
250
435
|
for (i = 0; i < A->n; ++i) {
|
|
436
|
+
scs_float ei = Et[i];
|
|
251
437
|
for (j = A->p[i]; j < A->p[i + 1]; ++j) {
|
|
252
|
-
A->x[j] *= Dt[A->i[j]];
|
|
438
|
+
A->x[j] *= Dt[A->i[j]] * ei;
|
|
253
439
|
}
|
|
254
440
|
}
|
|
255
441
|
|
|
256
|
-
/* scale the cols of A with E */
|
|
257
|
-
for (i = 0; i < A->n; ++i) {
|
|
258
|
-
SCS(scale_array)(&(A->x[A->p[i]]), Et[i], A->p[i + 1] - A->p[i]);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
442
|
if (P) {
|
|
262
|
-
/*
|
|
443
|
+
/* Fuse row and col scaling of P: P[i,j] *= Et[i] * Et[j]. */
|
|
263
444
|
for (i = 0; i < P->n; ++i) {
|
|
445
|
+
scs_float ei = Et[i];
|
|
264
446
|
for (j = P->p[i]; j < P->p[i + 1]; ++j) {
|
|
265
|
-
P->x[j] *= Et[P->i[j]];
|
|
447
|
+
P->x[j] *= Et[P->i[j]] * ei;
|
|
266
448
|
}
|
|
267
449
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Scale the running b/c copies by their row/col factors and the tau
|
|
453
|
+
* scalar (the b entries live at (row i, col tau), c at (row tau, col j)
|
|
454
|
+
* of the stacked operator). */
|
|
455
|
+
for (i = 0; i < A->m; ++i) {
|
|
456
|
+
bt[i] *= Dt[i] * st;
|
|
457
|
+
}
|
|
458
|
+
for (i = 0; i < A->n; ++i) {
|
|
459
|
+
ct[i] *= Et[i] * st;
|
|
272
460
|
}
|
|
273
461
|
|
|
274
462
|
/* Accumulate scaling */
|
|
@@ -278,6 +466,7 @@ static void rescale(ScsMatrix *P, ScsMatrix *A, scs_float *Dt, scs_float *Et,
|
|
|
278
466
|
for (i = 0; i < A->n; ++i) {
|
|
279
467
|
scal->E[i] *= Et[i];
|
|
280
468
|
}
|
|
469
|
+
scal->tau_scale *= st;
|
|
281
470
|
|
|
282
471
|
/* no need to scale P since later primal_scale = dual_scale */
|
|
283
472
|
/*
|
|
@@ -288,6 +477,8 @@ static void rescale(ScsMatrix *P, ScsMatrix *A, scs_float *Dt, scs_float *Et,
|
|
|
288
477
|
*/
|
|
289
478
|
}
|
|
290
479
|
|
|
480
|
+
/* ======================== Normalization Public API ======================== */
|
|
481
|
+
|
|
291
482
|
/* Will rescale as P -> EPE, A -> DAE in-place.
|
|
292
483
|
* Essentially trying to rescale this matrix:
|
|
293
484
|
*
|
|
@@ -310,13 +501,37 @@ static void rescale(ScsMatrix *P, ScsMatrix *A, scs_float *Dt, scs_float *Et,
|
|
|
310
501
|
* The main complication is that D has to respect cone boundaries.
|
|
311
502
|
*
|
|
312
503
|
*/
|
|
313
|
-
ScsScaling *SCS(normalize_a_p)(ScsMatrix *P, ScsMatrix *A,
|
|
504
|
+
ScsScaling *SCS(normalize_a_p)(ScsMatrix *P, ScsMatrix *A, const scs_float *b,
|
|
505
|
+
const scs_float *c, ScsConeWork *cone) {
|
|
314
506
|
scs_int i;
|
|
507
|
+
scs_float st;
|
|
315
508
|
ScsScaling *scal = (ScsScaling *)scs_calloc(1, sizeof(ScsScaling));
|
|
316
509
|
scs_float *Dt = (scs_float *)scs_calloc(A->m, sizeof(scs_float));
|
|
317
510
|
scs_float *Et = (scs_float *)scs_calloc(A->n, sizeof(scs_float));
|
|
511
|
+
/* running scaled copies of b, c (originals must not be modified here;
|
|
512
|
+
* their actual scaling happens per-solve in normalize_b_c) */
|
|
513
|
+
scs_float *bt = (scs_float *)scs_malloc(A->m * sizeof(scs_float));
|
|
514
|
+
scs_float *ct = (scs_float *)scs_malloc(A->n * sizeof(scs_float));
|
|
515
|
+
if (!scal || !Dt || !Et || !bt || !ct) {
|
|
516
|
+
scs_free(scal);
|
|
517
|
+
scs_free(Dt);
|
|
518
|
+
scs_free(Et);
|
|
519
|
+
scs_free(bt);
|
|
520
|
+
scs_free(ct);
|
|
521
|
+
return SCS_NULL;
|
|
522
|
+
}
|
|
523
|
+
memcpy(bt, b, A->m * sizeof(scs_float));
|
|
524
|
+
memcpy(ct, c, A->n * sizeof(scs_float));
|
|
318
525
|
scal->D = (scs_float *)scs_calloc(A->m, sizeof(scs_float));
|
|
319
526
|
scal->E = (scs_float *)scs_calloc(A->n, sizeof(scs_float));
|
|
527
|
+
if (!scal->D || !scal->E) {
|
|
528
|
+
scs_free(scal->D);
|
|
529
|
+
scs_free(scal->E);
|
|
530
|
+
scs_free(scal);
|
|
531
|
+
scs_free(Dt);
|
|
532
|
+
scs_free(Et);
|
|
533
|
+
return SCS_NULL;
|
|
534
|
+
}
|
|
320
535
|
|
|
321
536
|
#if VERBOSITY > 5
|
|
322
537
|
SCS(timer) normalize_timer;
|
|
@@ -335,16 +550,20 @@ ScsScaling *SCS(normalize_a_p)(ScsMatrix *P, ScsMatrix *A, ScsConeWork *cone) {
|
|
|
335
550
|
}
|
|
336
551
|
scal->primal_scale = 1.;
|
|
337
552
|
scal->dual_scale = 1.;
|
|
553
|
+
scal->tau_scale = 1.;
|
|
338
554
|
for (i = 0; i < NUM_RUIZ_PASSES; ++i) {
|
|
339
|
-
compute_ruiz_mats(P, A, Dt, Et, cone);
|
|
340
|
-
rescale(P, A, Dt, Et, scal, cone);
|
|
555
|
+
compute_ruiz_mats(P, A, bt, ct, Dt, Et, &st, cone);
|
|
556
|
+
rescale(P, A, bt, ct, st, Dt, Et, scal, cone);
|
|
341
557
|
}
|
|
342
558
|
for (i = 0; i < NUM_L2_PASSES; ++i) {
|
|
343
|
-
compute_l2_mats(P, A, Dt, Et, cone);
|
|
344
|
-
rescale(P, A, Dt, Et, scal, cone);
|
|
559
|
+
compute_l2_mats(P, A, bt, ct, Dt, Et, &st, cone);
|
|
560
|
+
rescale(P, A, bt, ct, st, Dt, Et, scal, cone);
|
|
345
561
|
}
|
|
562
|
+
|
|
346
563
|
scs_free(Dt);
|
|
347
564
|
scs_free(Et);
|
|
565
|
+
scs_free(bt);
|
|
566
|
+
scs_free(ct);
|
|
348
567
|
|
|
349
568
|
#if VERBOSITY > 5
|
|
350
569
|
scs_printf("finished normalizing A and P, time: %1.2es\n",
|
|
@@ -360,96 +579,3 @@ ScsScaling *SCS(normalize_a_p)(ScsMatrix *P, ScsMatrix *A, ScsConeWork *cone) {
|
|
|
360
579
|
#endif
|
|
361
580
|
return scal;
|
|
362
581
|
}
|
|
363
|
-
|
|
364
|
-
/*
|
|
365
|
-
void SCS(un_normalize_a_p)(ScsMatrix *A, ScsMatrix *P, const ScsScaling *scal) {
|
|
366
|
-
scs_int i, j;
|
|
367
|
-
scs_float *D = scal->D;
|
|
368
|
-
scs_float *E = scal->E;
|
|
369
|
-
for (i = 0; i < A->n; ++i) {
|
|
370
|
-
SCS(scale_array)
|
|
371
|
-
(&(A->x[A->p[i]]), 1. / E[i], A->p[i + 1] - A->p[i]);
|
|
372
|
-
}
|
|
373
|
-
for (i = 0; i < A->n; ++i) {
|
|
374
|
-
for (j = A->p[i]; j < A->p[i + 1]; ++j) {
|
|
375
|
-
A->x[j] /= D[A->i[j]];
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
if (P) {
|
|
379
|
-
for (i = 0; i < P->n; ++i) {
|
|
380
|
-
SCS(scale_array)
|
|
381
|
-
(&(P->x[P->p[i]]), 1. / E[i], P->p[i + 1] - P->p[i]);
|
|
382
|
-
}
|
|
383
|
-
for (i = 0; i < P->n; ++i) {
|
|
384
|
-
for (j = P->p[i]; j < P->p[i + 1]; ++j) {
|
|
385
|
-
P->x[j] /= E[P->i[j]];
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
*/
|
|
391
|
-
|
|
392
|
-
void SCS(accum_by_atrans)(const ScsMatrix *A, const scs_float *x,
|
|
393
|
-
scs_float *y) {
|
|
394
|
-
/* y += A'*x
|
|
395
|
-
A in column compressed format
|
|
396
|
-
parallelizes over columns (rows of A')
|
|
397
|
-
*/
|
|
398
|
-
scs_int p, j;
|
|
399
|
-
scs_int c1, c2;
|
|
400
|
-
scs_float yj;
|
|
401
|
-
scs_int n = A->n;
|
|
402
|
-
scs_int *Ap = A->p;
|
|
403
|
-
scs_int *Ai = A->i;
|
|
404
|
-
scs_float *Ax = A->x;
|
|
405
|
-
#ifdef _OPENMP
|
|
406
|
-
#pragma omp parallel for private(p, c1, c2, yj)
|
|
407
|
-
#endif
|
|
408
|
-
for (j = 0; j < n; j++) {
|
|
409
|
-
yj = y[j];
|
|
410
|
-
c1 = Ap[j];
|
|
411
|
-
c2 = Ap[j + 1];
|
|
412
|
-
for (p = c1; p < c2; p++) {
|
|
413
|
-
yj += Ax[p] * x[Ai[p]];
|
|
414
|
-
}
|
|
415
|
-
y[j] = yj;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
void SCS(accum_by_a)(const ScsMatrix *A, const scs_float *x, scs_float *y) {
|
|
420
|
-
/*y += A*x
|
|
421
|
-
A in column compressed format
|
|
422
|
-
*/
|
|
423
|
-
scs_int p, j, i;
|
|
424
|
-
scs_int n = A->n;
|
|
425
|
-
scs_int *Ap = A->p;
|
|
426
|
-
scs_int *Ai = A->i;
|
|
427
|
-
scs_float *Ax = A->x;
|
|
428
|
-
for (j = 0; j < n; j++) { /* col */
|
|
429
|
-
for (p = Ap[j]; p < Ap[j + 1]; p++) {
|
|
430
|
-
i = Ai[p]; /* row */
|
|
431
|
-
y[i] += Ax[p] * x[j];
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/* Since P is upper triangular need to be clever here */
|
|
437
|
-
void SCS(accum_by_p)(const ScsMatrix *P, const scs_float *x, scs_float *y) {
|
|
438
|
-
/* returns y += P x */
|
|
439
|
-
scs_int p, j, i;
|
|
440
|
-
scs_int n = P->n;
|
|
441
|
-
scs_int *Pp = P->p;
|
|
442
|
-
scs_int *Pi = P->i;
|
|
443
|
-
scs_float *Px = P->x;
|
|
444
|
-
/* y += P_upper x but skip diagonal entries*/
|
|
445
|
-
for (j = 0; j < n; j++) { /* col */
|
|
446
|
-
for (p = Pp[j]; p < Pp[j + 1]; p++) {
|
|
447
|
-
i = Pi[p]; /* row */
|
|
448
|
-
if (i != j) { /* skip the diagonal */
|
|
449
|
-
y[i] += Px[p] * x[j];
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
/* y += P_lower x */
|
|
454
|
-
SCS(accum_by_atrans)(P, x, y);
|
|
455
|
-
}
|