scs 0.5.1 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/scs/ffi.rb +2 -0
- data/lib/scs/version.rb +1 -1
- data/vendor/scs/CITATION.cff +2 -2
- data/vendor/scs/CMakeLists.txt +136 -6
- data/vendor/scs/Makefile +53 -3
- data/vendor/scs/README.md +1 -1
- data/vendor/scs/include/cones.h +47 -2
- data/vendor/scs/include/glbopts.h +1 -1
- data/vendor/scs/include/scs.h +29 -0
- data/vendor/scs/include/scs_blas.h +4 -0
- data/vendor/scs/include/scs_types.h +3 -1
- data/vendor/scs/include/util_spectral_cones.h +45 -0
- data/vendor/scs/linsys/cpu/direct/private.c +3 -3
- data/vendor/scs/linsys/cpu/direct/private.h +2 -1
- data/vendor/scs/linsys/csparse.c +1 -1
- data/vendor/scs/linsys/cudss/direct/private.c +279 -0
- data/vendor/scs/linsys/cudss/direct/private.h +63 -0
- data/vendor/scs/linsys/external/qdldl/qdldl_types.h +1 -1
- data/vendor/scs/linsys/gpu/indirect/private.c +14 -21
- data/vendor/scs/scs.mk +17 -2
- data/vendor/scs/src/aa.c +8 -12
- data/vendor/scs/src/cones.c +783 -12
- data/vendor/scs/src/rw.c +15 -1
- data/vendor/scs/src/scs.c +4 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +660 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +279 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +205 -0
- data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +143 -0
- data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +221 -0
- data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +99 -0
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +196 -0
- data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +140 -0
- data/vendor/scs/src/spectral_cones/util_spectral_cones.c +52 -0
- data/vendor/scs/test/problems/complex_PSD.h +83 -0
- data/vendor/scs/test/rng.h +4 -4
- data/vendor/scs/test/run_tests.c +25 -0
- data/vendor/scs/test/spectral_cones_problems/exp_design.h +141 -0
- data/vendor/scs/test/spectral_cones_problems/graph_partitioning.h +275 -0
- data/vendor/scs/test/spectral_cones_problems/robust_pca.h +253 -0
- data/vendor/scs/test/spectral_cones_problems/several_logdet_cones.h +222 -0
- data/vendor/scs/test/spectral_cones_problems/several_nuc_cone.h +285 -0
- data/vendor/scs/test/spectral_cones_problems/several_sum_largest.h +420 -0
- metadata +21 -2
@@ -0,0 +1,221 @@
|
|
1
|
+
#include "cones.h"
|
2
|
+
// #include "scs.h"
|
3
|
+
#include "linalg.h"
|
4
|
+
#include "scs_blas.h"
|
5
|
+
#include "scs_types.h"
|
6
|
+
#include "util.h" // just for timer
|
7
|
+
#include "util_spectral_cones.h"
|
8
|
+
#include <stdlib.h> // qsort
|
9
|
+
|
10
|
+
/*
|
11
|
+
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
12
|
+
* Cones" by Daniel Cederberg and Stephen Boyd, 2024.
|
13
|
+
*
|
14
|
+
* If you have any questions on the code, please reach out to the code author
|
15
|
+
* Daniel Cederberg.
|
16
|
+
*
|
17
|
+
* This file implements code for projecting onto the ell1-norm cone.
|
18
|
+
*
|
19
|
+
* Last modified: 25 August 2024.
|
20
|
+
*/
|
21
|
+
|
22
|
+
#ifdef __cplusplus
|
23
|
+
extern "C" {
|
24
|
+
#endif
|
25
|
+
|
26
|
+
void BLAS(axpy)(blas_int *n, const scs_float *a, const scs_float *x,
|
27
|
+
blas_int *incx, scs_float *y, blas_int *incy);
|
28
|
+
|
29
|
+
scs_float BLAS(dot)(const blas_int *n, const scs_float *x, const blas_int *incx,
|
30
|
+
const scs_float *y, const blas_int *incy);
|
31
|
+
|
32
|
+
#ifdef __cplusplus
|
33
|
+
}
|
34
|
+
#endif
|
35
|
+
|
36
|
+
#ifdef SPECTRAL_DEBUG
|
37
|
+
static void compute_cone_residuals_ell1(const scs_float *tx, scs_float t0,
|
38
|
+
const scs_float *x0, scs_int n,
|
39
|
+
scs_float residuals[3]) {
|
40
|
+
scs_float dual_res, pri_res, complementarity;
|
41
|
+
|
42
|
+
// -------------------------------------
|
43
|
+
// Compute Lagrange multiplier.
|
44
|
+
// (This function is not used in production so it is fine to allocate
|
45
|
+
// memory here)
|
46
|
+
// -------------------------------------
|
47
|
+
scs_float dualt = tx[0] - t0;
|
48
|
+
scs_float *dualx = malloc(n * sizeof(scs_float));
|
49
|
+
memcpy(dualx, tx + 1, n * sizeof(scs_float));
|
50
|
+
blas_int int_n = n;
|
51
|
+
scs_float negOne = -1.0;
|
52
|
+
blas_int one = 1;
|
53
|
+
BLAS(axpy)(&int_n, &negOne, x0, &one, dualx, &one);
|
54
|
+
|
55
|
+
// ---------------------------------------
|
56
|
+
// Compute complementarity measure
|
57
|
+
// ---------------------------------------
|
58
|
+
complementarity =
|
59
|
+
tx[0] * dualt + BLAS(dot)(&int_n, dualx, &one, tx + 1, &one);
|
60
|
+
|
61
|
+
// -----------------------------------------------
|
62
|
+
// Compute primal feasibility measure
|
63
|
+
// -----------------------------------------------
|
64
|
+
scs_float ell1_norm = 0;
|
65
|
+
for (const scs_float *xi = tx + 1; xi < tx + 1 + n; ++xi) {
|
66
|
+
ell1_norm += fabs(*xi);
|
67
|
+
}
|
68
|
+
pri_res = ell1_norm - tx[0];
|
69
|
+
|
70
|
+
// ---------------------------------------
|
71
|
+
// Compute dual feasibility measure
|
72
|
+
// ---------------------------------------
|
73
|
+
scs_float inf_norm = 0;
|
74
|
+
for (scs_int i = 0; i < n; ++i) {
|
75
|
+
scs_float abs_val = fabs(dualx[i]);
|
76
|
+
if (abs_val > inf_norm) {
|
77
|
+
inf_norm = abs_val;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
dual_res = inf_norm - dualt;
|
81
|
+
|
82
|
+
// ------------------------------------------
|
83
|
+
// Assign result and free allocated memory
|
84
|
+
// ------------------------------------------
|
85
|
+
residuals[0] = dual_res;
|
86
|
+
residuals[1] = pri_res;
|
87
|
+
residuals[2] = complementarity;
|
88
|
+
free(dualx);
|
89
|
+
}
|
90
|
+
#endif
|
91
|
+
|
92
|
+
// Asssumes that all components of x0 are positive and
|
93
|
+
// x0[0] >= x0[1] >= ... x0[n-1].
|
94
|
+
scs_int ell1_cone_proj_sorted(scs_float t0, const scs_float *x0,
|
95
|
+
scs_float *proj, scs_int n) {
|
96
|
+
if (-t0 >= x0[0]) {
|
97
|
+
memset(proj, 0, (n + 1) * sizeof(*x0));
|
98
|
+
return 0;
|
99
|
+
}
|
100
|
+
|
101
|
+
// -------------------------------------------
|
102
|
+
// Find the value on k
|
103
|
+
// -------------------------------------------
|
104
|
+
|
105
|
+
// check if k = 0 suffices
|
106
|
+
if (-t0 >= x0[0]) {
|
107
|
+
memset(proj, 0, (n + 1) * sizeof(*x0));
|
108
|
+
return 0;
|
109
|
+
}
|
110
|
+
|
111
|
+
scs_float xSum = 0;
|
112
|
+
scs_float tempSum = 0;
|
113
|
+
int k = -1;
|
114
|
+
for (scs_int kk = 1; kk < n; ++kk) {
|
115
|
+
xSum += x0[kk - 1];
|
116
|
+
tempSum = (-t0 + xSum) / (kk + 1);
|
117
|
+
|
118
|
+
if (x0[kk - 1] > tempSum && x0[kk] <= tempSum) {
|
119
|
+
k = (int)kk;
|
120
|
+
break;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
if (k == -1) {
|
125
|
+
k = n;
|
126
|
+
xSum += x0[n - 1];
|
127
|
+
}
|
128
|
+
|
129
|
+
// ---------------------------------------------
|
130
|
+
// Execute projection
|
131
|
+
// ---------------------------------------------
|
132
|
+
proj[0] = -t0 + xSum;
|
133
|
+
|
134
|
+
if (proj[0] > 0) {
|
135
|
+
proj[0] = t0 + proj[0] / (k + 1);
|
136
|
+
} else {
|
137
|
+
proj[0] = t0;
|
138
|
+
}
|
139
|
+
|
140
|
+
memcpy(proj + 1, x0, k * sizeof(*x0));
|
141
|
+
scs_float diff = proj[0] - t0;
|
142
|
+
for (int i = 1; i < k + 1; i++) {
|
143
|
+
proj[i] -= diff;
|
144
|
+
}
|
145
|
+
memset(proj + 1 + k, 0, (n - k) * sizeof(*x0));
|
146
|
+
|
147
|
+
#ifdef SPECTRAL_DEBUG
|
148
|
+
//-------------------------------------------------------------------------
|
149
|
+
// Check residuals - not needed in production
|
150
|
+
//-------------------------------------------------------------------------
|
151
|
+
scs_float residuals[3];
|
152
|
+
compute_cone_residuals_ell1(proj, t0, x0, n, residuals);
|
153
|
+
|
154
|
+
if (residuals[0] > 1e-8 || residuals[1] > 1e-8 || residuals[2] > 1e-8) {
|
155
|
+
scs_printf("WARN: something is wrong in nuclear norm cone projection.\n");
|
156
|
+
scs_printf("dual_res / primal_res / comp : %.3e, %.3e, %.3e\n",
|
157
|
+
residuals[0], residuals[1], residuals[2]);
|
158
|
+
return -1;
|
159
|
+
}
|
160
|
+
#endif
|
161
|
+
|
162
|
+
return 0;
|
163
|
+
}
|
164
|
+
|
165
|
+
static void in_place_shuffle_ell1(scs_float *x, Value_index *work, scs_int n) {
|
166
|
+
for (scs_int i = 0; i < n; ++i) {
|
167
|
+
while (work[i].index != i) {
|
168
|
+
// Swap elements in `x`
|
169
|
+
scs_int target_idx = work[i].index;
|
170
|
+
scs_float temp_x = x[i];
|
171
|
+
x[i] = x[target_idx];
|
172
|
+
x[target_idx] = temp_x;
|
173
|
+
|
174
|
+
// Swap indices in `idxs` to reflect the change
|
175
|
+
scs_int temp_idx = work[i].index;
|
176
|
+
work[i].index = work[target_idx].index;
|
177
|
+
work[target_idx].index = temp_idx;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
int custom_cmp(const void *a, const void *b) {
|
183
|
+
Value_index *elemA = (Value_index *)a;
|
184
|
+
Value_index *elemB = (Value_index *)b;
|
185
|
+
return fabs(elemB->value) - fabs(elemA->value) > 0 ? 1 : -1;
|
186
|
+
}
|
187
|
+
|
188
|
+
void SCS(proj_ell_one)(scs_float *tx, scs_int n, ScsConeWork *c) {
|
189
|
+
scs_float t0 = tx[0];
|
190
|
+
scs_float *x0 = tx + 1;
|
191
|
+
scs_float *proj = c->work_ell1_proj;
|
192
|
+
Value_index *work = c->work_ell1;
|
193
|
+
// ------------------------------------------------------
|
194
|
+
// Preprocess vector so it is nonnegative and sorted
|
195
|
+
// ------------------------------------------------------
|
196
|
+
for (scs_int i = 0; i < n; ++i) {
|
197
|
+
work[i].value = fabs(x0[i]);
|
198
|
+
work[i].index = i;
|
199
|
+
}
|
200
|
+
|
201
|
+
qsort(work, n, sizeof(Value_index), custom_cmp);
|
202
|
+
|
203
|
+
for (scs_int i = 0; i < n; ++i) {
|
204
|
+
proj[i + 1] = work[i].value;
|
205
|
+
}
|
206
|
+
|
207
|
+
// ------------------------------------------
|
208
|
+
// project preprocessed vector
|
209
|
+
// ------------------------------------------
|
210
|
+
ell1_cone_proj_sorted(t0, proj + 1, proj, n);
|
211
|
+
|
212
|
+
// -------------------------------------------
|
213
|
+
// recover original vector
|
214
|
+
// -------------------------------------------
|
215
|
+
in_place_shuffle_ell1(proj + 1, work, n);
|
216
|
+
for (scs_int i = 0; i < n; i++) {
|
217
|
+
proj[i + 1] = proj[i + 1] * (x0[i] >= 0 ? 1 : -1);
|
218
|
+
}
|
219
|
+
|
220
|
+
memcpy(tx, proj, (n + 1) * sizeof(*proj));
|
221
|
+
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#include "cones.h"
|
2
|
+
// #include "scs.h"
|
3
|
+
#include "linalg.h"
|
4
|
+
#include "scs_blas.h"
|
5
|
+
#include "scs_types.h"
|
6
|
+
#include "util.h" // just for timer
|
7
|
+
|
8
|
+
/*
|
9
|
+
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
10
|
+
* Cones" by Daniel Cederberg and Stephen Boyd, 2024.
|
11
|
+
*
|
12
|
+
* If you have any questions on the code, please reach out to the code author
|
13
|
+
* Daniel Cederberg.
|
14
|
+
*
|
15
|
+
* This file implements code for projecting onto the nuclear norm cone.
|
16
|
+
*
|
17
|
+
* Last modified: 25 August 2024.
|
18
|
+
*/
|
19
|
+
|
20
|
+
#ifdef __cplusplus
|
21
|
+
extern "C" {
|
22
|
+
#endif
|
23
|
+
|
24
|
+
void BLAS(gemm)(const char *transa, const char *transb, blas_int *m,
|
25
|
+
blas_int *n, blas_int *k, scs_float *alpha, scs_float *a,
|
26
|
+
blas_int *lda, scs_float *b, blas_int *ldb, scs_float *beta,
|
27
|
+
scs_float *c, blas_int *ldc);
|
28
|
+
|
29
|
+
void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
|
30
|
+
const blas_int *incx);
|
31
|
+
|
32
|
+
void BLAS(gesvd)(const char *jobu, const char *jobvt, const blas_int *m,
|
33
|
+
const blas_int *n, scs_float *a, const blas_int *lda,
|
34
|
+
scs_float *s, scs_float *u, const blas_int *ldu, scs_float *vt,
|
35
|
+
const blas_int *ldvt, scs_float *work, const blas_int *lwork,
|
36
|
+
blas_int *info);
|
37
|
+
|
38
|
+
#ifdef __cplusplus
|
39
|
+
}
|
40
|
+
#endif
|
41
|
+
|
42
|
+
// forward declaration from ell1_cone.c
|
43
|
+
scs_int ell1_cone_proj_sorted(scs_float t0, const scs_float *x0,
|
44
|
+
scs_float *proj, scs_int n);
|
45
|
+
|
46
|
+
// X is of size m x n, stored column major. It is assumed that m >= n.
|
47
|
+
scs_int SCS(proj_nuclear_cone)(scs_float *tX, scs_int m, scs_int n,
|
48
|
+
ScsConeWork *c) {
|
49
|
+
assert(m >= n);
|
50
|
+
scs_float *X = tX + 1;
|
51
|
+
blas_int bm = m;
|
52
|
+
blas_int bn = n;
|
53
|
+
|
54
|
+
// -------------------------------------------------------------------------
|
55
|
+
// Compute SVD
|
56
|
+
// -------------------------------------------------------------------------
|
57
|
+
scs_float *s = c->s_nuc;
|
58
|
+
scs_float *u = c->u_nuc;
|
59
|
+
scs_float *vt = c->vt_nuc;
|
60
|
+
|
61
|
+
scs_float *work = c->work_nuc;
|
62
|
+
int lwork = c->lwork_nuc;
|
63
|
+
int info = 0;
|
64
|
+
|
65
|
+
BLAS(gesvd)("S", "A", &bm, &bn, X, &bm, s, u, &bm, vt, &bn, work, &lwork,
|
66
|
+
&info);
|
67
|
+
if (info != 0) {
|
68
|
+
printf("WARN: LAPACK gesvd error, info = %i\n", (int)info);
|
69
|
+
if (info < 0) {
|
70
|
+
return info;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
// -------------------------------------------------------------------------
|
74
|
+
// Project onto spectral *vector* cone
|
75
|
+
// -------------------------------------------------------------------------
|
76
|
+
SPECTRAL_TIMING(SCS(timer) _timer; SCS(tic)(&_timer);)
|
77
|
+
scs_int status = ell1_cone_proj_sorted(tX[0], s, tX, n);
|
78
|
+
SPECTRAL_TIMING(c->tot_time_vec_cone_proj += SCS(tocq)(&_timer);)
|
79
|
+
|
80
|
+
if (status < 0) {
|
81
|
+
return status;
|
82
|
+
}
|
83
|
+
|
84
|
+
// -------------------------------------------------------------------------
|
85
|
+
// Recover projection onto spectral *matrix* cone
|
86
|
+
// -------------------------------------------------------------------------
|
87
|
+
int one = 1;
|
88
|
+
for (scs_int i = 0; i < n; ++i) {
|
89
|
+
BLAS(scal)(&bm, &tX[i + 1], &u[i * m], &one);
|
90
|
+
}
|
91
|
+
|
92
|
+
char trans = 'N';
|
93
|
+
scs_float alpha = 1.0;
|
94
|
+
scs_float beta = 0.0;
|
95
|
+
BLAS(gemm)(&trans, &trans, &bm, &bn, &bn, &alpha, u, &bm, vt, &bn, &beta,
|
96
|
+
tX + 1, &bm);
|
97
|
+
|
98
|
+
return 0;
|
99
|
+
}
|
@@ -0,0 +1,196 @@
|
|
1
|
+
#include "cones.h"
|
2
|
+
// #include "scs.h"
|
3
|
+
#include "linalg.h"
|
4
|
+
#include "scs_blas.h"
|
5
|
+
#include "scs_types.h"
|
6
|
+
#include "util_spectral_cones.h"
|
7
|
+
#include <stdlib.h> // qsort
|
8
|
+
|
9
|
+
#define TOL_LARGEST_CONE 1e-9
|
10
|
+
|
11
|
+
/*
|
12
|
+
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
13
|
+
* Cones" by Daniel Cederberg and Stephen Boyd, 2024.
|
14
|
+
*
|
15
|
+
* If you have any questions on the code, please reach out to the code author
|
16
|
+
* Daniel Cederberg.
|
17
|
+
*
|
18
|
+
* This file implements code for projecting onto the sum-of-largest cone.
|
19
|
+
* It assumes that the input is sorted. If you need code that does not make
|
20
|
+
* this assumption, please reach out.
|
21
|
+
*
|
22
|
+
* Last modified: 25 August 2024.
|
23
|
+
*/
|
24
|
+
|
25
|
+
#ifdef SPECTRAL_DEBUG
|
26
|
+
static void compute_cone_residuals(scs_float t, const scs_float *x,
|
27
|
+
scs_float t0, const scs_float *x0,
|
28
|
+
scs_float residuals[3], scs_int n,
|
29
|
+
scs_int k);
|
30
|
+
#endif
|
31
|
+
|
32
|
+
scs_int assert_sorted(scs_float *x, scs_int n) {
|
33
|
+
for (scs_int i = 0; i < n - 1; ++i) {
|
34
|
+
if (x[i] < x[i + 1]) {
|
35
|
+
return -1;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
return 0;
|
39
|
+
}
|
40
|
+
|
41
|
+
scs_int proj_sum_largest_cone_sorted(scs_float *t, scs_float *x, scs_int n,
|
42
|
+
scs_int k) {
|
43
|
+
#ifdef SPECTRAL_DEBUG
|
44
|
+
scs_float t00 = *t;
|
45
|
+
scs_float *x0 = scs_malloc(n * sizeof(*x0));
|
46
|
+
memcpy(x0, x, n * sizeof(*x0));
|
47
|
+
scs_int status = assert_sorted(x, n);
|
48
|
+
if (status < 0) {
|
49
|
+
scs_printf("NOT SORTED! \n");
|
50
|
+
return status;
|
51
|
+
}
|
52
|
+
#endif
|
53
|
+
|
54
|
+
// -------------------------------
|
55
|
+
// Initialize state variables
|
56
|
+
// -------------------------------
|
57
|
+
assert(k < n && k > 0);
|
58
|
+
scs_int nu = k, nt = 0;
|
59
|
+
scs_float eta = 0.0;
|
60
|
+
scs_float t0 = *t;
|
61
|
+
scs_float S = x[0];
|
62
|
+
for (scs_int i = 1; i < k; ++i) {
|
63
|
+
S += x[i];
|
64
|
+
}
|
65
|
+
|
66
|
+
scs_float a_u = x[nu - 1], a_t = x[nu];
|
67
|
+
|
68
|
+
// ---------------------------------
|
69
|
+
// main loop
|
70
|
+
// ---------------------------------
|
71
|
+
scs_float ratio, s1, s3, s;
|
72
|
+
while (S > *t + TOL_LARGEST_CONE) {
|
73
|
+
ratio = (nu == k) ? 1.0 : (scs_float)(nt) / (k - nu);
|
74
|
+
|
75
|
+
// ------------------------------------------------------
|
76
|
+
// compute step size
|
77
|
+
// ------------------------------------------------------
|
78
|
+
s1 = (nu == k) ? a_u - a_t : (a_u - a_t) / (ratio - 1);
|
79
|
+
s3 = (S - *t) / (ratio * (nu + 1) + (k - nu));
|
80
|
+
s = (nu == 0) ? s3 : MIN(s3, s1);
|
81
|
+
|
82
|
+
if (!(nu + nt == n || nt == 0)) {
|
83
|
+
scs_float val = a_t - x[nu + nt];
|
84
|
+
s = MIN(s, val);
|
85
|
+
}
|
86
|
+
|
87
|
+
// --------------------------
|
88
|
+
// update state
|
89
|
+
// --------------------------
|
90
|
+
eta += s * ratio;
|
91
|
+
S -= s * (ratio * nu + k - nu);
|
92
|
+
*t = t0 + eta;
|
93
|
+
|
94
|
+
if (nt > 0) {
|
95
|
+
a_t -= s;
|
96
|
+
}
|
97
|
+
|
98
|
+
if (nu != 0 && s == s1) {
|
99
|
+
nu -= 1;
|
100
|
+
} else {
|
101
|
+
assert(s != s1);
|
102
|
+
}
|
103
|
+
|
104
|
+
if (nu > 0) {
|
105
|
+
a_u = x[nu - 1] - eta;
|
106
|
+
}
|
107
|
+
|
108
|
+
nt = (nt == 0) ? 2 : nt + 1;
|
109
|
+
}
|
110
|
+
|
111
|
+
nt -= 1;
|
112
|
+
|
113
|
+
// update projection
|
114
|
+
scs_int i;
|
115
|
+
for (i = 0; i < nu; ++i) {
|
116
|
+
x[i] -= eta;
|
117
|
+
}
|
118
|
+
|
119
|
+
for (i = nu; i < nu + nt; ++i) {
|
120
|
+
x[i] = a_t;
|
121
|
+
}
|
122
|
+
|
123
|
+
#ifdef SPECTRAL_DEBUG
|
124
|
+
scs_float residuals[3];
|
125
|
+
compute_cone_residuals(*t, x, t00, x0, residuals, n, k);
|
126
|
+
scs_free(x0);
|
127
|
+
if (residuals[0] > 1e-10 || residuals[1] > 1e-10 ||
|
128
|
+
fabs(residuals[2]) > 1e-10) {
|
129
|
+
return -1;
|
130
|
+
}
|
131
|
+
#endif
|
132
|
+
|
133
|
+
return 0;
|
134
|
+
}
|
135
|
+
|
136
|
+
scs_int cmp_desc(const void *a, const void *b) {
|
137
|
+
scs_float da = *(const scs_float *)a;
|
138
|
+
scs_float db = *(const scs_float *)b;
|
139
|
+
if (da < db)
|
140
|
+
return 1;
|
141
|
+
if (da > db)
|
142
|
+
return -1;
|
143
|
+
return 0;
|
144
|
+
}
|
145
|
+
|
146
|
+
#ifdef SPECTRAL_DEBUG
|
147
|
+
// this function is not used in production so fine to allocate memory
|
148
|
+
static scs_float sum_largest_val(const scs_float *x, scs_int n, scs_int k) {
|
149
|
+
scs_float *x_temp = scs_malloc(n * sizeof(*x));
|
150
|
+
memcpy(x_temp, x, n * sizeof(*x));
|
151
|
+
qsort(x_temp, n, sizeof(*x_temp), cmp_desc);
|
152
|
+
|
153
|
+
scs_float val = 0.0;
|
154
|
+
for (scs_int i = 0; i < k; ++i) {
|
155
|
+
val += x_temp[i];
|
156
|
+
}
|
157
|
+
scs_free(x_temp);
|
158
|
+
return val;
|
159
|
+
}
|
160
|
+
|
161
|
+
static void compute_cone_residuals(scs_float t, const scs_float *x,
|
162
|
+
scs_float t0, const scs_float *x0,
|
163
|
+
scs_float residuals[3], scs_int n,
|
164
|
+
scs_int k) {
|
165
|
+
scs_float lmbda_t = t - t0;
|
166
|
+
scs_float *lmbda_x = scs_malloc(n * sizeof(*x));
|
167
|
+
scs_float sum_lmbda_x = 0.0;
|
168
|
+
for (scs_int i = 0; i < n; ++i) {
|
169
|
+
lmbda_x[i] = x[i] - x0[i];
|
170
|
+
sum_lmbda_x += lmbda_x[i];
|
171
|
+
}
|
172
|
+
|
173
|
+
scs_float comp = lmbda_t * t + SCS(dot)(lmbda_x, x, n);
|
174
|
+
scs_float pri_res = sum_largest_val(x, n, k) - t;
|
175
|
+
|
176
|
+
scs_float dual_res = fabs(sum_lmbda_x + lmbda_t * k);
|
177
|
+
dual_res *= dual_res;
|
178
|
+
|
179
|
+
for (scs_int i = 0; i < n; ++i) {
|
180
|
+
if (lmbda_x[i] > 0) {
|
181
|
+
dual_res += lmbda_x[i] * lmbda_x[i];
|
182
|
+
}
|
183
|
+
|
184
|
+
if (lmbda_x[i] + lmbda_t < 0) {
|
185
|
+
dual_res += (lmbda_x[i] + lmbda_t) * (lmbda_x[i] + lmbda_t);
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
residuals[0] = dual_res;
|
190
|
+
residuals[1] = pri_res;
|
191
|
+
residuals[2] = comp;
|
192
|
+
|
193
|
+
scs_free(lmbda_x);
|
194
|
+
}
|
195
|
+
#endif
|
196
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#include "cones.h"
|
2
|
+
#include "linalg.h"
|
3
|
+
#include "scs_blas.h"
|
4
|
+
#include "scs_types.h"
|
5
|
+
#include "util.h" // just for timer
|
6
|
+
|
7
|
+
/*
|
8
|
+
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
9
|
+
* Cones" by Daniel Cederberg and Stephen Boyd, 2024.
|
10
|
+
*
|
11
|
+
* If you have any questions on the code, please reach out to the code author
|
12
|
+
* Daniel Cederberg.
|
13
|
+
*
|
14
|
+
* This file implements code for projecting onto the sum-of-largest eigenvalues
|
15
|
+
* cone.
|
16
|
+
*
|
17
|
+
* Last modified: 25 August 2024.
|
18
|
+
*/
|
19
|
+
|
20
|
+
#ifdef __cplusplus
|
21
|
+
extern "C" {
|
22
|
+
#endif
|
23
|
+
|
24
|
+
void BLAS(syev)(const char *jobz, const char *uplo, blas_int *n, scs_float *a,
|
25
|
+
blas_int *lda, scs_float *w, scs_float *work, blas_int *lwork,
|
26
|
+
blas_int *info);
|
27
|
+
|
28
|
+
void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
|
29
|
+
const blas_int *incx);
|
30
|
+
|
31
|
+
void BLAS(gemm)(const char *transa, const char *transb, blas_int *m,
|
32
|
+
blas_int *n, blas_int *k, scs_float *alpha, scs_float *a,
|
33
|
+
blas_int *lda, scs_float *b, blas_int *ldb, scs_float *beta,
|
34
|
+
scs_float *c, blas_int *ldc);
|
35
|
+
|
36
|
+
#ifdef __cplusplus
|
37
|
+
}
|
38
|
+
#endif
|
39
|
+
|
40
|
+
// forward declaration
|
41
|
+
scs_int proj_sum_largest_cone_sorted(scs_float *t, scs_float *x, scs_int n,
|
42
|
+
scs_int k);
|
43
|
+
|
44
|
+
void flip(scs_float *x, int n) {
|
45
|
+
scs_float temp;
|
46
|
+
for (int i = 0; i < n / 2; i++) {
|
47
|
+
temp = x[i];
|
48
|
+
x[i] = x[n - i - 1];
|
49
|
+
x[n - i - 1] = temp;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
scs_int SCS(proj_sum_largest_evals)(scs_float *tX, scs_int n, scs_int k,
|
54
|
+
ScsConeWork *c) {
|
55
|
+
// tvX = [t, X], where X represents the lower triangular part of a matrix
|
56
|
+
// stored in a compact form and off-diagonal elements have been scaled by
|
57
|
+
// sqrt(2)
|
58
|
+
scs_float *X = tX + 1;
|
59
|
+
|
60
|
+
// ----------------------------------------------------------------------
|
61
|
+
// compute eigendecomposition
|
62
|
+
// ----------------------------------------------------------------------
|
63
|
+
scs_int i;
|
64
|
+
blas_int nb = (blas_int)n;
|
65
|
+
blas_int nb_plus_one = (blas_int)(n + 1);
|
66
|
+
blas_int one_int = 1;
|
67
|
+
scs_float sqrt2 = sqrt(2.0);
|
68
|
+
scs_float sqrt2_inv = 1.0 / sqrt2;
|
69
|
+
scs_float *Xs = c->Xs;
|
70
|
+
scs_float *e = c->e;
|
71
|
+
scs_float *Z = c->Z;
|
72
|
+
scs_float *work = c->work;
|
73
|
+
blas_int lwork = c->lwork;
|
74
|
+
blas_int info = 0;
|
75
|
+
|
76
|
+
// copy lower triangular matrix into full matrix
|
77
|
+
for (i = 0; i < n; ++i) {
|
78
|
+
memcpy(&(Xs[i * (n + 1)]), &(X[i * n - ((i - 1) * i) / 2]),
|
79
|
+
(n - i) * sizeof(scs_float));
|
80
|
+
}
|
81
|
+
|
82
|
+
// rescale diags by sqrt(2)
|
83
|
+
BLAS(scal)(&nb, &sqrt2, Xs, &nb_plus_one);
|
84
|
+
|
85
|
+
// Eigendecomposition. On exit, the lower triangular part of Xs stores
|
86
|
+
// the eigenvectors. The vector e stores the eigenvalues in ascending
|
87
|
+
// order (smallest eigenvalue first) */
|
88
|
+
BLAS(syev)("Vectors", "Lower", &nb, Xs, &nb, e, work, &lwork, &info);
|
89
|
+
if (info != 0) {
|
90
|
+
scs_printf("WARN: LAPACK syev error, info = %i\n", (int)info);
|
91
|
+
if (info < 0) {
|
92
|
+
return info;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
// ----------------------------------------------------------------------
|
97
|
+
// Project onto spectral *vector* cone. Note that e is sqrt(2) times
|
98
|
+
// the eigenvalue vector we want to project. We therefore multiply
|
99
|
+
// tvX[0] by sqrt(2).
|
100
|
+
// ----------------------------------------------------------------------
|
101
|
+
tX[0] *= sqrt2;
|
102
|
+
SPECTRAL_TIMING(SCS(timer) _timer; SCS(tic)(&_timer);)
|
103
|
+
flip(e, n);
|
104
|
+
scs_int status = proj_sum_largest_cone_sorted(&tX[0], e, n, k);
|
105
|
+
flip(e, n);
|
106
|
+
SPECTRAL_TIMING(c->tot_time_vec_cone_proj += SCS(tocq)(&_timer);)
|
107
|
+
|
108
|
+
if (status < 0) {
|
109
|
+
return status;
|
110
|
+
}
|
111
|
+
|
112
|
+
// ----------------------------------------------------------------------
|
113
|
+
// recover projection onto spectral *matrix* cone
|
114
|
+
// ----------------------------------------------------------------------
|
115
|
+
memcpy(c->work_sum_of_largest, Xs, n * n * sizeof(*Xs));
|
116
|
+
for (i = 0; i < n; ++i) {
|
117
|
+
BLAS(scal)(&nb, &e[i], &Xs[i * n], &one_int);
|
118
|
+
}
|
119
|
+
|
120
|
+
char transN = 'N';
|
121
|
+
char transY = 'T';
|
122
|
+
scs_float one = 1.0;
|
123
|
+
scs_float zero = 0.0;
|
124
|
+
|
125
|
+
// it is not safe to have overlapping matrices for dgemm_.
|
126
|
+
BLAS(gemm)(&transN, &transY, &nb, &nb, &nb, &one, Xs, &nb,
|
127
|
+
c->work_sum_of_largest, &nb, &zero, Z, &nb);
|
128
|
+
|
129
|
+
BLAS(scal)(&nb, &sqrt2_inv, Z, &nb_plus_one);
|
130
|
+
|
131
|
+
for (i = 0; i < n; ++i) {
|
132
|
+
memcpy(&(X[i * n - ((i - 1) * i) / 2]), &(Z[i * (n + 1)]),
|
133
|
+
(n - i) * sizeof(scs_float));
|
134
|
+
}
|
135
|
+
|
136
|
+
tX[0] *= sqrt2_inv;
|
137
|
+
|
138
|
+
return 0;
|
139
|
+
}
|
140
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#include "util_spectral_cones.h"
|
2
|
+
|
3
|
+
bool is_pos(const scs_float *x, scs_int n) {
|
4
|
+
for (scs_int i = 0; i < n; ++i) {
|
5
|
+
if (x[i] <= 0.0) {
|
6
|
+
return false;
|
7
|
+
}
|
8
|
+
}
|
9
|
+
return true;
|
10
|
+
}
|
11
|
+
|
12
|
+
bool is_negative(const scs_float *x, scs_int n) {
|
13
|
+
for (scs_int i = 0; i < n; ++i) {
|
14
|
+
if (x[i] >= 0.0) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
return true;
|
19
|
+
}
|
20
|
+
|
21
|
+
void non_neg_proj(const scs_float *src, scs_float *dst, scs_int n) {
|
22
|
+
for (scs_int i = 0; i < n; ++i) {
|
23
|
+
dst[i] = (src[i] > 0.0) ? src[i] : 0.0;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
void print_vector(const scs_float *x, scs_int n) {
|
28
|
+
for (scs_int i = 0; i < n; ++i) {
|
29
|
+
printf("%f ", x[i]);
|
30
|
+
}
|
31
|
+
printf("\n");
|
32
|
+
}
|
33
|
+
|
34
|
+
scs_float min_vec(const scs_float *vec, scs_int n) {
|
35
|
+
scs_float minVal = vec[0];
|
36
|
+
|
37
|
+
for (scs_int i = 1; i < n; ++i) {
|
38
|
+
if (vec[i] < minVal) {
|
39
|
+
minVal = vec[i];
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
return minVal;
|
44
|
+
}
|
45
|
+
|
46
|
+
scs_float sum_log(const scs_float *x, scs_int n) {
|
47
|
+
scs_float sum = 0.0;
|
48
|
+
for (scs_int i = 0; i < n; ++i) {
|
49
|
+
sum += log(x[i]);
|
50
|
+
}
|
51
|
+
return sum;
|
52
|
+
}
|