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/scs.c
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Main SCS solver implementation.
|
|
3
|
+
*
|
|
4
|
+
* This file contains the ADMM iteration loop, workspace management,
|
|
5
|
+
* residual computation, convergence checking, and the public API
|
|
6
|
+
* functions (scs_init, scs_solve, scs_update, scs_finish, scs).
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
#include "scs.h"
|
|
2
10
|
#include "aa.h"
|
|
11
|
+
#include "cones.h"
|
|
3
12
|
#include "ctrlc.h"
|
|
4
13
|
#include "glbopts.h"
|
|
5
14
|
#include "linalg.h"
|
|
@@ -10,67 +19,142 @@
|
|
|
10
19
|
#include "scs_work.h"
|
|
11
20
|
#include "util.h"
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
#include <string.h>
|
|
23
|
+
|
|
24
|
+
#ifdef SCS_MKL
|
|
25
|
+
#define MKL_INTERFACE_LP64 0
|
|
26
|
+
#define MKL_INTERFACE_ILP64 1
|
|
27
|
+
/* MKL's interface-layer value is a bitmask: bit 0 selects the integer
|
|
28
|
+
* width (LP64/ILP64) and bit 1 is the GNU Fortran calling-convention
|
|
29
|
+
* flag, so MKL_Set_Interface_Layer can legitimately return 2 (LP64|GNU)
|
|
30
|
+
* or 3 (ILP64|GNU) -- e.g. when an MKL-backed NumPy initialized MKL
|
|
31
|
+
* first. Only the width bit affects integer-size safety. */
|
|
32
|
+
#define MKL_INTERFACE_GNU 2
|
|
33
|
+
/* bit 0 of the layer value selects the integer width (LP64/ILP64) */
|
|
34
|
+
#define MKL_INTERFACE_WIDTH_MASK MKL_INTERFACE_ILP64
|
|
35
|
+
/* Only libmkl_rt provides this; a static MKL link has nothing to negotiate,
|
|
36
|
+
* so the reference is weak and the check is skipped when the symbol is
|
|
37
|
+
* absent (MSVC links mkl_rt dynamically and keeps the strong reference). */
|
|
38
|
+
#if defined(__GNUC__)
|
|
39
|
+
int MKL_Set_Interface_Layer(int) __attribute__((weak));
|
|
40
|
+
#else
|
|
41
|
+
int MKL_Set_Interface_Layer(int);
|
|
42
|
+
#endif
|
|
21
43
|
|
|
22
|
-
static
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
static const char *scs_mkl_interface_name(int layer) {
|
|
45
|
+
switch (layer) {
|
|
46
|
+
case MKL_INTERFACE_LP64:
|
|
47
|
+
return "LP64";
|
|
48
|
+
case MKL_INTERFACE_ILP64:
|
|
49
|
+
return "ILP64";
|
|
50
|
+
case MKL_INTERFACE_LP64 | MKL_INTERFACE_GNU:
|
|
51
|
+
return "LP64 (GNU)";
|
|
52
|
+
case MKL_INTERFACE_ILP64 | MKL_INTERFACE_GNU:
|
|
53
|
+
return "ILP64 (GNU)";
|
|
54
|
+
default:
|
|
55
|
+
return "unknown";
|
|
31
56
|
}
|
|
32
57
|
}
|
|
33
58
|
|
|
34
|
-
static
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (w->stgs) {
|
|
59
|
-
if (w->stgs->log_csv_filename)
|
|
60
|
-
scs_free((char *)w->stgs->log_csv_filename);
|
|
61
|
-
if (w->stgs->write_data_filename)
|
|
62
|
-
scs_free((char *)w->stgs->write_data_filename);
|
|
63
|
-
scs_free(w->stgs);
|
|
59
|
+
static scs_int scs_init_mkl_runtime(void) {
|
|
60
|
+
/* Enforce the correct MKL interface layer for BLAS/LAPACK calls before any
|
|
61
|
+
* common SCS code reaches MKL-backed BLAS routines.
|
|
62
|
+
*
|
|
63
|
+
* The interface layer must match what we linked against:
|
|
64
|
+
* BLAS64 defined -> ILP64 (64-bit BLAS integers, mkl-dynamic-ilp64-*)
|
|
65
|
+
* BLAS64 undefined -> LP64 (32-bit BLAS integers, mkl-dynamic-lp64-*)
|
|
66
|
+
*
|
|
67
|
+
* If another library in the process set the wrong layer, our BLAS calls
|
|
68
|
+
* would silently receive the wrong integer width, causing memory corruption.
|
|
69
|
+
*
|
|
70
|
+
* This only protects the BLAS layer. The pardiso_64 entry point is
|
|
71
|
+
* unaffected by the interface layer — it always uses 64-bit integers. */
|
|
72
|
+
{
|
|
73
|
+
#ifdef BLAS64
|
|
74
|
+
int expected = MKL_INTERFACE_ILP64;
|
|
75
|
+
#else
|
|
76
|
+
int expected = MKL_INTERFACE_LP64;
|
|
77
|
+
#endif
|
|
78
|
+
int actual;
|
|
79
|
+
#if defined(__GNUC__)
|
|
80
|
+
if (!MKL_Set_Interface_Layer) {
|
|
81
|
+
/* static MKL: no runtime interface layer exists to query */
|
|
82
|
+
return 0;
|
|
64
83
|
}
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
#endif
|
|
85
|
+
actual = MKL_Set_Interface_Layer(expected);
|
|
86
|
+
/* MKL returns -1 for an invalid request; without this guard the width
|
|
87
|
+
* comparison below would misread it ((-1 & 1) == 1, i.e. ILP64) */
|
|
88
|
+
if (actual < 0) {
|
|
89
|
+
scs_printf("MKL_Set_Interface_Layer(%d) failed with error %d.\n",
|
|
90
|
+
expected, actual);
|
|
91
|
+
return -1;
|
|
67
92
|
}
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
/* compare only the integer-width bit: the GNU flag is a Fortran
|
|
94
|
+
* calling-convention variant with the same integer sizes */
|
|
95
|
+
if ((actual & MKL_INTERFACE_WIDTH_MASK) !=
|
|
96
|
+
(expected & MKL_INTERFACE_WIDTH_MASK)) {
|
|
97
|
+
scs_printf("MKL interface layer mismatch: expected %s, but MKL is using "
|
|
98
|
+
"%s (%d). Another library in this process likely "
|
|
99
|
+
"initialized MKL with an incompatible LP64/ILP64 setting.\n",
|
|
100
|
+
scs_mkl_interface_name(expected),
|
|
101
|
+
scs_mkl_interface_name(actual), actual);
|
|
102
|
+
return -1;
|
|
70
103
|
}
|
|
71
|
-
scs_free(w);
|
|
72
104
|
}
|
|
105
|
+
return 0;
|
|
73
106
|
}
|
|
107
|
+
#endif
|
|
108
|
+
|
|
109
|
+
/* ======================= Forward Declarations ====================== */
|
|
110
|
+
|
|
111
|
+
static void print_init_header(const ScsData *d, const ScsCone *k,
|
|
112
|
+
const ScsSettings *stgs);
|
|
113
|
+
static void print_header(ScsWork *w, const ScsCone *k);
|
|
114
|
+
static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer);
|
|
115
|
+
static void print_footer(ScsInfo *info);
|
|
116
|
+
static void free_residuals(ScsResiduals *r);
|
|
117
|
+
static ScsResiduals *init_residuals(const ScsData *d);
|
|
118
|
+
static void set_info_aa_stats(ScsInfo *info, const AaWork *accel);
|
|
119
|
+
static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
|
|
120
|
+
ScsInfo *info, scs_int status_val,
|
|
121
|
+
const char *msg);
|
|
122
|
+
static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
|
|
123
|
+
ScsInfo *info, scs_int stint, const char *msg,
|
|
124
|
+
const char *ststr);
|
|
125
|
+
static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n,
|
|
126
|
+
scs_float pd);
|
|
127
|
+
static void unnormalize_residuals(ScsWork *w);
|
|
128
|
+
static void populate_residual_struct(ScsWork *w, scs_int iter);
|
|
129
|
+
static scs_int has_converged(ScsWork *w, scs_int iter);
|
|
130
|
+
static void warm_start_vars(ScsWork *w, ScsSolution *sol);
|
|
131
|
+
static void cold_start_vars(ScsWork *w);
|
|
132
|
+
static scs_float root_plus(ScsWork *w, scs_float *p, scs_float *mu,
|
|
133
|
+
scs_float eta);
|
|
134
|
+
static scs_int project_lin_sys(ScsWork *w, scs_int iter);
|
|
135
|
+
static void compute_rsk(ScsWork *w);
|
|
136
|
+
static void update_dual_vars(ScsWork *w);
|
|
137
|
+
static scs_int project_cones(ScsWork *w, const ScsCone *k, scs_int iter);
|
|
138
|
+
static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
139
|
+
scs_int iter);
|
|
140
|
+
static void set_diag_r(ScsWork *w);
|
|
141
|
+
static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
142
|
+
const ScsSettings *stgs);
|
|
143
|
+
static void update_work_cache(ScsWork *w);
|
|
144
|
+
static void reset_tracking(ScsWork *w);
|
|
145
|
+
static scs_int update_work(ScsWork *w, ScsSolution *sol);
|
|
146
|
+
static scs_int update_scale(ScsWork *w, const ScsCone *k, scs_int iter);
|
|
147
|
+
static inline void normalize_v(scs_float *v, scs_int len);
|
|
148
|
+
|
|
149
|
+
/* ======================== Printing / Output ======================== */
|
|
150
|
+
|
|
151
|
+
static const char *HEADER[] = {
|
|
152
|
+
" iter ", " pri res ", " dua res ", " gap ",
|
|
153
|
+
" obj ", " scale ", " time (s)",
|
|
154
|
+
};
|
|
155
|
+
static const scs_int HSPACE = 9;
|
|
156
|
+
static const scs_int HEADER_LEN = 7;
|
|
157
|
+
static const scs_int LINE_LEN = 66;
|
|
74
158
|
|
|
75
159
|
static void print_init_header(const ScsData *d, const ScsCone *k,
|
|
76
160
|
const ScsSettings *stgs) {
|
|
@@ -96,15 +180,18 @@ static void print_init_header(const ScsData *d, const ScsCone *k,
|
|
|
96
180
|
scs_printf("\n");
|
|
97
181
|
scs_printf("problem: variables n: %i, constraints m: %i\n", (int)d->n,
|
|
98
182
|
(int)d->m);
|
|
99
|
-
|
|
100
|
-
|
|
183
|
+
if (cone_str) {
|
|
184
|
+
scs_printf("%s", cone_str);
|
|
185
|
+
scs_free(cone_str);
|
|
186
|
+
} else {
|
|
187
|
+
scs_printf("cones: <unavailable>\n");
|
|
188
|
+
}
|
|
101
189
|
scs_printf("settings: eps_abs: %.1e, eps_rel: %.1e, eps_infeas: %.1e\n"
|
|
102
190
|
"\t alpha: %.2f, scale: %.2e, adaptive_scale: %i\n"
|
|
103
191
|
"\t max_iters: %i, normalize: %i, rho_x: %.2e\n",
|
|
104
192
|
stgs->eps_abs, stgs->eps_rel, stgs->eps_infeas, stgs->alpha,
|
|
105
193
|
stgs->scale, (int)stgs->adaptive_scale, (int)stgs->max_iters,
|
|
106
194
|
(int)stgs->normalize, stgs->rho_x);
|
|
107
|
-
/* (int)stgs->warm_start); */
|
|
108
195
|
if (stgs->acceleration_lookback != 0) {
|
|
109
196
|
scs_printf("\t acceleration_lookback: %i, acceleration_interval: %i\n",
|
|
110
197
|
(int)acceleration_lookback, (int)acceleration_interval);
|
|
@@ -125,6 +212,148 @@ static void print_init_header(const ScsData *d, const ScsCone *k,
|
|
|
125
212
|
#endif
|
|
126
213
|
}
|
|
127
214
|
|
|
215
|
+
static void print_header(ScsWork *w, const ScsCone *k) {
|
|
216
|
+
scs_int i;
|
|
217
|
+
for (i = 0; i < LINE_LEN; ++i) {
|
|
218
|
+
scs_printf("-");
|
|
219
|
+
}
|
|
220
|
+
scs_printf("\n");
|
|
221
|
+
for (i = 0; i < HEADER_LEN - 1; ++i) {
|
|
222
|
+
scs_printf("%s|", HEADER[i]);
|
|
223
|
+
}
|
|
224
|
+
scs_printf("%s\n", HEADER[HEADER_LEN - 1]);
|
|
225
|
+
for (i = 0; i < LINE_LEN; ++i) {
|
|
226
|
+
scs_printf("-");
|
|
227
|
+
}
|
|
228
|
+
scs_printf("\n");
|
|
229
|
+
#ifdef MATLAB_MEX_FILE
|
|
230
|
+
mexEvalString("drawnow;");
|
|
231
|
+
#endif
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer) {
|
|
235
|
+
ScsResiduals *r = w->r_orig;
|
|
236
|
+
scs_printf("%*i|", (int)strlen(HEADER[0]), (int)i);
|
|
237
|
+
scs_printf("%*.2e ", (int)HSPACE, r->res_pri);
|
|
238
|
+
scs_printf("%*.2e ", (int)HSPACE, r->res_dual);
|
|
239
|
+
scs_printf("%*.2e ", (int)HSPACE, r->gap);
|
|
240
|
+
/* report mid point of primal and dual objective values */
|
|
241
|
+
scs_printf("%*.2e ", (int)HSPACE, 0.5 * (r->pobj + r->dobj));
|
|
242
|
+
scs_printf("%*.2e ", (int)HSPACE, w->stgs->scale);
|
|
243
|
+
/* Report TOTAL time, including setup */
|
|
244
|
+
scs_printf("%*.2e ", (int)HSPACE,
|
|
245
|
+
(SCS(tocq)(solve_timer) + w->setup_time) / 1e3);
|
|
246
|
+
scs_printf("\n");
|
|
247
|
+
|
|
248
|
+
#if VERBOSITY > 0
|
|
249
|
+
scs_printf("Norm u = %1.6e, ", SCS(norm_2)(w->u, w->d->n + w->d->m + 1));
|
|
250
|
+
scs_printf("Norm u_t = %1.6e, ", SCS(norm_2)(w->u_t, w->d->n + w->d->m + 1));
|
|
251
|
+
scs_printf("Norm v = %1.6e, ", SCS(norm_2)(w->v, w->d->n + w->d->m + 1));
|
|
252
|
+
scs_printf("Norm rsk = %1.6e, ", SCS(norm_2)(w->rsk, w->d->n + w->d->m + 1));
|
|
253
|
+
scs_printf("Norm x = %1.6e, ", SCS(norm_2)(w->xys_orig->x, w->d->n));
|
|
254
|
+
scs_printf("Norm y = %1.6e, ", SCS(norm_2)(w->xys_orig->y, w->d->m));
|
|
255
|
+
scs_printf("Norm s = %1.6e, ", SCS(norm_2)(w->xys_orig->s, w->d->m));
|
|
256
|
+
scs_printf("Norm |Ax + s| = %1.6e, ", SCS(norm_2)(r->ax_s, w->d->m));
|
|
257
|
+
scs_printf("tau = %1.6e, ", w->u[w->d->n + w->d->m]);
|
|
258
|
+
scs_printf("kappa = %1.6e, ", w->rsk[w->d->n + w->d->m]);
|
|
259
|
+
scs_printf("|u - u_t| = %1.6e, ",
|
|
260
|
+
SCS(norm_diff)(w->u, w->u_t, w->d->n + w->d->m + 1));
|
|
261
|
+
scs_printf("res_infeas = %1.6e, ", r->res_infeas);
|
|
262
|
+
scs_printf("res_unbdd_a = %1.6e, ", r->res_unbdd_a);
|
|
263
|
+
scs_printf("res_unbdd_p = %1.6e, ", r->res_unbdd_p);
|
|
264
|
+
scs_printf("ctx_tau = %1.6e, ", r->ctx_tau);
|
|
265
|
+
scs_printf("bty_tau = %1.2e\n", r->bty_tau);
|
|
266
|
+
#endif
|
|
267
|
+
|
|
268
|
+
#ifdef MATLAB_MEX_FILE
|
|
269
|
+
mexEvalString("drawnow;");
|
|
270
|
+
#endif
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
static void print_footer(ScsInfo *info) {
|
|
274
|
+
scs_int i;
|
|
275
|
+
|
|
276
|
+
for (i = 0; i < LINE_LEN; ++i) {
|
|
277
|
+
scs_printf("-");
|
|
278
|
+
}
|
|
279
|
+
scs_printf("\n");
|
|
280
|
+
scs_printf("status: %s\n", info->status);
|
|
281
|
+
scs_printf("timings: total: %1.2es = setup: %1.2es + solve: %1.2es\n",
|
|
282
|
+
(info->setup_time + info->solve_time) / 1e3,
|
|
283
|
+
info->setup_time / 1e3, info->solve_time / 1e3);
|
|
284
|
+
scs_printf("\t lin-sys: %1.2es, cones: %1.2es, accel: %1.2es\n",
|
|
285
|
+
info->lin_sys_time / 1e3, info->cone_time / 1e3,
|
|
286
|
+
info->accel_time / 1e3);
|
|
287
|
+
|
|
288
|
+
for (i = 0; i < LINE_LEN; ++i) {
|
|
289
|
+
scs_printf("-");
|
|
290
|
+
}
|
|
291
|
+
scs_printf("\n");
|
|
292
|
+
/* report mid point of primal and dual objective values */
|
|
293
|
+
scs_printf("objective = %.6f", 0.5 * (info->pobj + info->dobj));
|
|
294
|
+
switch (info->status_val) {
|
|
295
|
+
case SCS_SOLVED_INACCURATE:
|
|
296
|
+
case SCS_UNBOUNDED_INACCURATE:
|
|
297
|
+
case SCS_INFEASIBLE_INACCURATE:
|
|
298
|
+
scs_printf(" (inaccurate)");
|
|
299
|
+
/* fallthrough */
|
|
300
|
+
default:
|
|
301
|
+
scs_printf("\n");
|
|
302
|
+
}
|
|
303
|
+
for (i = 0; i < LINE_LEN; ++i) {
|
|
304
|
+
scs_printf("-");
|
|
305
|
+
}
|
|
306
|
+
scs_printf("\n");
|
|
307
|
+
#ifdef MATLAB_MEX_FILE
|
|
308
|
+
mexEvalString("drawnow;");
|
|
309
|
+
#endif
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* ======================== Memory Management ======================== */
|
|
313
|
+
|
|
314
|
+
static void free_residuals(ScsResiduals *r) {
|
|
315
|
+
if (r) {
|
|
316
|
+
scs_free(r->ax);
|
|
317
|
+
scs_free(r->ax_s);
|
|
318
|
+
scs_free(r->px);
|
|
319
|
+
scs_free(r->aty);
|
|
320
|
+
scs_free(r->ax_s_btau);
|
|
321
|
+
scs_free(r->px_aty_ctau);
|
|
322
|
+
scs_free(r);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static ScsResiduals *init_residuals(const ScsData *d) {
|
|
327
|
+
ScsResiduals *r = (ScsResiduals *)scs_calloc(1, sizeof(ScsResiduals));
|
|
328
|
+
if (!r)
|
|
329
|
+
return SCS_NULL;
|
|
330
|
+
r->ax = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
331
|
+
r->ax_s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
332
|
+
r->ax_s_btau = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
333
|
+
r->px = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
334
|
+
r->aty = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
335
|
+
r->px_aty_ctau = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
336
|
+
if (!r->ax || !r->ax_s || !r->ax_s_btau || !r->px || !r->aty ||
|
|
337
|
+
!r->px_aty_ctau) {
|
|
338
|
+
free_residuals(r);
|
|
339
|
+
return SCS_NULL;
|
|
340
|
+
}
|
|
341
|
+
return r;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
static void set_info_aa_stats(ScsInfo *info, const AaWork *accel) {
|
|
345
|
+
if (!info) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
memset(&info->aa_stats, 0, sizeof(info->aa_stats));
|
|
349
|
+
info->aa_stats.last_aa_norm = NAN;
|
|
350
|
+
if (accel) {
|
|
351
|
+
info->aa_stats = aa_get_stats(accel);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* ==================== Error / Failure Handling ===================== */
|
|
356
|
+
|
|
128
357
|
static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
|
|
129
358
|
ScsInfo *info, scs_int status_val,
|
|
130
359
|
const char *msg) {
|
|
@@ -144,17 +373,23 @@ static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
|
|
|
144
373
|
if (!sol->x) {
|
|
145
374
|
sol->x = (scs_float *)scs_calloc(n, sizeof(scs_float));
|
|
146
375
|
}
|
|
147
|
-
|
|
376
|
+
if (sol->x) {
|
|
377
|
+
SCS(scale_array)(sol->x, NAN, n);
|
|
378
|
+
}
|
|
148
379
|
}
|
|
149
380
|
if (m > 0) {
|
|
150
381
|
if (!sol->y) {
|
|
151
382
|
sol->y = (scs_float *)scs_calloc(m, sizeof(scs_float));
|
|
152
383
|
}
|
|
153
|
-
|
|
384
|
+
if (sol->y) {
|
|
385
|
+
SCS(scale_array)(sol->y, NAN, m);
|
|
386
|
+
}
|
|
154
387
|
if (!sol->s) {
|
|
155
388
|
sol->s = (scs_float *)scs_calloc(m, sizeof(scs_float));
|
|
156
389
|
}
|
|
157
|
-
|
|
390
|
+
if (sol->s) {
|
|
391
|
+
SCS(scale_array)(sol->s, NAN, m);
|
|
392
|
+
}
|
|
158
393
|
}
|
|
159
394
|
}
|
|
160
395
|
}
|
|
@@ -163,57 +398,127 @@ static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
|
|
|
163
398
|
ScsInfo *info, scs_int stint, const char *msg,
|
|
164
399
|
const char *ststr) {
|
|
165
400
|
scs_int status = stint;
|
|
401
|
+
SCS(close_csv_log_file)(w);
|
|
166
402
|
populate_on_failure(m, n, sol, info, status, ststr);
|
|
403
|
+
set_info_aa_stats(info, w ? w->accel : SCS_NULL);
|
|
167
404
|
scs_printf("Failure:%s\n", msg);
|
|
168
405
|
scs_end_interrupt_listener();
|
|
169
406
|
return status;
|
|
170
407
|
}
|
|
171
408
|
|
|
172
|
-
|
|
173
|
-
return x != x;
|
|
174
|
-
}
|
|
409
|
+
/* ========================= Validation ============================== */
|
|
175
410
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (w->stgs->normalize) {
|
|
184
|
-
SCS(normalize_sol)(w->scal, sol);
|
|
411
|
+
#if NO_VALIDATE == 0
|
|
412
|
+
static scs_int validate(const ScsData *d, const ScsCone *k,
|
|
413
|
+
const ScsSettings *stgs) {
|
|
414
|
+
if (d->m <= 0 || d->n <= 0) {
|
|
415
|
+
scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
|
|
416
|
+
(long)d->m, (long)d->n);
|
|
417
|
+
return -1;
|
|
185
418
|
}
|
|
186
|
-
|
|
187
|
-
|
|
419
|
+
if (d->A && (d->A->m != d->m || d->A->n != d->n)) {
|
|
420
|
+
scs_printf("A dimensions = (%li, %li), inconsistent with m = %li, "
|
|
421
|
+
"n = %li\n",
|
|
422
|
+
(long)d->A->m, (long)d->A->n, (long)d->m, (long)d->n);
|
|
423
|
+
return -1;
|
|
188
424
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
425
|
+
if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
|
|
426
|
+
scs_printf("invalid linear system input data\n");
|
|
427
|
+
return -1;
|
|
192
428
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
SCS(un_normalize_sol)(w->scal, sol);
|
|
429
|
+
if (SCS(validate_cones)(d, k) < 0) {
|
|
430
|
+
scs_printf("cone validation error\n");
|
|
431
|
+
return -1;
|
|
197
432
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
433
|
+
if (stgs->max_iters <= 0) {
|
|
434
|
+
scs_printf("max_iters must be positive\n");
|
|
435
|
+
return -1;
|
|
436
|
+
}
|
|
437
|
+
if (!isfinite(stgs->eps_abs) || stgs->eps_abs < 0) {
|
|
438
|
+
scs_printf("eps_abs tolerance must be a nonnegative finite number\n");
|
|
439
|
+
return -1;
|
|
440
|
+
}
|
|
441
|
+
if (!isfinite(stgs->eps_rel) || stgs->eps_rel < 0) {
|
|
442
|
+
scs_printf("eps_rel tolerance must be a nonnegative finite number\n");
|
|
443
|
+
return -1;
|
|
444
|
+
}
|
|
445
|
+
if (!isfinite(stgs->eps_infeas) || stgs->eps_infeas < 0) {
|
|
446
|
+
scs_printf("eps_infeas tolerance must be a nonnegative finite number\n");
|
|
447
|
+
return -1;
|
|
448
|
+
}
|
|
449
|
+
if (!isfinite(stgs->alpha) || stgs->alpha <= 0 || stgs->alpha >= 2) {
|
|
450
|
+
scs_printf("alpha must be in (0,2)\n");
|
|
451
|
+
return -1;
|
|
452
|
+
}
|
|
453
|
+
if (!isfinite(stgs->rho_x) || stgs->rho_x <= 0) {
|
|
454
|
+
scs_printf("rho_x must be a positive finite number (1e-3 works well).\n");
|
|
455
|
+
return -1;
|
|
456
|
+
}
|
|
457
|
+
if (stgs->adaptive_diag_scale < 0 || stgs->adaptive_diag_scale > 1) {
|
|
458
|
+
scs_printf("adaptive_diag_scale must be 0 (off) or 1 (on).\n");
|
|
459
|
+
return -1;
|
|
460
|
+
}
|
|
461
|
+
if (!isfinite(stgs->scale) || stgs->scale <= 0) {
|
|
462
|
+
scs_printf("scale must be a positive finite number (1 works well).\n");
|
|
463
|
+
return -1;
|
|
464
|
+
}
|
|
465
|
+
if (!isfinite(stgs->time_limit_secs) || stgs->time_limit_secs < 0) {
|
|
466
|
+
scs_printf("time_limit_secs must be a nonnegative finite number.\n");
|
|
467
|
+
return -1;
|
|
468
|
+
}
|
|
469
|
+
if (stgs->acceleration_interval <= 0) {
|
|
470
|
+
scs_printf("acceleration_interval must be positive (10 works well).\n");
|
|
471
|
+
return -1;
|
|
472
|
+
}
|
|
473
|
+
if (stgs->acceleration_lookback < 0) {
|
|
474
|
+
scs_printf("acceleration_lookback must be nonnegative "
|
|
475
|
+
"(use acceleration_type_1=0 for type-II AA).\n");
|
|
476
|
+
return -1;
|
|
477
|
+
}
|
|
478
|
+
if (!isfinite(stgs->acceleration_regularization)) {
|
|
479
|
+
/* Sign-encoded modes per include/aa.h: positive = scaled by
|
|
480
|
+
* ||A||_F ||Y||_F, negative = pinned absolute value, zero = off. */
|
|
481
|
+
scs_printf("acceleration_regularization must be a finite number.\n");
|
|
482
|
+
return -1;
|
|
483
|
+
}
|
|
484
|
+
if (!isfinite(stgs->acceleration_relaxation) ||
|
|
485
|
+
stgs->acceleration_relaxation < 0 ||
|
|
486
|
+
stgs->acceleration_relaxation > 2) {
|
|
487
|
+
scs_printf("acceleration_relaxation must be in [0, 2].\n");
|
|
488
|
+
return -1;
|
|
489
|
+
}
|
|
490
|
+
return 0;
|
|
491
|
+
}
|
|
492
|
+
#endif
|
|
493
|
+
|
|
494
|
+
/* ==================== Residual Computation ========================= */
|
|
495
|
+
|
|
496
|
+
/* pd = primal_scale * dual_scale = sigma^2 from the Ruiz equilibration.
|
|
497
|
+
* Pass pd = 1 when operating on normalised residuals (no amplification).
|
|
498
|
+
*
|
|
499
|
+
* After unnormalization bty_tau and ctx_tau are divided by pd, so
|
|
500
|
+
* floating-point noise is amplified by 1/pd. INFEAS_NEGATIVITY_TOL is
|
|
501
|
+
* calibrated for normalised scale, so the correct threshold in the
|
|
502
|
+
* un-normalised space is INFEAS_NEGATIVITY_TOL/pd (issue #350). */
|
|
503
|
+
static void compute_residuals(ScsResiduals *r, scs_int m, scs_int n,
|
|
504
|
+
scs_float pd) {
|
|
505
|
+
scs_float nm_ax_s, nm_px, nm_aty;
|
|
506
|
+
scs_float nm_ax_s_btau = NORM(r->ax_s_btau, m);
|
|
507
|
+
scs_float nm_px_aty_ctau = NORM(r->px_aty_ctau, n);
|
|
508
|
+
scs_float tol = INFEAS_NEGATIVITY_TOL / pd;
|
|
509
|
+
|
|
510
|
+
r->res_pri = SAFEDIV_POS(nm_ax_s_btau, r->tau);
|
|
206
511
|
r->res_dual = SAFEDIV_POS(nm_px_aty_ctau, r->tau);
|
|
207
512
|
r->res_unbdd_a = NAN;
|
|
208
513
|
r->res_unbdd_p = NAN;
|
|
209
514
|
r->res_infeas = NAN;
|
|
210
|
-
if (r->ctx_tau < -
|
|
515
|
+
if (r->ctx_tau < -tol) {
|
|
211
516
|
nm_ax_s = NORM(r->ax_s, m);
|
|
212
517
|
nm_px = NORM(r->px, n);
|
|
213
518
|
r->res_unbdd_a = SAFEDIV_POS(nm_ax_s, -r->ctx_tau);
|
|
214
519
|
r->res_unbdd_p = SAFEDIV_POS(nm_px, -r->ctx_tau);
|
|
215
520
|
}
|
|
216
|
-
if (r->bty_tau < -
|
|
521
|
+
if (r->bty_tau < -tol) {
|
|
217
522
|
nm_aty = NORM(r->aty, n);
|
|
218
523
|
r->res_infeas = SAFEDIV_POS(nm_aty, -r->bty_tau);
|
|
219
524
|
}
|
|
@@ -228,14 +533,6 @@ static void unnormalize_residuals(ScsWork *w) {
|
|
|
228
533
|
r->last_iter = r_n->last_iter;
|
|
229
534
|
r->tau = r_n->tau;
|
|
230
535
|
|
|
231
|
-
/* mem copy arrays */
|
|
232
|
-
memcpy(r->ax, r_n->ax, w->d->m * sizeof(scs_float));
|
|
233
|
-
memcpy(r->ax_s, r_n->ax_s, w->d->m * sizeof(scs_float));
|
|
234
|
-
memcpy(r->ax_s_btau, r_n->ax_s_btau, w->d->m * sizeof(scs_float));
|
|
235
|
-
memcpy(r->aty, r_n->aty, w->d->n * sizeof(scs_float));
|
|
236
|
-
memcpy(r->px, r_n->px, w->d->n * sizeof(scs_float));
|
|
237
|
-
memcpy(r->px_aty_ctau, r_n->px_aty_ctau, w->d->n * sizeof(scs_float));
|
|
238
|
-
|
|
239
536
|
/* unnormalize */
|
|
240
537
|
r->kap = r_n->kap / pd;
|
|
241
538
|
r->bty_tau = r_n->bty_tau / pd;
|
|
@@ -248,20 +545,35 @@ static void unnormalize_residuals(ScsWork *w) {
|
|
|
248
545
|
r->dobj = r_n->dobj / pd;
|
|
249
546
|
r->gap = r_n->gap / pd;
|
|
250
547
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
548
|
+
/* Fuse the six memcpy+un_normalize calls into two loops.
|
|
549
|
+
* Primal: divide by D[i]*dual_scale. Dual: divide by E[i]*primal_scale.
|
|
550
|
+
* This reduces 6 memcpy + 6 scale passes to 2 passes. */
|
|
551
|
+
{
|
|
552
|
+
scs_int i;
|
|
553
|
+
const scs_float *D = w->scal->D, *E = w->scal->E;
|
|
554
|
+
scs_float inv_ds = 1.0 / (w->scal->dual_scale);
|
|
555
|
+
scs_float inv_ps = 1.0 / (w->scal->primal_scale);
|
|
556
|
+
for (i = 0; i < w->d->m; ++i) {
|
|
557
|
+
scs_float f = inv_ds / D[i];
|
|
558
|
+
r->ax[i] = r_n->ax[i] * f;
|
|
559
|
+
r->ax_s[i] = r_n->ax_s[i] * f;
|
|
560
|
+
r->ax_s_btau[i] = r_n->ax_s_btau[i] * f;
|
|
561
|
+
}
|
|
562
|
+
for (i = 0; i < w->d->n; ++i) {
|
|
563
|
+
scs_float f = inv_ps / E[i];
|
|
564
|
+
r->aty[i] = r_n->aty[i] * f;
|
|
565
|
+
r->px[i] = r_n->px[i] * f;
|
|
566
|
+
r->px_aty_ctau[i] = r_n->px_aty_ctau[i] * f;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
257
569
|
|
|
258
|
-
compute_residuals(r, w->d->m, w->d->n);
|
|
570
|
+
compute_residuals(r, w->d->m, w->d->n, pd);
|
|
259
571
|
}
|
|
260
572
|
|
|
261
573
|
/* calculates un-normalized residual quantities */
|
|
262
574
|
/* this is somewhat slow but not a bottleneck */
|
|
263
575
|
static void populate_residual_struct(ScsWork *w, scs_int iter) {
|
|
264
|
-
scs_int n = w->d->n, m = w->d->m;
|
|
576
|
+
scs_int i, n = w->d->n, m = w->d->m;
|
|
265
577
|
/* normalized x,y,s terms */
|
|
266
578
|
scs_float *x = w->xys_normalized->x;
|
|
267
579
|
scs_float *y = w->xys_normalized->y;
|
|
@@ -286,13 +598,11 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
|
|
|
286
598
|
/* ax = Ax */
|
|
287
599
|
SCS(accum_by_a)(w->d->A, x, r->ax);
|
|
288
600
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
/* ax_s_btau = Ax + s - b * tau */
|
|
295
|
-
SCS(add_scaled_array)(r->ax_s_btau, w->d->b, m, -r->tau);
|
|
601
|
+
/* Build ax_s and ax_s_btau in one fused pass (saves 2 memcpy + 2 axpy). */
|
|
602
|
+
for (i = 0; i < m; ++i) {
|
|
603
|
+
r->ax_s[i] = r->ax[i] + s[i];
|
|
604
|
+
r->ax_s_btau[i] = r->ax_s[i] - r->tau * w->d->b[i];
|
|
605
|
+
}
|
|
296
606
|
|
|
297
607
|
/**************** DUAL *********************/
|
|
298
608
|
memset(r->px, 0, n * sizeof(scs_float));
|
|
@@ -308,12 +618,10 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
|
|
|
308
618
|
/* aty = A'y */
|
|
309
619
|
SCS(accum_by_atrans)(w->d->A, y, r->aty);
|
|
310
620
|
|
|
311
|
-
/*
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
/* r->px_aty_ctau = Px + A'y + c * tau */
|
|
316
|
-
SCS(add_scaled_array)(r->px_aty_ctau, w->d->c, n, r->tau);
|
|
621
|
+
/* Build px_aty_ctau in one fused pass (saves 1 memcpy + 2 axpy). */
|
|
622
|
+
for (i = 0; i < n; ++i) {
|
|
623
|
+
r->px_aty_ctau[i] = r->px[i] + r->aty[i] + r->tau * w->d->c[i];
|
|
624
|
+
}
|
|
317
625
|
|
|
318
626
|
/**************** OTHERS *****************/
|
|
319
627
|
r->bty_tau = SCS(dot)(y, w->d->b, m);
|
|
@@ -327,7 +635,7 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
|
|
|
327
635
|
r->pobj = r->xt_p_x / 2. + r->ctx;
|
|
328
636
|
r->dobj = -r->xt_p_x / 2. - r->bty;
|
|
329
637
|
|
|
330
|
-
compute_residuals(r, m, n);
|
|
638
|
+
compute_residuals(r, m, n, 1.0);
|
|
331
639
|
|
|
332
640
|
if (w->stgs->normalize) {
|
|
333
641
|
memcpy(w->xys_orig->x, w->xys_normalized->x, n * sizeof(scs_float));
|
|
@@ -338,31 +646,149 @@ static void populate_residual_struct(ScsWork *w, scs_int iter) {
|
|
|
338
646
|
}
|
|
339
647
|
}
|
|
340
648
|
|
|
649
|
+
/* ==================== Convergence Checking ========================= */
|
|
650
|
+
|
|
651
|
+
static scs_int has_converged(ScsWork *w, scs_int iter) {
|
|
652
|
+
scs_float abs_xt_p_x, abs_ctx, abs_bty;
|
|
653
|
+
scs_float nm_s, nm_px, nm_aty, nm_ax;
|
|
654
|
+
scs_float grl, prl, drl;
|
|
655
|
+
scs_float eps_abs = w->stgs->eps_abs;
|
|
656
|
+
scs_float eps_rel = w->stgs->eps_rel;
|
|
657
|
+
scs_float eps_infeas = w->stgs->eps_infeas;
|
|
658
|
+
|
|
659
|
+
ScsResiduals *r = w->r_orig;
|
|
660
|
+
|
|
661
|
+
if (r->tau > 0.) {
|
|
662
|
+
abs_xt_p_x = ABS(r->xt_p_x);
|
|
663
|
+
abs_ctx = ABS(r->ctx);
|
|
664
|
+
abs_bty = ABS(r->bty);
|
|
665
|
+
|
|
666
|
+
nm_s = NORM(w->xys_orig->s, w->d->m);
|
|
667
|
+
nm_px = NORM(r->px, w->d->n);
|
|
668
|
+
nm_aty = NORM(r->aty, w->d->n);
|
|
669
|
+
nm_ax = NORM(r->ax, w->d->m);
|
|
670
|
+
/* xt_p_x, ctx, bty already have tau divided out */
|
|
671
|
+
grl = MAX(MAX(abs_xt_p_x, abs_ctx), abs_bty);
|
|
672
|
+
/* s, ax, px, aty do *not* have tau divided out, so need to divide */
|
|
673
|
+
prl = MAX(MAX(w->nm_b_orig * r->tau, nm_s), nm_ax) / r->tau;
|
|
674
|
+
drl = MAX(MAX(w->nm_c_orig * r->tau, nm_px), nm_aty) / r->tau;
|
|
675
|
+
if (isless(r->res_pri, eps_abs + eps_rel * prl) &&
|
|
676
|
+
isless(r->res_dual, eps_abs + eps_rel * drl) &&
|
|
677
|
+
isless(r->gap, eps_abs + eps_rel * grl)) {
|
|
678
|
+
return SCS_SOLVED;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/* Certificate residuals divide the violation by the certificate's
|
|
682
|
+
* objective value (-c'x or -b'y), which bakes the data magnitudes into
|
|
683
|
+
* the effective tolerance: with e.g. ||c|| ~ 1e6 the unboundedness test
|
|
684
|
+
* is six orders looser than intended and produces false certificates
|
|
685
|
+
* (netlib agg/grow families). Tighten by the data-norm ratio so the
|
|
686
|
+
* test is invariant to rescaling of c (resp. b) vs A; only ever
|
|
687
|
+
* tighten. Additionally require the certificate to hold on consecutive
|
|
688
|
+
* checks to reject transient (e.g. accelerated) iterates. */
|
|
689
|
+
{
|
|
690
|
+
scs_float tighten_unbdd =
|
|
691
|
+
MAX(1., SAFEDIV_POS(w->nm_c_orig, w->nm_a_orig));
|
|
692
|
+
scs_float tighten_infeas =
|
|
693
|
+
MAX(1., SAFEDIV_POS(w->nm_b_orig, w->nm_a_orig));
|
|
694
|
+
if (isless(r->res_unbdd_a * tighten_unbdd, eps_infeas) &&
|
|
695
|
+
isless(r->res_unbdd_p, eps_infeas)) {
|
|
696
|
+
if (++w->unbdd_cert_streak >= CERT_PERSISTENCE_CHECKS) {
|
|
697
|
+
return SCS_UNBOUNDED;
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
w->unbdd_cert_streak = 0;
|
|
701
|
+
}
|
|
702
|
+
if (isless(r->res_infeas * tighten_infeas, eps_infeas)) {
|
|
703
|
+
if (++w->infeas_cert_streak >= CERT_PERSISTENCE_CHECKS) {
|
|
704
|
+
return SCS_INFEASIBLE;
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
w->infeas_cert_streak = 0;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return 0;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* =================== Warm / Cold Start Helpers ==================== */
|
|
714
|
+
|
|
715
|
+
static inline scs_int _is_nan(scs_float x) {
|
|
716
|
+
return x != x;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/* given x,y,s warm start, set v = [x; s / R + y; 1]
|
|
720
|
+
* check for nans and set to zero if present
|
|
721
|
+
*/
|
|
722
|
+
static void warm_start_vars(ScsWork *w, ScsSolution *sol) {
|
|
723
|
+
scs_int n = w->d->n, m = w->d->m, i;
|
|
724
|
+
scs_float *v = w->v;
|
|
725
|
+
/* normalize the warm-start */
|
|
726
|
+
if (w->stgs->normalize) {
|
|
727
|
+
SCS(normalize_sol)(w->scal, sol);
|
|
728
|
+
}
|
|
729
|
+
for (i = 0; i < n; ++i) {
|
|
730
|
+
v[i] = _is_nan(sol->x[i]) ? 0. : sol->x[i];
|
|
731
|
+
}
|
|
732
|
+
for (i = 0; i < m; ++i) {
|
|
733
|
+
v[i + n] = sol->y[i] + sol->s[i] / w->diag_r[i + n];
|
|
734
|
+
v[i + n] = _is_nan(v[i + n]) ? 0. : v[i + n];
|
|
735
|
+
}
|
|
736
|
+
v[n + m] = 1.0; /* tau = 1 */
|
|
737
|
+
/* un-normalize so sol unchanged */
|
|
738
|
+
if (w->stgs->normalize) {
|
|
739
|
+
SCS(un_normalize_sol)(w->scal, sol);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
341
743
|
static void cold_start_vars(ScsWork *w) {
|
|
342
744
|
scs_int l = w->d->n + w->d->m + 1;
|
|
343
745
|
memset(w->v, 0, l * sizeof(scs_float));
|
|
344
746
|
w->v[l - 1] = 1.;
|
|
345
747
|
}
|
|
346
748
|
|
|
347
|
-
/*
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
749
|
+
/* ====================== ADMM Iteration ============================= */
|
|
750
|
+
|
|
751
|
+
static scs_float root_plus_from_coeffs(scs_float a, scs_float b, scs_float c) {
|
|
752
|
+
scs_float rad, sqrt_rad, q;
|
|
753
|
+
if (!isfinite(a) || !isfinite(b) || !isfinite(c) || a <= 0.) {
|
|
754
|
+
return NAN;
|
|
755
|
+
}
|
|
756
|
+
rad = b * b - 4 * a * c;
|
|
757
|
+
if (!isfinite(rad)) {
|
|
758
|
+
return NAN;
|
|
354
759
|
}
|
|
355
|
-
|
|
760
|
+
if (rad < 0.) {
|
|
761
|
+
/* Preserve the historical repeated-root fallback for no-real-root cases. */
|
|
762
|
+
return -b / (2 * a);
|
|
763
|
+
}
|
|
764
|
+
sqrt_rad = SQRTF(rad);
|
|
765
|
+
if (b <= 0.) {
|
|
766
|
+
return (-b + sqrt_rad) / (2 * a);
|
|
767
|
+
}
|
|
768
|
+
q = -0.5 * (b + sqrt_rad);
|
|
769
|
+
return q != 0. ? c / q : 0.;
|
|
356
770
|
}
|
|
357
771
|
|
|
358
772
|
static scs_float root_plus(ScsWork *w, scs_float *p, scs_float *mu,
|
|
359
773
|
scs_float eta) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
774
|
+
/* Compute all five weighted dot products (g'Rg, mu'Rg, p'Rg, p'Rp, p'Rmu)
|
|
775
|
+
* in a single pass over diag_r to minimise memory traffic. */
|
|
776
|
+
scs_int i, nm = w->d->n + w->d->m;
|
|
777
|
+
scs_float gg = 0., mug = 0., pg = 0., pp = 0., pmu = 0.;
|
|
778
|
+
scs_float a, b, c, tau_scale = w->diag_r[nm];
|
|
779
|
+
const scs_float *g = w->g, *r = w->diag_r;
|
|
780
|
+
for (i = 0; i < nm; ++i) {
|
|
781
|
+
scs_float ri = r[i], gi = g[i], pi = p[i], mui = mu[i];
|
|
782
|
+
gg += gi * gi * ri;
|
|
783
|
+
mug += mui * gi * ri;
|
|
784
|
+
pg += pi * gi * ri;
|
|
785
|
+
pp += pi * pi * ri;
|
|
786
|
+
pmu += pi * mui * ri;
|
|
787
|
+
}
|
|
788
|
+
a = tau_scale + gg;
|
|
789
|
+
b = mug - 2 * pg - eta * tau_scale;
|
|
790
|
+
c = pp - pmu;
|
|
791
|
+
return root_plus_from_coeffs(a, b, c);
|
|
366
792
|
}
|
|
367
793
|
|
|
368
794
|
/* status != 0 indicates failure */
|
|
@@ -370,10 +796,14 @@ static scs_int project_lin_sys(ScsWork *w, scs_int iter) {
|
|
|
370
796
|
scs_int n = w->d->n, m = w->d->m, l = n + m + 1, status, i;
|
|
371
797
|
scs_float *warm_start = SCS_NULL;
|
|
372
798
|
scs_float tol = -1.0; /* only used for indirect methods, overridden later */
|
|
373
|
-
|
|
374
|
-
for (i = 0; i <
|
|
375
|
-
w->u_t[i]
|
|
799
|
+
/* Copy and scale in one pass, eliminating the intermediate memcpy. */
|
|
800
|
+
for (i = 0; i < n; ++i) {
|
|
801
|
+
w->u_t[i] = w->v[i] * w->diag_r[i];
|
|
802
|
+
}
|
|
803
|
+
for (i = n; i < l - 1; ++i) {
|
|
804
|
+
w->u_t[i] = -w->v[i] * w->diag_r[i];
|
|
376
805
|
}
|
|
806
|
+
w->u_t[l - 1] = w->v[l - 1];
|
|
377
807
|
#if INDIRECT > 0
|
|
378
808
|
scs_float nm_ax_s_btau, nm_px_aty_ctau, nm_ws;
|
|
379
809
|
/* compute warm start using the cone projection output */
|
|
@@ -441,6 +871,19 @@ static scs_int project_cones(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
|
441
871
|
return status;
|
|
442
872
|
}
|
|
443
873
|
|
|
874
|
+
/* scs is homogeneous so scale the iterate to keep norm reasonable */
|
|
875
|
+
static inline void normalize_v(scs_float *v, scs_int len) {
|
|
876
|
+
scs_float v_norm = SCS(norm_2)(v, len); /* always l2 norm */
|
|
877
|
+
if (v_norm == 0.) {
|
|
878
|
+
scs_printf("WARNING: normalize_v called with zero-norm iterate; this is "
|
|
879
|
+
"highly pathological (e.g., strong duality may not hold).\n");
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
SCS(scale_array)(v, SQRTF((scs_float)len) * ITERATE_NORM / v_norm, len);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/* ================== Solution Extraction / Finalization ============== */
|
|
886
|
+
|
|
444
887
|
static void sety(const ScsWork *w, ScsSolution *sol) {
|
|
445
888
|
if (!sol->y) {
|
|
446
889
|
sol->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
@@ -502,324 +945,108 @@ static void set_unbounded(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
|
|
|
502
945
|
info->status_val = SCS_UNBOUNDED;
|
|
503
946
|
}
|
|
504
947
|
|
|
505
|
-
/* not yet converged, take best guess */
|
|
506
|
-
static void set_unfinished(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
|
|
507
|
-
if (w->r_orig->
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
} else if (info->iter >= w->stgs->max_iters) {
|
|
521
|
-
strcat(info->status, " (inaccurate - reached max_iters)");
|
|
522
|
-
} else {
|
|
523
|
-
scs_printf("ERROR: should not be in this state (1).\n");
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/* sets solutions, re-scales by inner prods if infeasible or unbounded */
|
|
528
|
-
static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
529
|
-
scs_int iter) {
|
|
530
|
-
scs_float nm_s, nm_y, sty;
|
|
531
|
-
setx(w, sol);
|
|
532
|
-
sety(w, sol);
|
|
533
|
-
sets(w, sol);
|
|
534
|
-
if (w->stgs->normalize) {
|
|
535
|
-
SCS(un_normalize_sol)(w->scal, sol);
|
|
536
|
-
}
|
|
537
|
-
populate_residual_struct(w, iter);
|
|
538
|
-
|
|
539
|
-
nm_s = SCS(norm_inf)(sol->s, w->d->m);
|
|
540
|
-
nm_y = SCS(norm_inf)(sol->y, w->d->m);
|
|
541
|
-
sty = SCS(dot)(sol->s, sol->y, w->d->m);
|
|
542
|
-
|
|
543
|
-
info->setup_time = w->setup_time;
|
|
544
|
-
info->iter = iter;
|
|
545
|
-
info->res_infeas = w->r_orig->res_infeas;
|
|
546
|
-
info->res_unbdd_a = w->r_orig->res_unbdd_a;
|
|
547
|
-
info->res_unbdd_p = w->r_orig->res_unbdd_p;
|
|
548
|
-
info->scale = w->stgs->scale;
|
|
549
|
-
info->scale_updates = w->scale_updates;
|
|
550
|
-
info->rejected_accel_steps = w->rejected_accel_steps;
|
|
551
|
-
info->accepted_accel_steps = w->accepted_accel_steps;
|
|
552
|
-
info->comp_slack = ABS(sty);
|
|
553
|
-
#ifdef SPECTRAL_TIMING_FLAG
|
|
554
|
-
info->ave_time_matrix_cone_proj = w->cone_work->tot_time_mat_cone_proj / iter;
|
|
555
|
-
info->ave_time_vector_cone_proj = w->cone_work->tot_time_vec_cone_proj / iter;
|
|
556
|
-
#endif
|
|
557
|
-
if (info->comp_slack > 1e-5 * MAX(nm_s, nm_y)) {
|
|
558
|
-
scs_printf("WARNING - large complementary slackness residual: %f\n",
|
|
559
|
-
info->comp_slack);
|
|
560
|
-
}
|
|
561
|
-
switch (info->status_val) {
|
|
562
|
-
case SCS_SOLVED:
|
|
563
|
-
set_solved(w, sol, info);
|
|
564
|
-
break;
|
|
565
|
-
case SCS_INFEASIBLE:
|
|
566
|
-
set_infeasible(w, sol, info);
|
|
567
|
-
break;
|
|
568
|
-
case SCS_UNBOUNDED:
|
|
569
|
-
set_unbounded(w, sol, info);
|
|
570
|
-
break;
|
|
571
|
-
case SCS_UNFINISHED: /* When SCS reaches max_iters or time_limit_secs */
|
|
572
|
-
set_unfinished(w, sol, info);
|
|
573
|
-
break;
|
|
574
|
-
default:
|
|
575
|
-
scs_printf("ERROR: should not be in this state (2).\n");
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer) {
|
|
580
|
-
ScsResiduals *r = w->r_orig;
|
|
581
|
-
scs_printf("%*i|", (int)strlen(HEADER[0]), (int)i);
|
|
582
|
-
scs_printf("%*.2e ", (int)HSPACE, r->res_pri);
|
|
583
|
-
scs_printf("%*.2e ", (int)HSPACE, r->res_dual);
|
|
584
|
-
scs_printf("%*.2e ", (int)HSPACE, r->gap);
|
|
585
|
-
/* report mid point of primal and dual objective values */
|
|
586
|
-
scs_printf("%*.2e ", (int)HSPACE, 0.5 * (r->pobj + r->dobj));
|
|
587
|
-
scs_printf("%*.2e ", (int)HSPACE, w->stgs->scale);
|
|
588
|
-
/* Report TOTAL time, including setup */
|
|
589
|
-
scs_printf("%*.2e ", (int)HSPACE,
|
|
590
|
-
(SCS(tocq)(solve_timer) + w->setup_time) / 1e3);
|
|
591
|
-
scs_printf("\n");
|
|
592
|
-
|
|
593
|
-
#if VERBOSITY > 0
|
|
594
|
-
scs_printf("Norm u = %1.6e, ", SCS(norm_2)(w->u, w->d->n + w->d->m + 1));
|
|
595
|
-
scs_printf("Norm u_t = %1.6e, ", SCS(norm_2)(w->u_t, w->d->n + w->d->m + 1));
|
|
596
|
-
scs_printf("Norm v = %1.6e, ", SCS(norm_2)(w->v, w->d->n + w->d->m + 1));
|
|
597
|
-
scs_printf("Norm rsk = %1.6e, ", SCS(norm_2)(w->rsk, w->d->n + w->d->m + 1));
|
|
598
|
-
scs_printf("Norm x = %1.6e, ", SCS(norm_2)(w->xys_orig->x, w->d->n));
|
|
599
|
-
scs_printf("Norm y = %1.6e, ", SCS(norm_2)(w->xys_orig->y, w->d->m));
|
|
600
|
-
scs_printf("Norm s = %1.6e, ", SCS(norm_2)(w->xys_orig->s, w->d->m));
|
|
601
|
-
scs_printf("Norm |Ax + s| = %1.6e, ", SCS(norm_2)(r->ax_s, w->d->m));
|
|
602
|
-
scs_printf("tau = %1.6e, ", w->u[w->d->n + w->d->m]);
|
|
603
|
-
scs_printf("kappa = %1.6e, ", w->rsk[w->d->n + w->d->m]);
|
|
604
|
-
scs_printf("|u - u_t| = %1.6e, ",
|
|
605
|
-
SCS(norm_diff)(w->u, w->u_t, w->d->n + w->d->m + 1));
|
|
606
|
-
scs_printf("res_infeas = %1.6e, ", r->res_infeas);
|
|
607
|
-
scs_printf("res_unbdd_a = %1.6e, ", r->res_unbdd_a);
|
|
608
|
-
scs_printf("res_unbdd_p = %1.6e, ", r->res_unbdd_p);
|
|
609
|
-
scs_printf("ctx_tau = %1.6e, ", r->ctx_tau);
|
|
610
|
-
scs_printf("bty_tau = %1.2e\n", r->bty_tau);
|
|
611
|
-
#endif
|
|
612
|
-
|
|
613
|
-
#ifdef MATLAB_MEX_FILE
|
|
614
|
-
mexEvalString("drawnow;");
|
|
615
|
-
#endif
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
static void print_header(ScsWork *w, const ScsCone *k) {
|
|
619
|
-
scs_int i;
|
|
620
|
-
for (i = 0; i < LINE_LEN; ++i) {
|
|
621
|
-
scs_printf("-");
|
|
622
|
-
}
|
|
623
|
-
scs_printf("\n");
|
|
624
|
-
for (i = 0; i < HEADER_LEN - 1; ++i) {
|
|
625
|
-
scs_printf("%s|", HEADER[i]);
|
|
626
|
-
}
|
|
627
|
-
scs_printf("%s\n", HEADER[HEADER_LEN - 1]);
|
|
628
|
-
for (i = 0; i < LINE_LEN; ++i) {
|
|
629
|
-
scs_printf("-");
|
|
630
|
-
}
|
|
631
|
-
scs_printf("\n");
|
|
632
|
-
#ifdef MATLAB_MEX_FILE
|
|
633
|
-
mexEvalString("drawnow;");
|
|
634
|
-
#endif
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
static void print_footer(ScsInfo *info) {
|
|
638
|
-
scs_int i;
|
|
639
|
-
|
|
640
|
-
for (i = 0; i < LINE_LEN; ++i) {
|
|
641
|
-
scs_printf("-");
|
|
642
|
-
}
|
|
643
|
-
scs_printf("\n");
|
|
644
|
-
scs_printf("status: %s\n", info->status);
|
|
645
|
-
scs_printf("timings: total: %1.2es = setup: %1.2es + solve: %1.2es\n",
|
|
646
|
-
(info->setup_time + info->solve_time) / 1e3,
|
|
647
|
-
info->setup_time / 1e3, info->solve_time / 1e3);
|
|
648
|
-
scs_printf("\t lin-sys: %1.2es, cones: %1.2es, accel: %1.2es\n",
|
|
649
|
-
info->lin_sys_time / 1e3, info->cone_time / 1e3,
|
|
650
|
-
info->accel_time / 1e3);
|
|
651
|
-
|
|
652
|
-
for (i = 0; i < LINE_LEN; ++i) {
|
|
653
|
-
scs_printf("-");
|
|
654
|
-
}
|
|
655
|
-
scs_printf("\n");
|
|
656
|
-
/* report mid point of primal and dual objective values */
|
|
657
|
-
scs_printf("objective = %.6f", 0.5 * (info->pobj + info->dobj));
|
|
658
|
-
switch (info->status_val) {
|
|
659
|
-
case SCS_SOLVED_INACCURATE:
|
|
660
|
-
case SCS_UNBOUNDED_INACCURATE:
|
|
661
|
-
case SCS_INFEASIBLE_INACCURATE:
|
|
662
|
-
scs_printf(" (inaccurate)");
|
|
663
|
-
default:
|
|
664
|
-
scs_printf("\n");
|
|
665
|
-
}
|
|
666
|
-
for (i = 0; i < LINE_LEN; ++i) {
|
|
667
|
-
scs_printf("-");
|
|
668
|
-
}
|
|
669
|
-
scs_printf("\n");
|
|
670
|
-
#ifdef MATLAB_MEX_FILE
|
|
671
|
-
mexEvalString("drawnow;");
|
|
672
|
-
#endif
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
static scs_int has_converged(ScsWork *w, scs_int iter) {
|
|
676
|
-
scs_float abs_xt_p_x, abs_ctx, abs_bty;
|
|
677
|
-
scs_float nm_s, nm_px, nm_aty, nm_ax;
|
|
678
|
-
scs_float grl, prl, drl;
|
|
679
|
-
scs_float eps_abs = w->stgs->eps_abs;
|
|
680
|
-
scs_float eps_rel = w->stgs->eps_rel;
|
|
681
|
-
scs_float eps_infeas = w->stgs->eps_infeas;
|
|
682
|
-
|
|
683
|
-
ScsResiduals *r = w->r_orig;
|
|
684
|
-
|
|
685
|
-
if (r->tau > 0.) {
|
|
686
|
-
abs_xt_p_x = ABS(r->xt_p_x);
|
|
687
|
-
abs_ctx = ABS(r->ctx);
|
|
688
|
-
abs_bty = ABS(r->bty);
|
|
689
|
-
|
|
690
|
-
nm_s = NORM(w->xys_orig->s, w->d->m);
|
|
691
|
-
nm_px = NORM(r->px, w->d->n);
|
|
692
|
-
nm_aty = NORM(r->aty, w->d->n);
|
|
693
|
-
nm_ax = NORM(r->ax, w->d->m);
|
|
694
|
-
/* xt_p_x, ctx, bty already have tau divided out */
|
|
695
|
-
grl = MAX(MAX(abs_xt_p_x, abs_ctx), abs_bty);
|
|
696
|
-
/* s, ax, px, aty do *not* have tau divided out, so need to divide */
|
|
697
|
-
prl = MAX(MAX(w->nm_b_orig * r->tau, nm_s), nm_ax) / r->tau;
|
|
698
|
-
drl = MAX(MAX(w->nm_c_orig * r->tau, nm_px), nm_aty) / r->tau;
|
|
699
|
-
if (isless(r->res_pri, eps_abs + eps_rel * prl) &&
|
|
700
|
-
isless(r->res_dual, eps_abs + eps_rel * drl) &&
|
|
701
|
-
isless(r->gap, eps_abs + eps_rel * grl)) {
|
|
702
|
-
return SCS_SOLVED;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
if (isless(r->res_unbdd_a, eps_infeas) &&
|
|
706
|
-
isless(r->res_unbdd_p, eps_infeas)) {
|
|
707
|
-
return SCS_UNBOUNDED;
|
|
708
|
-
}
|
|
709
|
-
if (isless(r->res_infeas, eps_infeas)) {
|
|
710
|
-
return SCS_INFEASIBLE;
|
|
711
|
-
}
|
|
712
|
-
return 0;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
#if NO_VALIDATE == 0
|
|
716
|
-
static scs_int validate(const ScsData *d, const ScsCone *k,
|
|
717
|
-
const ScsSettings *stgs) {
|
|
718
|
-
if (d->m <= 0 || d->n <= 0) {
|
|
719
|
-
scs_printf("m and n must both be greater than 0; m = %li, n = %li\n",
|
|
720
|
-
(long)d->m, (long)d->n);
|
|
721
|
-
return -1;
|
|
722
|
-
}
|
|
723
|
-
if (d->m < d->n) {
|
|
724
|
-
/* scs_printf("WARN: m less than n, problem likely degenerate\n"); */
|
|
725
|
-
/* return -1; */
|
|
726
|
-
}
|
|
727
|
-
if (SCS(validate_lin_sys)(d->A, d->P) < 0) {
|
|
728
|
-
scs_printf("invalid linear system input data\n");
|
|
729
|
-
return -1;
|
|
730
|
-
}
|
|
731
|
-
if (SCS(validate_cones)(d, k) < 0) {
|
|
732
|
-
scs_printf("cone validation error\n");
|
|
733
|
-
return -1;
|
|
734
|
-
}
|
|
735
|
-
if (stgs->max_iters <= 0) {
|
|
736
|
-
scs_printf("max_iters must be positive\n");
|
|
737
|
-
return -1;
|
|
738
|
-
}
|
|
739
|
-
if (stgs->eps_abs < 0) {
|
|
740
|
-
scs_printf("eps_abs tolerance must be positive\n");
|
|
741
|
-
return -1;
|
|
742
|
-
}
|
|
743
|
-
if (stgs->eps_rel < 0) {
|
|
744
|
-
scs_printf("eps_rel tolerance must be positive\n");
|
|
745
|
-
return -1;
|
|
746
|
-
}
|
|
747
|
-
if (stgs->eps_infeas < 0) {
|
|
748
|
-
scs_printf("eps_infeas tolerance must be positive\n");
|
|
749
|
-
return -1;
|
|
750
|
-
}
|
|
751
|
-
if (stgs->alpha <= 0 || stgs->alpha >= 2) {
|
|
752
|
-
scs_printf("alpha must be in (0,2)\n");
|
|
753
|
-
return -1;
|
|
754
|
-
}
|
|
755
|
-
if (stgs->rho_x <= 0) {
|
|
756
|
-
scs_printf("rho_x must be positive (1e-3 works well).\n");
|
|
757
|
-
return -1;
|
|
758
|
-
}
|
|
759
|
-
if (stgs->scale <= 0) {
|
|
760
|
-
scs_printf("scale must be positive (1 works well).\n");
|
|
761
|
-
return -1;
|
|
762
|
-
}
|
|
763
|
-
if (stgs->acceleration_interval <= 0) {
|
|
764
|
-
scs_printf("acceleration_interval must be positive (10 works well).\n");
|
|
765
|
-
return -1;
|
|
766
|
-
}
|
|
767
|
-
return 0;
|
|
768
|
-
}
|
|
769
|
-
#endif
|
|
770
|
-
|
|
771
|
-
static ScsResiduals *init_residuals(const ScsData *d) {
|
|
772
|
-
ScsResiduals *r = (ScsResiduals *)scs_calloc(1, sizeof(ScsResiduals));
|
|
773
|
-
r->ax = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
774
|
-
r->ax_s = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
775
|
-
r->ax_s_btau = (scs_float *)scs_calloc(d->m, sizeof(scs_float));
|
|
776
|
-
r->px = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
777
|
-
r->aty = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
778
|
-
r->px_aty_ctau = (scs_float *)scs_calloc(d->n, sizeof(scs_float));
|
|
779
|
-
return r;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
scs_int scs_update(ScsWork *w, scs_float *b, scs_float *c) {
|
|
783
|
-
SCS(timer) update_timer;
|
|
784
|
-
SCS(tic)(&update_timer);
|
|
785
|
-
|
|
786
|
-
if (b) {
|
|
787
|
-
memcpy(w->b_orig, b, w->d->m * sizeof(scs_float));
|
|
788
|
-
memcpy(w->d->b, b, w->d->m * sizeof(scs_float));
|
|
948
|
+
/* not yet converged, take best guess */
|
|
949
|
+
static void set_unfinished(const ScsWork *w, ScsSolution *sol, ScsInfo *info) {
|
|
950
|
+
if (w->r_orig->kap > w->r_orig->tau &&
|
|
951
|
+
(w->r_orig->bty_tau < 0 || w->r_orig->ctx_tau < 0)) {
|
|
952
|
+
if (w->r_orig->bty_tau < 0 &&
|
|
953
|
+
w->r_orig->bty_tau < w->r_orig->ctx_tau) {
|
|
954
|
+
set_infeasible(w, sol, info);
|
|
955
|
+
info->status_val = SCS_INFEASIBLE_INACCURATE;
|
|
956
|
+
} else {
|
|
957
|
+
set_unbounded(w, sol, info);
|
|
958
|
+
info->status_val = SCS_UNBOUNDED_INACCURATE;
|
|
959
|
+
}
|
|
960
|
+
} else if (w->r_orig->tau > 0) {
|
|
961
|
+
set_solved(w, sol, info);
|
|
962
|
+
info->status_val = SCS_SOLVED_INACCURATE;
|
|
789
963
|
} else {
|
|
790
|
-
|
|
964
|
+
scs_printf("ERROR: could not determine problem status.\n");
|
|
965
|
+
info->status_val = SCS_FAILED;
|
|
791
966
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
967
|
+
/* Append inaccurate to the status string */
|
|
968
|
+
if (w->time_limit_reached) {
|
|
969
|
+
strcat(info->status, " (inaccurate - reached time_limit_secs)");
|
|
970
|
+
} else if (info->iter >= w->stgs->max_iters) {
|
|
971
|
+
strcat(info->status, " (inaccurate - reached max_iters)");
|
|
797
972
|
} else {
|
|
798
|
-
|
|
973
|
+
scs_printf("ERROR: should not be in this state (1).\n");
|
|
799
974
|
}
|
|
800
|
-
|
|
975
|
+
}
|
|
801
976
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
977
|
+
/* sets solutions, re-scales by inner prods if infeasible or unbounded */
|
|
978
|
+
static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
979
|
+
scs_int iter) {
|
|
980
|
+
scs_float nm_s, nm_y, sty;
|
|
981
|
+
setx(w, sol);
|
|
982
|
+
sety(w, sol);
|
|
983
|
+
sets(w, sol);
|
|
984
|
+
if (w->stgs->normalize) {
|
|
985
|
+
SCS(un_normalize_sol)(w->scal, sol);
|
|
805
986
|
}
|
|
987
|
+
populate_residual_struct(w, iter);
|
|
806
988
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
989
|
+
nm_s = SCS(norm_inf)(sol->s, w->d->m);
|
|
990
|
+
nm_y = SCS(norm_inf)(sol->y, w->d->m);
|
|
991
|
+
sty = SCS(dot)(sol->s, sol->y, w->d->m);
|
|
992
|
+
|
|
993
|
+
info->setup_time = w->setup_time;
|
|
994
|
+
info->iter = iter;
|
|
995
|
+
info->res_infeas = w->r_orig->res_infeas;
|
|
996
|
+
info->res_unbdd_a = w->r_orig->res_unbdd_a;
|
|
997
|
+
info->res_unbdd_p = w->r_orig->res_unbdd_p;
|
|
998
|
+
info->scale = w->stgs->scale;
|
|
999
|
+
info->scale_updates = w->scale_updates;
|
|
1000
|
+
info->rejected_accel_steps = w->rejected_accel_steps;
|
|
1001
|
+
info->accepted_accel_steps = w->accepted_accel_steps;
|
|
1002
|
+
set_info_aa_stats(info, w->accel);
|
|
1003
|
+
info->comp_slack = ABS(sty);
|
|
1004
|
+
#ifdef SPECTRAL_TIMING_FLAG
|
|
1005
|
+
info->ave_time_matrix_cone_proj = w->cone_work->tot_time_mat_cone_proj / iter;
|
|
1006
|
+
info->ave_time_vector_cone_proj = w->cone_work->tot_time_vec_cone_proj / iter;
|
|
1007
|
+
#endif
|
|
1008
|
+
if (info->comp_slack > 1e-5 * MAX(nm_s, nm_y)) {
|
|
1009
|
+
scs_printf("WARNING - large complementary slackness residual: %f\n",
|
|
1010
|
+
info->comp_slack);
|
|
1011
|
+
}
|
|
1012
|
+
switch (info->status_val) {
|
|
1013
|
+
case SCS_SOLVED:
|
|
1014
|
+
set_solved(w, sol, info);
|
|
1015
|
+
break;
|
|
1016
|
+
case SCS_INFEASIBLE:
|
|
1017
|
+
set_infeasible(w, sol, info);
|
|
1018
|
+
break;
|
|
1019
|
+
case SCS_UNBOUNDED:
|
|
1020
|
+
set_unbounded(w, sol, info);
|
|
1021
|
+
break;
|
|
1022
|
+
case SCS_UNFINISHED: /* When SCS reaches max_iters or time_limit_secs */
|
|
1023
|
+
set_unfinished(w, sol, info);
|
|
1024
|
+
break;
|
|
1025
|
+
default:
|
|
1026
|
+
scs_printf("ERROR: should not be in this state (2).\n");
|
|
1027
|
+
}
|
|
810
1028
|
}
|
|
811
1029
|
|
|
1030
|
+
/* ================ Workspace Init / Scale Updating ================== */
|
|
1031
|
+
|
|
812
1032
|
/* Sets the diag_r vector, given the scale parameters in work */
|
|
813
1033
|
static void set_diag_r(ScsWork *w) {
|
|
814
1034
|
scs_int i;
|
|
815
1035
|
for (i = 0; i < w->d->n; ++i) {
|
|
816
1036
|
w->diag_r[i] = w->stgs->rho_x;
|
|
817
1037
|
}
|
|
1038
|
+
|
|
818
1039
|
/* use cone information to set R_y */
|
|
819
1040
|
SCS(set_r_y)(w->cone_work, w->stgs->scale, &(w->diag_r[w->d->n]));
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1041
|
+
if (w->scale_mults) {
|
|
1042
|
+
/* per-row multipliers act like a per-row scale: R_y_i = base_i / f_i.
|
|
1043
|
+
* Multipliers are kept uniform within cone blocks (enforced at update
|
|
1044
|
+
* time) so no SCS(enforce_cone_boundaries) needed here. */
|
|
1045
|
+
for (i = 0; i < w->d->m; ++i) {
|
|
1046
|
+
w->diag_r[w->d->n + i] /= w->scale_mults[i];
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
w->diag_r[w->d->n + w->d->m] = TAU_FACTOR;
|
|
823
1050
|
}
|
|
824
1051
|
|
|
825
1052
|
static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
@@ -835,19 +1062,35 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
|
835
1062
|
}
|
|
836
1063
|
/* deep copy data */
|
|
837
1064
|
w->d = (ScsData *)scs_calloc(1, sizeof(ScsData));
|
|
838
|
-
SCS(deep_copy_data)(w->d, d)
|
|
1065
|
+
if (!w->d || !SCS(deep_copy_data)(w->d, d)) {
|
|
1066
|
+
scs_printf("ERROR: data copy failure\n");
|
|
1067
|
+
scs_finish(w);
|
|
1068
|
+
return SCS_NULL;
|
|
1069
|
+
}
|
|
839
1070
|
d = SCS_NULL; /* for safety */
|
|
840
1071
|
|
|
841
1072
|
/* deep copy cone */
|
|
842
1073
|
w->k = (ScsCone *)scs_calloc(1, sizeof(ScsCone));
|
|
843
|
-
SCS(deep_copy_cone)(w->k, k)
|
|
1074
|
+
if (!w->k || !SCS(deep_copy_cone)(w->k, k)) {
|
|
1075
|
+
scs_printf("ERROR: cone copy failure\n");
|
|
1076
|
+
scs_finish(w);
|
|
1077
|
+
return SCS_NULL;
|
|
1078
|
+
}
|
|
844
1079
|
k = SCS_NULL; /* for safety */
|
|
845
1080
|
|
|
846
1081
|
/* deep copy settings */
|
|
847
1082
|
w->stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings));
|
|
848
|
-
SCS(deep_copy_stgs)(w->stgs, stgs)
|
|
1083
|
+
if (!w->stgs || !SCS(deep_copy_stgs)(w->stgs, stgs)) {
|
|
1084
|
+
scs_printf("ERROR: settings copy failure\n");
|
|
1085
|
+
scs_finish(w);
|
|
1086
|
+
return SCS_NULL;
|
|
1087
|
+
}
|
|
849
1088
|
stgs = SCS_NULL; /* for safety */
|
|
850
1089
|
|
|
1090
|
+
/* record the original (pre-normalization) magnitude of A for the
|
|
1091
|
+
* data-scale-invariant certificate checks */
|
|
1092
|
+
w->nm_a_orig = SCS(norm_inf)(w->d->A->x, w->d->A->p[w->d->A->n]);
|
|
1093
|
+
|
|
851
1094
|
/* allocate workspace: */
|
|
852
1095
|
w->u = (scs_float *)scs_calloc(l, sizeof(scs_float));
|
|
853
1096
|
w->u_t = (scs_float *)scs_calloc(l, sizeof(scs_float));
|
|
@@ -860,32 +1103,89 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
|
860
1103
|
w->diag_r = (scs_float *)scs_calloc(l, sizeof(scs_float));
|
|
861
1104
|
/* x,y,s struct */
|
|
862
1105
|
w->xys_orig = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
|
|
1106
|
+
if (!w->xys_orig) {
|
|
1107
|
+
scs_printf("ERROR: work memory allocation failure\n");
|
|
1108
|
+
scs_finish(w);
|
|
1109
|
+
return SCS_NULL;
|
|
1110
|
+
}
|
|
863
1111
|
w->xys_orig->x = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
|
|
864
1112
|
w->xys_orig->s = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
865
1113
|
w->xys_orig->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
866
1114
|
w->r_orig = init_residuals(w->d);
|
|
867
1115
|
w->b_orig = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
868
1116
|
w->c_orig = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
|
|
1117
|
+
#ifdef USE_SPECTRAL_CONES
|
|
1118
|
+
if (w->stgs->adaptive_diag_scale &&
|
|
1119
|
+
(w->k->dsize || w->k->nucsize || w->k->ell1_size || w->k->sl_size)) {
|
|
1120
|
+
/* The spectral-cone projections are iterative inner solvers with
|
|
1121
|
+
* warm-start state that does not currently tolerate mid-solve metric
|
|
1122
|
+
* changes (observed as dual iterates leaving the cone). Disable
|
|
1123
|
+
* dynamic diagonal rescaling on such problems until the inner
|
|
1124
|
+
* solvers are made metric-change aware. */
|
|
1125
|
+
w->stgs->adaptive_diag_scale = 0;
|
|
1126
|
+
}
|
|
1127
|
+
#endif
|
|
1128
|
+
if (w->stgs->adaptive_diag_scale) {
|
|
1129
|
+
if (!w->stgs->adaptive_scale) {
|
|
1130
|
+
/* silently disable: diag scaling rides the adaptive-scale update
|
|
1131
|
+
* machinery, and since it defaults on, users who only turn off
|
|
1132
|
+
* adaptive_scale should not see a warning */
|
|
1133
|
+
w->stgs->adaptive_diag_scale = 0;
|
|
1134
|
+
} else {
|
|
1135
|
+
scs_int j;
|
|
1136
|
+
w->scale_mults = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
1137
|
+
if (!w->scale_mults) {
|
|
1138
|
+
scs_printf("ERROR: work memory allocation failure\n");
|
|
1139
|
+
scs_finish(w);
|
|
1140
|
+
return SCS_NULL;
|
|
1141
|
+
}
|
|
1142
|
+
for (j = 0; j < w->d->m; ++j) {
|
|
1143
|
+
w->scale_mults[j] = 1.0;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
869
1147
|
|
|
870
|
-
if (!w->
|
|
1148
|
+
if (!w->u || !w->u_t || !w->v || !w->v_prev || !w->rsk || !w->h || !w->g ||
|
|
1149
|
+
!w->lin_sys_warm_start || !w->diag_r || !w->xys_orig ||
|
|
1150
|
+
!w->xys_orig->x || !w->xys_orig->s || !w->xys_orig->y || !w->r_orig ||
|
|
1151
|
+
!w->b_orig || !w->c_orig) {
|
|
871
1152
|
scs_printf("ERROR: work memory allocation failure\n");
|
|
1153
|
+
scs_finish(w);
|
|
872
1154
|
return SCS_NULL;
|
|
873
1155
|
}
|
|
874
1156
|
|
|
875
1157
|
if (!(w->cone_work = SCS(init_cone)(w->k, w->d->m))) {
|
|
876
1158
|
scs_printf("ERROR: init_cone failure\n");
|
|
1159
|
+
scs_finish(w);
|
|
877
1160
|
return SCS_NULL;
|
|
878
1161
|
}
|
|
879
1162
|
set_diag_r(w);
|
|
880
1163
|
|
|
881
1164
|
if (w->stgs->normalize) {
|
|
882
1165
|
w->xys_normalized = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution));
|
|
1166
|
+
if (!w->xys_normalized) {
|
|
1167
|
+
scs_printf("ERROR: normalized work memory allocation failure\n");
|
|
1168
|
+
scs_finish(w);
|
|
1169
|
+
return SCS_NULL;
|
|
1170
|
+
}
|
|
883
1171
|
w->xys_normalized->x = (scs_float *)scs_calloc(w->d->n, sizeof(scs_float));
|
|
884
1172
|
w->xys_normalized->s = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
885
1173
|
w->xys_normalized->y = (scs_float *)scs_calloc(w->d->m, sizeof(scs_float));
|
|
886
1174
|
w->r_normalized = init_residuals(w->d);
|
|
1175
|
+
if (!w->xys_normalized || !w->xys_normalized->x || !w->xys_normalized->s ||
|
|
1176
|
+
!w->xys_normalized->y || !w->r_normalized) {
|
|
1177
|
+
scs_printf("ERROR: normalized work memory allocation failure\n");
|
|
1178
|
+
scs_finish(w);
|
|
1179
|
+
return SCS_NULL;
|
|
1180
|
+
}
|
|
887
1181
|
/* this allocates memory that must be freed */
|
|
888
|
-
w->scal = SCS(normalize_a_p)(w->d->P, w->d->A, w->
|
|
1182
|
+
w->scal = SCS(normalize_a_p)(w->d->P, w->d->A, w->d->b, w->d->c,
|
|
1183
|
+
w->cone_work);
|
|
1184
|
+
if (!w->scal) {
|
|
1185
|
+
scs_printf("ERROR: normalize_a_p failure\n");
|
|
1186
|
+
scs_finish(w);
|
|
1187
|
+
return SCS_NULL;
|
|
1188
|
+
}
|
|
889
1189
|
} else {
|
|
890
1190
|
w->xys_normalized = w->xys_orig;
|
|
891
1191
|
w->r_normalized = w->r_orig;
|
|
@@ -896,17 +1196,20 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
|
896
1196
|
|
|
897
1197
|
if (!(w->p = scs_init_lin_sys_work(w->d->A, w->d->P, w->diag_r))) {
|
|
898
1198
|
scs_printf("ERROR: init_lin_sys_work failure\n");
|
|
1199
|
+
scs_finish(w);
|
|
899
1200
|
return SCS_NULL;
|
|
900
1201
|
}
|
|
901
1202
|
if (w->stgs->acceleration_lookback) {
|
|
902
|
-
/*
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
w->stgs->acceleration_lookback
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1203
|
+
/* min_len = mem preserves the previous FILL_MEMORY_BEFORE_SOLVE
|
|
1204
|
+
gate — AA holds off until the sliding window is full. */
|
|
1205
|
+
if (!(w->accel = aa_init(l, w->stgs->acceleration_lookback,
|
|
1206
|
+
w->stgs->acceleration_lookback,
|
|
1207
|
+
w->stgs->acceleration_type_1,
|
|
1208
|
+
w->stgs->acceleration_regularization,
|
|
1209
|
+
w->stgs->acceleration_relaxation,
|
|
1210
|
+
AA_SAFEGUARD_FACTOR,
|
|
1211
|
+
AA_MAX_WEIGHT_NORM, AA_IR_MAX_STEPS,
|
|
1212
|
+
VERBOSITY))) {
|
|
910
1213
|
if (w->stgs->verbose) {
|
|
911
1214
|
scs_printf("WARN: aa_init returned NULL, no acceleration applied.\n");
|
|
912
1215
|
}
|
|
@@ -918,11 +1221,15 @@ static ScsWork *init_work(const ScsData *d, const ScsCone *k,
|
|
|
918
1221
|
}
|
|
919
1222
|
|
|
920
1223
|
static void update_work_cache(ScsWork *w) {
|
|
921
|
-
/* g = (I + M)^{-1}
|
|
922
|
-
|
|
923
|
-
|
|
1224
|
+
/* g = (I + M)^{-1} [c; -b]
|
|
1225
|
+
* Build g = [c; -b] directly in one pass, avoiding a separate memcpy of h
|
|
1226
|
+
* followed by a negate pass over g[n:]. */
|
|
1227
|
+
scs_int i, n = w->d->n, m = w->d->m;
|
|
1228
|
+
memcpy(w->g, w->d->c, n * sizeof(scs_float));
|
|
1229
|
+
for (i = 0; i < m; ++i) {
|
|
1230
|
+
w->g[n + i] = -w->d->b[i];
|
|
1231
|
+
}
|
|
924
1232
|
scs_solve_lin_sys(w->p, w->g, SCS_NULL, CG_BEST_TOL);
|
|
925
|
-
return;
|
|
926
1233
|
}
|
|
927
1234
|
|
|
928
1235
|
/* Reset quantities specific to current solve */
|
|
@@ -932,6 +1239,9 @@ static void reset_tracking(ScsWork *w) {
|
|
|
932
1239
|
w->n_log_scale_factor = 0;
|
|
933
1240
|
w->scale_updates = 0;
|
|
934
1241
|
w->time_limit_reached = 0;
|
|
1242
|
+
/* certificate persistence */
|
|
1243
|
+
w->infeas_cert_streak = 0;
|
|
1244
|
+
w->unbdd_cert_streak = 0;
|
|
935
1245
|
/* Acceleration */
|
|
936
1246
|
w->rejected_accel_steps = 0;
|
|
937
1247
|
w->accepted_accel_steps = 0;
|
|
@@ -950,19 +1260,49 @@ static scs_int update_work(ScsWork *w, ScsSolution *sol) {
|
|
|
950
1260
|
cold_start_vars(w);
|
|
951
1261
|
}
|
|
952
1262
|
|
|
953
|
-
/* h = [c;b] */
|
|
954
|
-
memcpy(w->h, w->d->c, w->d->n * sizeof(scs_float));
|
|
955
|
-
memcpy(&(w->h[w->d->n]), w->d->b, w->d->m * sizeof(scs_float));
|
|
956
1263
|
update_work_cache(w);
|
|
957
1264
|
return 0;
|
|
958
1265
|
}
|
|
959
1266
|
|
|
960
1267
|
/* will update if the factor is outside of range */
|
|
961
|
-
scs_int should_update_r(scs_float factor
|
|
1268
|
+
static scs_int should_update_r(scs_float factor) {
|
|
962
1269
|
return (factor > SQRTF(10.) || factor < 1. / SQRTF(10.));
|
|
963
1270
|
}
|
|
964
1271
|
|
|
965
|
-
|
|
1272
|
+
/* Relative primal residual of constraint row i in the normalized space
|
|
1273
|
+
* (where diag_r acts). All quantities carry tau consistently. */
|
|
1274
|
+
/* Relative primal residual of row i, used as the per-row profile.
|
|
1275
|
+
*
|
|
1276
|
+
* The denominator carries a floor at DEN_FLOOR_FRAC of the block's rms
|
|
1277
|
+
* denominator. Without it, a row whose b_i is zero divides only by
|
|
1278
|
+
* max(|(Ax)_i|, |s_i|) -- both of which shrink as the iterates converge
|
|
1279
|
+
* -- so its ratio inflates even when the row is converging perfectly
|
|
1280
|
+
* well, and the profile ends up reporting which terms a row happens to
|
|
1281
|
+
* contain rather than how it is converging. Rows with denominators of
|
|
1282
|
+
* ordinary size are unaffected; only the collapsing ones are floored. */
|
|
1283
|
+
static scs_float row_rel_res(const ScsWork *w, scs_int i) {
|
|
1284
|
+
const ScsResiduals *r = w->r_normalized;
|
|
1285
|
+
scs_float den = MAX(ABS(r->ax[i]), ABS(w->xys_normalized->s[i]));
|
|
1286
|
+
den = MAX(den, ABS(w->d->b[i]) * r->tau);
|
|
1287
|
+
den = MAX(den, w->den_floor);
|
|
1288
|
+
den = MAX(den, _DIV_EPS_TOL);
|
|
1289
|
+
return MAX(ABS(r->ax_s_btau[i]), _DIV_EPS_TOL) / den;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/* Recompute the row-profile denominator floor from the current iterate. */
|
|
1293
|
+
static void set_den_floor(ScsWork *w) {
|
|
1294
|
+
const ScsResiduals *r = w->r_normalized;
|
|
1295
|
+
scs_int i, m = w->d->m;
|
|
1296
|
+
scs_float s2 = 0.;
|
|
1297
|
+
for (i = 0; i < m; ++i) {
|
|
1298
|
+
scs_float d = MAX(ABS(r->ax[i]), ABS(w->xys_normalized->s[i]));
|
|
1299
|
+
d = MAX(d, ABS(w->d->b[i]) * r->tau);
|
|
1300
|
+
s2 += d * d;
|
|
1301
|
+
}
|
|
1302
|
+
w->den_floor = DEN_FLOOR_FRAC * SQRTF(s2 / (scs_float)MAX(m, 1));
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
static scs_int update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
966
1306
|
scs_int i;
|
|
967
1307
|
scs_float factor, new_scale, relative_res_pri, relative_res_dual;
|
|
968
1308
|
scs_float denom_pri, denom_dual;
|
|
@@ -977,6 +1317,9 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
|
977
1317
|
scs_float nm_ax_s_btau = SCALE_NORM(r->ax_s_btau, w->d->m);
|
|
978
1318
|
|
|
979
1319
|
scs_int iters_since_last_update = iter - w->last_scale_update_iter;
|
|
1320
|
+
if (w->scale_mults) {
|
|
1321
|
+
set_den_floor(w);
|
|
1322
|
+
}
|
|
980
1323
|
/* ||Ax + s - b * tau|| */
|
|
981
1324
|
denom_pri = MAX(nm_ax, nm_s);
|
|
982
1325
|
denom_pri = MAX(denom_pri, w->nm_b_orig * r->tau);
|
|
@@ -987,6 +1330,9 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
|
987
1330
|
relative_res_dual = SAFEDIV_POS(nm_px_aty_ctau, denom_dual);
|
|
988
1331
|
|
|
989
1332
|
/* higher scale makes res_pri go down faster, so increase if res_pri larger */
|
|
1333
|
+
/* clamp to avoid log(0) which would NaN-poison sum_log_scale_factor */
|
|
1334
|
+
relative_res_pri = MAX(relative_res_pri, _DIV_EPS_TOL);
|
|
1335
|
+
relative_res_dual = MAX(relative_res_dual, _DIV_EPS_TOL);
|
|
990
1336
|
w->sum_log_scale_factor += log(relative_res_pri) - log(relative_res_dual);
|
|
991
1337
|
w->n_log_scale_factor++;
|
|
992
1338
|
|
|
@@ -996,25 +1342,66 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
|
996
1342
|
|
|
997
1343
|
/* need at least RESCALING_MIN_ITERS since last update */
|
|
998
1344
|
if (iters_since_last_update < RESCALING_MIN_ITERS) {
|
|
999
|
-
return;
|
|
1345
|
+
return 0;
|
|
1000
1346
|
}
|
|
1001
1347
|
new_scale =
|
|
1002
1348
|
MIN(MAX(w->stgs->scale * factor, MIN_SCALE_VALUE), MAX_SCALE_VALUE);
|
|
1003
|
-
|
|
1004
|
-
|
|
1349
|
+
scs_int apply_scalar =
|
|
1350
|
+
(new_scale != w->stgs->scale) && should_update_r(factor);
|
|
1351
|
+
scs_int apply_diag = 0;
|
|
1352
|
+
scs_float log_g = 0., g, step, newf, change;
|
|
1353
|
+
if (w->stgs->adaptive_diag_scale) {
|
|
1354
|
+
/* residual profiles: adapt when the scalar updates, or when some
|
|
1355
|
+
* row's/column's damped *clamped* step exceeds the update threshold
|
|
1356
|
+
* (a railed scalar must not freeze the diagonal; a railed multiplier
|
|
1357
|
+
* must not keep triggering updates it cannot take). */
|
|
1358
|
+
scs_float drift = 1.0;
|
|
1359
|
+
for (i = 0; i < w->d->m; ++i) {
|
|
1360
|
+
log_g += log(row_rel_res(w, i));
|
|
1361
|
+
}
|
|
1362
|
+
log_g /= (scs_float)w->d->m;
|
|
1363
|
+
g = exp(log_g);
|
|
1364
|
+
for (i = 0; i < w->d->m; ++i) {
|
|
1365
|
+
step = POWF(row_rel_res(w, i) / g, DIAG_SCALE_DAMP);
|
|
1366
|
+
newf = MIN(MAX(w->scale_mults[i] * step, DIAG_SCALE_MULT_MIN),
|
|
1367
|
+
DIAG_SCALE_MULT_MAX);
|
|
1368
|
+
change = newf / w->scale_mults[i];
|
|
1369
|
+
drift = MAX(drift, MAX(change, 1. / change));
|
|
1370
|
+
}
|
|
1371
|
+
apply_diag = apply_scalar || should_update_r(drift);
|
|
1005
1372
|
}
|
|
1006
|
-
if (
|
|
1373
|
+
if (!apply_scalar && !apply_diag) {
|
|
1374
|
+
return 0;
|
|
1375
|
+
}
|
|
1376
|
+
{
|
|
1377
|
+
scs_int linsys_status;
|
|
1007
1378
|
w->scale_updates++;
|
|
1008
1379
|
w->sum_log_scale_factor = 0;
|
|
1009
1380
|
w->n_log_scale_factor = 0;
|
|
1010
1381
|
w->last_scale_update_iter = iter;
|
|
1011
|
-
|
|
1382
|
+
if (apply_scalar) {
|
|
1383
|
+
w->stgs->scale = new_scale;
|
|
1384
|
+
}
|
|
1385
|
+
if (apply_diag) {
|
|
1386
|
+
g = exp(log_g);
|
|
1387
|
+
for (i = 0; i < w->d->m; ++i) {
|
|
1388
|
+
step = POWF(row_rel_res(w, i) / g, DIAG_SCALE_DAMP);
|
|
1389
|
+
w->scale_mults[i] = MIN(
|
|
1390
|
+
MAX(w->scale_mults[i] * step, DIAG_SCALE_MULT_MIN),
|
|
1391
|
+
DIAG_SCALE_MULT_MAX);
|
|
1392
|
+
}
|
|
1393
|
+
/* non-polyhedral cone blocks must share a single metric entry */
|
|
1394
|
+
SCS(enforce_cone_boundaries)(w->cone_work, w->scale_mults, &SCS(mean), 1);
|
|
1395
|
+
}
|
|
1012
1396
|
|
|
1013
1397
|
/* update diag r vector */
|
|
1014
1398
|
set_diag_r(w);
|
|
1015
1399
|
|
|
1016
1400
|
/* update linear systems */
|
|
1017
|
-
scs_update_lin_sys_diag_r(w->p, w->diag_r);
|
|
1401
|
+
linsys_status = scs_update_lin_sys_diag_r(w->p, w->diag_r);
|
|
1402
|
+
if (linsys_status < 0) {
|
|
1403
|
+
return linsys_status;
|
|
1404
|
+
}
|
|
1018
1405
|
|
|
1019
1406
|
/* update pre-solved quantities */
|
|
1020
1407
|
update_work_cache(w);
|
|
@@ -1031,12 +1418,91 @@ static void update_scale(ScsWork *w, const ScsCone *k, scs_int iter) {
|
|
|
1031
1418
|
w->v[i] = w->rsk[i] / w->diag_r[i] + 2 * w->u_t[i] - w->u[i];
|
|
1032
1419
|
}
|
|
1033
1420
|
}
|
|
1421
|
+
return 0;
|
|
1034
1422
|
}
|
|
1035
1423
|
|
|
1036
|
-
/*
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1424
|
+
/* ========================== Public API ============================= */
|
|
1425
|
+
|
|
1426
|
+
ScsWork *scs_init(const ScsData *d, const ScsCone *k, const ScsSettings *stgs) {
|
|
1427
|
+
ScsWork *w;
|
|
1428
|
+
SCS(timer) init_timer;
|
|
1429
|
+
if (!d || !k || !stgs) {
|
|
1430
|
+
scs_printf("ERROR: Missing ScsData, ScsCone, or ScsSettings input\n");
|
|
1431
|
+
return SCS_NULL;
|
|
1432
|
+
}
|
|
1433
|
+
#ifdef SCS_MKL
|
|
1434
|
+
if (scs_init_mkl_runtime() != 0) {
|
|
1435
|
+
scs_printf("ERROR: MKL runtime initialization failed before solver setup. "
|
|
1436
|
+
"See the MKL message above for the specific cause.\n");
|
|
1437
|
+
return SCS_NULL;
|
|
1438
|
+
}
|
|
1439
|
+
#endif
|
|
1440
|
+
#if NO_VALIDATE == 0
|
|
1441
|
+
if (validate(d, k, stgs) < 0) {
|
|
1442
|
+
scs_printf("ERROR: Validation returned failure\n");
|
|
1443
|
+
return SCS_NULL;
|
|
1444
|
+
}
|
|
1445
|
+
#endif
|
|
1446
|
+
scs_start_interrupt_listener();
|
|
1447
|
+
#if VERBOSITY > 0
|
|
1448
|
+
scs_printf("size of scs_int = %lu, size of scs_float = %lu\n",
|
|
1449
|
+
(unsigned long)sizeof(scs_int), (unsigned long)sizeof(scs_float));
|
|
1450
|
+
#endif
|
|
1451
|
+
SCS(tic)(&init_timer);
|
|
1452
|
+
if (stgs->write_data_filename) {
|
|
1453
|
+
scs_printf("Writing raw problem data to %s\n", stgs->write_data_filename);
|
|
1454
|
+
SCS(write_data)(d, k, stgs);
|
|
1455
|
+
}
|
|
1456
|
+
if (stgs->log_csv_filename) {
|
|
1457
|
+
scs_printf("Logging run data to %s\n", stgs->log_csv_filename);
|
|
1458
|
+
/* logging done every iteration */
|
|
1459
|
+
}
|
|
1460
|
+
w = init_work(d, k, stgs);
|
|
1461
|
+
if (w) {
|
|
1462
|
+
w->setup_time = SCS(tocq)(&init_timer);
|
|
1463
|
+
}
|
|
1464
|
+
scs_end_interrupt_listener();
|
|
1465
|
+
return w;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
scs_int scs_update(ScsWork *w, scs_float *b, scs_float *c) {
|
|
1469
|
+
SCS(timer) update_timer;
|
|
1470
|
+
SCS(tic)(&update_timer);
|
|
1471
|
+
|
|
1472
|
+
if (b) {
|
|
1473
|
+
if (w->b_orig != b) {
|
|
1474
|
+
memcpy(w->b_orig, b, w->d->m * sizeof(scs_float));
|
|
1475
|
+
}
|
|
1476
|
+
if (w->d->b != b) {
|
|
1477
|
+
memcpy(w->d->b, b, w->d->m * sizeof(scs_float));
|
|
1478
|
+
}
|
|
1479
|
+
w->nm_b_orig = NORM(w->b_orig, w->d->m);
|
|
1480
|
+
} else {
|
|
1481
|
+
/* b_orig unchanged so no need to recompute the norm */
|
|
1482
|
+
memcpy(w->d->b, w->b_orig, w->d->m * sizeof(scs_float));
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if (c) {
|
|
1486
|
+
if (w->c_orig != c) {
|
|
1487
|
+
memcpy(w->c_orig, c, w->d->n * sizeof(scs_float));
|
|
1488
|
+
}
|
|
1489
|
+
if (w->d->c != c) {
|
|
1490
|
+
memcpy(w->d->c, c, w->d->n * sizeof(scs_float));
|
|
1491
|
+
}
|
|
1492
|
+
w->nm_c_orig = NORM(w->c_orig, w->d->n);
|
|
1493
|
+
} else {
|
|
1494
|
+
/* c_orig unchanged so no need to recompute the norm */
|
|
1495
|
+
memcpy(w->d->c, w->c_orig, w->d->n * sizeof(scs_float));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/* normalize */
|
|
1499
|
+
if (w->scal) {
|
|
1500
|
+
SCS(normalize_b_c)(w->scal, w->d->b, w->d->c);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/* override setup time with update time, since the update is the 'setup' */
|
|
1504
|
+
w->setup_time = SCS(tocq)(&update_timer);
|
|
1505
|
+
return 0;
|
|
1040
1506
|
}
|
|
1041
1507
|
|
|
1042
1508
|
scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
@@ -1061,6 +1527,7 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
|
1061
1527
|
strcpy(info->lin_sys_solver, scs_get_lin_sys_method());
|
|
1062
1528
|
info->status_val = SCS_UNFINISHED; /* not yet converged */
|
|
1063
1529
|
update_work(w, sol);
|
|
1530
|
+
SCS(open_csv_log_file)(w);
|
|
1064
1531
|
|
|
1065
1532
|
if (w->stgs->verbose) {
|
|
1066
1533
|
print_header(w, k);
|
|
@@ -1085,8 +1552,10 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
|
1085
1552
|
normalize_v(w->v, l);
|
|
1086
1553
|
}
|
|
1087
1554
|
|
|
1088
|
-
/* store v_prev = v
|
|
1089
|
-
|
|
1555
|
+
/* store v_prev = v for AA safeguard; skip when acceleration is off */
|
|
1556
|
+
if (w->accel) {
|
|
1557
|
+
memcpy(w->v_prev, w->v, l * sizeof(scs_float));
|
|
1558
|
+
}
|
|
1090
1559
|
|
|
1091
1560
|
/******************* linear system solve ********************/
|
|
1092
1561
|
SCS(tic)(&lin_sys_timer);
|
|
@@ -1133,7 +1602,10 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
|
1133
1602
|
|
|
1134
1603
|
/* If residuals are fresh then maybe compute new scale. */
|
|
1135
1604
|
if (w->stgs->adaptive_scale && i == w->r_orig->last_iter) {
|
|
1136
|
-
update_scale(w, k, i)
|
|
1605
|
+
if (update_scale(w, k, i) < 0) {
|
|
1606
|
+
return failure(w, w->d->m, w->d->n, sol, info, SCS_FAILED,
|
|
1607
|
+
"error in update_scale", "failure");
|
|
1608
|
+
}
|
|
1137
1609
|
}
|
|
1138
1610
|
|
|
1139
1611
|
/****************** dual variable step **********************/
|
|
@@ -1156,17 +1628,17 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
|
1156
1628
|
}
|
|
1157
1629
|
|
|
1158
1630
|
/* Log *after* updating scale so residual recalc does not affect alg */
|
|
1159
|
-
if (w->
|
|
1631
|
+
if (w->log_csv_fout) {
|
|
1160
1632
|
/* calc residuals every iter if logging to csv */
|
|
1161
1633
|
populate_residual_struct(w, i);
|
|
1162
|
-
SCS(log_data_to_csv)(k,
|
|
1634
|
+
SCS(log_data_to_csv)(k, w, i, &solve_timer);
|
|
1163
1635
|
}
|
|
1164
1636
|
}
|
|
1165
1637
|
|
|
1166
1638
|
/* Final logging after full run */
|
|
1167
|
-
if (w->
|
|
1639
|
+
if (w->log_csv_fout) {
|
|
1168
1640
|
populate_residual_struct(w, i);
|
|
1169
|
-
SCS(log_data_to_csv)(k,
|
|
1641
|
+
SCS(log_data_to_csv)(k, w, i, &solve_timer);
|
|
1170
1642
|
}
|
|
1171
1643
|
|
|
1172
1644
|
if (w->stgs->verbose) {
|
|
@@ -1187,56 +1659,61 @@ scs_int scs_solve(ScsWork *w, ScsSolution *sol, ScsInfo *info,
|
|
|
1187
1659
|
print_footer(info);
|
|
1188
1660
|
}
|
|
1189
1661
|
|
|
1662
|
+
SCS(close_csv_log_file)(w);
|
|
1190
1663
|
scs_end_interrupt_listener();
|
|
1191
1664
|
return info->status_val;
|
|
1192
1665
|
}
|
|
1193
1666
|
|
|
1194
1667
|
void scs_finish(ScsWork *w) {
|
|
1195
1668
|
if (w) {
|
|
1196
|
-
SCS(
|
|
1669
|
+
SCS(close_csv_log_file)(w);
|
|
1670
|
+
if (w->cone_work) {
|
|
1671
|
+
SCS(finish_cone)(w->cone_work);
|
|
1672
|
+
}
|
|
1197
1673
|
if (w->p) {
|
|
1198
1674
|
scs_free_lin_sys_work(w->p);
|
|
1199
1675
|
}
|
|
1200
1676
|
if (w->accel) {
|
|
1201
1677
|
aa_finish(w->accel);
|
|
1202
1678
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
w->
|
|
1679
|
+
scs_free(w->u);
|
|
1680
|
+
scs_free(w->u_t);
|
|
1681
|
+
scs_free(w->v);
|
|
1682
|
+
scs_free(w->v_prev);
|
|
1683
|
+
scs_free(w->rsk);
|
|
1684
|
+
scs_free(w->h);
|
|
1685
|
+
scs_free(w->g);
|
|
1686
|
+
scs_free(w->b_orig);
|
|
1687
|
+
scs_free(w->c_orig);
|
|
1688
|
+
scs_free(w->lin_sys_warm_start);
|
|
1689
|
+
scs_free(w->diag_r);
|
|
1690
|
+
scs_free(w->scale_mults);
|
|
1691
|
+
SCS(free_sol)(w->xys_orig);
|
|
1692
|
+
if (w->scal) {
|
|
1693
|
+
scs_free(w->scal->D);
|
|
1694
|
+
scs_free(w->scal->E);
|
|
1695
|
+
scs_free(w->scal);
|
|
1696
|
+
}
|
|
1697
|
+
free_residuals(w->r_orig);
|
|
1698
|
+
if (w->stgs && w->stgs->normalize) {
|
|
1699
|
+
SCS(free_sol)(w->xys_normalized);
|
|
1700
|
+
free_residuals(w->r_normalized);
|
|
1701
|
+
}
|
|
1702
|
+
if (w->stgs) {
|
|
1703
|
+
if (w->stgs->log_csv_filename)
|
|
1704
|
+
scs_free((char *)w->stgs->log_csv_filename);
|
|
1705
|
+
if (w->stgs->write_data_filename)
|
|
1706
|
+
scs_free((char *)w->stgs->write_data_filename);
|
|
1707
|
+
scs_free(w->stgs);
|
|
1708
|
+
}
|
|
1709
|
+
if (w->k) { /* deep copy */
|
|
1710
|
+
SCS(free_cone)(w->k);
|
|
1711
|
+
}
|
|
1712
|
+
if (w->d) { /* deep copy */
|
|
1713
|
+
SCS(free_data)(w->d);
|
|
1714
|
+
}
|
|
1715
|
+
scs_free(w);
|
|
1237
1716
|
}
|
|
1238
|
-
scs_end_interrupt_listener();
|
|
1239
|
-
return w;
|
|
1240
1717
|
}
|
|
1241
1718
|
|
|
1242
1719
|
/* this just calls scs_init, scs_solve, and scs_finish */
|