scs 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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);