|
| 1 | +#include "glbopts.h" |
| 2 | +#include "linalg.h" |
| 3 | +#include "minunit.h" |
| 4 | +#include "problem_utils.h" |
| 5 | +#include "scs.h" |
| 6 | +#include "scs_matrix.h" |
| 7 | +#include "util.h" |
| 8 | + |
| 9 | +/* Partial warm start tests on the qafiro QP (n=32, m=60). |
| 10 | + * Tests various partial initialization patterns. |
| 11 | + */ |
| 12 | +static const char *partial_warm_start_qafiro(void) { |
| 13 | + ScsCone *k = (ScsCone *)scs_calloc(1, sizeof(ScsCone)); |
| 14 | + ScsData *d = (ScsData *)scs_calloc(1, sizeof(ScsData)); |
| 15 | + ScsSettings *stgs = (ScsSettings *)scs_calloc(1, sizeof(ScsSettings)); |
| 16 | + ScsSolution *sol = (ScsSolution *)scs_calloc(1, sizeof(ScsSolution)); |
| 17 | + ScsInfo info = {0}; |
| 18 | + scs_int exitflag; |
| 19 | + scs_int cold_iters; |
| 20 | + scs_int i; |
| 21 | + scs_float perr, derr; |
| 22 | + scs_int success; |
| 23 | + const char *fail; |
| 24 | + |
| 25 | + /* saved solution for partial warm starts */ |
| 26 | + scs_float saved_x[32]; |
| 27 | + scs_float saved_y[60]; |
| 28 | + scs_float saved_s[60]; |
| 29 | + |
| 30 | + /* data (same as qafiro_tiny_qp) */ |
| 31 | + scs_float Ax[] = { |
| 32 | + -1., -1.06, -1., -0.301, -1., 1., 1., -1., 1., |
| 33 | + 1., -1., 1., -1., -1., -1., -1.06, -1., -0.301, |
| 34 | + -1., -1., -1.06, -1., -0.313, -1., -1., -0.96, -1., |
| 35 | + -0.313, -1., -1., -0.86, -1., -0.326, -1., 1., -2.364, |
| 36 | + -1., 1., -2.386, -1., 1., -2.408, -1., 1., -2.429, |
| 37 | + -1., 1., -1.4, -1., 1., 1., -1., 1., -1., |
| 38 | + -1., -0.43, -1., -1., -0.109, -1., 1., 1., -1., |
| 39 | + 1., 1., -1., 1., 1., -1., 1., -1., -1., |
| 40 | + 1., -0.43, -1., -0.109, -1., 1., -0.43, -1., -0.108, |
| 41 | + -1., 1., -0.39, -1., -0.108, -1., 1., -0.37, -1., |
| 42 | + -0.107, -1., 1., -2.191, -1., 1., -2.219, -1., 1., |
| 43 | + -2.249, -1., 1., -2.279, -1., -1., -1.4, -1., 1., |
| 44 | + 1., -1., 1., -1., -1., 1., -1.}; |
| 45 | + scs_int Ai[] = {0, 1, 16, 24, 28, 0, 15, 29, 0, 22, 30, 1, 26, 31, 4, |
| 46 | + 5, 12, 25, 32, 4, 5, 11, 25, 33, 4, 5, 9, 25, 34, 4, |
| 47 | + 5, 10, 25, 35, 12, 21, 36, 11, 21, 37, 9, 21, 38, 10, 21, |
| 48 | + 39, 4, 15, 40, 4, 23, 41, 5, 27, 42, 6, 7, 13, 22, 43, |
| 49 | + 7, 14, 44, 7, 24, 45, 7, 21, 46, 6, 26, 47, 2, 3, 17, |
| 50 | + 23, 48, 2, 3, 18, 23, 49, 2, 3, 19, 23, 50, 2, 3, 20, |
| 51 | + 23, 51, 17, 21, 52, 18, 21, 53, 19, 21, 54, 20, 21, 55, 2, |
| 52 | + 14, 56, 2, 25, 57, 3, 27, 58, 2, 59}; |
| 53 | + scs_int Ap[] = {0, 5, 8, 11, 14, 19, 24, 29, 34, 37, 40, |
| 54 | + 43, 46, 49, 52, 55, 60, 63, 66, 69, 72, 77, |
| 55 | + 82, 87, 92, 95, 98, 101, 104, 107, 110, 113, 115}; |
| 56 | + |
| 57 | + scs_float Px[] = {10., 1., 10., 1., 1., 10.}; |
| 58 | + scs_int Pi[] = {0, 0, 1, 0, 1, 2}; |
| 59 | + scs_int Pp[] = {0, 1, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 60 | + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}; |
| 61 | + |
| 62 | + scs_float b[] = { |
| 63 | + 0.00000000e+00, 0.00000000e+00, 4.40000000e+01, -2.22044605e-16, |
| 64 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 65 | + 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 66 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 67 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 68 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 69 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 70 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 71 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 72 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 73 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 74 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 75 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 76 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, |
| 77 | + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00}; |
| 78 | + scs_float c_data[] = {0., -0.4, 0., 0., 0., 0., 0., 0., 0., 0., 0., |
| 79 | + 0., -0.32, 0., 0., 0., -0.6, 0., 0., 0., 0., 0., |
| 80 | + 0., 0., 0., 0., 0., 0., -0.48, 0., 0., 10.}; |
| 81 | + |
| 82 | + scs_int m_val = 60; |
| 83 | + scs_int n_val = 32; |
| 84 | + |
| 85 | + scs_float bl[] = { |
| 86 | + -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, |
| 87 | + -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, -1e+20, |
| 88 | + -1e+20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, |
| 89 | + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, |
| 90 | + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, |
| 91 | + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; |
| 92 | + scs_float bu[] = {0.0, 0.0, 0.0, 80.0, 500.0, 0.0, 0.0, 80.0, |
| 93 | + 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, |
| 94 | + 0.0, 310.0, 300.0, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, |
| 95 | + 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, |
| 96 | + 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, |
| 97 | + 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 1e+20, |
| 98 | + 1e+20, 1e+20, 1e+20}; |
| 99 | + scs_int bsize = 52; |
| 100 | + scs_int z = 8; |
| 101 | + |
| 102 | + scs_float opt = -1.5907818; |
| 103 | + /* end data */ |
| 104 | + |
| 105 | + d->m = m_val; |
| 106 | + d->n = n_val; |
| 107 | + d->b = b; |
| 108 | + d->c = c_data; |
| 109 | + |
| 110 | + d->A = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix)); |
| 111 | + d->P = (ScsMatrix *)scs_calloc(1, sizeof(ScsMatrix)); |
| 112 | + |
| 113 | + d->A->m = m_val; |
| 114 | + d->A->n = n_val; |
| 115 | + |
| 116 | + d->A->x = Ax; |
| 117 | + d->A->i = Ai; |
| 118 | + d->A->p = Ap; |
| 119 | + |
| 120 | + d->P->m = n_val; |
| 121 | + d->P->n = n_val; |
| 122 | + |
| 123 | + d->P->x = Px; |
| 124 | + d->P->i = Pi; |
| 125 | + d->P->p = Pp; |
| 126 | + |
| 127 | + k->bsize = bsize; |
| 128 | + k->bl = bl; |
| 129 | + k->bu = bu; |
| 130 | + k->z = z; |
| 131 | + |
| 132 | + scs_set_default_settings(stgs); |
| 133 | + stgs->eps_abs = 1e-9; |
| 134 | + stgs->eps_rel = 1e-9; |
| 135 | + stgs->eps_infeas = 0.; |
| 136 | + |
| 137 | + /* Step 1: Cold solve */ |
| 138 | + exitflag = scs(d, k, stgs, sol, &info); |
| 139 | + |
| 140 | + perr = info.pobj - opt; |
| 141 | + derr = info.dobj - opt; |
| 142 | + |
| 143 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 144 | + mu_assert("partial_warm_start_qafiro: cold solve failed", success); |
| 145 | + fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag); |
| 146 | + if (fail) { |
| 147 | + goto cleanup; |
| 148 | + } |
| 149 | + |
| 150 | + cold_iters = info.iter; |
| 151 | + scs_printf("partial_warm_start_qafiro: cold solve took %li iters\n", |
| 152 | + (long)cold_iters); |
| 153 | + |
| 154 | + /* Save solution */ |
| 155 | + memcpy(saved_x, sol->x, n_val * sizeof(scs_float)); |
| 156 | + memcpy(saved_y, sol->y, m_val * sizeof(scs_float)); |
| 157 | + memcpy(saved_s, sol->s, m_val * sizeof(scs_float)); |
| 158 | + |
| 159 | + /* Step 2: Keep x, NaN out s and y */ |
| 160 | + memcpy(sol->x, saved_x, n_val * sizeof(scs_float)); |
| 161 | + for (i = 0; i < m_val; ++i) { |
| 162 | + sol->s[i] = NAN; |
| 163 | + sol->y[i] = NAN; |
| 164 | + } |
| 165 | + |
| 166 | + stgs->warm_start = 1; |
| 167 | + exitflag = scs(d, k, stgs, sol, &info); |
| 168 | + |
| 169 | + perr = info.pobj - opt; |
| 170 | + derr = info.dobj - opt; |
| 171 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 172 | + mu_assert("partial_warm_start_qafiro: x-only warm start failed", success); |
| 173 | + scs_printf( |
| 174 | + "partial_warm_start_qafiro: x-only warm start took %li iters\n", |
| 175 | + (long)info.iter); |
| 176 | + |
| 177 | + /* Step 3: Keep first half of x, NaN rest of x, s, y */ |
| 178 | + for (i = 0; i < n_val; ++i) { |
| 179 | + sol->x[i] = (i < n_val / 2) ? saved_x[i] : NAN; |
| 180 | + } |
| 181 | + for (i = 0; i < m_val; ++i) { |
| 182 | + sol->s[i] = NAN; |
| 183 | + sol->y[i] = NAN; |
| 184 | + } |
| 185 | + |
| 186 | + stgs->warm_start = 1; |
| 187 | + exitflag = scs(d, k, stgs, sol, &info); |
| 188 | + |
| 189 | + perr = info.pobj - opt; |
| 190 | + derr = info.dobj - opt; |
| 191 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 192 | + mu_assert("partial_warm_start_qafiro: half-x warm start failed", success); |
| 193 | + scs_printf( |
| 194 | + "partial_warm_start_qafiro: half-x warm start took %li iters\n", |
| 195 | + (long)info.iter); |
| 196 | + |
| 197 | + /* Step 4: Keep x and y, NaN out only s |
| 198 | + * s = b - Ax from optimal x gives optimal s, so this should help a lot |
| 199 | + */ |
| 200 | + memcpy(sol->x, saved_x, n_val * sizeof(scs_float)); |
| 201 | + memcpy(sol->y, saved_y, m_val * sizeof(scs_float)); |
| 202 | + for (i = 0; i < m_val; ++i) { |
| 203 | + sol->s[i] = NAN; |
| 204 | + } |
| 205 | + |
| 206 | + stgs->warm_start = 1; |
| 207 | + exitflag = scs(d, k, stgs, sol, &info); |
| 208 | + |
| 209 | + perr = info.pobj - opt; |
| 210 | + derr = info.dobj - opt; |
| 211 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 212 | + mu_assert("partial_warm_start_qafiro: x+y warm start (NaN s) failed", |
| 213 | + success); |
| 214 | + scs_printf( |
| 215 | + "partial_warm_start_qafiro: x+y warm start (NaN s) took %li iters\n", |
| 216 | + (long)info.iter); |
| 217 | + mu_assert( |
| 218 | + "partial_warm_start_qafiro: x+y (NaN s) should beat cold start", |
| 219 | + info.iter < cold_iters); |
| 220 | + |
| 221 | + /* Step 5: Keep x and s, NaN out only y */ |
| 222 | + memcpy(sol->x, saved_x, n_val * sizeof(scs_float)); |
| 223 | + memcpy(sol->s, saved_s, m_val * sizeof(scs_float)); |
| 224 | + for (i = 0; i < m_val; ++i) { |
| 225 | + sol->y[i] = NAN; |
| 226 | + } |
| 227 | + |
| 228 | + stgs->warm_start = 1; |
| 229 | + exitflag = scs(d, k, stgs, sol, &info); |
| 230 | + |
| 231 | + perr = info.pobj - opt; |
| 232 | + derr = info.dobj - opt; |
| 233 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 234 | + mu_assert("partial_warm_start_qafiro: x+s warm start (NaN y) failed", |
| 235 | + success); |
| 236 | + scs_printf( |
| 237 | + "partial_warm_start_qafiro: x+s warm start (NaN y) took %li iters\n", |
| 238 | + (long)info.iter); |
| 239 | + mu_assert( |
| 240 | + "partial_warm_start_qafiro: x+s (NaN y) should converge in <= cold iters", |
| 241 | + info.iter <= cold_iters); |
| 242 | + |
| 243 | + /* Step 6: Keep only y, NaN out x and s */ |
| 244 | + for (i = 0; i < n_val; ++i) { |
| 245 | + sol->x[i] = NAN; |
| 246 | + } |
| 247 | + memcpy(sol->y, saved_y, m_val * sizeof(scs_float)); |
| 248 | + for (i = 0; i < m_val; ++i) { |
| 249 | + sol->s[i] = NAN; |
| 250 | + } |
| 251 | + |
| 252 | + stgs->warm_start = 1; |
| 253 | + exitflag = scs(d, k, stgs, sol, &info); |
| 254 | + |
| 255 | + perr = info.pobj - opt; |
| 256 | + derr = info.dobj - opt; |
| 257 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 258 | + mu_assert("partial_warm_start_qafiro: y-only warm start failed", success); |
| 259 | + scs_printf( |
| 260 | + "partial_warm_start_qafiro: y-only warm start took %li iters\n", |
| 261 | + (long)info.iter); |
| 262 | + mu_assert( |
| 263 | + "partial_warm_start_qafiro: y-only should beat cold start", |
| 264 | + info.iter < cold_iters); |
| 265 | + |
| 266 | + /* Step 7: Perturbed x and y, NaN s (simulates nearby problem solution) */ |
| 267 | + for (i = 0; i < n_val; ++i) { |
| 268 | + sol->x[i] = saved_x[i] * 1.01; /* 1% perturbation */ |
| 269 | + } |
| 270 | + for (i = 0; i < m_val; ++i) { |
| 271 | + sol->y[i] = saved_y[i] * 1.01; |
| 272 | + sol->s[i] = NAN; |
| 273 | + } |
| 274 | + |
| 275 | + stgs->warm_start = 1; |
| 276 | + exitflag = scs(d, k, stgs, sol, &info); |
| 277 | + |
| 278 | + perr = info.pobj - opt; |
| 279 | + derr = info.dobj - opt; |
| 280 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 281 | + mu_assert("partial_warm_start_qafiro: perturbed x+y (1%%) failed", success); |
| 282 | + scs_printf( |
| 283 | + "partial_warm_start_qafiro: perturbed x+y 1%% (NaN s) took %li iters\n", |
| 284 | + (long)info.iter); |
| 285 | + mu_assert( |
| 286 | + "partial_warm_start_qafiro: perturbed x+y (1%%) should beat cold start", |
| 287 | + info.iter < cold_iters); |
| 288 | + |
| 289 | + /* Step 8: Larger perturbation */ |
| 290 | + for (i = 0; i < n_val; ++i) { |
| 291 | + sol->x[i] = saved_x[i] * 1.1; /* 10% perturbation */ |
| 292 | + } |
| 293 | + for (i = 0; i < m_val; ++i) { |
| 294 | + sol->y[i] = saved_y[i] * 1.1; |
| 295 | + sol->s[i] = NAN; |
| 296 | + } |
| 297 | + |
| 298 | + stgs->warm_start = 1; |
| 299 | + exitflag = scs(d, k, stgs, sol, &info); |
| 300 | + |
| 301 | + perr = info.pobj - opt; |
| 302 | + derr = info.dobj - opt; |
| 303 | + success = ABS(perr) < 1e-3 && ABS(derr) < 1e-3 && exitflag == SCS_SOLVED; |
| 304 | + mu_assert("partial_warm_start_qafiro: perturbed x+y (10%%) failed", success); |
| 305 | + scs_printf( |
| 306 | + "partial_warm_start_qafiro: perturbed x+y 10%% (NaN s) took %li iters\n", |
| 307 | + (long)info.iter); |
| 308 | + mu_assert( |
| 309 | + "partial_warm_start_qafiro: perturbed x+y (10%%) should beat cold start", |
| 310 | + info.iter < cold_iters); |
| 311 | + |
| 312 | +cleanup: |
| 313 | + SCS(free_sol)(sol); |
| 314 | + scs_free(d->A); |
| 315 | + scs_free(d->P); |
| 316 | + scs_free(k); |
| 317 | + scs_free(stgs); |
| 318 | + scs_free(d); |
| 319 | + return fail; |
| 320 | +} |
0 commit comments