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
|
@@ -1,47 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const char *scs_get_lin_sys_method(void) {
|
|
4
|
-
return "sparse-indirect-scs";
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/* Not possible to do this on the fly due to M_ii += a_i' (R_y)^-1 a_i */
|
|
8
|
-
/* set M = inv ( diag ( R_x + P + A' R_y^{-1} A ) ) */
|
|
9
|
-
static void set_preconditioner(ScsLinSysWork *p) {
|
|
10
|
-
scs_int i, k;
|
|
11
|
-
scs_float *M = p->M;
|
|
12
|
-
const ScsMatrix *A = p->A;
|
|
13
|
-
const ScsMatrix *P = p->P;
|
|
1
|
+
/* ======================== Includes / Types ======================== */
|
|
14
2
|
|
|
15
|
-
#
|
|
16
|
-
scs_printf("getting pre-conditioner\n");
|
|
17
|
-
#endif
|
|
3
|
+
#include "private.h"
|
|
18
4
|
|
|
19
|
-
|
|
20
|
-
for (i = 0; i < A->n; ++i) { /* cols */
|
|
21
|
-
/* M_ii = (R_x)_i */
|
|
22
|
-
M[i] = p->diag_r[i];
|
|
23
|
-
/* M_ii += a_i' (R_y)^-1 a_i */
|
|
24
|
-
for (k = A->p[i]; k < A->p[i + 1]; ++k) {
|
|
25
|
-
/* A->i[k] is row of entry k with value A->x[k] */
|
|
26
|
-
M[i] += A->x[k] * A->x[k] / p->diag_r[A->n + A->i[k]];
|
|
27
|
-
}
|
|
28
|
-
if (P) {
|
|
29
|
-
for (k = P->p[i]; k < P->p[i + 1]; k++) {
|
|
30
|
-
/* diagonal element only */
|
|
31
|
-
if (P->i[k] == i) { /* row == col */
|
|
32
|
-
/* M_ii += P_ii */
|
|
33
|
-
M[i] += P->x[k];
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/* finally invert for pre-conditioner */
|
|
39
|
-
M[i] = 1. / M[i];
|
|
40
|
-
}
|
|
41
|
-
#if VERBOSITY > 0
|
|
42
|
-
scs_printf("finished getting pre-conditioner\n");
|
|
43
|
-
#endif
|
|
44
|
-
}
|
|
5
|
+
/* ======================== Matrix Helpers ======================== */
|
|
45
6
|
|
|
46
7
|
static void transpose(const ScsMatrix *A, ScsLinSysWork *p) {
|
|
47
8
|
scs_int *Ci = p->At->i;
|
|
@@ -84,30 +45,45 @@ static void transpose(const ScsMatrix *A, ScsLinSysWork *p) {
|
|
|
84
45
|
#endif
|
|
85
46
|
}
|
|
86
47
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
/* Not possible to do this on the fly due to M_ii += a_i' (R_y)^-1 a_i */
|
|
49
|
+
/* set M = inv ( diag ( R_x + P + A' R_y^{-1} A ) ) */
|
|
50
|
+
static void set_preconditioner(ScsLinSysWork *p) {
|
|
51
|
+
scs_int i, k;
|
|
52
|
+
scs_float *M = p->M;
|
|
53
|
+
const ScsMatrix *A = p->A;
|
|
54
|
+
const ScsMatrix *P = p->P;
|
|
55
|
+
|
|
56
|
+
#if VERBOSITY > 0
|
|
57
|
+
scs_printf("getting pre-conditioner\n");
|
|
58
|
+
#endif
|
|
59
|
+
|
|
60
|
+
/* M_ii = (R_x)_i + P_ii + a_i' (R_y)^-1 a_i */
|
|
61
|
+
for (i = 0; i < A->n; ++i) { /* cols */
|
|
62
|
+
/* M_ii = (R_x)_i */
|
|
63
|
+
M[i] = p->diag_r[i];
|
|
64
|
+
/* M_ii += a_i' (R_y)^-1 a_i */
|
|
65
|
+
for (k = A->p[i]; k < A->p[i + 1]; ++k) {
|
|
66
|
+
/* A->i[k] is row of entry k with value A->x[k] */
|
|
67
|
+
M[i] += A->x[k] * A->x[k] / p->diag_r[A->n + A->i[k]];
|
|
98
68
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
69
|
+
if (P) {
|
|
70
|
+
for (k = P->p[i]; k < P->p[i + 1]; ++k) {
|
|
71
|
+
if (P->i[k] == i) {
|
|
72
|
+
M[i] += P->x[k];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/* finally invert for pre-conditioner */
|
|
77
|
+
M[i] = 1. / M[i];
|
|
102
78
|
}
|
|
79
|
+
#if VERBOSITY > 0
|
|
80
|
+
scs_printf("finished getting pre-conditioner\n");
|
|
81
|
+
#endif
|
|
103
82
|
}
|
|
104
83
|
|
|
105
|
-
/*
|
|
106
|
-
static void
|
|
107
|
-
|
|
108
|
-
for (i = 0; i < p->m; ++i) {
|
|
109
|
-
vec[i] /= p->diag_r[p->n + i];
|
|
110
|
-
}
|
|
84
|
+
/* we use a different accum_by_a here for speed */
|
|
85
|
+
static void accum_by_a(ScsLinSysWork *p, const scs_float *x, scs_float *y) {
|
|
86
|
+
SCS(accum_by_atrans)(p->At, x, y);
|
|
111
87
|
}
|
|
112
88
|
|
|
113
89
|
/* y += R_x * x */
|
|
@@ -118,9 +94,12 @@ static void accum_by_r_x(scs_float *y, const scs_float *x, ScsLinSysWork *p) {
|
|
|
118
94
|
}
|
|
119
95
|
}
|
|
120
96
|
|
|
121
|
-
/*
|
|
122
|
-
static void
|
|
123
|
-
|
|
97
|
+
/* vec -> R_y^{-1} vec */
|
|
98
|
+
static void scale_by_r_y_inv(scs_float *vec, ScsLinSysWork *p) {
|
|
99
|
+
scs_int i;
|
|
100
|
+
for (i = 0; i < p->m; ++i) {
|
|
101
|
+
vec[i] /= p->diag_r[p->n + i];
|
|
102
|
+
}
|
|
124
103
|
}
|
|
125
104
|
|
|
126
105
|
/* y = (R_x + P + A' R_y^{-1} A) x */
|
|
@@ -148,49 +127,389 @@ static void apply_pre_conditioner(scs_float *z, scs_float *r, scs_int n,
|
|
|
148
127
|
}
|
|
149
128
|
}
|
|
150
129
|
|
|
151
|
-
/*
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
130
|
+
/* ======================== eigCG deflation ======================== */
|
|
131
|
+
|
|
132
|
+
#define EIGCG_WINDOW (60)
|
|
133
|
+
|
|
134
|
+
#ifdef USE_LAPACK
|
|
135
|
+
#include "scs_blas.h"
|
|
136
|
+
|
|
137
|
+
#ifdef __cplusplus
|
|
138
|
+
extern "C" {
|
|
139
|
+
#endif
|
|
140
|
+
|
|
141
|
+
void BLAS(syev)(const char *jobz, const char *uplo, blas_int *n, scs_float *a,
|
|
142
|
+
blas_int *lda, scs_float *w, scs_float *work, blas_int *lwork,
|
|
143
|
+
blas_int *info);
|
|
144
|
+
void BLAS(gemm)(const char *transa, const char *transb, blas_int *m,
|
|
145
|
+
blas_int *n, blas_int *k, scs_float *alpha, scs_float *a,
|
|
146
|
+
blas_int *lda, scs_float *b, blas_int *ldb, scs_float *beta,
|
|
147
|
+
scs_float *c, blas_int *ldc);
|
|
148
|
+
|
|
149
|
+
#ifdef __cplusplus
|
|
155
150
|
}
|
|
151
|
+
#endif
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
153
|
+
/* Cholesky of a k x k column-major matrix, lower triangle, in place.
|
|
154
|
+
* Returns nonzero on failure (also catches NaN via the negated compare). */
|
|
155
|
+
static scs_int eigcg_chol(scs_float *G, scs_int k) {
|
|
156
|
+
scs_int c, r, t;
|
|
157
|
+
for (c = 0; c < k; ++c) {
|
|
158
|
+
for (r = c; r < k; ++r) {
|
|
159
|
+
scs_float acc = G[c * k + r];
|
|
160
|
+
for (t = 0; t < c; ++t) {
|
|
161
|
+
acc -= G[t * k + r] * G[t * k + c];
|
|
162
|
+
}
|
|
163
|
+
G[c * k + r] = acc;
|
|
164
|
+
}
|
|
165
|
+
if (!(G[c * k + c] > 1e-300)) {
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
G[c * k + c] = SQRTF(G[c * k + c]);
|
|
169
|
+
for (r = c + 1; r < k; ++r) {
|
|
170
|
+
G[c * k + r] /= G[c * k + c];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
164
175
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
176
|
+
/* solve L L' x = rhs in place given the factor from eigcg_chol */
|
|
177
|
+
static void eigcg_chol_solve(const scs_float *G, scs_float *x, scs_int k) {
|
|
178
|
+
scs_int c, t;
|
|
179
|
+
for (c = 0; c < k; ++c) {
|
|
180
|
+
for (t = 0; t < c; ++t) {
|
|
181
|
+
x[c] -= G[t * k + c] * x[t];
|
|
182
|
+
}
|
|
183
|
+
x[c] /= G[c * k + c];
|
|
184
|
+
}
|
|
185
|
+
for (c = k - 1; c >= 0; --c) {
|
|
186
|
+
for (t = c + 1; t < k; ++t) {
|
|
187
|
+
x[c] -= G[c * k + t] * x[t];
|
|
188
|
+
}
|
|
189
|
+
x[c] /= G[c * k + c];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
169
192
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
/* eigendecomposition of the leading j x j block of eig_T (lda eig_win):
|
|
194
|
+
* eigenvectors into E (lda j), ascending eigenvalues into w */
|
|
195
|
+
static scs_int eigcg_eig_leading(ScsLinSysWork *pr, scs_int j, scs_float *E,
|
|
196
|
+
scs_float *w) {
|
|
197
|
+
blas_int bn = (blas_int)j, lda = (blas_int)j,
|
|
198
|
+
lwork = (blas_int)pr->eig_lwork, info = 0;
|
|
199
|
+
scs_int a, b;
|
|
200
|
+
for (a = 0; a < j; ++a) {
|
|
201
|
+
for (b = 0; b < j; ++b) {
|
|
202
|
+
E[a * j + b] = pr->eig_T[a * pr->eig_win + b];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
BLAS(syev)("V", "U", &bn, E, &lda, w, pr->eig_work, &lwork, &info);
|
|
206
|
+
return (scs_int)info;
|
|
207
|
+
}
|
|
178
208
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
209
|
+
/* modified Gram-Schmidt on the columns of S, in place with compaction;
|
|
210
|
+
* returns the numerical rank kept */
|
|
211
|
+
static scs_int eigcg_mgs(scs_float *S, scs_int rows, scs_int cols) {
|
|
212
|
+
scs_int c, k, t, pass, kept = 0;
|
|
213
|
+
for (c = 0; c < cols; ++c) {
|
|
214
|
+
scs_float *v = &(S[c * rows]);
|
|
215
|
+
scs_float nrm = 0.;
|
|
216
|
+
for (pass = 0; pass < 2; ++pass) {
|
|
217
|
+
for (k = 0; k < kept; ++k) {
|
|
218
|
+
const scs_float *qk = &(S[k * rows]);
|
|
219
|
+
scs_float d = 0.;
|
|
220
|
+
for (t = 0; t < rows; ++t) {
|
|
221
|
+
d += qk[t] * v[t];
|
|
222
|
+
}
|
|
223
|
+
for (t = 0; t < rows; ++t) {
|
|
224
|
+
v[t] -= d * qk[t];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (t = 0; t < rows; ++t) {
|
|
229
|
+
nrm += v[t] * v[t];
|
|
230
|
+
}
|
|
231
|
+
nrm = SQRTF(nrm);
|
|
232
|
+
if (nrm > 1e-8) {
|
|
233
|
+
for (t = 0; t < rows; ++t) {
|
|
234
|
+
v[t] /= nrm;
|
|
235
|
+
}
|
|
236
|
+
if (kept != c) {
|
|
237
|
+
memcpy(&(S[kept * rows]), v, rows * sizeof(scs_float));
|
|
238
|
+
}
|
|
239
|
+
kept++;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return kept;
|
|
243
|
+
}
|
|
184
244
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
245
|
+
/* Thick restart: compress the full window to the lowest-nev Ritz vectors of
|
|
246
|
+
* this window AND of the previous one (the eigCG doubling trick, which is
|
|
247
|
+
* what makes the Ritz pairs converge despite restarting), re-diagonalise,
|
|
248
|
+
* and leave the arrowhead border that couples the compressed basis to the
|
|
249
|
+
* next incoming Lanczos vector. The CG recurrence is never touched. */
|
|
250
|
+
static void eigcg_restart(ScsLinSysWork *pr, scs_float tlink) {
|
|
251
|
+
scs_int win = pr->eig_win, nev = pr->dfl_max, n = pr->n, q, a, t;
|
|
252
|
+
scs_float *E = pr->eig_E, *w = pr->eig_w, *S = pr->eig_S, *TS = pr->eig_TS,
|
|
253
|
+
*H = pr->eig_H, *G = pr->eig_G, *T = pr->eig_T;
|
|
254
|
+
scs_float one = 1., zero = 0.;
|
|
255
|
+
blas_int bm, bn2, bk, ld1, ld2, ld3, info = 0,
|
|
256
|
+
lwork = (blas_int)pr->eig_lwork;
|
|
257
|
+
/* lowest nev of T_win */
|
|
258
|
+
if (eigcg_eig_leading(pr, win, E, w)) {
|
|
259
|
+
pr->eig_dead = 1;
|
|
260
|
+
return;
|
|
190
261
|
}
|
|
191
|
-
|
|
262
|
+
for (a = 0; a < nev; ++a) {
|
|
263
|
+
memcpy(&(S[a * win]), &(E[a * win]), win * sizeof(scs_float));
|
|
264
|
+
}
|
|
265
|
+
/* lowest nev of the leading (win-1) block, zero-padded last row */
|
|
266
|
+
if (eigcg_eig_leading(pr, win - 1, E, w)) {
|
|
267
|
+
pr->eig_dead = 1;
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (a = 0; a < nev; ++a) {
|
|
271
|
+
for (t = 0; t < win - 1; ++t) {
|
|
272
|
+
S[(nev + a) * win + t] = E[a * (win - 1) + t];
|
|
273
|
+
}
|
|
274
|
+
S[(nev + a) * win + win - 1] = 0.;
|
|
275
|
+
}
|
|
276
|
+
q = eigcg_mgs(S, win, 2 * nev);
|
|
277
|
+
if (q <= 0) {
|
|
278
|
+
pr->eig_dead = 1;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
/* H = S' T S */
|
|
282
|
+
bm = (blas_int)win;
|
|
283
|
+
bn2 = (blas_int)q;
|
|
284
|
+
bk = (blas_int)win;
|
|
285
|
+
ld1 = (blas_int)win;
|
|
286
|
+
BLAS(gemm)("N", "N", &bm, &bn2, &bk, &one, T, &ld1, S, &ld1, &zero, TS,
|
|
287
|
+
&ld1);
|
|
288
|
+
bm = (blas_int)q;
|
|
289
|
+
ld2 = (blas_int)q;
|
|
290
|
+
BLAS(gemm)("T", "N", &bm, &bn2, &bk, &one, S, &ld1, TS, &ld1, &zero, H,
|
|
291
|
+
&ld2);
|
|
292
|
+
BLAS(syev)("V", "U", &bm, H, &ld2, w, pr->eig_work, &lwork, &info);
|
|
293
|
+
if (info) {
|
|
294
|
+
pr->eig_dead = 1;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
/* G = S U; V(:, 0:q) = V(:, 0:win) G */
|
|
298
|
+
bm = (blas_int)win;
|
|
299
|
+
bk = (blas_int)q;
|
|
300
|
+
BLAS(gemm)("N", "N", &bm, &bn2, &bk, &one, S, &ld1, H, &ld2, &zero, G,
|
|
301
|
+
&ld1);
|
|
302
|
+
bm = (blas_int)n;
|
|
303
|
+
bk = (blas_int)win;
|
|
304
|
+
ld3 = (blas_int)n;
|
|
305
|
+
BLAS(gemm)("N", "N", &bm, &bn2, &bk, &one, pr->eig_V, &ld3, G, &ld1, &zero,
|
|
306
|
+
pr->eig_VS, &ld3);
|
|
307
|
+
memcpy(pr->eig_V, pr->eig_VS, (size_t)q * n * sizeof(scs_float));
|
|
308
|
+
/* T = diag(ritz values) + arrowhead border to the incoming vector */
|
|
309
|
+
memset(T, 0, (size_t)win * win * sizeof(scs_float));
|
|
310
|
+
for (a = 0; a < q; ++a) {
|
|
311
|
+
T[a * win + a] = w[a];
|
|
312
|
+
}
|
|
313
|
+
for (a = 0; a < q; ++a) {
|
|
314
|
+
scs_float sa = tlink * G[a * win + (win - 1)];
|
|
315
|
+
T[q * win + a] = sa;
|
|
316
|
+
T[a * win + q] = sa;
|
|
317
|
+
}
|
|
318
|
+
pr->eig_j = q;
|
|
319
|
+
pr->eig_jd = q;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* the window is transient: live only between eigcg_start and
|
|
323
|
+
* eigcg_finish, i.e. during the one deep solve per metric change, so the
|
|
324
|
+
* persistent footprint of deflation is just W and AW (2 k n) */
|
|
325
|
+
static void eigcg_free_window(ScsLinSysWork *pr) {
|
|
326
|
+
scs_free(pr->eig_V);
|
|
327
|
+
scs_free(pr->eig_T);
|
|
328
|
+
scs_free(pr->eig_E);
|
|
329
|
+
scs_free(pr->eig_w);
|
|
330
|
+
scs_free(pr->eig_S);
|
|
331
|
+
scs_free(pr->eig_TS);
|
|
332
|
+
scs_free(pr->eig_H);
|
|
333
|
+
scs_free(pr->eig_G);
|
|
334
|
+
scs_free(pr->eig_VS);
|
|
335
|
+
scs_free(pr->eig_work);
|
|
336
|
+
pr->eig_V = pr->eig_T = pr->eig_E = pr->eig_w = pr->eig_S = pr->eig_TS =
|
|
337
|
+
pr->eig_H = pr->eig_G = pr->eig_VS = pr->eig_work = SCS_NULL;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static scs_int eigcg_alloc_window(ScsLinSysWork *pr) {
|
|
341
|
+
scs_int n = pr->n, win = pr->eig_win, nev2 = 2 * pr->dfl_max;
|
|
342
|
+
pr->eig_V = (scs_float *)scs_calloc((size_t)n * win, sizeof(scs_float));
|
|
343
|
+
pr->eig_T = (scs_float *)scs_calloc((size_t)win * win, sizeof(scs_float));
|
|
344
|
+
pr->eig_E = (scs_float *)scs_calloc((size_t)win * win, sizeof(scs_float));
|
|
345
|
+
pr->eig_w = (scs_float *)scs_calloc(win, sizeof(scs_float));
|
|
346
|
+
pr->eig_S = (scs_float *)scs_calloc((size_t)win * nev2, sizeof(scs_float));
|
|
347
|
+
pr->eig_TS = (scs_float *)scs_calloc((size_t)win * nev2, sizeof(scs_float));
|
|
348
|
+
pr->eig_H = (scs_float *)scs_calloc((size_t)nev2 * nev2, sizeof(scs_float));
|
|
349
|
+
pr->eig_G = (scs_float *)scs_calloc((size_t)win * nev2, sizeof(scs_float));
|
|
350
|
+
pr->eig_VS = (scs_float *)scs_calloc((size_t)n * nev2, sizeof(scs_float));
|
|
351
|
+
pr->eig_work =
|
|
352
|
+
(scs_float *)scs_calloc(MAX(pr->eig_lwork, 1), sizeof(scs_float));
|
|
353
|
+
if (!pr->eig_V || !pr->eig_T || !pr->eig_E || !pr->eig_w || !pr->eig_S ||
|
|
354
|
+
!pr->eig_TS || !pr->eig_H || !pr->eig_G || !pr->eig_VS ||
|
|
355
|
+
!pr->eig_work) {
|
|
356
|
+
eigcg_free_window(pr);
|
|
357
|
+
return 1;
|
|
358
|
+
}
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* begin a harvest: column 0 is the first preconditioned residual */
|
|
363
|
+
static void eigcg_start(ScsLinSysWork *pr, const scs_float *z, scs_float ztr) {
|
|
364
|
+
scs_int t, n = pr->n;
|
|
365
|
+
scs_float isr;
|
|
366
|
+
if (!pr->eig_V && eigcg_alloc_window(pr)) {
|
|
367
|
+
pr->eig_dead = 1;
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
pr->eig_j = 0;
|
|
371
|
+
pr->eig_jd = 0;
|
|
372
|
+
pr->eig_step = 0;
|
|
373
|
+
pr->eig_have_prev = 0;
|
|
374
|
+
pr->eig_dead = 0;
|
|
375
|
+
memset(pr->eig_T, 0, (size_t)pr->eig_win * pr->eig_win * sizeof(scs_float));
|
|
376
|
+
if (!(ztr > 0.)) {
|
|
377
|
+
pr->eig_dead = 1;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
isr = 1.0 / SQRTF(ztr);
|
|
381
|
+
for (t = 0; t < n; ++t) {
|
|
382
|
+
pr->eig_V[t] = isr * z[t];
|
|
383
|
+
}
|
|
384
|
+
pr->eig_j = 1;
|
|
385
|
+
pr->eig_step = 1;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* One observation per CG iteration, at the point where alpha_i, beta_{i+1},
|
|
389
|
+
* z_{i+1} and ztr_{i+1} are all fresh. Completes the pending diagonal entry
|
|
390
|
+
* (1/alpha_i + beta_i/alpha_{i-1}), links the incoming Lanczos vector with
|
|
391
|
+
* sqrt(beta_{i+1})/alpha_i, and appends it with the (-1)^step sign that the
|
|
392
|
+
* positive-offdiagonal tridiagonal convention requires. */
|
|
393
|
+
static void eigcg_accum(ScsLinSysWork *pr, scs_float alpha, scs_float beta_next,
|
|
394
|
+
const scs_float *z, scs_float ztr) {
|
|
395
|
+
scs_int n = pr->n, win = pr->eig_win, cur, t;
|
|
396
|
+
scs_float d, tlink, sgn;
|
|
397
|
+
scs_float *T = pr->eig_T;
|
|
398
|
+
if (pr->eig_dead) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (!(alpha > 0.) || !(beta_next >= 0.) || !(ztr > 0.)) {
|
|
402
|
+
pr->eig_dead = 1;
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
cur = pr->eig_j - 1;
|
|
406
|
+
d = 1. / alpha;
|
|
407
|
+
if (pr->eig_have_prev) {
|
|
408
|
+
d += pr->eig_pb / pr->eig_pa;
|
|
409
|
+
}
|
|
410
|
+
T[cur * win + cur] = d;
|
|
411
|
+
pr->eig_jd = pr->eig_j;
|
|
412
|
+
tlink = SQRTF(beta_next) / alpha;
|
|
413
|
+
if (pr->eig_j == win) {
|
|
414
|
+
eigcg_restart(pr, tlink);
|
|
415
|
+
if (pr->eig_dead) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
T[pr->eig_j * win + cur] = tlink;
|
|
420
|
+
T[cur * win + pr->eig_j] = tlink;
|
|
421
|
+
}
|
|
422
|
+
sgn = (pr->eig_step % 2) ? -1.0 / SQRTF(ztr) : 1.0 / SQRTF(ztr);
|
|
423
|
+
{
|
|
424
|
+
scs_float *col = &(pr->eig_V[pr->eig_j * n]);
|
|
425
|
+
for (t = 0; t < n; ++t) {
|
|
426
|
+
col[t] = sgn * z[t];
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
pr->eig_j++;
|
|
430
|
+
pr->eig_step++;
|
|
431
|
+
pr->eig_pa = alpha;
|
|
432
|
+
pr->eig_pb = beta_next;
|
|
433
|
+
pr->eig_have_prev = 1;
|
|
192
434
|
}
|
|
193
435
|
|
|
436
|
+
/* After the deep solve: extract the lowest-kv Ritz vectors, form their true
|
|
437
|
+
* images through the operator (kv extra matvecs, counted by the caller),
|
|
438
|
+
* factor the Gram matrix. Returns the number of matvecs spent. */
|
|
439
|
+
static scs_int eigcg_extract(ScsLinSysWork *pr) {
|
|
440
|
+
scs_int n = pr->n, jd = pr->eig_jd, kv, a, b, probes;
|
|
441
|
+
scs_float *E = pr->eig_E, *w = pr->eig_w;
|
|
442
|
+
scs_float one = 1., zero = 0.;
|
|
443
|
+
blas_int bm, bn2, bk, ld1, ld2;
|
|
444
|
+
pr->dfl_count = 0;
|
|
445
|
+
kv = MIN(pr->dfl_max, jd);
|
|
446
|
+
if (kv <= 0 || !pr->eig_V) {
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
if (eigcg_eig_leading(pr, jd, E, w)) {
|
|
450
|
+
return 0;
|
|
451
|
+
}
|
|
452
|
+
probes = kv;
|
|
453
|
+
bm = (blas_int)n;
|
|
454
|
+
bn2 = (blas_int)kv;
|
|
455
|
+
bk = (blas_int)jd;
|
|
456
|
+
ld1 = (blas_int)n;
|
|
457
|
+
ld2 = (blas_int)jd;
|
|
458
|
+
BLAS(gemm)("N", "N", &bm, &bn2, &bk, &one, pr->eig_V, &ld1, E, &ld2, &zero,
|
|
459
|
+
pr->dfl_p, &ld1);
|
|
460
|
+
{
|
|
461
|
+
/* Keep only columns whose eigenresidual A w = theta Mp w actually
|
|
462
|
+
* holds: deflation coefficients scale like 1/lambda, so a grossly
|
|
463
|
+
* wrong vector injects error amplified by the smallest eigenvalue.
|
|
464
|
+
* Exact vectors pass at machine precision; the tolerance is loose. */
|
|
465
|
+
scs_int kept = 0, t;
|
|
466
|
+
for (a = 0; a < kv; ++a) {
|
|
467
|
+
scs_float *wv = &(pr->dfl_p[a * n]);
|
|
468
|
+
scs_float *awv = &(pr->dfl_Ap[kept * n]);
|
|
469
|
+
scs_float rr = 0., mm = 0., th = w[a];
|
|
470
|
+
if (kept != a) {
|
|
471
|
+
memcpy(&(pr->dfl_p[kept * n]), wv, n * sizeof(scs_float));
|
|
472
|
+
wv = &(pr->dfl_p[kept * n]);
|
|
473
|
+
}
|
|
474
|
+
mat_vec(pr->A, pr->P, pr, wv, awv);
|
|
475
|
+
for (t = 0; t < n; ++t) {
|
|
476
|
+
scs_float mw = th * wv[t] / pr->M[t];
|
|
477
|
+
rr += (awv[t] - mw) * (awv[t] - mw);
|
|
478
|
+
mm += mw * mw;
|
|
479
|
+
}
|
|
480
|
+
if (mm > 0. && rr <= 0.25 * 0.25 * mm) {
|
|
481
|
+
w[kept] = th;
|
|
482
|
+
kept++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
kv = kept;
|
|
486
|
+
if (kv <= 0) {
|
|
487
|
+
return probes; /* matvecs were still spent probing */
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
for (a = 0; a < kv; ++a) {
|
|
491
|
+
for (b = 0; b < kv; ++b) {
|
|
492
|
+
pr->dfl_G[b * kv + a] =
|
|
493
|
+
SCS(dot)(&(pr->dfl_p[a * n]), &(pr->dfl_Ap[b * n]), n);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (eigcg_chol(pr->dfl_G, kv)) {
|
|
497
|
+
return probes;
|
|
498
|
+
}
|
|
499
|
+
pr->dfl_count = kv;
|
|
500
|
+
return probes;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
static scs_int eigcg_finish(ScsLinSysWork *pr) {
|
|
504
|
+
scs_int probes = eigcg_extract(pr);
|
|
505
|
+
eigcg_free_window(pr);
|
|
506
|
+
pr->eig_jd = 0;
|
|
507
|
+
return probes;
|
|
508
|
+
}
|
|
509
|
+
#endif /* USE_LAPACK */
|
|
510
|
+
|
|
511
|
+
/* ======================== Conjugate Gradient Solver ======================== */
|
|
512
|
+
|
|
194
513
|
/* solves (R_x * I + P + A' R_y^{-1} A)x = b, s warm start, solution in b */
|
|
195
514
|
static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *b,
|
|
196
515
|
scs_int max_its, scs_float tol) {
|
|
@@ -221,6 +540,50 @@ static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *b,
|
|
|
221
540
|
memcpy(b, s, n * sizeof(scs_float));
|
|
222
541
|
}
|
|
223
542
|
|
|
543
|
+
/* Deflation: Galerkin-project the residual against the harvested
|
|
544
|
+
* eigenvector approximations, solve (W'AW) c = W'r via the stored
|
|
545
|
+
* Cholesky factor, and remove the correction. Because W spans a
|
|
546
|
+
* near-invariant subspace of the preconditioned operator, CG does not
|
|
547
|
+
* reintroduce the deflated components -- a single projection here
|
|
548
|
+
* suffices (init-CG). Costs dot products and axpys only. */
|
|
549
|
+
#ifdef USE_LAPACK
|
|
550
|
+
if (pr->dfl_count > 0) {
|
|
551
|
+
scs_int j, k, kd = pr->dfl_count;
|
|
552
|
+
scs_float nrm0 = CG_NORM(r, n), nrm1;
|
|
553
|
+
for (j = 0; j < kd; ++j) {
|
|
554
|
+
pr->dfl_c[j] = SCS(dot)(&(pr->dfl_p[j * n]), r, n);
|
|
555
|
+
}
|
|
556
|
+
eigcg_chol_solve(pr->dfl_G, pr->dfl_c, kd);
|
|
557
|
+
for (j = 0; j < kd; ++j) {
|
|
558
|
+
const scs_float cj = pr->dfl_c[j];
|
|
559
|
+
const scs_float *wj = &(pr->dfl_p[j * n]);
|
|
560
|
+
const scs_float *awj = &(pr->dfl_Ap[j * n]);
|
|
561
|
+
if (cj == 0.) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
for (k = 0; k < n; ++k) {
|
|
565
|
+
b[k] += cj * wj[k];
|
|
566
|
+
r[k] -= cj * awj[k];
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/* For exact eigenvectors this projector is orthogonal and cannot
|
|
570
|
+
* grow the residual; growth means the Ritz error is being amplified
|
|
571
|
+
* by 1/lambda on this right-hand side -- take it back. */
|
|
572
|
+
nrm1 = CG_NORM(r, n);
|
|
573
|
+
if (!(nrm1 <= 1.5 * nrm0)) {
|
|
574
|
+
for (j = 0; j < kd; ++j) {
|
|
575
|
+
const scs_float cj = pr->dfl_c[j];
|
|
576
|
+
const scs_float *wj = &(pr->dfl_p[j * n]);
|
|
577
|
+
const scs_float *awj = &(pr->dfl_Ap[j * n]);
|
|
578
|
+
for (k = 0; k < n; ++k) {
|
|
579
|
+
b[k] -= cj * wj[k];
|
|
580
|
+
r[k] += cj * awj[k];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
#endif
|
|
586
|
+
|
|
224
587
|
/* check to see if we need to run CG at all */
|
|
225
588
|
if (CG_NORM(r, n) < MAX(tol, 1e-12)) {
|
|
226
589
|
return 0;
|
|
@@ -232,8 +595,16 @@ static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *b,
|
|
|
232
595
|
ztr = SCS(dot)(z, r, n);
|
|
233
596
|
/* p = z */
|
|
234
597
|
memcpy(p, z, n * sizeof(scs_float));
|
|
598
|
+
#ifdef USE_LAPACK
|
|
599
|
+
if (pr->dfl_harvest) {
|
|
600
|
+
eigcg_start(pr, z, ztr);
|
|
601
|
+
}
|
|
602
|
+
#endif
|
|
235
603
|
|
|
236
604
|
for (i = 0; i < max_its; ++i) {
|
|
605
|
+
scs_float norm_r, beta;
|
|
606
|
+
scs_int k;
|
|
607
|
+
scs_float *M = pr->M;
|
|
237
608
|
/* Gp = Mat * p */
|
|
238
609
|
mat_vec(A, P, pr, p, Gp);
|
|
239
610
|
/* alpha = z'r / p'G p */
|
|
@@ -243,26 +614,137 @@ static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *b,
|
|
|
243
614
|
/* r -= alpha * G p */
|
|
244
615
|
SCS(add_scaled_array)(r, Gp, n, -alpha);
|
|
245
616
|
|
|
617
|
+
/* Fuse convergence norm, preconditioner apply, and z'r dot product into
|
|
618
|
+
* one pass over r, saving two separate vector scans per CG iteration. */
|
|
619
|
+
ztr_prev = ztr;
|
|
620
|
+
norm_r = 0.0;
|
|
621
|
+
ztr = 0.0;
|
|
622
|
+
for (k = 0; k < n; ++k) {
|
|
623
|
+
scs_float rk = r[k], zk = rk * M[k], ark = ABS(rk);
|
|
624
|
+
z[k] = zk;
|
|
625
|
+
ztr += zk * rk;
|
|
626
|
+
if (ark > norm_r) norm_r = ark;
|
|
627
|
+
}
|
|
246
628
|
#if VERBOSITY > 3
|
|
247
|
-
scs_printf("tol: %.4e, resid: %.4e, iters: %li\n", tol,
|
|
629
|
+
scs_printf("tol: %.4e, resid: %.4e, iters: %li\n", tol, norm_r,
|
|
248
630
|
(long)i + 1);
|
|
249
631
|
#endif
|
|
250
|
-
if (
|
|
632
|
+
if (norm_r < tol) {
|
|
251
633
|
return i + 1;
|
|
252
634
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
/* p = beta * p
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
635
|
+
if (ztr_prev == 0.) {
|
|
636
|
+
/* preconditioned residual is zero; further CG steps would divide by
|
|
637
|
+
* zero, declare convergence (r must be negligibly small) */
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
/* p = z + beta * p — fuse scale and axpy into one loop */
|
|
641
|
+
beta = ztr / ztr_prev;
|
|
642
|
+
#ifdef USE_LAPACK
|
|
643
|
+
if (pr->dfl_harvest) {
|
|
644
|
+
eigcg_accum(pr, alpha, beta, z, ztr);
|
|
645
|
+
}
|
|
646
|
+
#endif
|
|
647
|
+
for (k = 0; k < n; ++k) {
|
|
648
|
+
p[k] = z[k] + beta * p[k];
|
|
649
|
+
}
|
|
262
650
|
}
|
|
263
651
|
return i;
|
|
264
652
|
}
|
|
265
653
|
|
|
654
|
+
/* ======================== Public API ======================== */
|
|
655
|
+
|
|
656
|
+
const char *scs_get_lin_sys_method(void) {
|
|
657
|
+
return "sparse-indirect-scs";
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
|
|
661
|
+
const scs_float *diag_r) {
|
|
662
|
+
ScsLinSysWork *p = (ScsLinSysWork *)scs_calloc(1, sizeof(ScsLinSysWork));
|
|
663
|
+
if (!p) {
|
|
664
|
+
return SCS_NULL;
|
|
665
|
+
}
|
|
666
|
+
p->A = A;
|
|
667
|
+
p->P = P;
|
|
668
|
+
p->m = A->m;
|
|
669
|
+
p->n = A->n;
|
|
670
|
+
|
|
671
|
+
p->p = (scs_float *)scs_calloc((A->n), sizeof(scs_float));
|
|
672
|
+
p->r = (scs_float *)scs_calloc((A->n), sizeof(scs_float));
|
|
673
|
+
p->Gp = (scs_float *)scs_calloc((A->n), sizeof(scs_float));
|
|
674
|
+
p->tmp = (scs_float *)scs_calloc((A->m), sizeof(scs_float));
|
|
675
|
+
|
|
676
|
+
/* memory for A transpose */
|
|
677
|
+
p->At = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix));
|
|
678
|
+
if (!p->p || !p->r || !p->Gp || !p->tmp || !p->At) {
|
|
679
|
+
scs_free_lin_sys_work(p);
|
|
680
|
+
return SCS_NULL;
|
|
681
|
+
}
|
|
682
|
+
p->At->m = A->n;
|
|
683
|
+
p->At->n = A->m;
|
|
684
|
+
p->At->i = (scs_int *)scs_calloc((A->p[A->n]), sizeof(scs_int));
|
|
685
|
+
p->At->p = (scs_int *)scs_calloc((A->m + 1), sizeof(scs_int));
|
|
686
|
+
p->At->x = (scs_float *)scs_calloc((A->p[A->n]), sizeof(scs_float));
|
|
687
|
+
if (!p->At->i || !p->At->p || !p->At->x) {
|
|
688
|
+
scs_free_lin_sys_work(p);
|
|
689
|
+
return SCS_NULL;
|
|
690
|
+
}
|
|
691
|
+
transpose(A, p);
|
|
692
|
+
|
|
693
|
+
/* preconditioner memory */
|
|
694
|
+
p->diag_r = diag_r;
|
|
695
|
+
p->z = (scs_float *)scs_calloc(A->n, sizeof(scs_float));
|
|
696
|
+
p->M = (scs_float *)scs_calloc(A->n, sizeof(scs_float));
|
|
697
|
+
if (!p->z || !p->M) {
|
|
698
|
+
scs_free_lin_sys_work(p);
|
|
699
|
+
return SCS_NULL;
|
|
700
|
+
}
|
|
701
|
+
set_preconditioner(p);
|
|
702
|
+
|
|
703
|
+
p->tot_cg_its = 0;
|
|
704
|
+
{
|
|
705
|
+
const char *dm = getenv("SCS_DEFLATE");
|
|
706
|
+
char *wm = getenv("SCS_EIGCG_WIN");
|
|
707
|
+
p->dfl_max = dm ? (scs_int)atoi(dm) : DEFLATE_VECTORS;
|
|
708
|
+
p->dfl_max = MIN(p->dfl_max, p->n);
|
|
709
|
+
p->dfl_count = 0;
|
|
710
|
+
p->dfl_harvest = 0;
|
|
711
|
+
#ifndef USE_LAPACK
|
|
712
|
+
p->dfl_max = 0; /* eigCG needs syev */
|
|
713
|
+
#endif
|
|
714
|
+
#ifdef SFLOAT
|
|
715
|
+
/* the Lanczos harvest and the small Cholesky sit below the single-
|
|
716
|
+
* precision noise floor; deflation is double-precision only */
|
|
717
|
+
p->dfl_max = 0;
|
|
718
|
+
#endif
|
|
719
|
+
if (p->dfl_max > 0) {
|
|
720
|
+
scs_int win = wm ? (scs_int)atoi(wm) : EIGCG_WINDOW;
|
|
721
|
+
win = MAX(win, 3 * p->dfl_max); /* >= nev free slots per cycle */
|
|
722
|
+
win = MAX(win, 2 * p->dfl_max + 2);
|
|
723
|
+
p->eig_win = win;
|
|
724
|
+
/* only W, AW and the small factor persist; the window itself is
|
|
725
|
+
* allocated per harvest and freed with the deep solve */
|
|
726
|
+
p->dfl_p = (scs_float *)scs_calloc(p->n * p->dfl_max, sizeof(scs_float));
|
|
727
|
+
p->dfl_Ap = (scs_float *)scs_calloc(p->n * p->dfl_max, sizeof(scs_float));
|
|
728
|
+
p->dfl_G =
|
|
729
|
+
(scs_float *)scs_calloc(p->dfl_max * p->dfl_max, sizeof(scs_float));
|
|
730
|
+
p->dfl_c = (scs_float *)scs_calloc(p->dfl_max, sizeof(scs_float));
|
|
731
|
+
#ifdef USE_LAPACK
|
|
732
|
+
{
|
|
733
|
+
scs_float wkopt = 0., dummy = 0.;
|
|
734
|
+
blas_int bn = (blas_int)win, lda = (blas_int)win, lw = -1, info = 0;
|
|
735
|
+
BLAS(syev)("V", "U", &bn, &dummy, &lda, &wkopt, &wkopt, &lw, &info);
|
|
736
|
+
p->eig_lwork = info ? 0 : (scs_int)wkopt;
|
|
737
|
+
}
|
|
738
|
+
#endif
|
|
739
|
+
if (!p->dfl_p || !p->dfl_Ap || !p->dfl_G || !p->dfl_c ||
|
|
740
|
+
p->eig_lwork <= 0) {
|
|
741
|
+
p->dfl_max = 0;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return p;
|
|
746
|
+
}
|
|
747
|
+
|
|
266
748
|
/* solves Mx = b, for x but stores result in b */
|
|
267
749
|
/* s contains warm-start (if available) */
|
|
268
750
|
/*
|
|
@@ -278,6 +760,8 @@ static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *b,
|
|
|
278
760
|
scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *s,
|
|
279
761
|
scs_float tol) {
|
|
280
762
|
scs_int cg_its, max_iters;
|
|
763
|
+
/* the cold, high-accuracy solve is the one worth harvesting from */
|
|
764
|
+
p->dfl_harvest = (p->dfl_max > 0 && !s && p->dfl_count == 0);
|
|
281
765
|
|
|
282
766
|
if (tol <= 0.) {
|
|
283
767
|
scs_printf("Warning: tol = %4f <= 0, likely compiled without setting "
|
|
@@ -285,13 +769,12 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *s,
|
|
|
285
769
|
tol);
|
|
286
770
|
}
|
|
287
771
|
|
|
772
|
+
/* use p->tmp here, and in mat_vec, can do both since they don't overlap */
|
|
773
|
+
/* b = [rx; ry] */
|
|
288
774
|
if (CG_NORM(b, p->n + p->m) <= 1e-12) {
|
|
289
775
|
memset(b, 0, (p->n + p->m) * sizeof(scs_float));
|
|
290
776
|
return 0;
|
|
291
777
|
}
|
|
292
|
-
|
|
293
|
-
/* use p->tmp here, and in mat_vec, can do both since they don't overlap */
|
|
294
|
-
/* b = [rx; ry] */
|
|
295
778
|
/* tmp = ry */
|
|
296
779
|
memcpy(p->tmp, &(b[p->n]), p->m * sizeof(scs_float));
|
|
297
780
|
/* tmp = R_y^{-1} * ry */
|
|
@@ -303,6 +786,14 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *s,
|
|
|
303
786
|
/* solves (R_x + P + A' R_y^{-1} A)x = b, s warm start, solution stored in
|
|
304
787
|
* b */
|
|
305
788
|
cg_its = pcg(p, s, b, max_iters, tol); /* b[:n] = x */
|
|
789
|
+
#ifdef USE_LAPACK
|
|
790
|
+
if (p->dfl_harvest) {
|
|
791
|
+
/* extract the Ritz vectors; the kv image matvecs it spends are added
|
|
792
|
+
* to the iteration count so the matvec accounting stays honest */
|
|
793
|
+
cg_its += eigcg_finish(p);
|
|
794
|
+
p->dfl_harvest = 0;
|
|
795
|
+
}
|
|
796
|
+
#endif
|
|
306
797
|
|
|
307
798
|
/* b[n:] = -ry */
|
|
308
799
|
SCS(scale_array)(&(b[p->n]), -1., p->m);
|
|
@@ -317,3 +808,46 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *s,
|
|
|
317
808
|
#endif
|
|
318
809
|
return 0;
|
|
319
810
|
}
|
|
811
|
+
|
|
812
|
+
/* no need to update anything in this case */
|
|
813
|
+
scs_int scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
|
|
814
|
+
p->diag_r = diag_r; /* this isn't needed but do it to be safe */
|
|
815
|
+
set_preconditioner(p);
|
|
816
|
+
/* the operator and preconditioner changed: the harvested eigenvectors
|
|
817
|
+
* live in the old preconditioned geometry, discard them; the next deep
|
|
818
|
+
* solve (triggered by this same metric change) re-harvests */
|
|
819
|
+
p->dfl_count = 0;
|
|
820
|
+
return 0;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
void scs_free_lin_sys_work(ScsLinSysWork *p) {
|
|
824
|
+
if (p) {
|
|
825
|
+
scs_free(p->dfl_p);
|
|
826
|
+
scs_free(p->dfl_Ap);
|
|
827
|
+
scs_free(p->dfl_G);
|
|
828
|
+
scs_free(p->dfl_c);
|
|
829
|
+
scs_free(p->eig_V);
|
|
830
|
+
scs_free(p->eig_T);
|
|
831
|
+
scs_free(p->eig_E);
|
|
832
|
+
scs_free(p->eig_w);
|
|
833
|
+
scs_free(p->eig_S);
|
|
834
|
+
scs_free(p->eig_TS);
|
|
835
|
+
scs_free(p->eig_H);
|
|
836
|
+
scs_free(p->eig_G);
|
|
837
|
+
scs_free(p->eig_VS);
|
|
838
|
+
scs_free(p->eig_work);
|
|
839
|
+
scs_free(p->p);
|
|
840
|
+
scs_free(p->r);
|
|
841
|
+
scs_free(p->Gp);
|
|
842
|
+
scs_free(p->tmp);
|
|
843
|
+
if (p->At) {
|
|
844
|
+
scs_free(p->At->i);
|
|
845
|
+
scs_free(p->At->x);
|
|
846
|
+
scs_free(p->At->p);
|
|
847
|
+
scs_free(p->At);
|
|
848
|
+
}
|
|
849
|
+
scs_free(p->z);
|
|
850
|
+
scs_free(p->M);
|
|
851
|
+
scs_free(p);
|
|
852
|
+
}
|
|
853
|
+
}
|