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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +1 -1
- data/lib/scs/ffi.rb +18 -0
- data/lib/scs/version.rb +1 -1
- data/vendor/scs/CITATION.cff +2 -3
- data/vendor/scs/CLAUDE.md +115 -0
- data/vendor/scs/CMakeLists.txt +330 -201
- data/vendor/scs/CONTRIBUTING.md +49 -0
- data/vendor/scs/Makefile +90 -43
- data/vendor/scs/README.md +96 -2
- data/vendor/scs/include/aa.h +59 -11
- data/vendor/scs/include/aa_stats.h +47 -0
- data/vendor/scs/include/cones.h +16 -7
- data/vendor/scs/include/glbopts.h +113 -26
- data/vendor/scs/include/linalg.h +5 -1
- data/vendor/scs/include/linsys.h +1 -2
- data/vendor/scs/include/normalize.h +6 -2
- data/vendor/scs/include/rw.h +9 -3
- data/vendor/scs/include/scs.h +17 -1
- data/vendor/scs/include/scs_blas.h +8 -0
- data/vendor/scs/include/scs_types.h +2 -2
- data/vendor/scs/include/scs_work.h +22 -1
- data/vendor/scs/include/util.h +3 -6
- data/vendor/scs/include/util_spectral_cones.h +3 -3
- data/vendor/scs/linsys/accelerate/direct/private.c +126 -0
- data/vendor/scs/linsys/accelerate/direct/private.h +34 -0
- data/vendor/scs/linsys/cpu/dense/private.c +250 -0
- data/vendor/scs/linsys/cpu/dense/private.h +31 -0
- data/vendor/scs/linsys/cpu/direct/private.c +86 -44
- data/vendor/scs/linsys/cpu/indirect/private.c +647 -113
- data/vendor/scs/linsys/cpu/indirect/private.h +28 -0
- data/vendor/scs/linsys/csparse.c +9 -3
- data/vendor/scs/linsys/csparse.h +4 -2
- data/vendor/scs/linsys/cudss/direct/private.c +77 -43
- data/vendor/scs/linsys/cudss/direct/private.h +33 -7
- data/vendor/scs/linsys/gpu/indirect/private.c +6 -2
- data/vendor/scs/linsys/mkl/direct/private.c +63 -31
- data/vendor/scs/linsys/mkl/direct/private.h +0 -1
- data/vendor/scs/linsys/scs_matrix.c +291 -165
- data/vendor/scs/linsys/scs_matrix.h +7 -9
- data/vendor/scs/scs.mk +35 -13
- data/vendor/scs/src/aa.c +673 -166
- data/vendor/scs/src/cones.c +545 -223
- data/vendor/scs/src/ctrlc.c +59 -16
- data/vendor/scs/src/exp_cone.c +70 -50
- data/vendor/scs/src/linalg.c +21 -2
- data/vendor/scs/src/normalize.c +24 -26
- data/vendor/scs/src/rw.c +596 -124
- data/vendor/scs/src/scs.c +990 -513
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +240 -187
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +108 -85
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +62 -63
- data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +85 -78
- data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +92 -97
- data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +39 -28
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +59 -40
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +37 -29
- data/vendor/scs/src/spectral_cones/util_spectral_cones.c +12 -6
- data/vendor/scs/src/util.c +37 -7
- data/vendor/scs/test/mkl_interface_mismatch.c +97 -0
- data/vendor/scs/test/packaging/CMakeLists.txt +21 -0
- data/vendor/scs/test/packaging/consume.c +50 -0
- data/vendor/scs/test/problem_utils.h +9 -2
- data/vendor/scs/test/problems/dense_qp.h +64 -0
- data/vendor/scs/test/problems/hs21_tiny_qp.h +6 -2
- data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +10 -2
- data/vendor/scs/test/problems/infeasible_lp.h +66 -0
- data/vendor/scs/test/problems/infeasible_socp.h +75 -0
- data/vendor/scs/test/problems/lp_update.h +110 -0
- data/vendor/scs/test/problems/qafiro_tiny_qp.h +3 -1
- data/vendor/scs/test/problems/rob_gauss_cov_est.h +6 -3
- data/vendor/scs/test/problems/small_qp.h +5 -0
- data/vendor/scs/test/problems/test_box_cone.h +92 -0
- data/vendor/scs/test/problems/test_dual_exp_cone.h +96 -0
- data/vendor/scs/test/problems/test_inaccurate.h +212 -0
- data/vendor/scs/test/problems/test_mixed_cones.h +113 -0
- data/vendor/scs/test/problems/test_normalize_roundtrip.h +279 -0
- data/vendor/scs/test/problems/test_power_cone.h +414 -0
- data/vendor/scs/test/problems/test_psd_metric.h +109 -0
- data/vendor/scs/test/problems/test_psd_n1.h +84 -0
- data/vendor/scs/test/problems/test_root_plus.h +191 -0
- data/vendor/scs/test/problems/test_rw_settings.h +112 -0
- data/vendor/scs/test/problems/test_soc_sizes.h +393 -0
- data/vendor/scs/test/problems/test_solver_options.h +558 -0
- data/vendor/scs/test/problems/test_validation.h +263 -24
- data/vendor/scs/test/problems/test_zero_cone.h +81 -0
- data/vendor/scs/test/problems/unbounded_lp.h +66 -0
- data/vendor/scs/test/problems/unbounded_socp.h +71 -0
- data/vendor/scs/test/run_from_file.c +6 -0
- data/vendor/scs/test/run_tests.c +71 -0
- data/vendor/scs/test/rw_settings.c +7 -0
- data/vendor/scs/test/spectral_cones_problems/test_ell1_and_nuc.h +128 -0
- data/vendor/scs/test/spectral_cones_problems/test_ell1_cone.h +115 -0
- metadata +35 -3
data/vendor/scs/src/cones.c
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Cone projection implementation.
|
|
3
|
+
*
|
|
4
|
+
* Handles projection onto the dual cone for all supported cone types:
|
|
5
|
+
* zero/free, non-negative, box, second-order, semidefinite, complex
|
|
6
|
+
* semidefinite, exponential, power, and (optionally) spectral cones.
|
|
7
|
+
* Uses Moreau decomposition: proj_K*(x) = x + proj_K(-x).
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
#include "cones.h"
|
|
2
11
|
#include "linalg.h"
|
|
3
12
|
#include "scs.h"
|
|
4
13
|
#include "scs_blas.h" /* contains BLAS(X) macros and type info */
|
|
5
14
|
#include "util.h"
|
|
6
15
|
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <string.h>
|
|
18
|
+
|
|
7
19
|
/*
|
|
8
20
|
* Cross-platform Complex Type Handling
|
|
9
21
|
* MSVC uses struct layout; GCC/Clang uses C99 _Complex
|
|
@@ -105,9 +117,7 @@ scs_int SCS(proj_sum_largest_evals)(scs_float *tX, scs_int n, scs_int k,
|
|
|
105
117
|
/* Forward declare exponential cone projection (exp_cone.c) */
|
|
106
118
|
scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal);
|
|
107
119
|
|
|
108
|
-
/*
|
|
109
|
-
* Memory Management
|
|
110
|
-
*/
|
|
120
|
+
/* ======================== Memory Management ======================== */
|
|
111
121
|
|
|
112
122
|
void SCS(free_cone)(ScsCone *k) {
|
|
113
123
|
if (k) {
|
|
@@ -141,13 +151,31 @@ void SCS(free_cone)(ScsCone *k) {
|
|
|
141
151
|
}
|
|
142
152
|
}
|
|
143
153
|
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
scs_int SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
155
|
+
memset(dest, 0, sizeof(*dest));
|
|
156
|
+
dest->z = src->z;
|
|
157
|
+
dest->l = src->l;
|
|
158
|
+
dest->bsize = src->bsize;
|
|
159
|
+
dest->qsize = src->qsize;
|
|
160
|
+
dest->ssize = src->ssize;
|
|
161
|
+
dest->cssize = src->cssize;
|
|
162
|
+
dest->ep = src->ep;
|
|
163
|
+
dest->ed = src->ed;
|
|
164
|
+
dest->psize = src->psize;
|
|
165
|
+
#ifdef USE_SPECTRAL_CONES
|
|
166
|
+
dest->dsize = src->dsize;
|
|
167
|
+
dest->nucsize = src->nucsize;
|
|
168
|
+
dest->ell1_size = src->ell1_size;
|
|
169
|
+
dest->sl_size = src->sl_size;
|
|
170
|
+
#endif
|
|
146
171
|
|
|
147
172
|
/* Box cone */
|
|
148
173
|
if (src->bsize > 1) {
|
|
149
174
|
dest->bu = (scs_float *)scs_calloc(src->bsize - 1, sizeof(scs_float));
|
|
150
175
|
dest->bl = (scs_float *)scs_calloc(src->bsize - 1, sizeof(scs_float));
|
|
176
|
+
if (!dest->bu || !dest->bl) {
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
151
179
|
memcpy(dest->bu, src->bu, (src->bsize - 1) * sizeof(scs_float));
|
|
152
180
|
memcpy(dest->bl, src->bl, (src->bsize - 1) * sizeof(scs_float));
|
|
153
181
|
} else {
|
|
@@ -158,6 +186,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
158
186
|
/* SOC */
|
|
159
187
|
if (src->qsize > 0) {
|
|
160
188
|
dest->q = (scs_int *)scs_calloc(src->qsize, sizeof(scs_int));
|
|
189
|
+
if (!dest->q) {
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
161
192
|
memcpy(dest->q, src->q, src->qsize * sizeof(scs_int));
|
|
162
193
|
} else {
|
|
163
194
|
dest->q = SCS_NULL;
|
|
@@ -166,6 +197,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
166
197
|
/* PSD */
|
|
167
198
|
if (src->ssize > 0) {
|
|
168
199
|
dest->s = (scs_int *)scs_calloc(src->ssize, sizeof(scs_int));
|
|
200
|
+
if (!dest->s) {
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
169
203
|
memcpy(dest->s, src->s, src->ssize * sizeof(scs_int));
|
|
170
204
|
} else {
|
|
171
205
|
dest->s = SCS_NULL;
|
|
@@ -174,6 +208,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
174
208
|
/* Complex PSD */
|
|
175
209
|
if (src->cssize > 0) {
|
|
176
210
|
dest->cs = (scs_int *)scs_calloc(src->cssize, sizeof(scs_int));
|
|
211
|
+
if (!dest->cs) {
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
177
214
|
memcpy(dest->cs, src->cs, src->cssize * sizeof(scs_int));
|
|
178
215
|
} else {
|
|
179
216
|
dest->cs = SCS_NULL;
|
|
@@ -182,6 +219,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
182
219
|
/* Power */
|
|
183
220
|
if (src->psize > 0) {
|
|
184
221
|
dest->p = (scs_float *)scs_calloc(src->psize, sizeof(scs_float));
|
|
222
|
+
if (!dest->p) {
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
185
225
|
memcpy(dest->p, src->p, src->psize * sizeof(scs_float));
|
|
186
226
|
} else {
|
|
187
227
|
dest->p = SCS_NULL;
|
|
@@ -191,6 +231,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
191
231
|
/* Logdet */
|
|
192
232
|
if (src->dsize > 0) {
|
|
193
233
|
dest->d = (scs_int *)scs_calloc(src->dsize, sizeof(scs_int));
|
|
234
|
+
if (!dest->d) {
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
194
237
|
memcpy(dest->d, src->d, src->dsize * sizeof(scs_int));
|
|
195
238
|
} else {
|
|
196
239
|
dest->d = SCS_NULL;
|
|
@@ -200,6 +243,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
200
243
|
if (src->nucsize > 0) {
|
|
201
244
|
dest->nuc_m = (scs_int *)scs_calloc(src->nucsize, sizeof(scs_int));
|
|
202
245
|
dest->nuc_n = (scs_int *)scs_calloc(src->nucsize, sizeof(scs_int));
|
|
246
|
+
if (!dest->nuc_m || !dest->nuc_n) {
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
203
249
|
memcpy(dest->nuc_m, src->nuc_m, src->nucsize * sizeof(scs_int));
|
|
204
250
|
memcpy(dest->nuc_n, src->nuc_n, src->nucsize * sizeof(scs_int));
|
|
205
251
|
} else {
|
|
@@ -210,6 +256,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
210
256
|
/* Ell1 */
|
|
211
257
|
if (src->ell1_size > 0) {
|
|
212
258
|
dest->ell1 = (scs_int *)scs_calloc(src->ell1_size, sizeof(scs_int));
|
|
259
|
+
if (!dest->ell1) {
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
213
262
|
memcpy(dest->ell1, src->ell1, src->ell1_size * sizeof(scs_int));
|
|
214
263
|
} else {
|
|
215
264
|
dest->ell1 = SCS_NULL;
|
|
@@ -219,6 +268,9 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
219
268
|
if (src->sl_size > 0) {
|
|
220
269
|
dest->sl_n = (scs_int *)scs_calloc(src->sl_size, sizeof(scs_int));
|
|
221
270
|
dest->sl_k = (scs_int *)scs_calloc(src->sl_size, sizeof(scs_int));
|
|
271
|
+
if (!dest->sl_n || !dest->sl_k) {
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
222
274
|
memcpy(dest->sl_n, src->sl_n, src->sl_size * sizeof(scs_int));
|
|
223
275
|
memcpy(dest->sl_k, src->sl_k, src->sl_size * sizeof(scs_int));
|
|
224
276
|
} else {
|
|
@@ -226,11 +278,70 @@ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
|
|
|
226
278
|
dest->sl_k = SCS_NULL;
|
|
227
279
|
}
|
|
228
280
|
#endif
|
|
281
|
+
return 1;
|
|
229
282
|
}
|
|
230
283
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
284
|
+
void SCS(finish_cone)(ScsConeWork *c) {
|
|
285
|
+
if (!c)
|
|
286
|
+
return;
|
|
287
|
+
#ifdef USE_LAPACK
|
|
288
|
+
if (c->Xs)
|
|
289
|
+
scs_free(c->Xs);
|
|
290
|
+
if (c->cXs)
|
|
291
|
+
scs_free(c->cXs);
|
|
292
|
+
if (c->Z)
|
|
293
|
+
scs_free(c->Z);
|
|
294
|
+
if (c->cZ)
|
|
295
|
+
scs_free(c->cZ);
|
|
296
|
+
if (c->e)
|
|
297
|
+
scs_free(c->e);
|
|
298
|
+
if (c->isuppz)
|
|
299
|
+
scs_free(c->isuppz);
|
|
300
|
+
if (c->work)
|
|
301
|
+
scs_free(c->work);
|
|
302
|
+
if (c->iwork)
|
|
303
|
+
scs_free(c->iwork);
|
|
304
|
+
if (c->cwork)
|
|
305
|
+
scs_free(c->cwork);
|
|
306
|
+
/* c->rwork is aliased to c->work in setup, no free needed */
|
|
307
|
+
#endif
|
|
308
|
+
if (c->cone_boundaries)
|
|
309
|
+
scs_free(c->cone_boundaries);
|
|
310
|
+
if (c->cone_boundaries_psd_n)
|
|
311
|
+
scs_free(c->cone_boundaries_psd_n);
|
|
312
|
+
if (c->psd_gamma)
|
|
313
|
+
scs_free(c->psd_gamma);
|
|
314
|
+
if (c->r_box_inv)
|
|
315
|
+
scs_free(c->r_box_inv);
|
|
316
|
+
if (c->s)
|
|
317
|
+
scs_free(c->s);
|
|
318
|
+
|
|
319
|
+
#ifdef USE_SPECTRAL_CONES
|
|
320
|
+
if (c->work_logdet)
|
|
321
|
+
scs_free(c->work_logdet);
|
|
322
|
+
if (c->saved_log_projs)
|
|
323
|
+
scs_free(c->saved_log_projs);
|
|
324
|
+
if (c->s_nuc)
|
|
325
|
+
scs_free(c->s_nuc);
|
|
326
|
+
if (c->u_nuc)
|
|
327
|
+
scs_free(c->u_nuc);
|
|
328
|
+
if (c->vt_nuc)
|
|
329
|
+
scs_free(c->vt_nuc);
|
|
330
|
+
if (c->work_nuc)
|
|
331
|
+
scs_free(c->work_nuc);
|
|
332
|
+
if (c->work_sum_of_largest)
|
|
333
|
+
scs_free(c->work_sum_of_largest);
|
|
334
|
+
if (c->log_cone_warmstarts)
|
|
335
|
+
scs_free(c->log_cone_warmstarts);
|
|
336
|
+
if (c->work_ell1)
|
|
337
|
+
scs_free(c->work_ell1);
|
|
338
|
+
if (c->work_ell1_proj)
|
|
339
|
+
scs_free(c->work_ell1_proj);
|
|
340
|
+
#endif
|
|
341
|
+
scs_free(c);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ========================= Cone Utilities ============================ */
|
|
234
345
|
|
|
235
346
|
static inline scs_int get_sd_cone_size(scs_int s) {
|
|
236
347
|
return (s * (s + 1)) / 2;
|
|
@@ -255,17 +366,102 @@ void SCS(set_r_y)(const ScsConeWork *c, scs_float scale, scs_float *r_y) {
|
|
|
255
366
|
}
|
|
256
367
|
}
|
|
257
368
|
|
|
258
|
-
/*
|
|
369
|
+
/* svec packed index of entry (i, j), i >= j, in an n x n lower
|
|
370
|
+
* triangle stored column-major. */
|
|
371
|
+
static scs_int svec_idx(scs_int i, scs_int j, scs_int n) {
|
|
372
|
+
return j * n - (j * (j - 1)) / 2 + (i - j);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Fit the rank-one form w_ij = delta_i * delta_j to a positive profile
|
|
376
|
+
* on one PSD block (svec packed, length n(n+1)/2) by least squares in
|
|
377
|
+
* log space, and overwrite the block with the fitted values. This is
|
|
378
|
+
* the richest svec-diagonal metric that maps the PSD cone to itself
|
|
379
|
+
* (the diagonal congruence X -> diag(delta) X diag(delta)), so unlike
|
|
380
|
+
* generic cones the block need not collapse to a single scalar.
|
|
381
|
+
*
|
|
382
|
+
* Normal equations: minimizing sum_{i<=j} (l_i + l_j - y_ij)^2 gives
|
|
383
|
+
* A'A = (n+2) I + 11', inverted in closed form by Sherman-Morrison.
|
|
384
|
+
* The fitted delta_i are clamped so every product w_ij stays within
|
|
385
|
+
* [DIAG_SCALE_MULT_MIN, DIAG_SCALE_MULT_MAX], preserving rank-one
|
|
386
|
+
* structure (clamping products directly would break it). */
|
|
387
|
+
static void fit_psd_block_weights(scs_float *vec, scs_int n) {
|
|
388
|
+
scs_int i, j;
|
|
389
|
+
scs_float alpha = (scs_float)(n + 2);
|
|
390
|
+
scs_float rhs_sum = 0., corr, li;
|
|
391
|
+
scs_float lo = SQRTF(DIAG_SCALE_MULT_MIN), hi = SQRTF(DIAG_SCALE_MULT_MAX);
|
|
392
|
+
scs_float *ell = (scs_float *)scs_calloc(n, sizeof(scs_float));
|
|
393
|
+
if (!ell) {
|
|
394
|
+
return; /* leave profile as-is; caller falls back to scalar */
|
|
395
|
+
}
|
|
396
|
+
/* Floor entries relative to the block max: (near-)zero rows carry no
|
|
397
|
+
* information about the block's scale structure (e.g. unconstrained
|
|
398
|
+
* off-diagonal svec rows are exactly zero in the equilibration
|
|
399
|
+
* profile) and an absolute floor lets them dominate the fit. */
|
|
400
|
+
{
|
|
401
|
+
scs_float vmax = 0., vfloor;
|
|
402
|
+
scs_int sz = (n * (n + 1)) / 2;
|
|
403
|
+
for (i = 0; i < sz; ++i) {
|
|
404
|
+
vmax = MAX(vmax, vec[i]);
|
|
405
|
+
}
|
|
406
|
+
vfloor = MAX(1e-3 * vmax, 1e-12);
|
|
407
|
+
for (i = 0; i < sz; ++i) {
|
|
408
|
+
vec[i] = MAX(vec[i], vfloor);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* rhs_k = 2 y_kk + sum_{j != k} y_kj */
|
|
413
|
+
for (j = 0; j < n; ++j) {
|
|
414
|
+
for (i = j; i < n; ++i) {
|
|
415
|
+
scs_float y = log(vec[svec_idx(i, j, n)]);
|
|
416
|
+
if (i == j) {
|
|
417
|
+
ell[i] += 2. * y;
|
|
418
|
+
} else {
|
|
419
|
+
ell[i] += y;
|
|
420
|
+
ell[j] += y;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
for (i = 0; i < n; ++i) {
|
|
425
|
+
rhs_sum += ell[i];
|
|
426
|
+
}
|
|
427
|
+
/* (alpha I + 11')^{-1} rhs = rhs/alpha - 1 (1'rhs) / (alpha(alpha+n)) */
|
|
428
|
+
corr = rhs_sum / (alpha * (alpha + (scs_float)n));
|
|
429
|
+
for (i = 0; i < n; ++i) {
|
|
430
|
+
li = exp(ell[i] / alpha - corr);
|
|
431
|
+
ell[i] = MIN(MAX(li, lo), hi);
|
|
432
|
+
}
|
|
433
|
+
for (j = 0; j < n; ++j) {
|
|
434
|
+
for (i = j; i < n; ++i) {
|
|
435
|
+
vec[svec_idx(i, j, n)] = ell[i] * ell[j];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
scs_free(ell);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* The function f aggregates the entries within each cone. With
|
|
442
|
+
* psd_rank1 set, real PSD blocks instead get the rank-one (diagonal
|
|
443
|
+
* congruence) fit, which is cone-invariant and strictly more
|
|
444
|
+
* expressive than a single scalar. The fit is only safe for the
|
|
445
|
+
* one-shot dynamic metric update: applied inside the iterated Ruiz
|
|
446
|
+
* equilibration loop it compounds pass over pass instead of
|
|
447
|
+
* contracting and destroys the scaling (mcp100 diverges), so the
|
|
448
|
+
* equilibration call sites keep the scalar collapse. */
|
|
259
449
|
void SCS(enforce_cone_boundaries)(const ScsConeWork *c, scs_float *vec,
|
|
260
|
-
scs_float (*f)(const scs_float *, scs_int)
|
|
450
|
+
scs_float (*f)(const scs_float *, scs_int),
|
|
451
|
+
scs_int psd_rank1) {
|
|
261
452
|
scs_int i, j, delta;
|
|
262
453
|
scs_int count = c->cone_boundaries[0];
|
|
263
454
|
scs_float wrk;
|
|
264
455
|
for (i = 1; i < c->cone_boundaries_len; ++i) {
|
|
265
456
|
delta = c->cone_boundaries[i];
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
vec[
|
|
457
|
+
if (psd_rank1 && c->cone_boundaries_psd_n &&
|
|
458
|
+
c->cone_boundaries_psd_n[i] > 1) {
|
|
459
|
+
fit_psd_block_weights(&(vec[count]), c->cone_boundaries_psd_n[i]);
|
|
460
|
+
} else {
|
|
461
|
+
wrk = f(&(vec[count]), delta);
|
|
462
|
+
for (j = count; j < count + delta; ++j) {
|
|
463
|
+
vec[j] = wrk;
|
|
464
|
+
}
|
|
269
465
|
}
|
|
270
466
|
count += delta;
|
|
271
467
|
}
|
|
@@ -287,13 +483,16 @@ void set_cone_boundaries(const ScsCone *k, ScsConeWork *c) {
|
|
|
287
483
|
k->qsize + k->ssize + k->cssize + k->ed + k->ep + k->psize;
|
|
288
484
|
#endif
|
|
289
485
|
scs_int *b = (scs_int *)scs_calloc(total_cones + 1, sizeof(scs_int));
|
|
486
|
+
scs_int *psd_n = (scs_int *)scs_calloc(total_cones + 1, sizeof(scs_int));
|
|
290
487
|
|
|
291
488
|
/* Cones that can be scaled independently */
|
|
292
489
|
b[count++] = k->z + k->l + k->bsize;
|
|
293
490
|
for (i = 0; i < k->qsize; ++i)
|
|
294
491
|
b[count++] = k->q[i];
|
|
295
|
-
for (i = 0; i < k->ssize; ++i)
|
|
492
|
+
for (i = 0; i < k->ssize; ++i) {
|
|
493
|
+
psd_n[count] = k->s[i];
|
|
296
494
|
b[count++] = get_sd_cone_size(k->s[i]);
|
|
495
|
+
}
|
|
297
496
|
for (i = 0; i < k->cssize; ++i)
|
|
298
497
|
b[count++] = get_csd_cone_size(k->cs[i]);
|
|
299
498
|
for (i = 0; i < k->ep + k->ed; ++i)
|
|
@@ -314,6 +513,7 @@ void set_cone_boundaries(const ScsCone *k, ScsConeWork *c) {
|
|
|
314
513
|
|
|
315
514
|
c->cone_boundaries = b;
|
|
316
515
|
c->cone_boundaries_len = total_cones + 1;
|
|
516
|
+
c->cone_boundaries_psd_n = psd_n;
|
|
317
517
|
}
|
|
318
518
|
|
|
319
519
|
static scs_int get_full_cone_dims(const ScsCone *k) {
|
|
@@ -338,36 +538,183 @@ static scs_int get_full_cone_dims(const ScsCone *k) {
|
|
|
338
538
|
return dims;
|
|
339
539
|
}
|
|
340
540
|
|
|
541
|
+
/* If buf is NULL, only accumulate the length that would be written.
|
|
542
|
+
* Otherwise append chunk, including its trailing '\0', at the current offset.
|
|
543
|
+
*/
|
|
544
|
+
static void append_to_header(char *buf, size_t *len, const char *chunk) {
|
|
545
|
+
size_t chunk_len = strlen(chunk);
|
|
546
|
+
if (buf) {
|
|
547
|
+
memcpy(buf + *len, chunk, chunk_len + 1);
|
|
548
|
+
}
|
|
549
|
+
*len += chunk_len;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/* Format the cone summary in one shared code path.
|
|
553
|
+
*
|
|
554
|
+
* When buf is NULL, this function performs a sizing pass only and updates
|
|
555
|
+
* *len with the number of characters required for the final string.
|
|
556
|
+
* When buf is non-NULL, it writes the same formatted output into buf and
|
|
557
|
+
* advances *len as it appends each line.
|
|
558
|
+
*
|
|
559
|
+
* This supports a two-pass implementation in get_cone_header:
|
|
560
|
+
* 1. call with buf == NULL to compute the exact allocation size
|
|
561
|
+
* 2. allocate once
|
|
562
|
+
* 3. call again with buf != NULL to render the final string
|
|
563
|
+
*/
|
|
564
|
+
static void format_cone_header(const ScsCone *k, char *buf, size_t *len) {
|
|
565
|
+
char line[128];
|
|
566
|
+
scs_int i, count;
|
|
567
|
+
#ifdef USE_SPECTRAL_CONES
|
|
568
|
+
scs_int ell1_vars, log_vars, nuc_vars, sl_vars;
|
|
569
|
+
#endif
|
|
570
|
+
|
|
571
|
+
append_to_header(buf, len, "cones: ");
|
|
572
|
+
if (k->z) {
|
|
573
|
+
sprintf(line, "\t z: primal zero / dual free vars: %li\n", (long)k->z);
|
|
574
|
+
append_to_header(buf, len, line);
|
|
575
|
+
}
|
|
576
|
+
if (k->l) {
|
|
577
|
+
sprintf(line, "\t l: linear vars: %li\n", (long)k->l);
|
|
578
|
+
append_to_header(buf, len, line);
|
|
579
|
+
}
|
|
580
|
+
if (k->bsize) {
|
|
581
|
+
sprintf(line, "\t b: box cone vars: %li\n", (long)k->bsize);
|
|
582
|
+
append_to_header(buf, len, line);
|
|
583
|
+
}
|
|
584
|
+
if (k->qsize) {
|
|
585
|
+
count = 0;
|
|
586
|
+
for (i = 0; i < k->qsize; ++i)
|
|
587
|
+
count += k->q[i];
|
|
588
|
+
sprintf(line, "\t q: soc vars: %li, qsize: %li\n", (long)count,
|
|
589
|
+
(long)k->qsize);
|
|
590
|
+
append_to_header(buf, len, line);
|
|
591
|
+
}
|
|
592
|
+
if (k->ssize) {
|
|
593
|
+
count = 0;
|
|
594
|
+
for (i = 0; i < k->ssize; ++i)
|
|
595
|
+
count += get_sd_cone_size(k->s[i]);
|
|
596
|
+
sprintf(line, "\t s: psd vars: %li, ssize: %li\n", (long)count,
|
|
597
|
+
(long)k->ssize);
|
|
598
|
+
append_to_header(buf, len, line);
|
|
599
|
+
}
|
|
600
|
+
if (k->cssize) {
|
|
601
|
+
count = 0;
|
|
602
|
+
for (i = 0; i < k->cssize; ++i)
|
|
603
|
+
count += get_csd_cone_size(k->cs[i]);
|
|
604
|
+
sprintf(line, "\t cs: complex psd vars: %li, cssize: %li\n", (long)count,
|
|
605
|
+
(long)k->cssize);
|
|
606
|
+
append_to_header(buf, len, line);
|
|
607
|
+
}
|
|
608
|
+
if (k->ep || k->ed) {
|
|
609
|
+
sprintf(line, "\t e: exp vars: %li, dual exp vars: %li\n",
|
|
610
|
+
(long)(3 * k->ep), (long)(3 * k->ed));
|
|
611
|
+
append_to_header(buf, len, line);
|
|
612
|
+
}
|
|
613
|
+
if (k->psize) {
|
|
614
|
+
sprintf(line, "\t p: primal + dual power vars: %li\n",
|
|
615
|
+
(long)(3 * k->psize));
|
|
616
|
+
append_to_header(buf, len, line);
|
|
617
|
+
}
|
|
618
|
+
#ifdef USE_SPECTRAL_CONES
|
|
619
|
+
log_vars = 0;
|
|
620
|
+
if (k->dsize && k->d) {
|
|
621
|
+
for (i = 0; i < k->dsize; i++) {
|
|
622
|
+
log_vars += get_sd_cone_size(k->d[i]) + 2;
|
|
623
|
+
}
|
|
624
|
+
sprintf(line, "\t d: logdet vars: %li, dsize: %li\n", (long)log_vars,
|
|
625
|
+
(long)k->dsize);
|
|
626
|
+
append_to_header(buf, len, line);
|
|
627
|
+
}
|
|
628
|
+
nuc_vars = 0;
|
|
629
|
+
if (k->nucsize && k->nuc_m && k->nuc_n) {
|
|
630
|
+
for (i = 0; i < k->nucsize; i++) {
|
|
631
|
+
nuc_vars += k->nuc_m[i] * k->nuc_n[i] + 1;
|
|
632
|
+
}
|
|
633
|
+
sprintf(line, "\t nuc: nuclear vars: %li, nucsize: %li\n",
|
|
634
|
+
(long)nuc_vars, (long)k->nucsize);
|
|
635
|
+
append_to_header(buf, len, line);
|
|
636
|
+
}
|
|
637
|
+
ell1_vars = 0;
|
|
638
|
+
if (k->ell1_size && k->ell1) {
|
|
639
|
+
for (i = 0; i < k->ell1_size; ++i) {
|
|
640
|
+
ell1_vars += k->ell1[i] + 1;
|
|
641
|
+
}
|
|
642
|
+
sprintf(line, "\t ell1: ell1 vars: %li, ell1_size: %li\n",
|
|
643
|
+
(long)ell1_vars, (long)k->ell1_size);
|
|
644
|
+
append_to_header(buf, len, line);
|
|
645
|
+
}
|
|
646
|
+
sl_vars = 0;
|
|
647
|
+
if (k->sl_size && k->sl_n) {
|
|
648
|
+
for (i = 0; i < k->sl_size; ++i) {
|
|
649
|
+
sl_vars += get_sd_cone_size(k->sl_n[i]) + 1;
|
|
650
|
+
}
|
|
651
|
+
sprintf(line, "\t sl: sl vars: %li, sl_size: %li\n", (long)sl_vars,
|
|
652
|
+
(long)k->sl_size);
|
|
653
|
+
append_to_header(buf, len, line);
|
|
654
|
+
}
|
|
655
|
+
#endif
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
char *SCS(get_cone_header)(const ScsCone *k) {
|
|
659
|
+
char *tmp;
|
|
660
|
+
size_t len = 0;
|
|
661
|
+
|
|
662
|
+
format_cone_header(k, SCS_NULL, &len);
|
|
663
|
+
|
|
664
|
+
tmp = (char *)scs_malloc(len + 1);
|
|
665
|
+
if (!tmp) {
|
|
666
|
+
return SCS_NULL;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
len = 0;
|
|
670
|
+
format_cone_header(k, tmp, &len);
|
|
671
|
+
return tmp;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* ========================== Validation =============================== */
|
|
675
|
+
|
|
341
676
|
scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
342
677
|
scs_int i;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
(long)get_full_cone_dims(k), (long)d->m);
|
|
346
|
-
return -1;
|
|
347
|
-
}
|
|
348
|
-
if (k->z && k->z < 0) {
|
|
678
|
+
scs_int cone_dims;
|
|
679
|
+
if (k->z < 0) {
|
|
349
680
|
scs_printf("free cone dimension error\n");
|
|
350
681
|
return -1;
|
|
351
682
|
}
|
|
352
|
-
if (k->l
|
|
683
|
+
if (k->l < 0) {
|
|
353
684
|
scs_printf("lp cone dimension error\n");
|
|
354
685
|
return -1;
|
|
355
686
|
}
|
|
356
|
-
if (k->bsize) {
|
|
357
|
-
|
|
358
|
-
|
|
687
|
+
if (k->bsize < 0) {
|
|
688
|
+
scs_printf("box cone dimension error\n");
|
|
689
|
+
return -1;
|
|
690
|
+
}
|
|
691
|
+
if (k->bsize > 1) {
|
|
692
|
+
if (!k->bl || !k->bu) {
|
|
693
|
+
scs_printf("box cone bounds missing\n");
|
|
359
694
|
return -1;
|
|
360
695
|
}
|
|
361
696
|
for (i = 0; i < k->bsize - 1; ++i) {
|
|
697
|
+
if (isnan(k->bl[i]) || isnan(k->bu[i])) {
|
|
698
|
+
scs_printf("box cone bounds cannot be NaN\n");
|
|
699
|
+
return -1;
|
|
700
|
+
}
|
|
701
|
+
if (k->bl[i] == INFINITY || k->bu[i] == -INFINITY) {
|
|
702
|
+
scs_printf("box cone bounds use invalid infinity direction\n");
|
|
703
|
+
return -1;
|
|
704
|
+
}
|
|
362
705
|
if (k->bl[i] > k->bu[i]) {
|
|
363
706
|
scs_printf("infeasible: box lower bound larger than upper bound\n");
|
|
364
707
|
return -1;
|
|
365
708
|
}
|
|
366
709
|
}
|
|
367
710
|
}
|
|
368
|
-
if (k->qsize
|
|
369
|
-
|
|
370
|
-
|
|
711
|
+
if (k->qsize < 0) {
|
|
712
|
+
scs_printf("soc cone dimension error\n");
|
|
713
|
+
return -1;
|
|
714
|
+
}
|
|
715
|
+
if (k->qsize > 0) {
|
|
716
|
+
if (!k->q) {
|
|
717
|
+
scs_printf("soc cone array missing\n");
|
|
371
718
|
return -1;
|
|
372
719
|
}
|
|
373
720
|
for (i = 0; i < k->qsize; ++i) {
|
|
@@ -377,9 +724,13 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
377
724
|
}
|
|
378
725
|
}
|
|
379
726
|
}
|
|
380
|
-
if (k->ssize
|
|
381
|
-
|
|
382
|
-
|
|
727
|
+
if (k->ssize < 0) {
|
|
728
|
+
scs_printf("sd cone dimension error\n");
|
|
729
|
+
return -1;
|
|
730
|
+
}
|
|
731
|
+
if (k->ssize > 0) {
|
|
732
|
+
if (!k->s) {
|
|
733
|
+
scs_printf("sd cone array missing\n");
|
|
383
734
|
return -1;
|
|
384
735
|
}
|
|
385
736
|
for (i = 0; i < k->ssize; ++i) {
|
|
@@ -389,9 +740,13 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
389
740
|
}
|
|
390
741
|
}
|
|
391
742
|
}
|
|
392
|
-
if (k->cssize
|
|
393
|
-
|
|
394
|
-
|
|
743
|
+
if (k->cssize < 0) {
|
|
744
|
+
scs_printf("complex psd cone dimension error\n");
|
|
745
|
+
return -1;
|
|
746
|
+
}
|
|
747
|
+
if (k->cssize > 0) {
|
|
748
|
+
if (!k->cs) {
|
|
749
|
+
scs_printf("complex psd cone array missing\n");
|
|
395
750
|
return -1;
|
|
396
751
|
}
|
|
397
752
|
for (i = 0; i < k->cssize; ++i) {
|
|
@@ -401,30 +756,38 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
401
756
|
}
|
|
402
757
|
}
|
|
403
758
|
}
|
|
404
|
-
if (k->ed
|
|
759
|
+
if (k->ed < 0) {
|
|
760
|
+
scs_printf("ed cone dimension error\n");
|
|
761
|
+
return -1;
|
|
762
|
+
}
|
|
763
|
+
if (k->ep < 0) {
|
|
405
764
|
scs_printf("ep cone dimension error\n");
|
|
406
765
|
return -1;
|
|
407
766
|
}
|
|
408
|
-
if (k->
|
|
409
|
-
scs_printf("
|
|
767
|
+
if (k->psize < 0) {
|
|
768
|
+
scs_printf("power cone dimension error\n");
|
|
410
769
|
return -1;
|
|
411
770
|
}
|
|
412
|
-
if (k->psize
|
|
413
|
-
if (k->
|
|
414
|
-
scs_printf("power cone
|
|
771
|
+
if (k->psize > 0) {
|
|
772
|
+
if (!k->p) {
|
|
773
|
+
scs_printf("power cone array missing\n");
|
|
415
774
|
return -1;
|
|
416
775
|
}
|
|
417
776
|
for (i = 0; i < k->psize; ++i) {
|
|
418
|
-
if (k->p[i] < -1 || k->p[i] > 1) {
|
|
419
|
-
scs_printf("power cone error, values must be in [-1,1]\n");
|
|
777
|
+
if (!isfinite(k->p[i]) || k->p[i] < -1 || k->p[i] > 1) {
|
|
778
|
+
scs_printf("power cone error, values must be finite and in [-1,1]\n");
|
|
420
779
|
return -1;
|
|
421
780
|
}
|
|
422
781
|
}
|
|
423
782
|
}
|
|
424
783
|
#ifdef USE_SPECTRAL_CONES
|
|
425
|
-
if (k->dsize
|
|
426
|
-
|
|
427
|
-
|
|
784
|
+
if (k->dsize < 0) {
|
|
785
|
+
scs_printf("logdet cone dimension error\n");
|
|
786
|
+
return -1;
|
|
787
|
+
}
|
|
788
|
+
if (k->dsize > 0) {
|
|
789
|
+
if (!k->d) {
|
|
790
|
+
scs_printf("logdet cone array missing\n");
|
|
428
791
|
return -1;
|
|
429
792
|
}
|
|
430
793
|
for (i = 0; i < k->dsize; ++i) {
|
|
@@ -434,9 +797,13 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
434
797
|
}
|
|
435
798
|
}
|
|
436
799
|
}
|
|
437
|
-
if (k->nucsize
|
|
438
|
-
|
|
439
|
-
|
|
800
|
+
if (k->nucsize < 0) {
|
|
801
|
+
scs_printf("nuclear cone dimension error\n");
|
|
802
|
+
return -1;
|
|
803
|
+
}
|
|
804
|
+
if (k->nucsize > 0) {
|
|
805
|
+
if (!k->nuc_m || !k->nuc_n) {
|
|
806
|
+
scs_printf("nuclear cone arrays missing\n");
|
|
440
807
|
return -1;
|
|
441
808
|
}
|
|
442
809
|
for (i = 0; i < k->nucsize; ++i) {
|
|
@@ -446,9 +813,13 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
446
813
|
}
|
|
447
814
|
}
|
|
448
815
|
}
|
|
449
|
-
if (k->ell1_size
|
|
450
|
-
|
|
451
|
-
|
|
816
|
+
if (k->ell1_size < 0) {
|
|
817
|
+
scs_printf("ell1 cone dimension error\n");
|
|
818
|
+
return -1;
|
|
819
|
+
}
|
|
820
|
+
if (k->ell1_size > 0) {
|
|
821
|
+
if (!k->ell1) {
|
|
822
|
+
scs_printf("ell1 cone array missing\n");
|
|
452
823
|
return -1;
|
|
453
824
|
}
|
|
454
825
|
for (i = 0; i < k->ell1_size; ++i) {
|
|
@@ -458,7 +829,15 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
458
829
|
}
|
|
459
830
|
}
|
|
460
831
|
}
|
|
461
|
-
if (k->sl_size
|
|
832
|
+
if (k->sl_size < 0) {
|
|
833
|
+
scs_printf("sum-of-largest-eigenvalues cone dimension error\n");
|
|
834
|
+
return -1;
|
|
835
|
+
}
|
|
836
|
+
if (k->sl_size > 0) {
|
|
837
|
+
if (!k->sl_n || !k->sl_k) {
|
|
838
|
+
scs_printf("sum-of-largest-eigenvalues cone arrays missing\n");
|
|
839
|
+
return -1;
|
|
840
|
+
}
|
|
462
841
|
for (i = 0; i < k->sl_size; ++i) {
|
|
463
842
|
if ((k->sl_k[i] >= k->sl_n[i]) || k->sl_k[i] <= 0) {
|
|
464
843
|
scs_printf("sum-of-largest-eigenvalues cone dimension error\n");
|
|
@@ -467,146 +846,18 @@ scs_int SCS(validate_cones)(const ScsData *d, const ScsCone *k) {
|
|
|
467
846
|
}
|
|
468
847
|
}
|
|
469
848
|
#endif
|
|
849
|
+
cone_dims = get_full_cone_dims(k);
|
|
850
|
+
if (cone_dims != d->m) {
|
|
851
|
+
scs_printf("Error: Cone dims %li != rows in A %li\n", (long)cone_dims,
|
|
852
|
+
(long)d->m);
|
|
853
|
+
return -1;
|
|
854
|
+
}
|
|
470
855
|
return 0;
|
|
471
856
|
}
|
|
472
857
|
|
|
473
|
-
|
|
474
|
-
if (!c)
|
|
475
|
-
return;
|
|
476
|
-
#ifdef USE_LAPACK
|
|
477
|
-
if (c->Xs)
|
|
478
|
-
scs_free(c->Xs);
|
|
479
|
-
if (c->cXs)
|
|
480
|
-
scs_free(c->cXs);
|
|
481
|
-
if (c->Z)
|
|
482
|
-
scs_free(c->Z);
|
|
483
|
-
if (c->cZ)
|
|
484
|
-
scs_free(c->cZ);
|
|
485
|
-
if (c->e)
|
|
486
|
-
scs_free(c->e);
|
|
487
|
-
if (c->isuppz)
|
|
488
|
-
scs_free(c->isuppz);
|
|
489
|
-
if (c->work)
|
|
490
|
-
scs_free(c->work);
|
|
491
|
-
if (c->iwork)
|
|
492
|
-
scs_free(c->iwork);
|
|
493
|
-
if (c->cwork)
|
|
494
|
-
scs_free(c->cwork);
|
|
495
|
-
/* c->rwork is aliased to c->work in setup, no free needed */
|
|
496
|
-
#endif
|
|
497
|
-
if (c->cone_boundaries)
|
|
498
|
-
scs_free(c->cone_boundaries);
|
|
499
|
-
if (c->s)
|
|
500
|
-
scs_free(c->s);
|
|
501
|
-
|
|
502
|
-
#ifdef USE_SPECTRAL_CONES
|
|
503
|
-
if (c->work_logdet)
|
|
504
|
-
scs_free(c->work_logdet);
|
|
505
|
-
if (c->saved_log_projs)
|
|
506
|
-
scs_free(c->saved_log_projs);
|
|
507
|
-
if (c->s_nuc)
|
|
508
|
-
scs_free(c->s_nuc);
|
|
509
|
-
if (c->u_nuc)
|
|
510
|
-
scs_free(c->u_nuc);
|
|
511
|
-
if (c->vt_nuc)
|
|
512
|
-
scs_free(c->vt_nuc);
|
|
513
|
-
if (c->work_nuc)
|
|
514
|
-
scs_free(c->work_nuc);
|
|
515
|
-
if (c->work_sum_of_largest)
|
|
516
|
-
scs_free(c->work_sum_of_largest);
|
|
517
|
-
if (c->log_cone_warmstarts)
|
|
518
|
-
scs_free(c->log_cone_warmstarts);
|
|
519
|
-
if (c->work_ell1)
|
|
520
|
-
scs_free(c->work_ell1);
|
|
521
|
-
if (c->work_ell1_proj)
|
|
522
|
-
scs_free(c->work_ell1_proj);
|
|
523
|
-
#endif
|
|
524
|
-
scs_free(c);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
char *SCS(get_cone_header)(const ScsCone *k) {
|
|
528
|
-
char *tmp = (char *)scs_malloc(512);
|
|
529
|
-
scs_int i, count;
|
|
530
|
-
|
|
531
|
-
sprintf(tmp, "cones: ");
|
|
532
|
-
if (k->z)
|
|
533
|
-
sprintf(tmp + strlen(tmp), "\t z: primal zero / dual free vars: %li\n",
|
|
534
|
-
(long)k->z);
|
|
535
|
-
if (k->l)
|
|
536
|
-
sprintf(tmp + strlen(tmp), "\t l: linear vars: %li\n", (long)k->l);
|
|
537
|
-
if (k->bsize)
|
|
538
|
-
sprintf(tmp + strlen(tmp), "\t b: box cone vars: %li\n", (long)k->bsize);
|
|
539
|
-
|
|
540
|
-
if (k->qsize) {
|
|
541
|
-
count = 0;
|
|
542
|
-
for (i = 0; i < k->qsize; ++i)
|
|
543
|
-
count += k->q[i];
|
|
544
|
-
sprintf(tmp + strlen(tmp), "\t q: soc vars: %li, qsize: %li\n",
|
|
545
|
-
(long)count, (long)k->qsize);
|
|
546
|
-
}
|
|
547
|
-
if (k->ssize) {
|
|
548
|
-
count = 0;
|
|
549
|
-
for (i = 0; i < k->ssize; ++i)
|
|
550
|
-
count += get_sd_cone_size(k->s[i]);
|
|
551
|
-
sprintf(tmp + strlen(tmp), "\t s: psd vars: %li, ssize: %li\n",
|
|
552
|
-
(long)count, (long)k->ssize);
|
|
553
|
-
}
|
|
554
|
-
if (k->cssize) {
|
|
555
|
-
count = 0;
|
|
556
|
-
for (i = 0; i < k->cssize; ++i)
|
|
557
|
-
count += get_csd_cone_size(k->cs[i]);
|
|
558
|
-
sprintf(tmp + strlen(tmp), "\t cs: complex psd vars: %li, cssize: %li\n",
|
|
559
|
-
(long)count, (long)k->cssize);
|
|
560
|
-
}
|
|
561
|
-
if (k->ep || k->ed) {
|
|
562
|
-
sprintf(tmp + strlen(tmp), "\t e: exp vars: %li, dual exp vars: %li\n",
|
|
563
|
-
(long)(3 * k->ep), (long)(3 * k->ed));
|
|
564
|
-
}
|
|
565
|
-
if (k->psize) {
|
|
566
|
-
sprintf(tmp + strlen(tmp), "\t p: primal + dual power vars: %li\n",
|
|
567
|
-
(long)(3 * k->psize));
|
|
568
|
-
}
|
|
569
|
-
#ifdef USE_SPECTRAL_CONES
|
|
570
|
-
scs_int ell1_vars, log_vars, nuc_vars, sl_vars;
|
|
571
|
-
log_vars = 0;
|
|
572
|
-
if (k->dsize && k->d) {
|
|
573
|
-
for (i = 0; i < k->dsize; i++) {
|
|
574
|
-
log_vars += get_sd_cone_size(k->d[i]) + 2;
|
|
575
|
-
}
|
|
576
|
-
sprintf(tmp + strlen(tmp), "\t d: logdet vars: %li, dsize: %li\n",
|
|
577
|
-
(long)log_vars, (long)k->dsize);
|
|
578
|
-
}
|
|
579
|
-
nuc_vars = 0;
|
|
580
|
-
if (k->nucsize && k->nuc_m && k->nuc_n) {
|
|
581
|
-
for (i = 0; i < k->nucsize; i++) {
|
|
582
|
-
nuc_vars += k->nuc_m[i] * k->nuc_n[i] + 1;
|
|
583
|
-
}
|
|
584
|
-
sprintf(tmp + strlen(tmp), "\t nuc: nuclear vars: %li, nucsize: %li\n",
|
|
585
|
-
(long)nuc_vars, (long)k->nucsize);
|
|
586
|
-
}
|
|
587
|
-
ell1_vars = 0;
|
|
588
|
-
if (k->ell1_size && k->ell1) {
|
|
589
|
-
for (i = 0; i < k->ell1_size; ++i) {
|
|
590
|
-
ell1_vars += k->ell1[i];
|
|
591
|
-
}
|
|
592
|
-
sprintf(tmp + strlen(tmp), "\t ell1: ell1 vars: %li, ell1_size: %li\n",
|
|
593
|
-
(long)ell1_vars, (long)k->ell1_size);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
sl_vars = 0;
|
|
597
|
-
if (k->sl_size && k->sl_n) {
|
|
598
|
-
for (i = 0; i < k->sl_size; ++i) {
|
|
599
|
-
sl_vars += get_sd_cone_size(k->sl_n[i]) + 1;
|
|
600
|
-
}
|
|
601
|
-
sprintf(tmp + strlen(tmp), "\t sl: sl vars: %li, sl_size: %li\n",
|
|
602
|
-
(long)sl_vars, (long)k->sl_size);
|
|
603
|
-
}
|
|
604
|
-
#endif
|
|
605
|
-
return tmp;
|
|
606
|
-
}
|
|
858
|
+
/* ======================== Workspace Setup ============================= */
|
|
607
859
|
|
|
608
860
|
/*
|
|
609
|
-
* Workspace Setup
|
|
610
861
|
* Consolidated setup for Real PSD, Complex PSD, and Spectral cones.
|
|
611
862
|
*/
|
|
612
863
|
static scs_int set_up_cone_work_spaces(ScsConeWork *c, const ScsCone *k) {
|
|
@@ -684,6 +935,7 @@ static scs_int set_up_cone_work_spaces(ScsConeWork *c, const ScsCone *k) {
|
|
|
684
935
|
* 'e' stores eigenvalues, 'isuppz' supports them. Shared by Real/Complex.
|
|
685
936
|
*/
|
|
686
937
|
c->e = (scs_float *)scs_calloc(n_max, sizeof(scs_float));
|
|
938
|
+
c->psd_gamma = (scs_float *)scs_calloc(n_max, sizeof(scs_float));
|
|
687
939
|
c->isuppz = (blas_int *)scs_calloc(MAX(2, 2 * n_max), sizeof(blas_int));
|
|
688
940
|
if (!c->e || !c->isuppz)
|
|
689
941
|
return -1;
|
|
@@ -833,11 +1085,23 @@ static scs_int set_up_ell1_cone_work_space(ScsConeWork *c, const ScsCone *k) {
|
|
|
833
1085
|
}
|
|
834
1086
|
#endif
|
|
835
1087
|
|
|
1088
|
+
/* ====================== Cone Projections ============================= */
|
|
1089
|
+
|
|
836
1090
|
/*
|
|
837
1091
|
* Projection: Real Semi-Definite Cone
|
|
838
1092
|
*/
|
|
1093
|
+
/* Project one svec-packed block onto the PSD cone. When rho (the
|
|
1094
|
+
* block's slice of the diagonal metric R) is non-uniform it must have
|
|
1095
|
+
* the rank-one structure w_ij = delta_i * delta_j (enforced by
|
|
1096
|
+
* fit_psd_block_weights). The projection under the induced R^{-1}
|
|
1097
|
+
* metric is then a diagonal congruence sandwich: with gamma_i =
|
|
1098
|
+
* w_ii^{-1/4}, project Gamma X Gamma and unscale. Positive homogeneity
|
|
1099
|
+
* of the cone makes any uniform factor in rho drop out, so the
|
|
1100
|
+
* historical scalar-per-block case reduces to the unscaled path. */
|
|
839
1101
|
static scs_int proj_semi_definite_cone(scs_float *X, const scs_int n,
|
|
840
|
-
ScsConeWork *c
|
|
1102
|
+
ScsConeWork *c,
|
|
1103
|
+
const scs_float *rho) {
|
|
1104
|
+
scs_int scaled = 0;
|
|
841
1105
|
if (n == 0)
|
|
842
1106
|
return 0;
|
|
843
1107
|
if (n == 1) {
|
|
@@ -855,6 +1119,31 @@ static scs_int proj_semi_definite_cone(scs_float *X, const scs_int n,
|
|
|
855
1119
|
blas_int m = 0, d_i = 0;
|
|
856
1120
|
scs_int first_idx = -1;
|
|
857
1121
|
|
|
1122
|
+
/* Diagonal-congruence pre-scale (skip when the metric is uniform on
|
|
1123
|
+
* this block so the historical path stays bit-identical). */
|
|
1124
|
+
if (rho && c->psd_gamma) {
|
|
1125
|
+
scs_float wmin = rho[0], wmax = rho[0];
|
|
1126
|
+
for (i = 1; i < n; ++i) {
|
|
1127
|
+
scs_float wii = rho[i * n - ((i - 1) * i) / 2];
|
|
1128
|
+
wmin = MIN(wmin, wii);
|
|
1129
|
+
wmax = MAX(wmax, wii);
|
|
1130
|
+
}
|
|
1131
|
+
if (wmax > wmin * (1. + 1e-12)) {
|
|
1132
|
+
scs_int jj, ii;
|
|
1133
|
+
scaled = 1;
|
|
1134
|
+
for (i = 0; i < n; ++i) {
|
|
1135
|
+
c->psd_gamma[i] = POWF(rho[i * n - ((i - 1) * i) / 2], -0.25);
|
|
1136
|
+
}
|
|
1137
|
+
for (jj = 0; jj < n; ++jj) {
|
|
1138
|
+
scs_float gj = c->psd_gamma[jj];
|
|
1139
|
+
scs_float *col = &(X[jj * n - ((jj - 1) * jj) / 2]);
|
|
1140
|
+
for (ii = jj; ii < n; ++ii) {
|
|
1141
|
+
col[ii - jj] *= c->psd_gamma[ii] * gj;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
858
1147
|
/* Copy lower triangular part to full matrix buffer Xs */
|
|
859
1148
|
for (i = 0; i < n; ++i) {
|
|
860
1149
|
memcpy(&(c->Xs[i * (n + 1)]), &(X[i * n - ((i - 1) * i) / 2]),
|
|
@@ -900,6 +1189,18 @@ static scs_int proj_semi_definite_cone(scs_float *X, const scs_int n,
|
|
|
900
1189
|
memcpy(&(X[i * n - ((i - 1) * i) / 2]), &(c->Xs[i * (n + 1)]),
|
|
901
1190
|
(n - i) * sizeof(scs_float));
|
|
902
1191
|
}
|
|
1192
|
+
|
|
1193
|
+
/* Undo the congruence pre-scale */
|
|
1194
|
+
if (scaled) {
|
|
1195
|
+
scs_int jj, ii;
|
|
1196
|
+
for (jj = 0; jj < n; ++jj) {
|
|
1197
|
+
scs_float gj = c->psd_gamma[jj];
|
|
1198
|
+
scs_float *col = &(X[jj * n - ((jj - 1) * jj) / 2]);
|
|
1199
|
+
for (ii = jj; ii < n; ++ii) {
|
|
1200
|
+
col[ii - jj] /= c->psd_gamma[ii] * gj;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
903
1204
|
return 0;
|
|
904
1205
|
#else
|
|
905
1206
|
return -1;
|
|
@@ -1021,7 +1322,8 @@ static void normalize_box_cone(ScsCone *k, scs_float *D, scs_int bsize) {
|
|
|
1021
1322
|
*/
|
|
1022
1323
|
static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
|
|
1023
1324
|
const scs_float *bu, scs_int bsize,
|
|
1024
|
-
scs_float t_wm, scs_float *r_box
|
|
1325
|
+
scs_float t_wm, scs_float *r_box,
|
|
1326
|
+
scs_float *r_box_inv) {
|
|
1025
1327
|
scs_float *x = &(tx[1]);
|
|
1026
1328
|
scs_float gt, ht, t = t_wm, t_prev, r;
|
|
1027
1329
|
scs_float rho_t = 1.0;
|
|
@@ -1035,6 +1337,12 @@ static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
|
|
|
1035
1337
|
if (r_box) {
|
|
1036
1338
|
rho_t = 1.0 / r_box[0];
|
|
1037
1339
|
rho = &(r_box[1]);
|
|
1340
|
+
/* Precompute reciprocals once, used across all Newton iterations */
|
|
1341
|
+
if (r_box_inv) {
|
|
1342
|
+
for (j = 0; j < bsize - 1; j++) {
|
|
1343
|
+
r_box_inv[j] = 1.0 / rho[j];
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1038
1346
|
}
|
|
1039
1347
|
|
|
1040
1348
|
/* Newton's method for t */
|
|
@@ -1044,7 +1352,7 @@ static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
|
|
|
1044
1352
|
ht = rho_t;
|
|
1045
1353
|
|
|
1046
1354
|
for (j = 0; j < bsize - 1; j++) {
|
|
1047
|
-
r = rho
|
|
1355
|
+
r = (rho && r_box_inv) ? r_box_inv[j] : 1.0;
|
|
1048
1356
|
if (x[j] > t * bu[j]) {
|
|
1049
1357
|
gt += r * (t * bu[j] - x[j]) * bu[j];
|
|
1050
1358
|
ht += r * bu[j] * bu[j];
|
|
@@ -1081,6 +1389,7 @@ static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
|
|
|
1081
1389
|
* Projection: Second Order Cone
|
|
1082
1390
|
*/
|
|
1083
1391
|
static void proj_soc(scs_float *x, scs_int q) {
|
|
1392
|
+
scs_float v1, s, alpha;
|
|
1084
1393
|
if (q <= 0)
|
|
1085
1394
|
return;
|
|
1086
1395
|
if (q == 1) {
|
|
@@ -1088,9 +1397,17 @@ static void proj_soc(scs_float *x, scs_int q) {
|
|
|
1088
1397
|
return;
|
|
1089
1398
|
}
|
|
1090
1399
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1400
|
+
v1 = x[0];
|
|
1401
|
+
/* Fast paths for the two most common small SOC sizes avoid BLAS call
|
|
1402
|
+
* overhead (function pointer dispatch + Fortran ABI arguments). */
|
|
1403
|
+
if (q == 2) {
|
|
1404
|
+
s = ABS(x[1]);
|
|
1405
|
+
} else if (q == 3) {
|
|
1406
|
+
s = SQRTF(x[1] * x[1] + x[2] * x[2]);
|
|
1407
|
+
} else {
|
|
1408
|
+
s = SCS(norm_2)(&(x[1]), q - 1);
|
|
1409
|
+
}
|
|
1410
|
+
alpha = (s + v1) / 2.0;
|
|
1094
1411
|
|
|
1095
1412
|
if (s <= v1)
|
|
1096
1413
|
return; /* Inside cone */
|
|
@@ -1111,12 +1428,6 @@ static scs_float pow_calc_x(scs_float r, scs_float xh, scs_float rh,
|
|
|
1111
1428
|
return MAX(x, 1e-12);
|
|
1112
1429
|
}
|
|
1113
1430
|
|
|
1114
|
-
static scs_float pow_calc_fp(scs_float x, scs_float y, scs_float dxdr,
|
|
1115
|
-
scs_float dydr, scs_float a) {
|
|
1116
|
-
return POWF(x, a) * POWF(y, (1 - a)) * (a * dxdr / x + (1 - a) * dydr / y) -
|
|
1117
|
-
1;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
1431
|
static void proj_power_cone(scs_float *v, scs_float a) {
|
|
1121
1432
|
scs_float xh = v[0], yh = v[1], rh = ABS(v[2]);
|
|
1122
1433
|
scs_float x = 0.0, y = 0.0, r;
|
|
@@ -1137,17 +1448,21 @@ static void proj_power_cone(scs_float *v, scs_float a) {
|
|
|
1137
1448
|
|
|
1138
1449
|
r = rh / 2;
|
|
1139
1450
|
for (i = 0; i < POW_CONE_MAX_ITERS; ++i) {
|
|
1140
|
-
scs_float f, fp, dxdr, dydr;
|
|
1451
|
+
scs_float f, fp, dxdr, dydr, xa, y1a;
|
|
1141
1452
|
x = pow_calc_x(r, xh, rh, a);
|
|
1142
1453
|
y = pow_calc_x(r, yh, rh, 1 - a);
|
|
1143
1454
|
|
|
1144
|
-
|
|
1455
|
+
/* Cache POWF(x,a) and POWF(y,1-a): both are needed for f and fp,
|
|
1456
|
+
* so computing them once saves two POWF calls per Newton iteration. */
|
|
1457
|
+
xa = POWF(x, a);
|
|
1458
|
+
y1a = POWF(y, (1 - a));
|
|
1459
|
+
f = xa * y1a - r;
|
|
1145
1460
|
if (ABS(f) < POW_CONE_TOL)
|
|
1146
1461
|
break;
|
|
1147
1462
|
|
|
1148
1463
|
dxdr = a * (rh - 2 * r) / (2 * x - xh);
|
|
1149
1464
|
dydr = (1 - a) * (rh - 2 * r) / (2 * y - yh);
|
|
1150
|
-
fp =
|
|
1465
|
+
fp = xa * y1a * (a * dxdr / x + (1 - a) * dydr / y) - 1;
|
|
1151
1466
|
|
|
1152
1467
|
r = MAX(r - f / fp, 0);
|
|
1153
1468
|
r = MIN(r, rh);
|
|
@@ -1157,6 +1472,8 @@ static void proj_power_cone(scs_float *v, scs_float a) {
|
|
|
1157
1472
|
v[2] = (v[2] < 0) ? -r : r;
|
|
1158
1473
|
}
|
|
1159
1474
|
|
|
1475
|
+
/* ================ Main Cone Projection Dispatch ====================== */
|
|
1476
|
+
|
|
1160
1477
|
/* Project onto the primal K cone in the paper */
|
|
1161
1478
|
/* The r_y vector determines the INVERSE metric, ie, project under the
|
|
1162
1479
|
* diag(r_y)^-1 norm.
|
|
@@ -1187,7 +1504,8 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
|
|
|
1187
1504
|
if (r_y)
|
|
1188
1505
|
r_box = &(r_y[count]);
|
|
1189
1506
|
c->box_t_warm_start = proj_box_cone(&(x[count]), k->bl, k->bu, k->bsize,
|
|
1190
|
-
c->box_t_warm_start, r_box
|
|
1507
|
+
c->box_t_warm_start, r_box,
|
|
1508
|
+
c->r_box_inv);
|
|
1191
1509
|
count += k->bsize;
|
|
1192
1510
|
}
|
|
1193
1511
|
|
|
@@ -1205,7 +1523,8 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
|
|
|
1205
1523
|
#ifdef USE_SPECTRAL_CONES
|
|
1206
1524
|
SPECTRAL_TIMING(SCS(tic)(&spec_mat_proj_timer);)
|
|
1207
1525
|
#endif
|
|
1208
|
-
status = proj_semi_definite_cone(&(x[count]), k->s[i], c
|
|
1526
|
+
status = proj_semi_definite_cone(&(x[count]), k->s[i], c,
|
|
1527
|
+
r_y ? &(r_y[count]) : SCS_NULL);
|
|
1209
1528
|
#ifdef USE_SPECTRAL_CONES
|
|
1210
1529
|
SPECTRAL_TIMING(c->tot_time_mat_cone_proj +=
|
|
1211
1530
|
SCS(tocq)(&spec_mat_proj_timer);)
|
|
@@ -1316,9 +1635,7 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
|
|
|
1316
1635
|
return 0;
|
|
1317
1636
|
}
|
|
1318
1637
|
|
|
1319
|
-
/*
|
|
1320
|
-
* Public API
|
|
1321
|
-
*/
|
|
1638
|
+
/* =========================== Public API ============================== */
|
|
1322
1639
|
|
|
1323
1640
|
ScsConeWork *SCS(init_cone)(ScsCone *k, scs_int m) {
|
|
1324
1641
|
ScsConeWork *c = (ScsConeWork *)scs_calloc(1, sizeof(ScsConeWork));
|
|
@@ -1332,6 +1649,12 @@ ScsConeWork *SCS(init_cone)(ScsCone *k, scs_int m) {
|
|
|
1332
1649
|
set_cone_boundaries(k, c);
|
|
1333
1650
|
c->s = (scs_float *)scs_calloc(m, sizeof(scs_float));
|
|
1334
1651
|
|
|
1652
|
+
/* Preallocate workspace for box cone reciprocal metric */
|
|
1653
|
+
if (k->bsize > 1) {
|
|
1654
|
+
c->r_box_inv =
|
|
1655
|
+
(scs_float *)scs_calloc(k->bsize - 1, sizeof(scs_float));
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1335
1658
|
/* Set up workspaces if matrix cones are present */
|
|
1336
1659
|
if ((k->ssize && k->s) || (k->cssize && k->cs)
|
|
1337
1660
|
#ifdef USE_SPECTRAL_CONES
|
|
@@ -1356,16 +1679,6 @@ ScsConeWork *SCS(init_cone)(ScsCone *k, scs_int m) {
|
|
|
1356
1679
|
return c;
|
|
1357
1680
|
}
|
|
1358
1681
|
|
|
1359
|
-
void scale_box_cone(ScsCone *k, ScsConeWork *c, const ScsScaling *scal) {
|
|
1360
|
-
if (k->bsize && k->bu && k->bl) {
|
|
1361
|
-
c->box_t_warm_start = 1.;
|
|
1362
|
-
if (scal) {
|
|
1363
|
-
/* also does some sanitizing */
|
|
1364
|
-
normalize_box_cone(k, &(scal->D[k->z + k->l]), k->bsize);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
1682
|
/* Outward facing cone projection routine, performs projection in-place.
|
|
1370
1683
|
If normalize > 0 then will use normalized (equilibrated) cones if applicable.
|
|
1371
1684
|
|
|
@@ -1396,20 +1709,29 @@ scs_int SCS(proj_dual_cone)(scs_float *x, ScsConeWork *c,
|
|
|
1396
1709
|
/* Copy s = x */
|
|
1397
1710
|
memcpy(c->s, x, c->m * sizeof(scs_float));
|
|
1398
1711
|
|
|
1399
|
-
/* x -> - Rx */
|
|
1400
|
-
|
|
1401
|
-
|
|
1712
|
+
/* x -> - Rx; hoist the r_y != NULL check outside the loop */
|
|
1713
|
+
if (r_y) {
|
|
1714
|
+
for (i = 0; i < c->m; ++i) {
|
|
1715
|
+
x[i] *= -r_y[i];
|
|
1716
|
+
}
|
|
1717
|
+
} else {
|
|
1718
|
+
for (i = 0; i < c->m; ++i) {
|
|
1719
|
+
x[i] = -x[i];
|
|
1720
|
+
}
|
|
1402
1721
|
}
|
|
1403
1722
|
|
|
1404
1723
|
/* Project -x onto cone, x -> \Pi_{C^*}^{R^{-1}}(-x) under r_y metric */
|
|
1405
1724
|
status = proj_cone(x, k, c, scal ? 1 : 0, r_y);
|
|
1406
1725
|
|
|
1407
|
-
/* Return x + R^{-1} \Pi_{C^*}^{R^{-1}} ( -x ) */
|
|
1408
|
-
|
|
1409
|
-
|
|
1726
|
+
/* Return x + R^{-1} \Pi_{C^*}^{R^{-1}} ( -x ); hoist r_y check */
|
|
1727
|
+
if (r_y) {
|
|
1728
|
+
for (i = 0; i < c->m; ++i) {
|
|
1410
1729
|
x[i] = x[i] / r_y[i] + c->s[i];
|
|
1411
|
-
|
|
1730
|
+
}
|
|
1731
|
+
} else {
|
|
1732
|
+
for (i = 0; i < c->m; ++i) {
|
|
1412
1733
|
x[i] += c->s[i];
|
|
1734
|
+
}
|
|
1413
1735
|
}
|
|
1414
1736
|
|
|
1415
1737
|
return status;
|