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
@@ -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
- scs_int Anz = src->p[src->n];
18
- ScsMatrix *A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
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, r_max, Anz;
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
- /* detects some errors in A col ptrs: */
47
- Anz = A->p[A->n];
48
- /* Disable this check which is slowish and typically just produces noise. */
49
- /*
50
- if (Anz > 0) {
51
- for (i = 0; i < A->n; ++i) {
52
- if (A->p[i] == A->p[i + 1]) {
53
- scs_printf("WARN: A->p (column pointers) not strictly increasing, "
54
- "column %li empty\n",
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] > r_max) {
71
- r_max = A->i[i];
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
- void SCS(free_scs_matrix)(ScsMatrix *A) {
101
- if (A) {
102
- scs_free(A->x);
103
- scs_free(A->i);
104
- scs_free(A->p);
105
- scs_free(A);
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
- static void compute_ruiz_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
117
- scs_float *Et, ScsConeWork *cone) {
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] = 0.;
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] = 0.;
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 = SCS(norm_inf)(&(A->x[A->p[i]]), A->p[i + 1] - A->p[i]);
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
- static void compute_l2_mats(ScsMatrix *P, ScsMatrix *A, scs_float *Dt,
182
- scs_float *Et, ScsConeWork *cone) {
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] = 0.;
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
- Dt[i] = SQRTF(Dt[i]); /* l2 norm of rows */
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] = 0.;
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 *Dt, scs_float *Et,
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
- /* scale the rows of A with D */
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
- /* scale the rows of P with E */
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
- /* scale the cols of P with E */
269
- for (i = 0; i < P->n; ++i) {
270
- SCS(scale_array)(&(P->x[P->p[i]]), Et[i], P->p[i + 1] - P->p[i]);
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, ScsConeWork *cone) {
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
- }