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,8 +1,8 @@
|
|
|
1
|
-
#include "glbopts.h"
|
|
1
|
+
#include "glbopts.h" /* for scs_printf */
|
|
2
2
|
#include "linalg.h"
|
|
3
3
|
#include "scs_blas.h"
|
|
4
4
|
#include "util_spectral_cones.h"
|
|
5
|
-
#include <string.h>
|
|
5
|
+
#include <string.h> /* for memcpy */
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
8
|
* Spectral matrix cone projections, from "Projection onto Spectral Matrix
|
|
@@ -33,18 +33,23 @@
|
|
|
33
33
|
#define TERMINATE_DUE_TO_ZEROS -5
|
|
34
34
|
#define MAX_GRAD_STEPS_REACHED -6
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/*
|
|
37
|
+
* the CALLER of this function must make sure that the arguments belong to the
|
|
38
|
+
* domain
|
|
39
|
+
*/
|
|
38
40
|
static scs_float obj_val(const scs_float *u, scs_float t0, scs_float v0,
|
|
39
41
|
const scs_float *x0, scs_int n) {
|
|
40
42
|
scs_float v = u[0];
|
|
41
43
|
const scs_float *x = u + 1;
|
|
44
|
+
scs_float sx;
|
|
45
|
+
scs_float obj;
|
|
46
|
+
scs_int i;
|
|
42
47
|
|
|
43
48
|
assert(v > 0 && min_vec(x, n) > 0);
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
for (
|
|
50
|
+
sx = -(v * sum_log(x, n) - n * v * log(v));
|
|
51
|
+
obj = 0.5 * (sx - t0) * (sx - t0) + 0.5 * (v - v0) * (v - v0);
|
|
52
|
+
for (i = 0; i < n; ++i) {
|
|
48
53
|
obj += 0.5 * (x[i] - x0[i]) * (x[i] - x0[i]);
|
|
49
54
|
}
|
|
50
55
|
return obj;
|
|
@@ -58,17 +63,27 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
58
63
|
scs_float *x = u + 2;
|
|
59
64
|
scs_int n_plus_one = n + 1;
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
// check cone membership and special cases with analytic projections
|
|
63
|
-
// -------------------------------------------------------------------------
|
|
66
|
+
/* check cone membership and special cases with analytic projections */
|
|
64
67
|
bool ix_x0_non_neg = true;
|
|
65
68
|
bool is_x0_pos = true;
|
|
66
69
|
scs_int i = 0;
|
|
70
|
+
|
|
71
|
+
scs_float obj_old;
|
|
72
|
+
scs_float *grad;
|
|
73
|
+
scs_float *d;
|
|
74
|
+
scs_float *w;
|
|
75
|
+
scs_float *du;
|
|
76
|
+
scs_float *temp1;
|
|
77
|
+
scs_float *u_new;
|
|
78
|
+
size_t num_grad_steps;
|
|
79
|
+
scs_int iter;
|
|
80
|
+
scs_float newton_decrement;
|
|
81
|
+
|
|
67
82
|
while (ix_x0_non_neg || is_x0_pos) {
|
|
68
|
-
if (
|
|
83
|
+
if (x0[i] < 0) {
|
|
69
84
|
ix_x0_non_neg = false;
|
|
70
85
|
is_x0_pos = false;
|
|
71
|
-
} else if (
|
|
86
|
+
} else if (x0[i] == 0) {
|
|
72
87
|
is_x0_pos = false;
|
|
73
88
|
}
|
|
74
89
|
|
|
@@ -78,7 +93,7 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
78
93
|
++i;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
/* if (t0, v0, x0) belongs to cone */
|
|
82
97
|
if ((v0 > 0 && is_x0_pos && -v0 * (sum_log(x0, n) - n * log(v0)) <= t0) ||
|
|
83
98
|
(v0 == 0 && ix_x0_non_neg && t0 >= 0)) {
|
|
84
99
|
*t = t0;
|
|
@@ -89,7 +104,7 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
89
104
|
return 0;
|
|
90
105
|
}
|
|
91
106
|
|
|
92
|
-
|
|
107
|
+
/* if (t0, v0, x0) belongs to negative dual cone */
|
|
93
108
|
if (t0 < 0 && is_negative(x0, n)) {
|
|
94
109
|
scs_float sum = -n;
|
|
95
110
|
for (i = 0; i < n; ++i) {
|
|
@@ -100,14 +115,16 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
100
115
|
if (v0 <= sum) {
|
|
101
116
|
memset(u, 0, (n + 2) * sizeof(*x0));
|
|
102
117
|
stats->iter = IN_NEGATIVE_DUAL_CONE;
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
/*
|
|
119
|
+
* if 0 is the solution we should not use it to warmstart the next
|
|
120
|
+
* iteration
|
|
121
|
+
*/
|
|
105
122
|
*warm_start = false;
|
|
106
123
|
return 0;
|
|
107
124
|
}
|
|
108
125
|
}
|
|
109
126
|
|
|
110
|
-
|
|
127
|
+
/* special case with analytic solution */
|
|
111
128
|
if (v0 <= 0 && t0 >= 0) {
|
|
112
129
|
*t = t0;
|
|
113
130
|
*v = 0;
|
|
@@ -117,84 +134,94 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
117
134
|
return 0;
|
|
118
135
|
}
|
|
119
136
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
/*
|
|
138
|
+
* if 'warm_start' is false we initialize in the point
|
|
139
|
+
* (v, x) = (max(v0, MIN_INIT_LOG_CONE), max(x0, MIN_INIT_LOG_CONE)),
|
|
140
|
+
* otherwise it is assumed that 'proj' has already been
|
|
141
|
+
* initialized / warmstarted.
|
|
142
|
+
*/
|
|
126
143
|
if (!(*warm_start)) {
|
|
127
144
|
*v = (v0 > MIN_INIT_LOG_CONE) ? v0 : MIN_INIT_LOG_CONE;
|
|
128
145
|
|
|
129
|
-
for (
|
|
146
|
+
for (i = 0; i < n; ++i) {
|
|
130
147
|
x[i] = (x0[i] > MIN_INIT_LOG_CONE) ? x0[i] : MIN_INIT_LOG_CONE;
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
150
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
scs_int iter = 0;
|
|
148
|
-
scs_float newton_decrement = 0;
|
|
151
|
+
obj_old = obj_val(u + 1, t0, v0, x0, n);
|
|
152
|
+
|
|
153
|
+
/* parse workspace */
|
|
154
|
+
grad = workspace;
|
|
155
|
+
d = grad + n_plus_one;
|
|
156
|
+
w = d + n_plus_one;
|
|
157
|
+
du = w + n_plus_one;
|
|
158
|
+
temp1 = du + n_plus_one;
|
|
159
|
+
u_new = du + n_plus_one + 1;
|
|
160
|
+
|
|
161
|
+
num_grad_steps = 0;
|
|
162
|
+
iter = 0;
|
|
163
|
+
newton_decrement = 0;
|
|
149
164
|
for (iter = 1; iter <= MAX_ITER_NEWTON; ++iter) {
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
scs_float temp0;
|
|
166
|
+
scs_float a;
|
|
167
|
+
scs_float c;
|
|
168
|
+
scs_float v_inv;
|
|
169
|
+
scs_float av;
|
|
170
|
+
scs_float nominator;
|
|
171
|
+
scs_float denominator;
|
|
172
|
+
scs_float ratio;
|
|
173
|
+
scs_float dirDer;
|
|
174
|
+
scs_float step_size;
|
|
175
|
+
scs_float new_obj;
|
|
176
|
+
|
|
177
|
+
/*
|
|
178
|
+
* A small value on v indicates that Newton's method converges to the
|
|
179
|
+
* origin. In this case we should abort and apply an IPM.
|
|
180
|
+
*/
|
|
152
181
|
if (*v < MIN_V) {
|
|
153
182
|
stats->iter = TERMINATE_DUE_TO_ZEROS;
|
|
154
183
|
return -1;
|
|
155
184
|
}
|
|
156
185
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for (
|
|
186
|
+
/*
|
|
187
|
+
* To avoid pathological cases where some components of
|
|
188
|
+
* x approach 0 we use a minimum threshold.
|
|
189
|
+
*/
|
|
190
|
+
for (i = 0; i < n; i++) {
|
|
162
191
|
x[i] = MAX(x[i], MIN_X);
|
|
163
192
|
}
|
|
164
193
|
|
|
165
|
-
|
|
166
|
-
// compute gradient and Hessian
|
|
167
|
-
// ----------------------------------------------------------------
|
|
194
|
+
/* compute gradient and Hessian */
|
|
168
195
|
assert(*v > MIN_FLOAT && min_vec(x, n) > MIN_FLOAT);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
196
|
+
temp0 = -sum_log(x, n) + n * log(*v);
|
|
197
|
+
a = (*v) * temp0 - t0;
|
|
198
|
+
c = temp0 + n;
|
|
172
199
|
|
|
173
200
|
grad[0] = a * c + (*v) - v0;
|
|
174
|
-
|
|
201
|
+
v_inv = 1 / (*v);
|
|
175
202
|
d[0] = 1 + a * (-a * (v_inv * v_inv) + n * v_inv - 2 * c * v_inv);
|
|
176
203
|
w[0] = -(a + (*v) * c) * v_inv;
|
|
177
|
-
|
|
204
|
+
av = a * (*v);
|
|
178
205
|
|
|
179
206
|
for (i = 1; i < n + 1; ++i) {
|
|
180
|
-
assert(x[i - 1] > 0);
|
|
181
207
|
scs_float x_inv = 1 / x[i - 1];
|
|
208
|
+
assert(x[i - 1] > 0);
|
|
182
209
|
grad[i] = -av * x_inv + x[i - 1] - x0[i - 1];
|
|
183
210
|
d[i] = 1 + av * (x_inv * x_inv);
|
|
184
211
|
w[i] = (*v) * x_inv;
|
|
185
212
|
}
|
|
186
213
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
/*
|
|
215
|
+
* Solve for Newton step. I have seen a scenario when the variable
|
|
216
|
+
* 'denominator' becomes 0. This occurred when Newton's method
|
|
217
|
+
* converged to the origin and the IPM was necessary
|
|
218
|
+
* to converge to the correct point. We therefore abort when the
|
|
219
|
+
* denominator becomes very close to 0, since this may indicate
|
|
220
|
+
* that Newton's method is converging to the wrong point and in this
|
|
221
|
+
* case it is necessary to apply an IPM.
|
|
222
|
+
*/
|
|
223
|
+
nominator = 0;
|
|
224
|
+
denominator = 1;
|
|
198
225
|
for (i = 0; i < n + 1; ++i) {
|
|
199
226
|
assert(fabs(d[i]) > MIN_FLOAT);
|
|
200
227
|
du[i] = -grad[i] / d[i];
|
|
@@ -208,15 +235,15 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
208
235
|
return -1;
|
|
209
236
|
}
|
|
210
237
|
|
|
211
|
-
|
|
238
|
+
ratio = -nominator / denominator;
|
|
212
239
|
SCS(add_scaled_array)(du, temp1, n_plus_one, ratio);
|
|
213
240
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
241
|
+
/*
|
|
242
|
+
* if the Newton direction is not descent we use the negative gradient
|
|
243
|
+
* as the search direction, provided that it hasn't been used many times
|
|
244
|
+
* before.
|
|
245
|
+
*/
|
|
246
|
+
dirDer = SCS(dot)(grad, du, n_plus_one);
|
|
220
247
|
if (dirDer > 0) {
|
|
221
248
|
if (num_grad_steps >= MAX_GRAD_STEPS) {
|
|
222
249
|
stats->iter = MAX_GRAD_STEPS_REACHED;
|
|
@@ -232,18 +259,14 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
232
259
|
}
|
|
233
260
|
}
|
|
234
261
|
|
|
235
|
-
|
|
236
|
-
// compute Newton decrement and check termination criteria
|
|
237
|
-
// -------------------------------------------------------
|
|
262
|
+
/* compute Newton decrement and check termination criteria */
|
|
238
263
|
newton_decrement = -dirDer;
|
|
239
264
|
if (newton_decrement <= 2 * TOL_NEWTON) {
|
|
240
265
|
break;
|
|
241
266
|
}
|
|
242
267
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// --------------------------------------------------
|
|
246
|
-
scs_float step_size = 1;
|
|
268
|
+
/* find largest step size with respect to the domain */
|
|
269
|
+
step_size = 1;
|
|
247
270
|
for (i = 0; i < n + 1; i++) {
|
|
248
271
|
if (du[i] < 0) {
|
|
249
272
|
scs_float max_step = -0.99 * u[i + 1] / du[i];
|
|
@@ -251,13 +274,13 @@ scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
|
251
274
|
}
|
|
252
275
|
}
|
|
253
276
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
277
|
+
/*
|
|
278
|
+
* backtracking line search. First two lines do
|
|
279
|
+
* u_new = u + t * du;
|
|
280
|
+
*/
|
|
258
281
|
memcpy(u_new + 1, u + 1, n_plus_one * sizeof(*u));
|
|
259
282
|
SCS(add_scaled_array)(u_new + 1, du, n_plus_one, step_size);
|
|
260
|
-
|
|
283
|
+
new_obj = obj_val(u_new + 1, t0, v0, x0, n);
|
|
261
284
|
while ((1 - LINESEARCH_RELATIVE_TOL) * new_obj >
|
|
262
285
|
obj_old + ALPHA_NEWTON * step_size * dirDer) {
|
|
263
286
|
step_size *= BETA_NEWTON;
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
* Last modified: 25 August 2024.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/* forward declare from log_cone_Newton.c */
|
|
32
32
|
scs_int log_cone_Newton(scs_float t0, scs_float v0, const scs_float *x0,
|
|
33
33
|
scs_float *proj, scs_int n, scs_float *workspace,
|
|
34
34
|
Newton_stats *stats, bool *warm_start);
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/* forward declare form log_cone_IPM.c */
|
|
37
37
|
scs_int log_cone_IPM(scs_float t0, scs_float v0, scs_float *x0, scs_float *u1,
|
|
38
38
|
scs_int n, scs_float *workspace, Newton_stats *stats,
|
|
39
39
|
scs_int variant);
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/* forward declare from this file */
|
|
42
42
|
static void check_opt_cond_log_cone(const scs_float *tvx, scs_float t0,
|
|
43
43
|
scs_float v0, const scs_float *x0,
|
|
44
44
|
scs_int n, scs_float residuals[3],
|
|
@@ -48,17 +48,17 @@ scs_int log_cone_proj_wrapper(scs_float t0, scs_float v0, scs_float *x0,
|
|
|
48
48
|
scs_float *proj, scs_int n, scs_float *workspace,
|
|
49
49
|
Newton_stats *stats, bool *warm_start) {
|
|
50
50
|
scs_int status;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
/*
|
|
52
|
+
* 1. Solve problem with Newton's method. From Lagrange multiplier theory
|
|
53
|
+
* we know that the t-component of the projection must always be greater
|
|
54
|
+
* than t0, so if proj[0] < t0 it means that Newton's method has converged
|
|
55
|
+
* to wrong point. We return if proj[0] >= t0 and the residuals suggest
|
|
56
|
+
* that we converged to the correct point.
|
|
57
|
+
* 2. We warmstart Newton using the solution of the previous iteration
|
|
58
|
+
* except for the first iteration.
|
|
59
|
+
* 3. The current implementation of the warmstart assumes that there is only
|
|
60
|
+
* one spectral matrix cone so 'proj' isn't overwritten.
|
|
61
|
+
*/
|
|
62
62
|
status = log_cone_Newton(t0, v0, x0, proj, n, workspace, stats, warm_start);
|
|
63
63
|
|
|
64
64
|
if (proj[0] >= t0 - 0.1 * fabs(t0)) {
|
|
@@ -74,14 +74,14 @@ scs_int log_cone_proj_wrapper(scs_float t0, scs_float v0, scs_float *x0,
|
|
|
74
74
|
return 0;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
/*
|
|
78
|
+
* Solve problem with primal-dual IPM incorporating Mehrotra's correction
|
|
79
|
+
* etc.
|
|
80
|
+
*/
|
|
81
81
|
status = log_cone_IPM(t0, v0, x0, proj, n, workspace, stats, 0);
|
|
82
82
|
check_opt_cond_log_cone(proj, t0, v0, x0, n, stats->residuals, workspace);
|
|
83
83
|
|
|
84
|
-
*warm_start = true;
|
|
84
|
+
*warm_start = true; /* next iteration Newton should be warmstarted */
|
|
85
85
|
|
|
86
86
|
if (status == 0 && stats->residuals[0] < DUAL_FEAS_TOL &&
|
|
87
87
|
stats->residuals[1] < PRI_FEAS_TOL &&
|
|
@@ -90,15 +90,15 @@ scs_int log_cone_proj_wrapper(scs_float t0, scs_float v0, scs_float *x0,
|
|
|
90
90
|
return 0;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
/*
|
|
94
|
+
* Solve problem with primal-dual IPM without Mehrotra's correction
|
|
95
|
+
* etc. (In all experiments in the paper by Cederberg and Boyd,
|
|
96
|
+
* the IPM above solves the problem correctly so the code below never
|
|
97
|
+
* runs. However, during development I ran into a (possibly pathological
|
|
98
|
+
* case) where the first IPM fails. If this happens we run below another
|
|
99
|
+
* version. This version of the IPM solves the problem that the first version
|
|
100
|
+
* failed on.)
|
|
101
|
+
*/
|
|
102
102
|
status = log_cone_IPM(t0, v0, x0, proj, n, workspace, stats, 1);
|
|
103
103
|
check_opt_cond_log_cone(proj, t0, v0, x0, n, stats->residuals, workspace);
|
|
104
104
|
|
|
@@ -110,71 +110,73 @@ scs_int log_cone_proj_wrapper(scs_float t0, scs_float v0, scs_float *x0,
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
#ifdef SPECTRAL_DEBUG
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
scs_printf(" %.10e",
|
|
113
|
+
{
|
|
114
|
+
scs_int i;
|
|
115
|
+
scs_printf("FAILURE: logarithmic cone projection");
|
|
116
|
+
scs_printf("dual_res / pri_res / comp / iter: %.3e, %.3e, %.3e, %d\n",
|
|
117
|
+
stats->residuals[0], stats->residuals[1], stats->residuals[2],
|
|
118
|
+
stats->iter);
|
|
119
|
+
|
|
120
|
+
scs_printf("Projecting the following point:\n %.10e, %.10e", t0, v0);
|
|
121
|
+
for (i = 0; i < n; i++) {
|
|
122
|
+
scs_printf(" %.10e", x0[i]);
|
|
123
|
+
}
|
|
124
|
+
scs_printf("\n");
|
|
121
125
|
}
|
|
122
|
-
scs_printf("\n");
|
|
123
126
|
#endif
|
|
124
127
|
|
|
125
128
|
return -1;
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
/* tvx = [t, v, x]. */
|
|
129
132
|
static void check_opt_cond_log_cone(const scs_float *tvx, scs_float t0,
|
|
130
133
|
scs_float v0, const scs_float *x0,
|
|
131
134
|
scs_int n, scs_float residuals[3],
|
|
132
135
|
scs_float *dualx) {
|
|
133
136
|
scs_float pri_res, dual_res, complementarity;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
scs_float
|
|
137
|
+
scs_float dualt;
|
|
138
|
+
scs_float dualv;
|
|
139
|
+
scs_int i;
|
|
140
|
+
scs_float dual_norm;
|
|
141
|
+
scs_float pri_norm;
|
|
142
|
+
double scale;
|
|
143
|
+
|
|
144
|
+
/* Compute Lagrange multiplier */
|
|
145
|
+
dualt = tvx[0] - t0;
|
|
139
146
|
if (fabs(dualt) < DUAL_T_THRESHOLD) {
|
|
140
147
|
dualt = DUAL_T_THRESHOLD;
|
|
141
148
|
}
|
|
142
|
-
|
|
143
|
-
for (
|
|
149
|
+
dualv = tvx[1] - v0;
|
|
150
|
+
for (i = 0; i < n; ++i) {
|
|
144
151
|
dualx[i] = tvx[i + 2] - x0[i];
|
|
145
152
|
if (fabs(dualx[i]) < DUAL_X_THRESHOLD) {
|
|
146
153
|
dualx[i] = DUAL_X_THRESHOLD;
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
|
|
150
|
-
|
|
151
|
-
// Compute complementarity measure
|
|
152
|
-
// ---------------------------------------------------------------------
|
|
157
|
+
/* Compute complementarity measure */
|
|
153
158
|
complementarity =
|
|
154
159
|
tvx[0] * dualt + tvx[1] * dualv + SCS(dot)(dualx, tvx + 2, n);
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
// Compute primal feasibility measure
|
|
158
|
-
// ----------------------------------------------------------------------
|
|
161
|
+
/* Compute primal feasibility measure */
|
|
159
162
|
if (tvx[1] > 0 && is_pos(tvx + 2, n)) {
|
|
160
163
|
pri_res = -tvx[1] * (sum_log(tvx + 2, n) - n * log(tvx[1])) - tvx[0];
|
|
161
164
|
} else {
|
|
165
|
+
const scs_float *xi;
|
|
162
166
|
pri_res = tvx[1] * tvx[1];
|
|
163
167
|
|
|
164
168
|
if (tvx[0] < 0) {
|
|
165
169
|
pri_res += tvx[0] * tvx[0];
|
|
166
170
|
}
|
|
167
171
|
|
|
168
|
-
for (
|
|
172
|
+
for (xi = tvx + 2; xi < tvx + 2 + n; ++xi) {
|
|
169
173
|
if (*xi < 0) {
|
|
170
174
|
pri_res += (*xi) * (*xi);
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
|
|
175
|
-
|
|
176
|
-
// Compute dual feasibility measure
|
|
177
|
-
// ----------------------------------------------------------------------
|
|
179
|
+
/* Compute dual feasibility measure */
|
|
178
180
|
if (dualt > 0 && is_pos(dualx, n)) {
|
|
179
181
|
dual_res = dualt * (n * log(dualt) - n - sum_log(dualx, n)) - dualv;
|
|
180
182
|
} else {
|
|
@@ -184,22 +186,19 @@ static void check_opt_cond_log_cone(const scs_float *tvx, scs_float t0,
|
|
|
184
186
|
dual_res += dualv * dualv;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
for (
|
|
189
|
+
for (i = 0; i < n; i++) {
|
|
188
190
|
if (dualx[i] < 0) {
|
|
189
191
|
dual_res += dualx[i] * dualx[i];
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// --------------------------------------------------------
|
|
197
|
-
scs_float dual_norm =
|
|
196
|
+
/* Normalize the residuals and assign the result */
|
|
197
|
+
dual_norm =
|
|
198
198
|
sqrt(dualt * dualt + dualv * dualv + SCS(norm_sq)(dualx, n));
|
|
199
|
-
|
|
199
|
+
pri_norm = SCS(norm_2)(tvx, n + 2);
|
|
200
200
|
residuals[0] = dual_res / MAX(dual_norm, 1.0);
|
|
201
201
|
residuals[1] = pri_res / MAX(pri_norm, 1.0);
|
|
202
|
-
|
|
202
|
+
scale = MAX(pri_norm, 1.0);
|
|
203
203
|
residuals[2] = complementarity / MAX(scale, dual_norm);
|
|
204
204
|
}
|
|
205
|
-
|