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
@@ -33,49 +33,92 @@ int scs_is_interrupted(void) {
33
33
  #elif (defined _WIN32 || _WIN64 || defined _WINDLL)
34
34
  #include <windows.h>
35
35
 
36
- static int int_detected;
36
+ static volatile LONG int_detected;
37
+ static INIT_ONCE ctrlc_init_once = INIT_ONCE_STATIC_INIT;
38
+ static CRITICAL_SECTION ctrlc_cs;
39
+ static int listener_count = 0;
40
+
41
+ static BOOL CALLBACK init_ctrlc_cs(PINIT_ONCE once, PVOID param, PVOID *ctx) {
42
+ (void)once;
43
+ (void)param;
44
+ (void)ctx;
45
+ InitializeCriticalSection(&ctrlc_cs);
46
+ return TRUE;
47
+ }
48
+
37
49
  static BOOL WINAPI scs_handle_ctrlc(DWORD dwCtrlType) {
38
50
  if (dwCtrlType != CTRL_C_EVENT) {
39
51
  return FALSE;
40
52
  }
41
- int_detected = 1;
53
+ InterlockedExchange(&int_detected, 1);
42
54
  return TRUE;
43
55
  }
44
56
 
45
57
  void scs_start_interrupt_listener(void) {
46
- int_detected = 0;
47
- SetConsoleCtrlHandler(scs_handle_ctrlc, TRUE);
58
+ InitOnceExecuteOnce(&ctrlc_init_once, init_ctrlc_cs, NULL, NULL);
59
+ EnterCriticalSection(&ctrlc_cs);
60
+ if (listener_count == 0) {
61
+ InterlockedExchange(&int_detected, 0);
62
+ SetConsoleCtrlHandler(scs_handle_ctrlc, TRUE);
63
+ }
64
+ listener_count++;
65
+ LeaveCriticalSection(&ctrlc_cs);
48
66
  }
49
67
 
50
68
  void scs_end_interrupt_listener(void) {
51
- SetConsoleCtrlHandler(scs_handle_ctrlc, FALSE);
69
+ InitOnceExecuteOnce(&ctrlc_init_once, init_ctrlc_cs, NULL, NULL);
70
+ EnterCriticalSection(&ctrlc_cs);
71
+ if (listener_count > 0) {
72
+ listener_count--;
73
+ if (listener_count == 0) {
74
+ SetConsoleCtrlHandler(scs_handle_ctrlc, FALSE);
75
+ }
76
+ }
77
+ LeaveCriticalSection(&ctrlc_cs);
52
78
  }
53
79
 
54
80
  int scs_is_interrupted(void) {
55
- return int_detected;
81
+ return (int)InterlockedCompareExchange(&int_detected, 0, 0);
56
82
  }
57
83
 
58
84
  #else /* Unix */
59
85
 
60
86
  #include <signal.h>
61
- static int int_detected;
62
- struct sigaction oact;
87
+ #include <pthread.h>
88
+
89
+ static volatile sig_atomic_t int_detected;
90
+ static struct sigaction oact;
91
+ static pthread_mutex_t ctrlc_mutex = PTHREAD_MUTEX_INITIALIZER;
92
+ static int listener_count = 0;
93
+
63
94
  static void scs_handle_ctrlc(int dummy) {
64
95
  int_detected = dummy ? dummy : -1;
65
96
  }
66
97
 
67
98
  void scs_start_interrupt_listener(void) {
68
- struct sigaction act;
69
- int_detected = 0;
70
- act.sa_flags = 0;
71
- sigemptyset(&act.sa_mask);
72
- act.sa_handler = scs_handle_ctrlc;
73
- sigaction(SIGINT, &act, &oact);
99
+ pthread_mutex_lock(&ctrlc_mutex);
100
+ if (listener_count == 0) {
101
+ struct sigaction act;
102
+ int_detected = 0;
103
+ act.sa_flags = 0;
104
+ sigemptyset(&act.sa_mask);
105
+ act.sa_handler = scs_handle_ctrlc;
106
+ sigaction(SIGINT, &act, &oact);
107
+ }
108
+ listener_count++;
109
+ pthread_mutex_unlock(&ctrlc_mutex);
74
110
  }
75
111
 
76
112
  void scs_end_interrupt_listener(void) {
77
- struct sigaction act;
78
- sigaction(SIGINT, &oact, &act);
113
+ pthread_mutex_lock(&ctrlc_mutex);
114
+ if (listener_count > 0) {
115
+ listener_count--;
116
+ if (listener_count == 0) {
117
+ struct sigaction act;
118
+ sigaction(SIGINT, &oact, &act);
119
+ }
120
+ }
121
+ pthread_mutex_unlock(&ctrlc_mutex);
79
122
  }
80
123
 
81
124
  int scs_is_interrupted(void) {
@@ -1,3 +1,8 @@
1
+ /*
2
+ * Exponential cone projection via Newton's method.
3
+ * Handles both primal and dual exponential cones using Moreau decomposition.
4
+ */
5
+
1
6
  #include "cones.h"
2
7
  #include "glbopts.h"
3
8
  #include "linalg.h"
@@ -25,14 +30,29 @@ static inline void _copy(scs_float *dest, const scs_float *src) {
25
30
  dest[2] = src[2];
26
31
  }
27
32
 
28
- /* As defined in Friberg, 2021 (multiplied by positive polynomial) */
29
- static void hfun(const scs_float *v0, scs_float rho, scs_float *f,
30
- scs_float *df) {
33
+ static inline scs_float norm_diff_sq_3(const scs_float *a, const scs_float *b) {
34
+ scs_float d0 = a[0] - b[0];
35
+ scs_float d1 = a[1] - b[1];
36
+ scs_float d2 = a[2] - b[2];
37
+ return d0 * d0 + d1 * d1 + d2 * d2;
38
+ }
39
+
40
+ /* As defined in Friberg, 2021 (multiplied by positive polynomial). */
41
+ static scs_float hfun_f(const scs_float *v0, scs_float rho) {
42
+ scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
43
+ scs_float exprho = EXPF(rho);
44
+ scs_float expnegrho = 1.0 / exprho; /* EXPF(-rho) == 1/EXPF(rho) */
45
+
46
+ return ((rho - 1) * r0 + s0) * exprho - (r0 - rho * s0) * expnegrho -
47
+ (rho * (rho - 1) + 1) * t0;
48
+ }
49
+
50
+ static void hfun_fd(const scs_float *v0, scs_float rho, scs_float *f,
51
+ scs_float *df) {
31
52
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
32
53
  scs_float exprho = EXPF(rho);
33
- scs_float expnegrho = EXPF(-rho);
54
+ scs_float expnegrho = 1.0 / exprho; /* EXPF(-rho) == 1/EXPF(rho) */
34
55
 
35
- /* Function value at v0 */
36
56
  *f = ((rho - 1) * r0 + s0) * exprho - (r0 - rho * s0) * expnegrho -
37
57
  (rho * (rho - 1) + 1) * t0;
38
58
 
@@ -47,14 +67,14 @@ static scs_float root_search_binary(const scs_float *v0, scs_float xl,
47
67
  const scs_float EPS = 1e-12; /* looser tolerance for binary search */
48
68
  const scs_int MAXITER = 40;
49
69
  scs_int i;
50
- scs_float x_plus = x, f, df;
70
+ scs_float x_plus = x, f;
51
71
 
52
72
  #if VERBOSITY > 0
53
73
  scs_printf("Exp cone: Newton method failed, resorting to binary search.\n");
54
74
  #endif
55
75
 
56
76
  for (i = 0; i < MAXITER; i++) {
57
- hfun(v0, x, &f, &df);
77
+ f = hfun_f(v0, x);
58
78
 
59
79
  if (f < 0.0) {
60
80
  xl = x;
@@ -88,7 +108,7 @@ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
88
108
  scs_int i;
89
109
 
90
110
  for (i = 0; i < MAXITER; i++) {
91
- hfun(v0, x, &f, &df);
111
+ hfun_fd(v0, x, &f, &df);
92
112
 
93
113
  if (ABS(f) <= EPS) { /* root found */
94
114
  break;
@@ -145,52 +165,52 @@ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
145
165
  static scs_float proj_primal_exp_cone_heuristic(const scs_float *v0,
146
166
  scs_float *vp) {
147
167
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
148
- scs_float dist, tp, newdist;
168
+ scs_float dist_sq, tp, newdist_sq;
149
169
 
150
170
  /* Perspective boundary */
151
171
  vp[2] = MAX(t0, 0.0);
152
172
  vp[1] = 0.0;
153
173
  vp[0] = MIN(r0, 0.0);
154
- dist = SCS(norm_diff)(v0, vp, 3);
174
+ dist_sq = norm_diff_sq_3(v0, vp);
155
175
 
156
176
  /* Perspective interior */
157
177
  if (s0 > 0.0) {
158
178
  tp = MAX(t0, s0 * EXPF(r0 / s0));
159
- newdist = tp - t0;
160
- if (newdist < dist) {
179
+ newdist_sq = (tp - t0) * (tp - t0);
180
+ if (newdist_sq < dist_sq) {
161
181
  vp[2] = tp;
162
182
  vp[1] = s0;
163
183
  vp[0] = r0;
164
- dist = newdist;
184
+ dist_sq = newdist_sq;
165
185
  }
166
186
  }
167
- return dist;
187
+ return dist_sq;
168
188
  }
169
189
 
170
190
  /* Try heuristic (cheap) projection for polar cone */
171
191
  static scs_float proj_polar_exp_cone_heuristic(const scs_float *v0,
172
192
  scs_float *vd) {
173
193
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
174
- scs_float dist, td, newdist;
194
+ scs_float dist_sq, td, newdist_sq;
175
195
 
176
196
  /* Perspective boundary */
177
197
  vd[2] = MIN(t0, 0.0);
178
198
  vd[1] = MIN(s0, 0.0);
179
199
  vd[0] = 0.0;
180
- dist = SCS(norm_diff)(v0, vd, 3);
200
+ dist_sq = norm_diff_sq_3(v0, vd);
181
201
 
182
202
  /* Perspective interior */
183
203
  if (r0 > 0.0) {
184
204
  td = MIN(t0, -r0 * EXPF(s0 / r0 - 1.0));
185
- newdist = t0 - td;
186
- if (newdist < dist) {
205
+ newdist_sq = (t0 - td) * (t0 - td);
206
+ if (newdist_sq < dist_sq) {
187
207
  vd[2] = td;
188
208
  vd[1] = s0;
189
209
  vd[0] = r0;
190
- dist = newdist;
210
+ dist_sq = newdist_sq;
191
211
  }
192
212
  }
193
- return dist;
213
+ return dist_sq;
194
214
  }
195
215
 
196
216
  static scs_float ppsi(const scs_float *v0) {
@@ -238,18 +258,18 @@ static scs_float domega(scs_float rho) {
238
258
  }
239
259
 
240
260
  /* Generate upper and lower search bounds for root of hfun */
241
- static void exp_search_bracket(const scs_float *v0, scs_float pdist,
242
- scs_float ddist, scs_float *low_out,
261
+ static void exp_search_bracket(const scs_float *v0, scs_float pdist_sq,
262
+ scs_float ddist_sq, scs_float *low_out,
243
263
  scs_float *upr_out) {
244
264
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
245
265
  scs_float baselow = -EXP_CONE_INFINITY_VALUE;
246
266
  scs_float baseupr = EXP_CONE_INFINITY_VALUE;
247
267
  scs_float low = -EXP_CONE_INFINITY_VALUE;
248
268
  scs_float upr = EXP_CONE_INFINITY_VALUE;
249
- scs_float Dp, Dd, curbnd, fl, fu, df, tpu, tdl, sgn, val;
269
+ scs_float Dp, Dd, curbnd, fl, fu, tpu, tdl, sgn, val;
250
270
 
251
- Dp = SQRTF(pdist * pdist - MIN(s0, 0.0) * MIN(s0, 0.0));
252
- Dd = SQRTF(ddist * ddist - MIN(r0, 0.0) * MIN(r0, 0.0));
271
+ Dp = SQRTF(MAX(pdist_sq - MIN(s0, 0.0) * MIN(s0, 0.0), 0.0));
272
+ Dd = SQRTF(MAX(ddist_sq - MIN(r0, 0.0) * MIN(r0, 0.0), 0.0));
253
273
 
254
274
  if (t0 > 0.0) {
255
275
  curbnd = LOGF(t0 / ppsi(v0));
@@ -286,8 +306,8 @@ static void exp_search_bracket(const scs_float *v0, scs_float pdist,
286
306
  upr = _clip(MAX(low, upr), baselow, baseupr);
287
307
 
288
308
  if (low != upr) {
289
- hfun(v0, low, &fl, &df);
290
- hfun(v0, upr, &fu, &df);
309
+ fl = hfun_f(v0, low);
310
+ fu = hfun_f(v0, upr);
291
311
 
292
312
  if (fl * fu > 0.0) {
293
313
  if (ABS(fl) < ABS(fu)) {
@@ -307,21 +327,21 @@ static scs_float proj_sol_primal_exp_cone(const scs_float *v0, scs_float rho,
307
327
  scs_float *vp) {
308
328
  scs_float linrho = (rho - 1.0) * v0[0] + v0[1];
309
329
  scs_float exprho = EXPF(rho);
310
- scs_float quadrho, dist;
330
+ scs_float quadrho, dist_sq;
311
331
 
312
332
  if (linrho > 0.0 && _isfinite(exprho)) {
313
333
  quadrho = rho * (rho - 1.0) + 1.0;
314
334
  vp[2] = exprho * linrho / quadrho;
315
335
  vp[1] = linrho / quadrho;
316
336
  vp[0] = rho * linrho / quadrho;
317
- dist = SCS(norm_diff)(vp, v0, 3);
337
+ dist_sq = norm_diff_sq_3(vp, v0);
318
338
  } else {
319
339
  vp[2] = EXP_CONE_INFINITY_VALUE;
320
340
  vp[1] = 0.0;
321
341
  vp[0] = 0.0;
322
- dist = EXP_CONE_INFINITY_VALUE;
342
+ dist_sq = EXP_CONE_INFINITY_VALUE;
323
343
  }
324
- return dist;
344
+ return dist_sq;
325
345
  }
326
346
 
327
347
  /* convert from rho to polar projection */
@@ -329,21 +349,21 @@ static scs_float proj_sol_polar_exp_cone(const scs_float *v0, scs_float rho,
329
349
  scs_float *vd) {
330
350
  scs_float linrho = v0[0] - rho * v0[1];
331
351
  scs_float exprho = EXPF(-rho);
332
- scs_float quadrho, dist;
352
+ scs_float quadrho, dist_sq;
333
353
 
334
354
  if (linrho > 0.0 && _isfinite(exprho)) {
335
355
  quadrho = rho * (rho - 1.0) + 1.0;
336
356
  vd[2] = -exprho * linrho / quadrho;
337
357
  vd[1] = (1.0 - rho) * linrho / quadrho;
338
358
  vd[0] = linrho / quadrho;
339
- dist = SCS(norm_diff)(v0, vd, 3);
359
+ dist_sq = norm_diff_sq_3(v0, vd);
340
360
  } else {
341
361
  vd[2] = -EXP_CONE_INFINITY_VALUE;
342
362
  vd[1] = 0.0;
343
363
  vd[0] = 0.0;
344
- dist = EXP_CONE_INFINITY_VALUE;
364
+ dist_sq = EXP_CONE_INFINITY_VALUE;
345
365
  }
346
- return dist;
366
+ return dist_sq;
347
367
  }
348
368
 
349
369
  /* Project onto primal or dual exponential cone, performed in-place.
@@ -352,7 +372,7 @@ static scs_float proj_sol_polar_exp_cone(const scs_float *v0, scs_float rho,
352
372
  */
353
373
  scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
354
374
  scs_float TOL = 1e-8; /* pow(1e-10, 2.0 / 3.0); */
355
- scs_float xl, xh, pdist, ddist, err, rho, dist_hat;
375
+ scs_float xl, xh, pdist_sq, ddist_sq, err, rho, dist_hat_sq;
356
376
  scs_float vp[3], vd[3], v_hat[3];
357
377
  scs_int opt;
358
378
 
@@ -366,8 +386,8 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
366
386
  v0[2] *= -1.0;
367
387
  }
368
388
 
369
- pdist = proj_primal_exp_cone_heuristic(v0, vp);
370
- ddist = proj_polar_exp_cone_heuristic(v0, vd);
389
+ pdist_sq = proj_primal_exp_cone_heuristic(v0, vp);
390
+ ddist_sq = proj_polar_exp_cone_heuristic(v0, vd);
371
391
 
372
392
  err = ABS(vp[0] + vd[0] - v0[0]);
373
393
  err = MAX(err, ABS(vp[1] + vd[1] - v0[1]));
@@ -377,45 +397,45 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
377
397
  * or optimality conditions are satisfied
378
398
  */
379
399
  opt = (v0[1] <= 0.0 && v0[0] <= 0.0);
380
- opt |= (MIN(pdist, ddist) <= TOL);
400
+ opt |= (MIN(pdist_sq, ddist_sq) <= TOL * TOL);
381
401
  opt |= (err <= TOL && SCS(dot)(vp, vd, 3) <= TOL);
382
402
 
383
403
  if (opt) {
384
404
  if (primal) {
385
405
  _copy(v0, vp);
386
- return pdist;
406
+ return SQRTF(pdist_sq);
387
407
  }
388
408
  /* Polar cone -> dual cone. */
389
409
  v0[0] = -vd[0];
390
410
  v0[1] = -vd[1];
391
411
  v0[2] = -vd[2];
392
- return ddist;
412
+ return SQRTF(ddist_sq);
393
413
  }
394
414
 
395
- exp_search_bracket(v0, pdist, ddist, &xl, &xh);
415
+ exp_search_bracket(v0, pdist_sq, ddist_sq, &xl, &xh);
396
416
  rho = root_search_newton(v0, xl, xh, 0.5 * (xl + xh));
397
417
 
398
418
  if (primal) {
399
419
  /* Primal cone projection. */
400
- dist_hat = proj_sol_primal_exp_cone(v0, rho, v_hat);
401
- if (dist_hat <= pdist) {
420
+ dist_hat_sq = proj_sol_primal_exp_cone(v0, rho, v_hat);
421
+ if (dist_hat_sq <= pdist_sq) {
402
422
  _copy(vp, v_hat);
403
- pdist = dist_hat;
423
+ pdist_sq = dist_hat_sq;
404
424
  }
405
425
  _copy(v0, vp);
406
- return pdist;
426
+ return SQRTF(pdist_sq);
407
427
  }
408
428
 
409
429
  /* Polar cone projection. */
410
- dist_hat = proj_sol_polar_exp_cone(v0, rho, v_hat);
411
- if (dist_hat <= ddist) {
430
+ dist_hat_sq = proj_sol_polar_exp_cone(v0, rho, v_hat);
431
+ if (dist_hat_sq <= ddist_sq) {
412
432
  _copy(vd, v_hat);
413
- ddist = dist_hat;
433
+ ddist_sq = dist_hat_sq;
414
434
  }
415
435
 
416
436
  /* Polar cone -> dual cone. */
417
437
  v0[0] = -vd[0];
418
438
  v0[1] = -vd[1];
419
439
  v0[2] = -vd[2];
420
- return ddist;
440
+ return SQRTF(ddist_sq);
421
441
  }
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Dense linear algebra operations: norms, dot products, scaling, axpy.
3
+ * Has two implementations: a plain C fallback and an optimized BLAS version
4
+ * (selected at compile time via USE_LAPACK).
5
+ */
6
+
1
7
  #include "linalg.h"
2
8
  #include "scs_blas.h"
3
9
  #include <math.h>
@@ -136,8 +142,9 @@ scs_float SCS(dot)(const scs_float *x, const scs_float *y, scs_int len) {
136
142
 
137
143
  /* ||v||_2^2 */
138
144
  scs_float SCS(norm_sq)(const scs_float *v, scs_int len) {
139
- scs_float nrm = SCS(norm_2)(v, len);
140
- return nrm * nrm;
145
+ blas_int bone = 1;
146
+ blas_int blen = (blas_int)len;
147
+ return BLAS(dot)(&blen, v, &bone, v, &bone);
141
148
  }
142
149
 
143
150
  /* ||v||_2 */
@@ -163,6 +170,18 @@ scs_float SCS(norm_inf)(const scs_float *a, scs_int len) {
163
170
  if (len <= 0) {
164
171
  return 0.0;
165
172
  }
173
+ /* For short vectors the BLAS function-call overhead (pointer args,
174
+ * Fortran ABI, 1-based index return) exceeds the arithmetic cost.
175
+ * Use a scalar loop below a threshold. */
176
+ if (len <= 16) {
177
+ scs_float mx = 0.0, tmp;
178
+ scs_int i;
179
+ for (i = 0; i < len; ++i) {
180
+ tmp = ABS(a[i]);
181
+ if (tmp > mx) mx = tmp;
182
+ }
183
+ return mx;
184
+ }
166
185
  idx = (scs_int)BLASI(amax)(&blen, a, &bone);
167
186
  /* Returned idx is 1-based. */
168
187
  return ABS(a[idx - 1]);
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Problem data normalization (equilibration) of b and c vectors,
3
+ * and solution normalization/un-normalization routines.
4
+ * Matrix normalization (A, P) is in linsys/scs_matrix.c.
5
+ */
6
+
1
7
  #include "normalize.h"
2
8
 
3
9
  #include "linalg.h"
@@ -37,13 +43,22 @@ void SCS(normalize_b_c)(ScsScaling *scal, scs_float *b, scs_float *c) {
37
43
  b[i] *= scal->D[i];
38
44
  }
39
45
 
40
- /* calculate primal and dual scales */
41
- nm_c = SCS(norm_inf)(c, scal->n);
42
- nm_b = SCS(norm_inf)(b, scal->m);
43
- sigma = MAX(nm_c, nm_b);
44
- sigma = sigma < MIN_NORMALIZATION_FACTOR ? 1.0 : sigma;
45
- sigma = sigma > MAX_NORMALIZATION_FACTOR ? MAX_NORMALIZATION_FACTOR : sigma;
46
- sigma = SAFEDIV_POS(1.0, sigma);
46
+ /* sigma comes from the stacked-operator equilibration (the tau-slot
47
+ * scaling computed jointly with D, E by normalize_a_p), replacing the
48
+ * old one-shot clamped max-norm heuristic which left problems with
49
+ * extreme cost/rhs magnitudes badly imbalanced. Fall back to the
50
+ * heuristic if unset (defensive; normalize_a_p always sets it). */
51
+ if (scal->tau_scale > 0.) {
52
+ sigma = scal->tau_scale;
53
+ } else {
54
+ nm_c = SCS(norm_inf)(c, scal->n);
55
+ nm_b = SCS(norm_inf)(b, scal->m);
56
+ sigma = MAX(nm_c, nm_b);
57
+ sigma = sigma < MIN_NORMALIZATION_FACTOR ? 1.0 : sigma;
58
+ sigma =
59
+ sigma > MAX_NORMALIZATION_FACTOR ? MAX_NORMALIZATION_FACTOR : sigma;
60
+ sigma = SAFEDIV_POS(1.0, sigma);
61
+ }
47
62
 
48
63
  /* Scale b, c */
49
64
  SCS(scale_array)(c, sigma, scal->n);
@@ -62,10 +77,9 @@ void SCS(normalize_sol)(ScsScaling *scal, ScsSolution *sol) {
62
77
  for (i = 0; i < scal->n; ++i) {
63
78
  sol->x[i] /= (E[i] / scal->dual_scale);
64
79
  }
80
+ /* Fuse the y and s loops: both index D[i], halving D reads. */
65
81
  for (i = 0; i < scal->m; ++i) {
66
82
  sol->y[i] /= (D[i] / scal->primal_scale);
67
- }
68
- for (i = 0; i < scal->m; ++i) {
69
83
  sol->s[i] *= (D[i] * scal->dual_scale);
70
84
  }
71
85
  }
@@ -77,26 +91,10 @@ void SCS(un_normalize_sol)(ScsScaling *scal, ScsSolution *sol) {
77
91
  for (i = 0; i < scal->n; ++i) {
78
92
  sol->x[i] *= (E[i] / scal->dual_scale);
79
93
  }
94
+ /* Fuse the y and s loops: both index D[i], halving D reads. */
80
95
  for (i = 0; i < scal->m; ++i) {
81
96
  sol->y[i] *= (D[i] / scal->primal_scale);
82
- }
83
- for (i = 0; i < scal->m; ++i) {
84
97
  sol->s[i] /= (D[i] * scal->dual_scale);
85
98
  }
86
99
  }
87
100
 
88
- void SCS(un_normalize_primal)(ScsScaling *scal, scs_float *r) {
89
- scs_int i;
90
- scs_float *D = scal->D;
91
- for (i = 0; i < scal->m; ++i) {
92
- r[i] /= (D[i] * scal->dual_scale);
93
- }
94
- }
95
-
96
- void SCS(un_normalize_dual)(ScsScaling *scal, scs_float *r) {
97
- scs_int i;
98
- scs_float *E = scal->E;
99
- for (i = 0; i < scal->n; ++i) {
100
- r[i] /= (E[i] * scal->primal_scale);
101
- }
102
- }