scs 0.4.0 → 0.4.2

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +1 -1
  5. data/lib/scs/ffi.rb +2 -2
  6. data/lib/scs/version.rb +1 -1
  7. data/lib/scs.rb +3 -3
  8. data/vendor/scs/CITATION.cff +2 -2
  9. data/vendor/scs/CMakeLists.txt +305 -171
  10. data/vendor/scs/Makefile +44 -19
  11. data/vendor/scs/README.md +1 -1
  12. data/vendor/scs/include/glbopts.h +34 -14
  13. data/vendor/scs/include/linsys.h +8 -8
  14. data/vendor/scs/include/scs.h +6 -2
  15. data/vendor/scs/include/scs_blas.h +4 -0
  16. data/vendor/scs/include/scs_types.h +3 -1
  17. data/vendor/scs/include/scs_work.h +9 -8
  18. data/vendor/scs/include/util.h +1 -1
  19. data/vendor/scs/linsys/cpu/direct/private.c +32 -153
  20. data/vendor/scs/linsys/cpu/direct/private.h +6 -6
  21. data/vendor/scs/linsys/cpu/indirect/private.c +9 -22
  22. data/vendor/scs/linsys/cpu/indirect/private.h +4 -2
  23. data/vendor/scs/linsys/csparse.c +140 -12
  24. data/vendor/scs/linsys/csparse.h +10 -17
  25. data/vendor/scs/linsys/gpu/gpu.c +4 -4
  26. data/vendor/scs/linsys/gpu/gpu.h +1 -1
  27. data/vendor/scs/linsys/gpu/indirect/private.c +15 -26
  28. data/vendor/scs/linsys/mkl/direct/private.c +182 -0
  29. data/vendor/scs/linsys/mkl/direct/private.h +38 -0
  30. data/vendor/scs/linsys/scs_matrix.c +11 -5
  31. data/vendor/scs/scs.mk +40 -27
  32. data/vendor/scs/src/cones.c +17 -161
  33. data/vendor/scs/src/exp_cone.c +399 -0
  34. data/vendor/scs/src/linalg.c +17 -3
  35. data/vendor/scs/src/normalize.c +4 -2
  36. data/vendor/scs/src/rw.c +107 -38
  37. data/vendor/scs/src/scs.c +103 -69
  38. data/vendor/scs/src/util.c +12 -3
  39. data/vendor/scs/test/minunit.h +2 -1
  40. data/vendor/scs/test/problem_utils.h +2 -1
  41. data/vendor/scs/test/problems/hs21_tiny_qp.h +1 -1
  42. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +8 -3
  43. data/vendor/scs/test/problems/max_ent +0 -0
  44. data/vendor/scs/test/problems/max_ent.h +8 -0
  45. data/vendor/scs/test/problems/mpc_bug.h +19 -0
  46. data/vendor/scs/test/problems/mpc_bug1 +0 -0
  47. data/vendor/scs/test/problems/mpc_bug2 +0 -0
  48. data/vendor/scs/test/problems/mpc_bug3 +0 -0
  49. data/vendor/scs/test/problems/random_prob.h +2 -43
  50. data/vendor/scs/test/problems/rob_gauss_cov_est.h +7 -2
  51. data/vendor/scs/test/problems/test_exp_cone.h +84 -0
  52. data/vendor/scs/test/problems/test_prob_from_data_file.h +73 -0
  53. data/vendor/scs/test/run_from_file.c +7 -1
  54. data/vendor/scs/test/run_tests.c +25 -9
  55. metadata +14 -3
@@ -4,10 +4,8 @@
4
4
  #include "scs_blas.h" /* contains BLAS(X) macros and type info */
5
5
  #include "util.h"
6
6
 
7
- #define CONE_TOL (1e-9)
8
- #define CONE_THRESH (1e-8)
9
- #define EXP_CONE_MAX_ITERS (100)
10
7
  #define BOX_CONE_MAX_ITERS (25)
8
+ #define POW_CONE_TOL (1e-9)
11
9
  #define POW_CONE_MAX_ITERS (20)
12
10
 
13
11
  /* Box cone limits (+ or -) taken to be INF */
@@ -35,6 +33,11 @@ void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
35
33
 
36
34
  #endif
37
35
 
