scs 0.5.3 → 0.5.4

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.
@@ -6,11 +6,9 @@
6
6
  #define EXP_CONE_INFINITY_VALUE (1E15)
7
7
 
8
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
- *
9
+ * Exponential cone projection routines, based on:
10
+ * "Projection onto the exponential cone: a univariate root-finding problem"
11
+ * by Henrik A. Friberg, 2021.
14
12
  */
15
13
 
16
14
  static inline scs_int _isfinite(scs_float x) {
@@ -21,16 +19,24 @@ static inline scs_float _clip(scs_float x, scs_float l, scs_float u) {
21
19
  return MAX(l, MIN(u, x));
22
20
  }
23
21
 
22
+ static inline void _copy(scs_float *dest, const scs_float *src) {
23
+ dest[0] = src[0];
24
+ dest[1] = src[1];
25
+ dest[2] = src[2];
26
+ }
27
+
24
28
  /* As defined in Friberg, 2021 (multiplied by positive polynomial) */
25
29
  static void hfun(const scs_float *v0, scs_float rho, scs_float *f,
26
30
  scs_float *df) {
27
31
  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 */
32
+ scs_float exprho = EXPF(rho);
33
+ scs_float expnegrho = EXPF(-rho);
34
+
35
+ /* Function value at v0 */
31
36
  *f = ((rho - 1) * r0 + s0) * exprho - (r0 - rho * s0) * expnegrho -
32
37
  (rho * (rho - 1) + 1) * t0;
33
- /* gradient of function at v0 */
38
+
39
+ /* Gradient of function at v0 */
34
40
  *df = (rho * r0 + s0) * exprho + (r0 - (rho - 1) * s0) * expnegrho -
35
41
  (2 * rho - 1) * t0;
36
42
  }
@@ -38,23 +44,28 @@ static void hfun(const scs_float *v0, scs_float rho, scs_float *f,
38
44
  /* Binary search for the root of the hfun function */
39
45
  static scs_float root_search_binary(const scs_float *v0, scs_float xl,
40
46
  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 */
47
+ const scs_float EPS = 1e-12; /* looser tolerance for binary search */
45
48
  const scs_int MAXITER = 40;
46
49
  scs_int i;
47
50
  scs_float x_plus = x, f, df;
51
+
52
+ #if VERBOSITY > 0
53
+ scs_printf("Exp cone: Newton method failed, resorting to binary search.\n");
54
+ #endif
55
+
48
56
  for (i = 0; i < MAXITER; i++) {
49
57
  hfun(v0, x, &f, &df);
58
+
50
59
  if (f < 0.0) {
51
60
  xl = x;
52
61
  } else {
53
62
  xu = x;
54
63
  }
55
- /* binary search step */
64
+
65
+ /* Binary search step */
56
66
  x_plus = 0.5 * (xl + xu);
57
- if (ABS(x_plus - x) <= EPS * MAX(1., ABS(x_plus)) || (x_plus == xl) ||
67
+
68
+ if (ABS(x_plus - x) <= EPS * MAX(1.0, ABS(x_plus)) || (x_plus == xl) ||
58
69
  (x_plus == xu)) {
59
70
  break;
60
71
  }
@@ -63,10 +74,10 @@ static scs_float root_search_binary(const scs_float *v0, scs_float xl,
63
74
  return x_plus;
64
75
  }
65
76
 
66
- /* Use damped Newton's to find the root of the hfun function */
77
+ /* Use damped Newton's method to find the root of the hfun function */
67
78
  static scs_float root_search_newton(const scs_float *v0, scs_float xl,
68
79
  scs_float xu, scs_float x) {
69
- /* params taken from Friberg code */
80
+ /* Paraameters taken from Friberg code. */
70
81
  const scs_float EPS = 1e-15;
71
82
  const scs_float DFTOL = 1e-13; /* pow(EPS, 6.0 / 7.0) */
72
83
  const scs_int MAXITER = 20;
@@ -83,18 +94,21 @@ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
83
94
  break;
84
95
  }
85
96
 
97
+ /* Update bounds */
86
98
  if (f < 0.0) {
87
99
  xl = x;
88
100
  } else {
89
101
  xu = x;
90
102
  }
91
103
 
104
+ /* If bracket collapsed */
92
105
  if (xu <= xl) {
93
106
  xu = 0.5 * (xu + xl);
94
107
  xl = xu;
95
108
  break;
96
109
  }
97
110
 
111
+ /* Check for flat gradient or infinity */
98
112
  if (!_isfinite(f) || df < DFTOL) {
99
113
  break;
100
114
  }
@@ -102,10 +116,11 @@ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
102
116
  /* Newton step */
103
117
  x_plus = x - f / df;
104
118
 
105
- if (ABS(x_plus - x) <= EPS * MAX(1., ABS(x_plus))) {
119
+ if (ABS(x_plus - x) <= EPS * MAX(1.0, ABS(x_plus))) {
106
120
  break;
107
121
  }
108
122
 
123
+ /* Damped update with projection onto bounds */
109
124
  if (x_plus >= xu) {
110
125
  x = MIN(LODAMP * x + HIDAMP * xu, xu);
111
126
  } else if (x_plus <= xl) {
@@ -114,30 +129,33 @@ static scs_float root_search_newton(const scs_float *v0, scs_float xl,
114
129
  x = x_plus;
115
130
  }
116
131
  }
132
+
117
133
  if (i < MAXITER) { /* Newton's method converged */
118
134
  #if VERBOSITY > 0
119
135
  scs_printf("Exp cone: Newton iters:%i, f:%.4e, df:%.4e\n", (int)i, f, df);
120
136
  #endif
121
137
  return _clip(x, xl, xu);
122
138
  }
139
+
123
140
  /* Fall back to binary search if Newton failed */
124
141
  return root_search_binary(v0, xl, xu, x);
125
142
  }
126
143
 
127
- /* try heuristic (cheap) projection */
144
+ /* Try heuristic (cheap) projection for primal cone */
128
145
  static scs_float proj_primal_exp_cone_heuristic(const scs_float *v0,
129
146
  scs_float *vp) {
130
147
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
131
148
  scs_float dist, tp, newdist;
132
- /* perspective boundary */
133
- vp[2] = MAX(t0, 0);
149
+
150
+ /* Perspective boundary */
151
+ vp[2] = MAX(t0, 0.0);
134
152
  vp[1] = 0.0;
135
- vp[0] = MIN(r0, 0);
153
+ vp[0] = MIN(r0, 0.0);
136
154
  dist = SCS(norm_diff)(v0, vp, 3);
137
155
 
138
- /* perspective interior */
156
+ /* Perspective interior */
139
157
  if (s0 > 0.0) {
140
- tp = MAX(t0, s0 * exp(r0 / s0));
158
+ tp = MAX(t0, s0 * EXPF(r0 / s0));
141
159
  newdist = tp - t0;
142
160
  if (newdist < dist) {
143
161
  vp[2] = tp;
@@ -149,20 +167,21 @@ static scs_float proj_primal_exp_cone_heuristic(const scs_float *v0,
149
167
  return dist;
150
168
  }
151
169
 
152
- /* try heuristic (cheap) projection */
170
+ /* Try heuristic (cheap) projection for polar cone */
153
171
  static scs_float proj_polar_exp_cone_heuristic(const scs_float *v0,
154
172
  scs_float *vd) {
155
173
  scs_float t0 = v0[2], s0 = v0[1], r0 = v0[0];
156
174
  scs_float dist, td, newdist;
157
- /* perspective boundary */
158
- vd[2] = MIN(t0, 0);
159
- vd[1] = MIN(s0, 0);
175
+
176
+ /* Perspective boundary */
177
+ vd[2] = MIN(t0, 0.0);
178
+ vd[1] = MIN(s0, 0.0);
160
179
  vd[0] = 0.0;
161
180
  dist = SCS(norm_diff)(v0, vd, 3);
162
181
 
163
- /* perspective interior */
182
+ /* Perspective interior */
164
183
  if (r0 > 0.0) {
165
- td = MIN(t0, -r0 * exp(s0 / r0 - 1));
184
+ td = MIN(t0, -r0 * EXPF(s0 / r0 - 1.0));
166
185
  newdist = t0 - td;
167
186
  if (newdist < dist) {
168
187
  vd[2] = td;
@@ -179,21 +198,20 @@ static scs_float ppsi(const scs_float *v0) {
179
198
  scs_float psi;
180
199
 
181
200
  if (r0 > s0) {
182
- psi = (r0 - s0 + sqrt(r0 * r0 + s0 * s0 - r0 * s0)) / r0;
201
+ psi = (r0 - s0 + SQRTF(r0 * r0 + s0 * s0 - r0 * s0)) / r0;
183
202
  } else {
184
- psi = -s0 / (r0 - s0 - sqrt(r0 * r0 + s0 * s0 - r0 * s0));
203
+ psi = -s0 / (r0 - s0 - SQRTF(r0 * r0 + s0 * s0 - r0 * s0));
185
204
  }
186
205
 
187
- return ((psi - 1) * r0 + s0) / (psi * (psi - 1) + 1);
206
+ return ((psi - 1.0) * r0 + s0) / (psi * (psi - 1.0) + 1.0);
188
207
  }
189
208
 
190
209
  static scs_float pomega(scs_float rho) {
191
- scs_float val = exp(rho) / (rho * (rho - 1) + 1);
210
+ scs_float val = EXPF(rho) / (rho * (rho - 1.0) + 1.0);
192
211
 
193
212
  if (rho < 2.0) {
194
- val = MIN(val, exp(2.0) / 3);
213
+ val = MIN(val, EXPF(2.0) / 3.0);
195
214
  }
196
-
197
215
  return val;
198
216
  }
199
217
 
@@ -202,21 +220,20 @@ static scs_float dpsi(const scs_float *v0) {
202
220
  scs_float psi;
203
221
 
204
222
  if (s0 > r0) {
205
- psi = (r0 - sqrt(r0 * r0 + s0 * s0 - r0 * s0)) / s0;
223
+ psi = (r0 - SQRTF(r0 * r0 + s0 * s0 - r0 * s0)) / s0;
206
224
  } else {
207
- psi = (r0 - s0) / (r0 + sqrt(r0 * r0 + s0 * s0 - r0 * s0));
225
+ psi = (r0 - s0) / (r0 + SQRTF(r0 * r0 + s0 * s0 - r0 * s0));
208
226
  }
209
227
 
210
- return (r0 - psi * s0) / (psi * (psi - 1) + 1);
228
+ return (r0 - psi * s0) / (psi * (psi - 1.0) + 1.0);
211
229
  }
212
230
 
213
231
  static scs_float domega(scs_float rho) {
214
- scs_float val = -exp(-rho) / (rho * (rho - 1) + 1);
232
+ scs_float val = -EXPF(-rho) / (rho * (rho - 1.0) + 1.0);
215
233
 
216
234
  if (rho > -1.0) {
217
- val = MAX(val, -exp(1.0) / 3);
235
+ val = MAX(val, -EXPF(1.0) / 3.0);
218
236
  }
219
-
220
237
  return val;
221
238
  }
222
239
 
@@ -225,41 +242,46 @@ static void exp_search_bracket(const scs_float *v0, scs_float pdist,
225
242
  scs_float ddist, scs_float *low_out,
226
243
  scs_float *upr_out) {
227
244
  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));
245
+ scs_float baselow = -EXP_CONE_INFINITY_VALUE;
246
+ scs_float baseupr = EXP_CONE_INFINITY_VALUE;
247
+ scs_float low = -EXP_CONE_INFINITY_VALUE;
248
+ scs_float upr = EXP_CONE_INFINITY_VALUE;
249
+ scs_float Dp, Dd, curbnd, fl, fu, df, tpu, tdl, sgn, val;
234
250
 
235
- scs_float curbnd, fl, fu, df, tpu, tdl;
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));
236
253
 
237
- if (t0 > 0) {
238
- curbnd = log(t0 / ppsi(v0));
254
+ if (t0 > 0.0) {
255
+ curbnd = LOGF(t0 / ppsi(v0));
239
256
  low = MAX(low, curbnd);
240
- } else if (t0 < 0) {
241
- curbnd = -log(-t0 / dpsi(v0));
257
+ } else if (t0 < 0.0) {
258
+ curbnd = -LOGF(-t0 / dpsi(v0));
242
259
  upr = MIN(upr, curbnd);
243
260
  }
244
261
 
245
- if (r0 > 0) {
246
- baselow = 1 - s0 / r0;
262
+ if (r0 > 0.0) {
263
+ baselow = 1.0 - s0 / r0;
247
264
  low = MAX(low, baselow);
248
265
  tpu = MAX(1e-12, MIN(Dd, Dp + t0));
249
- curbnd = MAX(low, baselow + tpu / r0 / pomega(low));
266
+ val = r0 * pomega(low);
267
+ sgn = val < 0 ? -1 : 1;
268
+ curbnd = MAX(low, baselow + SAFEDIV_POS(tpu, ABS(val)) * sgn);
250
269
  upr = MIN(upr, curbnd);
251
270
  }
252
271
 
253
- if (s0 > 0) {
272
+ if (s0 > 0.0) {
254
273
  baseupr = r0 / s0;
255
274
  upr = MIN(upr, baseupr);
256
275
  tdl = -MAX(1e-12, MIN(Dp, Dd - t0));
257
- curbnd = MIN(upr, baseupr - tdl / s0 / domega(upr));
276
+ val = s0 * domega(upr);
277
+ sgn = val < 0 ? -1 : 1;
278
+ curbnd = MIN(upr, baseupr - SAFEDIV_POS(tdl, ABS(val)) * sgn);
258
279
  low = MAX(low, curbnd);
259
280
  }
260
281
 
261
- /* Guarantee valid bracket */
262
- /* TODO do we need these 2 lines? */
282
+ /* Guarantee valid bracket. Floating point errors can push bounds
283
+ * slightly outside base interval or flip low/upr.
284
+ */
263
285
  low = _clip(MIN(low, upr), baselow, baseupr);
264
286
  upr = _clip(MAX(low, upr), baselow, baseupr);
265
287
 
@@ -267,7 +289,7 @@ static void exp_search_bracket(const scs_float *v0, scs_float pdist,
267
289
  hfun(v0, low, &fl, &df);
268
290
  hfun(v0, upr, &fu, &df);
269
291
 
270
- if (fl * fu > 0) {
292
+ if (fl * fu > 0.0) {
271
293
  if (ABS(fl) < ABS(fu)) {
272
294
  upr = low;
273
295
  } else {
@@ -283,11 +305,12 @@ static void exp_search_bracket(const scs_float *v0, scs_float pdist,
283
305
  /* convert from rho to primal projection */
284
306
  static scs_float proj_sol_primal_exp_cone(const scs_float *v0, scs_float rho,
285
307
  scs_float *vp) {
286
- scs_float linrho = (rho - 1) * v0[0] + v0[1];
287
- scs_float exprho = exp(rho);
308
+ scs_float linrho = (rho - 1.0) * v0[0] + v0[1];
309
+ scs_float exprho = EXPF(rho);
288
310
  scs_float quadrho, dist;
289
- if (linrho > 0 && _isfinite(exprho)) {
290
- quadrho = rho * (rho - 1) + 1;
311
+
312
+ if (linrho > 0.0 && _isfinite(exprho)) {
313
+ quadrho = rho * (rho - 1.0) + 1.0;
291
314
  vp[2] = exprho * linrho / quadrho;
292
315
  vp[1] = linrho / quadrho;
293
316
  vp[0] = rho * linrho / quadrho;
@@ -305,12 +328,13 @@ static scs_float proj_sol_primal_exp_cone(const scs_float *v0, scs_float rho,
305
328
  static scs_float proj_sol_polar_exp_cone(const scs_float *v0, scs_float rho,
306
329
  scs_float *vd) {
307
330
  scs_float linrho = v0[0] - rho * v0[1];
308
- scs_float exprho = exp(-rho);
331
+ scs_float exprho = EXPF(-rho);
309
332
  scs_float quadrho, dist;
310
- if (linrho > 0 && _isfinite(exprho)) {
311
- quadrho = rho * (rho - 1) + 1;
333
+
334
+ if (linrho > 0.0 && _isfinite(exprho)) {
335
+ quadrho = rho * (rho - 1.0) + 1.0;
312
336
  vd[2] = -exprho * linrho / quadrho;
313
- vd[1] = (1 - rho) * linrho / quadrho;
337
+ vd[1] = (1.0 - rho) * linrho / quadrho;
314
338
  vd[0] = linrho / quadrho;
315
339
  dist = SCS(norm_diff)(v0, vd, 3);
316
340
  } else {
@@ -322,12 +346,6 @@ static scs_float proj_sol_polar_exp_cone(const scs_float *v0, scs_float rho,
322
346
  return dist;
323
347
  }
324
348
 
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
349
  /* Project onto primal or dual exponential cone, performed in-place.
332
350
  * If `primal=0` then project on the dual cone, otherwise project
333
351
  * onto primal. Taken from algorithm in Friberg, 2021.
@@ -337,14 +355,15 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
337
355
  scs_float xl, xh, pdist, ddist, err, rho, dist_hat;
338
356
  scs_float vp[3], vd[3], v_hat[3];
339
357
  scs_int opt;
358
+
340
359
  if (!primal) {
341
360
  /* This routine actually projects onto primal and polar cones
342
361
  * simultaneously. So to make it project onto dual, use this:
343
362
  * Pi_{C^*}(v0) = -Pi_{C^polar}(-v0)
344
363
  */
345
- v0[0] *= -1.;
346
- v0[1] *= -1.;
347
- v0[2] *= -1.;
364
+ v0[0] *= -1.0;
365
+ v0[1] *= -1.0;
366
+ v0[2] *= -1.0;
348
367
  }
349
368
 
350
369
  pdist = proj_primal_exp_cone_heuristic(v0, vp);
@@ -357,15 +376,16 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
357
376
  /* Skip root search if presolve rules apply
358
377
  * or optimality conditions are satisfied
359
378
  */
360
- opt = (v0[1] <= 0 && v0[0] <= 0);
379
+ opt = (v0[1] <= 0.0 && v0[0] <= 0.0);
361
380
  opt |= (MIN(pdist, ddist) <= TOL);
362
381
  opt |= (err <= TOL && SCS(dot)(vp, vd, 3) <= TOL);
382
+
363
383
  if (opt) {
364
384
  if (primal) {
365
385
  _copy(v0, vp);
366
386
  return pdist;
367
387
  }
368
- /* polar cone -> dual cone */
388
+ /* Polar cone -> dual cone. */
369
389
  v0[0] = -vd[0];
370
390
  v0[1] = -vd[1];
371
391
  v0[2] = -vd[2];
@@ -376,7 +396,7 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
376
396
  rho = root_search_newton(v0, xl, xh, 0.5 * (xl + xh));
377
397
 
378
398
  if (primal) {
379
- /* primal cone projection */
399
+ /* Primal cone projection. */
380
400
  dist_hat = proj_sol_primal_exp_cone(v0, rho, v_hat);
381
401
  if (dist_hat <= pdist) {
382
402
  _copy(vp, v_hat);
@@ -385,13 +405,15 @@ scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal) {
385
405
  _copy(v0, vp);
386
406
  return pdist;
387
407
  }
388
- /* polar cone projection */
408
+
409
+ /* Polar cone projection. */
389
410
  dist_hat = proj_sol_polar_exp_cone(v0, rho, v_hat);
390
411
  if (dist_hat <= ddist) {
391
412
  _copy(vd, v_hat);
392
413
  ddist = dist_hat;
393
414
  }
394
- /* polar cone -> dual cone */
415
+
416
+ /* Polar cone -> dual cone. */
395
417
  v0[0] = -vd[0];
396
418
  v0[1] = -vd[1];
397
419
  v0[2] = -vd[2];
@@ -86,6 +86,9 @@ void SCS(add_scaled_array)(scs_float *a, const scs_float *b, scs_int n,
86
86
  scs_float SCS(mean)(const scs_float *x, scs_int n) {
87
87
  scs_int i;
88
88
  scs_float mean = 0.;
89
+ if (n == 0) {
90
+ return 0.;
91
+ }
89
92
  for (i = 0; i < n; ++i) {
90
93
  mean += x[i];
91
94
  }
@@ -178,6 +181,9 @@ scs_float SCS(mean)(const scs_float *x, scs_int n) {
178
181
  blas_int bzero = 0;
179
182
  blas_int blen = (blas_int)n;
180
183
  scs_float y = 1.0;
184
+ if (n == 0) {
185
+ return 0.;
186
+ }
181
187
  return BLAS(dot)(&blen, x, &bone, &y, &bzero) / n;
182
188
  }
183
189
 
data/vendor/scs/src/scs.c CHANGED
@@ -900,7 +900,7 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
900
900
  }
901
901
  if (w->stgs->acceleration_lookback) {
902
902
  /* TODO(HACK!) negative acceleration_lookback interpreted as type-II */
903
- if (!(w->accel = aa_init(l, ABS(w->stgs->acceleration_lookback),
903
+ if (!(w->accel = aa_init(l, abs(w->stgs->acceleration_lookback),
904
904
  w->stgs->acceleration_lookback > 0,
905
905
  w->stgs->acceleration_lookback > 0
906
906
  ? AA_REGULARIZATION_TYPE_1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
169
  - !ruby/object:Gem::Version
170
170
  version: '0'
171
171
  requirements: []
172
- rubygems_version: 3.6.9
172
+ rubygems_version: 4.0.3
173
173
  specification_version: 4
174
174
  summary: SCS - the splitting conic solver - for Ruby
175
175
  test_files: []