36
+ /* Forward declare exponential cone projection routine.
37
+ * Implemented in exp_cone.c.
38
+ */
39
+ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal);
40
+
38
41
  void SCS(free_cone)(ScsCone *k) {
39
42
  if (k) {
40
43
  if (k->bu)
@@ -285,7 +288,7 @@ void SCS(finish_cone)(ScsConeWork *c) {
285
288
  }
286
289
 
287
290
  char *SCS(get_cone_header)(const ScsCone *k) {
288
- char *tmp = (char *)scs_malloc(sizeof(char) * 512);
291
+ char *tmp = (char *)scs_malloc(512); /* sizeof(char) = 1 */
289
292
  scs_int i, soc_vars, sd_vars;
290
293
  sprintf(tmp, "cones: ");
291
294
  if (k->z) {
@@ -325,122 +328,10 @@ char *SCS(get_cone_header)(const ScsCone *k) {
325
328
  return tmp;
326
329
  }
327
330
 
328
- static scs_float exp_newton_one_d(scs_float rho, scs_float y_hat,
329
- scs_float z_hat, scs_float w) {
330
- scs_float t_prev, t = MAX(w - z_hat, MAX(-z_hat, 1e-9));
331
- scs_float f = 1., fp = 1.;
332
- scs_int i;
333
- for (i = 0; i < EXP_CONE_MAX_ITERS; ++i) {
334
- t_prev = t;
335
- f = t * (t + z_hat) / rho / rho - y_hat / rho + log(t / rho) + 1;
336
- fp = (2 * t + z_hat) / rho / rho + 1 / t;
337
-
338
- t = t - f / fp;
339
-
340
- if (t <= -z_hat) {
341
- t = -z_hat;
342
- break;
343
- } else if (t <= 0) {
344
- t = 0;
345
- break;
346
- } else if (ABS(t - t_prev) < CONE_TOL) {
347
- break;
348
- } else if (SQRTF(f * f / fp) < CONE_TOL) {
349
- break;
350
- }
351
- }
352
- if (i == EXP_CONE_MAX_ITERS) {
353
- scs_printf("warning: exp cone newton step hit maximum %i iters\n", (int)i);
354
- scs_printf("rho=%1.5e; y_hat=%1.5e; z_hat=%1.5e; w=%1.5e; f=%1.5e, "
355
- "fp=%1.5e, t=%1.5e, t_prev= %1.5e\n",
356
- rho, y_hat, z_hat, w, f, fp, t, t_prev);
357
- }
358
- return t + z_hat;
359
- }
360
-
361
- static void exp_solve_for_x_with_rho(const scs_float *v, scs_float *x,
362
- scs_float rho, scs_float w) {
363
- x[2] = exp_newton_one_d(rho, v[1], v[2], w);
364
- x[1] = (x[2] - v[2]) * x[2] / rho;
365
- x[0] = v[0] - rho;
366
- }
367
-
368
- static scs_float exp_calc_grad(const scs_float *v, scs_float *x, scs_float rho,
369
- scs_float w) {
370
- exp_solve_for_x_with_rho(v, x, rho, w);
371
- if (x[1] <= 1e-12) {
372
- return x[0];
373
- }
374
- return x[0] + x[1] * log(x[1] / x[2]);
375
- }
376
-
377
- static void exp_get_rho_ub(const scs_float *v, scs_float *x, scs_float *ub,
378
- scs_float *lb) {
379
- *lb = 0;
380
- *ub = 0.125;
381
- while (exp_calc_grad(v, x, *ub, v[1]) > 0) {
382
- *lb = *ub;
383
- (*ub) *= 2;
384
- }
385
- }
386
-
387
- /* project onto the exponential cone, v has dimension *exactly* 3 */
388
- static scs_int proj_exp_cone(scs_float *v) {
389
- scs_int i;
390
- scs_float ub, lb, rho, g, x[3];
391
- scs_float r = v[0], s = v[1], t = v[2];
392
-
393
- /* v in cl(Kexp) */
394
- if ((s * exp(r / s) - t <= CONE_THRESH && s > 0) ||
395
- (r <= 0 && s == 0 && t >= 0)) {
396
- return 0;
397
- }
398
-
399
- /* -v in Kexp^* */
400
- if ((r > 0 && r * exp(s / r) + exp(1) * t <= CONE_THRESH) ||
401
- (r == 0 && s <= 0 && t <= 0)) {
402
- memset(v, 0, 3 * sizeof(scs_float));
403
- return 0;
404
- }
405
-
406
- /* special case with analytical solution */
407
- if (r < 0 && s < 0) {
408
- v[1] = 0.0;
409
- v[2] = MAX(v[2], 0);
410
- return 0;
411
- }
412
-
413
- /* iterative procedure to find projection, bisects on dual variable: */
414
- exp_get_rho_ub(v, x, &ub, &lb); /* get starting upper and lower bounds */
415
- for (i = 0; i < EXP_CONE_MAX_ITERS; ++i) {
416
- rho = (ub + lb) / 2; /* halfway between upper and lower bounds */
417
- g = exp_calc_grad(v, x, rho, x[1]); /* calculates gradient wrt dual var */
418
- if (g > 0) {
419
- lb = rho;
420
- } else {
421
- ub = rho;
422
- }
423
- if (ub - lb < CONE_TOL) {
424
- break;
425
- }
426
- }
427
- #if VERBOSITY > 10
428
- scs_printf("exponential cone proj iters %i\n", (int)i);
429
- #endif
430
- if (i == EXP_CONE_MAX_ITERS) {
431
- scs_printf("warning: exp cone outer step hit maximum %i iters\n", (int)i);
432
- scs_printf("r=%1.5e; s=%1.5e; t=%1.5e\n", r, s, t);
433
- }
434
- v[0] = x[0];
435
- v[1] = x[1];
436
- v[2] = x[2];
437
- return 0;
438
- }
439
-
440
331
  static scs_int set_up_sd_cone_work_space(ScsConeWork *c, const ScsCone *k) {
441
332
  scs_int i;
442
333
  #ifdef USE_LAPACK
443
- blas_int n_max = 0;
334
+ blas_int n_max = 1;
444
335
  blas_int neg_one = -1;
445
336
  blas_int info = 0;
446
337
  scs_float wkopt = 0.0;
@@ -465,7 +356,7 @@ static scs_int set_up_sd_cone_work_space(ScsConeWork *c, const ScsCone *k) {
465
356
  &info);
466
357
 
467
358
  if (info != 0) {
468
- scs_printf("FATAL: syev failure, info = %li\n", (long)info);
359
+ scs_printf("FATAL: syev workspace query failure, info = %li\n", (long)info);
469
360
  return -1;
470
361
  }
471
362
  c->lwork = (blas_int)(wkopt + 1); /* +1 for int casting safety */
@@ -741,13 +632,13 @@ static void proj_power_cone(scs_float *v, scs_float a) {
741
632
  scs_int i;
742
633
  /* v in K_a */
743
634
  if (xh >= 0 && yh >= 0 &&
744
- CONE_THRESH + POWF(xh, a) * POWF(yh, (1 - a)) >= rh) {
635
+ POW_CONE_TOL + POWF(xh, a) * POWF(yh, (1 - a)) >= rh) {
745
636
  return;
746
637
  }
747
638
 
748
639
  /* -v in K_a^* */
749
640
  if (xh <= 0 && yh <= 0 &&
750
- CONE_THRESH + POWF(-xh, a) * POWF(-yh, 1 - a) >=
641
+ POW_CONE_TOL + POWF(-xh, a) * POWF(-yh, 1 - a) >=
751
642
  rh * POWF(a, a) * POWF(1 - a, 1 - a)) {
752
643
  v[0] = v[1] = v[2] = 0;
753
644
  return;
@@ -760,7 +651,7 @@ static void proj_power_cone(scs_float *v, scs_float a) {
760
651
  y = pow_calc_x(r, yh, rh, 1 - a);
761
652
 
762
653
  f = pow_calc_f(x, y, r, a);
763
- if (ABS(f) < CONE_TOL) {
654
+ if (ABS(f) < POW_CONE_TOL) {
764
655
  break;
765
656
  }
766
657
 
@@ -829,51 +720,16 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
829
720
  }
830
721
  }
831
722
 
832
- if (k->ep) { /* doesn't use r_y */
833
- /*
834
- * exponential cone is not self dual, if s \in K
835
- * then y \in K^* and so if K is the primal cone
836
- * here we project onto K^*, via Moreau
837
- * \Pi_C^*(y) = y + \Pi_C(-y)
838
- */
723
+ if (k->ep || k->ed) { /* doesn't use r_y */
839
724
  #ifdef _OPENMP
840
725
  #pragma omp parallel for
841
726
  #endif
842
- for (i = 0; i < k->ep; ++i) {
843
- proj_exp_cone(&(x[count + 3 * i]));
727
+ for (i = 0; i < k->ep + k->ed; ++i) {
728
+ /* provided in exp_cone.c */
729
+ SCS(proj_pd_exp_cone)(&(x[count + 3 * i]), i < k->ep);
844
730
  }
845
- count += 3 * k->ep;
731
+ count += 3 * (k->ep + k->ed);
846
732
  }
847
-
848
- /* dual exponential cone */
849
- if (k->ed) { /* doesn't use r_y */
850
- /*
851
- * exponential cone is not self dual, if s \in K
852
- * then y \in K^* and so if K is the primal cone
853
- * here we project onto K^*, via Moreau
854
- * \Pi_C^*(y) = y + \Pi_C(-y)
855
- */
856
- scs_int idx;
857
- scs_float r, s, t;
858
- SCS(scale_array)(&(x[count]), -1, 3 * k->ed); /* x = -x; */
859
- #ifdef _OPENMP
860
- #pragma omp parallel for private(r, s, t, idx)
861
- #endif
862
- for (i = 0; i < k->ed; ++i) {
863
- idx = count + 3 * i;
864
- r = x[idx];
865
- s = x[idx + 1];
866
- t = x[idx + 2];
867
-
868
- proj_exp_cone(&(x[idx]));
869
-
870
- x[idx] -= r;
871
- x[idx + 1] -= s;
872
- x[idx + 2] -= t;
873
- }
874
- count += 3 * k->ed;
875
- }
876
-
877
733
  if (k->psize && k->p) { /* doesn't use r_y */
878
734
  scs_float v[3];
879
735
  scs_int idx;
@@ -0,0 +1,399 @@
1
+ #include "cones.h"
2
+ #include "glbopts.h"
3
+ #include "linalg.h"
4
+ #include "scs.h"
5
+
6
+ #define EXP_CONE_INFINITY_VALUE (1E15)
7
+
8
+ /*
9
+ * Exponential cone projection routines, from:
10
+ *
11
+ * Projection onto the exponential cone: a univariate root-finding problem,
12
+ * by Henrik A. Friberg, 2021.
13
+ *
14
+ */
15
+
16
+ static inline scs_int _isfinite(scs_float x) {
17
+ return ABS(x) < EXP_CONE_INFINITY_VALUE;
18
+ }
19
+
20
+ static inline scs_float _clip(scs_float x, scs_float l, scs_float u) {
21
+ return MAX(l, MIN(u, x));
22
+ }
23
+
24
+ /* As defined in Friberg, 2021 (multiplied by positive polynomial) */
25
+ static void hfun(const scs_float *v0, scs_float rho, scs_float *f,
26
+ scs_float *df) {
27
+ scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
28
+ scs_float exprho = exp(rho);
29
+ scs_float expnegrho = exp(-rho);
30
+ /* function value at v0 */
31
+ *f = ((rho - 1) * r0 + s0) * exprho - (r0 - rho * s0) * expnegrho -
32
+ (rho * (rho - 1) + 1) * t0;
33
+ /* gradient of function at v0 */
34
+ *df = (rho * r0 + s0) * exprho + (r0 - (rho - 1) * s0) * expnegrho -
35
+ (2 * rho - 1) * t0;
36
+ }
37
+
38
+ /* Binary search for the root of the hfun function */
39
+ static scs_float root_search_binary(const scs_float *v0, scs_float xl,
40
+ scs_float xu, scs_float x) {
41
+ #if VERBOSITY > 0
42
+ scs_printf("Exp cone: Newton method failed, resorting to binary search.\n");
43
+ #endif
44
+ const scs_float EPS = 1e-12; /* expensive so loosen tol */
45
+ const scs_int MAXITER = 40;
46
+ scs_int i;
47
+ scs_float x_plus = x, f, df;
48
+ for (i = 0; i < MAXITER; i++) {
49
+ hfun(v0, x, &f, &df);
50
+ if (f < 0.0) {
51
+ xl = x;
52
+ } else {
53
+ xu = x;
54
+ }
55
+ /* binary search step */
56
+ x_plus = 0.5 * (xl + xu);
57
+ if (ABS(x_plus - x) <= EPS * MAX(1., ABS(x_plus)) || (x_plus == xl) ||
58
+ (x_plus == xu)) {
59
+ break;
60
+ }
61
+ x = x_plus;
62
+ }
63
+ return x_plus;
64
+ }
65
+
66
+ /* Use damped Newton's to find the root of the hfun function */
67
+ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
68
+ scs_float xu, scs_float x) {
69
+ /* params taken from Friberg code */
70
+ const scs_float EPS = 1e-15;
71
+ const scs_float DFTOL = 1e-13; /* pow(EPS, 6.0 / 7.0) */
72
+ const scs_int MAXITER = 20;
73
+ const scs_float LODAMP = 0.05;
74
+ const scs_float HIDAMP = 0.95;
75
+
76
+ scs_float x_plus, f, df;
77
+ scs_int i;
78
+
79
+ for (i = 0; i < MAXITER; i++) {
80
+ hfun(v0, x, &f, &df);
81
+
82
+ if (ABS(f) <= EPS) { /* root found */
83
+ break;
84
+ }
85
+
86
+ if (f < 0.0) {
87
+ xl = x;
88
+ } else {
89
+ xu = x;
90
+ }
91
+
92
+ if (xu <= xl) {
93
+ xu = 0.5 * (xu + xl);
94
+ xl = xu;
95
+ break;
96
+ }
97
+
98
+ if (!_isfinite(f) || df < DFTOL) {
99
+ break;
100
+ }
101
+
102
+ /* Newton step */
103
+ x_plus = x - f / df;
104
+
105
+ if (ABS(x_plus - x) <= EPS * MAX(1., ABS(x_plus))) {
106
+ break;
107
+ }
108
+
109
+ if (x_plus >= xu) {
110
+ x = MIN(LODAMP * x + HIDAMP * xu, xu);
111
+ } else if (x_plus <= xl) {
112
+ x = MAX(LODAMP * x + HIDAMP * xl, xl);
113
+ } else {
114
+ x = x_plus;
115
+ }
116
+ }
117
+ if (i < MAXITER) { /* Newton's method converged */
118
+ #if VERBOSITY > 0
119
+ scs_printf("Exp cone: Newton iters:%i, f:%.4e, df:%.4e\n", (int)i, f, df);
120
+ #endif
121
+ return _clip(x, xl, xu);
122
+ }
123
+ /* Fall back to binary search if Newton failed */
124
+ return root_search_binary(v0, xl, xu, x);
125
+ }
126
+
127
+ /* try heuristic (cheap) projection */
128
+ static scs_float proj_primal_exp_cone_heuristic(const scs_float *v0,
129
+ scs_float *vp) {
130
+ scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
131
+ scs_float dist, tp, newdist;
132
+ /* perspective boundary */
133
+ vp[2] = MAX(t0, 0);
134
+ vp[1] = 0.0;
135
+ vp[0] = MIN(r0, 0);
136
+ dist = SCS(norm_diff)(v0, vp, 3);
137
+
138
+ /* perspective interior */
139
+ if (s0 > 0.0) {
140
+ tp = MAX(t0, s0 * exp(r0 / s0));
141
+ newdist = tp - t0;
142
+ if (newdist < dist) {
143
+ vp[2] = tp;
144
+ vp[1] = s0;
145
+ vp[0] = r0;
146
+ dist = newdist;
147
+ }
148
+ }
149
+ return dist;
150
+ }
151
+
152
+ /* try heuristic (cheap) projection */
153
+ static scs_float proj_polar_exp_cone_heuristic(const scs_float *v0,
154
+ scs_float *vd) {
155
+ scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
156
+ scs_float dist, td, newdist;
157
+ /* perspective boundary */
158
+ vd[2] = MIN(t0, 0);
159
+ vd[1] = MIN(s0, 0);
160
+ vd[0] = 0.0;
161
+ dist = SCS(norm_diff)(v0, vd, 3);
162
+
163
+ /* perspective interior */
164
+ if (r0 > 0.0) {
165
+ td = MIN(t0, -r0 * exp(s0 / r0 - 1));
166
+ newdist = t0 - td;
167
+ if (newdist < dist) {
168
+ vd[2] = td;
169
+ vd[1] = s0;
170
+ vd[0] = r0;
171
+ dist = newdist;
172
+ }
173
+ }
174
+ return dist;
175
+ }
176
+
177
+ static scs_float ppsi(const scs_float *v0) {
178
+ scs_float s0 = v0[1], r0 = v0[0];
179
+ scs_float psi;
180
+
181
+ if (r0 > s0) {
182
+ psi = (r0 - s0 + sqrt(r0 * r0 + s0 * s0 - r0 * s0)) / r0;
183
+ } else {
184
+ psi = -s0 / (r0 - s0 - sqrt(r0 * r0 + s0 * s0 - r0 * s0));
185
+ }
186
+
187
+ return ((psi - 1) * r0 + s0) / (psi * (psi - 1) + 1);
188
+ }
189
+
190
+ static scs_float pomega(scs_float rho) {
191
+ scs_float val = exp(rho) / (rho * (rho - 1) + 1);
192
+
193
+ if (rho < 2.0) {
194
+ val = MIN(val, exp(2.0) / 3);
195
+ }
196
+
197
+ return val;
198
+ }
199
+
200
+ static scs_float dpsi(const scs_float *v0) {
201
+ scs_float s0 = v0[1], r0 = v0[0];
202
+ scs_float psi;
203
+
204
+ if (s0 > r0) {
205
+ psi = (r0 - sqrt(r0 * r0 + s0 * s0 - r0 * s0)) / s0;
206
+ } else {
207
+ psi = (r0 - s0) / (r0 + sqrt(r0 * r0 + s0 * s0 - r0 * s0));
208
+ }
209
+
210
+ return (r0 - psi * s0) / (psi * (psi - 1) + 1);
211
+ }
212
+
213
+ static scs_float domega(scs_float rho) {
214
+ scs_float val = -exp(-rho) / (rho * (rho - 1) + 1);
215
+
216
+ if (rho > -1.0) {
217
+ val = MAX(val, -exp(1.0) / 3);
218
+ }
219
+
220
+ return val;
221
+ }
222
+
223
+ /* Generate upper and lower search bounds for root of hfun */
224
+ static void exp_search_bracket(const scs_float *v0, scs_float pdist,
225
+ scs_float ddist, scs_float *low_out,
226
+ scs_float *upr_out) {
227
+ scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
228
+ scs_float baselow = -EXP_CONE_INFINITY_VALUE,
229
+ baseupr = EXP_CONE_INFINITY_VALUE;
230
+ scs_float low = -EXP_CONE_INFINITY_VALUE, upr = EXP_CONE_INFINITY_VALUE;
231
+
232
+ scs_float Dp = SQRTF(pdist * pdist - MIN(s0, 0) * MIN(s0, 0));
233
+ scs_float Dd = SQRTF(ddist * ddist - MIN(r0, 0) * MIN(r0, 0));
234
+
235
+ scs_float curbnd, fl, fu, df, tpu, tdl;
236
+
237
+ if (t0 > 0) {
238
+ curbnd = log(t0 / ppsi(v0));
239
+ low = MAX(low, curbnd);
240
+ } else if (t0 < 0) {
241
+ curbnd = -log(-t0 / dpsi(v0));
242
+ upr = MIN(upr, curbnd);
243
+ }
244
+
245
+ if (r0 > 0) {
246
+ baselow = 1 - s0 / r0;
247
+ low = MAX(low, baselow);
248
+ tpu = MAX(1e-12, MIN(Dd, Dp + t0));
249
+ curbnd = MAX(low, baselow + tpu / r0 / pomega(low));
250
+ upr = MIN(upr, curbnd);
251
+ }
252
+
253
+ if (s0 > 0) {
254
+ baseupr = r0 / s0;
255
+ upr = MIN(upr, baseupr);
256
+ tdl = -MAX(1e-12, MIN(Dp, Dd - t0));
257
+ curbnd = MIN(upr, baseupr - tdl / s0 / domega(upr));
258
+ low = MAX(low, curbnd);
259
+ }
260
+
261
+ /* Guarantee valid bracket */
262
+ /* TODO do we need these 2 lines? */
263
+ low = _clip(MIN(low, upr), baselow, baseupr);
264
+ upr = _clip(MAX(low, upr), baselow, baseupr);
265
+
266
+ if (low != upr) {
267
+ hfun(v0, low, &fl, &df);
268
+ hfun(v0, upr, &fu, &df);
269
+
270
+ if (fl * fu > 0) {
271
+ if (ABS(fl) < ABS(fu)) {
272
+ upr = low;
273
+ } else {
274
+ low = upr;
275
+ }
276
+ }
277
+ }
278
+
279
+ *low_out = low;
280
+ *upr_out = upr;
281
+ }
282
+
283
+ /* convert from rho to primal projection */
284
+ static scs_float proj_sol_primal_exp_cone(const scs_float *v0, scs_float rho,
285
+ scs_float *vp) {
286
+ scs_float linrho = (rho - 1) * v0[0] + v0[1];
287
+ scs_float exprho = exp(rho);
288
+ scs_float quadrho, dist;
289
+ if (linrho > 0 && _isfinite(exprho)) {
290
+ quadrho = rho * (rho - 1) + 1;
291
+ vp[2] = exprho * linrho / quadrho;
292
+ vp[1] = linrho / quadrho;
293
+ vp[0] = rho * linrho / quadrho;
294
+ dist = SCS(norm_diff)(vp, v0, 3);
295
+ } else {
296
+ vp[2] = EXP_CONE_INFINITY_VALUE;
297
+ vp[1] = 0.0;
298
+ vp[0] = 0.0;
299
+ dist = EXP_CONE_INFINITY_VALUE;
300
+ }
301
+ return dist;
302
+ }
303
+
304
+ /* convert from rho to polar projection */
305
+ static scs_float proj_sol_polar_exp_cone(const scs_float *v0, scs_float rho,
306
+ scs_float *vd) {
307
+ scs_float linrho = v0[0] - rho * v0[1];
308
+ scs_float exprho = exp(-rho);
309
+ scs_float quadrho, dist;
310
+ if (linrho > 0 && _isfinite(exprho)) {
311
+ quadrho = rho * (rho - 1) + 1;
312
+ vd[2] = -exprho * linrho / quadrho;
313
+ vd[1] = (1 - rho) * linrho / quadrho;
314
+ vd[0] = linrho / quadrho;
315
+ dist = SCS(norm_diff)(v0, vd, 3);
316
+ } else {
317
+ vd[2] = -EXP_CONE_INFINITY_VALUE;
318
+ vd[1] = 0.0;
319
+ vd[0] = 0.0;
320
+ dist = EXP_CONE_INFINITY_VALUE;
321
+ }
322
+ return dist;
323
+ }
324
+
325
+ static inline void _copy(scs_float *dest, scs_float *src) {
326
+ dest[0] = src[0];
327
+ dest[1] = src[1];
328
+ dest[2] = src[2];
329
+ }
330
+
331
+ /* Project onto primal or dual exponential cone, performed in-place.
332
+ * If `primal=0` then project on the dual cone, otherwise project
333
+ * onto primal. Taken from algorithm in Friberg, 2021.
334
+ */
335
+ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
336
+ scs_float TOL = 1e-8; /* pow(1e-10, 2.0 / 3.0); */
337
+ scs_float xl, xh, pdist, ddist, err, rho, dist_hat;
338
+ scs_float vp[3], vd[3], v_hat[3];
339
+ scs_int opt;
340
+ if (!primal) {
341
+ /* This routine actually projects onto primal and polar cones
342
+ * simultaneously. So to make it project onto dual, use this:
343
+ * Pi_{C^*}(v0) = -Pi_{C^polar}(-v0)
344
+ */
345
+ v0[0] *= -1.;
346
+ v0[1] *= -1.;
347
+ v0[2] *= -1.;
348
+ }
349
+
350
+ pdist = proj_primal_exp_cone_heuristic(v0, vp);
351
+ ddist = proj_polar_exp_cone_heuristic(v0, vd);
352
+
353
+ err = ABS(vp[0] + vd[0] - v0[0]);
354
+ err = MAX(err, ABS(vp[1] + vd[1] - v0[1]));
355
+ err = MAX(err, ABS(vp[2] + vd[2] - v0[2]));
356
+
357
+ /* Skip root search if presolve rules apply
358
+ * or optimality conditions are satisfied
359
+ */
360
+ opt = (v0[1] <= 0 && v0[0] <= 0);
361
+ opt |= (MIN(pdist, ddist) <= TOL);
362
+ opt |= (err <= TOL && SCS(dot)(vp, vd, 3) <= TOL);
363
+ if (opt) {
364
+ if (primal) {
365
+ _copy(v0, vp);
366
+ return pdist;
367
+ }
368
+ /* polar cone -> dual cone */
369
+ v0[0] = -vd[0];
370
+ v0[1] = -vd[1];
371
+ v0[2] = -vd[2];
372
+ return ddist;
373
+ }
374
+
375
+ exp_search_bracket(v0, pdist, ddist, &xl, &xh);
376
+ rho = root_search_newton(v0, xl, xh, 0.5 * (xl + xh));
377
+
378
+ if (primal) {
379
+ /* primal cone projection */
380
+ dist_hat = proj_sol_primal_exp_cone(v0, rho, v_hat);
381
+ if (dist_hat <= pdist) {
382
+ _copy(vp, v_hat);
383
+ pdist = dist_hat;
384
+ }
385
+ _copy(v0, vp);
386
+ return pdist;
387
+ }
388
+ /* polar cone projection */
389
+ dist_hat = proj_sol_polar_exp_cone(v0, rho, v_hat);
390
+ if (dist_hat <= ddist) {
391
+ _copy(vd, v_hat);
392
+ ddist = dist_hat;
393
+ }
394
+ /* polar cone -> dual cone */
395
+ v0[0] = -vd[0];
396
+ v0[1] = -vd[1];
397
+ v0[2] = -vd[2];
398
+ return ddist;
399
+ }
@@ -102,12 +102,16 @@ extern "C" {
102
102
  scs_float BLAS(nrm2)(blas_int *n, const scs_float *x, blas_int *incx);
103
103
  scs_float BLAS(dot)(const blas_int *n, const scs_float *x, const blas_int *incx,
104
104
  const scs_float *y, const blas_int *incy);
105
- scs_float BLAS(lange)(const char *norm, const blas_int *m, const blas_int *n,
106
- const scs_float *a, blas_int *lda, scs_float *work);
107
105
  void BLAS(axpy)(blas_int *n, const scs_float *a, const scs_float *x,
108
106
  blas_int *incx, scs_float *y, blas_int *incy);
109
107
  void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
110
108
  const blas_int *incx);
109
+ blas_int BLASI(amax)(blas_int *n, const scs_float *x, blas_int *incx);
110
+
111
+ /* Possibly not working correctly on all platforms.
112
+ scs_float BLAS(lange)(const char *norm, const blas_int *m, const blas_int *n,
113
+ const scs_float *a, blas_int *lda, scs_float *work);
114
+ */
111
115
 
112
116
  #ifdef __cplusplus
113
117
  }
@@ -140,10 +144,20 @@ scs_float SCS(norm_2)(const scs_float *v, scs_int len) {
140
144
  return BLAS(nrm2)(&blen, v, &bone);
141
145
  }
142
146
 
147
+ /* Possibly not working correctly on all platforms.
148
+ scs_float SCS(norm_inf)(const scs_float *a, scs_int len) {
149
+ blas_int bone = 1;
150
+ blas_int blen = (blas_int)len;
151
+ return BLAS(lange)("Max", &blen, &bone, a, &blen, SCS_NULL);
152
+ }
153
+ */
154
+
143
155
  scs_float SCS(norm_inf)(const scs_float *a, scs_int len) {
144
156
  blas_int bone = 1;
145
157
  blas_int blen = (blas_int)len;
146
- return BLAS(lange)("Max", &blen, &bone, a, &bone, SCS_NULL);
158
+ scs_int idx = (scs_int)BLASI(amax)(&blen, a, &bone);
159
+ /* Returned idx is 1-based. */
160
+ return ABS(a[idx - 1]);
147
161
  }
148
162
 
149
163
  /* axpy a += sc*b */
@@ -26,7 +26,7 @@
26
26
  */
27
27
  void SCS(normalize_b_c)(ScsScaling *scal, scs_float *b, scs_float *c) {
28
28
  scs_int i;
29
- scs_float sigma;
29
+ scs_float sigma, nm_c, nm_b;
30
30
 
31
31
  /* scale c */
32
32
  for (i = 0; i < scal->n; ++i) {
@@ -38,7 +38,9 @@ void SCS(normalize_b_c)(ScsScaling *scal, scs_float *b, scs_float *c) {
38
38
  }
39
39
 
40
40
  /* calculate primal and dual scales */
41
- sigma = MAX(SCS(norm_inf)(c, scal->n), SCS(norm_inf)(b, scal->m));
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);
42
44
  sigma = sigma < MIN_NORMALIZATION_FACTOR ? 1.0 : sigma;
43
45
  sigma = sigma > MAX_NORMALIZATION_FACTOR ? MAX_NORMALIZATION_FACTOR : sigma;
44
46
  sigma = SAFEDIV_POS(1.0, sigma);