vendor/scs/src/cones.c in scs-0.3.1 vs vendor/scs/src/cones.c in scs-0.3.2

- old
+ new

@@ -8,59 +8,65 @@ #define CONE_THRESH (1e-8) #define EXP_CONE_MAX_ITERS (100) #define BOX_CONE_MAX_ITERS (25) #define POW_CONE_MAX_ITERS (20) -/* In the box cone projection we penalize the `t` term additionally by this - * factor. This encourages the `t` term to stay close to the incoming `t` term, - * which should provide better convergence since typically the `t` term does - * not appear in the linear system other than `t = 1`. Setting to 1 is - * the vanilla projection. - */ -#define BOX_T_SCALE (1.) - /* Box cone limits (+ or -) taken to be INF */ #define MAX_BOX_VAL (1e15) #ifdef USE_LAPACK + +#ifdef __cplusplus +extern "C" { +#endif + void BLAS(syev)(const char *jobz, const char *uplo, blas_int *n, scs_float *a, blas_int *lda, scs_float *w, scs_float *work, blas_int *lwork, blas_int *info); blas_int BLAS(syrk)(const char *uplo, const char *trans, const blas_int *n, const blas_int *k, const scs_float *alpha, const scs_float *a, const blas_int *lda, const scs_float *beta, scs_float *c, const blas_int *ldc); void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx, const blas_int *incx); + +#ifdef __cplusplus +} #endif +#endif + /* set the vector of rho y terms, based on scale and cones */ -void SCS(set_rho_y_vec)(const ScsCone *k, scs_float scale, scs_float *rho_y_vec, - scs_int m) { - scs_int i, count = 0; - /* f cone */ - for (i = 0; i < k->z; ++i) { +void SCS(set_r_y)(const ScsConeWork *c, scs_float scale, scs_float *r_y) { + scs_int i; + /* z cone */ + for (i = 0; i < c->k->z; ++i) { /* set rho_y small for z, similar to rho_x term, since z corresponds to * dual free cone, this effectively decreases penalty on those entries * and lets them be determined almost entirely by the linear system solve */ - rho_y_vec[i] = 1.0 / (1000. * scale); + r_y[i] = 1.0 / (1000. * scale); } - count += k->z; /* others */ - for (i = count; i < m; ++i) { - rho_y_vec[i] = 1.0 / scale; + for (i = c->k->z; i < c->m; ++i) { + r_y[i] = 1.0 / scale; } +} - /* Note, if updating this to use different scales for other cones (e.g. box) - * then you must be careful to also include the effect of the rho_y_vec - * in the cone projection operator. - */ - - /* Increase rho_y_vec for the t term in the box cone */ - if (k->bsize) { - rho_y_vec[k->z + k->l] *= BOX_T_SCALE; +/* the function f aggregates the entries within each cone */ +void SCS(enforce_cone_boundaries)(const ScsConeWork *c, scs_float *vec, + scs_float (*f)(const scs_float *, scs_int)) { + scs_int i, j, delta; + scs_int count = c->cone_boundaries[0]; + scs_float wrk; + for (i = 1; i < c->cone_boundaries_len; ++i) { + delta = c->cone_boundaries[i]; + wrk = f(&(vec[count]), delta); + for (j = count; j < count + delta; ++j) { + vec[j] = wrk; + } + count += delta; } } static inline scs_int get_sd_cone_size(scs_int s) { return (s * (s + 1)) / 2; @@ -69,11 +75,11 @@ /* * boundaries will contain array of indices of rows of A corresponding to * cone boundaries, boundaries[0] is starting index for cones of size strictly * larger than 1, boundaries malloc-ed here so should be freed. */ -scs_int SCS(set_cone_boundaries)(const ScsCone *k, scs_int **cone_boundaries) { +void set_cone_boundaries(const ScsCone *k, ScsConeWork *c) { scs_int i, s_cone_sz, count = 0; scs_int cone_boundaries_len = 1 + k->qsize + k->ssize + k->ed + k->ep + k->psize; scs_int *b = (scs_int *)scs_calloc(cone_boundaries_len, sizeof(scs_int)); /* cones that can be scaled independently */ @@ -97,12 +103,12 @@ for (i = 0; i < k->psize; ++i) { b[count + i] = 3; } count += k->psize; /* other cones */ - *cone_boundaries = b; - return cone_boundaries_len; + c->cone_boundaries = b; + c->cone_boundaries_len = cone_boundaries_len; } static scs_int get_full_cone_dims(const ScsCone *k) { scs_int i, c = k->z + k->l + k->bsize; if (k->qsize) { @@ -119,11 +125,11 @@ c += 3 * k->ed; } if (k->ep) { c += 3 * k->ep; } - if (k->p) { + if (k->psize) { c += 3 * k->psize; } return c; } @@ -214,10 +220,13 @@ } if (c->work) { scs_free(c->work); } #endif + if (c->cone_boundaries) { + scs_free(c->cone_boundaries); + } if (c->s) { scs_free(c->s); } if (c->bu) { scs_free(c->bu); @@ -482,11 +491,11 @@ BLAS(scal)(&nb, &sqrt2, Xs, &nb_plus_one); /* not n_squared */ /* Solve eigenproblem, reuse workspaces */ BLAS(syev)("Vectors", "Lower", &nb, Xs, &nb, e, work, &lwork, &info); if (info != 0) { - scs_printf("WARN: LAPACK syev error, info = %i\n", info); + scs_printf("WARN: LAPACK syev error, info = %i\n", (int)info); if (info < 0) { return info; } } @@ -585,40 +594,44 @@ c->bl[j] = D ? D[j + 1] * c->bl[j] / D[0] : c->bl[j]; } } } -/* project onto { (t, s) | t * l <= s <= t * u, t >= 0 }, Newton's method on t - tx = [t; s], total length = bsize - uses Moreau since \Pi_K*(tx) = \Pi_K(-tx) + tx +/* Project onto { (t, s) | t * l <= s <= t * u, t >= 0 }, Newton's method on t + tx = [t; s], total length = bsize, under Euclidean metric 1/r_box. */ static scs_float proj_box_cone(scs_float *tx, const scs_float *bl, const scs_float *bu, scs_int bsize, - scs_float t_warm_start) { + scs_float t_warm_start, scs_float *r_box) { scs_float *x, gt, ht, t_prev, t = t_warm_start; + scs_float rho_t = 1, *rho = SCS_NULL, r; scs_int iter, j; if (bsize == 1) { /* special case */ tx[0] = MAX(tx[0], 0.0); return tx[0]; } - x = &(tx[1]); + if (r_box) { + rho_t = 1.0 / r_box[0]; + rho = &(r_box[1]); + } + /* should only require about 5 or so iterations, 1 or 2 if warm-started */ for (iter = 0; iter < BOX_CONE_MAX_ITERS; iter++) { t_prev = t; - /* incorporate the additional BOX_T_SCALE factor into the projection */ - gt = BOX_T_SCALE * (t - tx[0]); /* gradient */ - ht = BOX_T_SCALE; /* hessian */ + gt = rho_t * (t - tx[0]); /* gradient */ + ht = rho_t; /* hessian */ for (j = 0; j < bsize - 1; j++) { + r = rho ? 1.0 / rho[j] : 1.; if (x[j] > t * bu[j]) { - gt += (t * bu[j] - x[j]) * bu[j]; /* gradient */ - ht += bu[j] * bu[j]; /* hessian */ + gt += r * (t * bu[j] - x[j]) * bu[j]; /* gradient */ + ht += r * bu[j] * bu[j]; /* hessian */ } else if (x[j] < t * bl[j]) { - gt += (t * bl[j] - x[j]) * bl[j]; /* gradient */ - ht += bl[j] * bl[j]; /* hessian */ + gt += r * (t * bl[j] - x[j]) * bl[j]; /* gradient */ + ht += r * bl[j] * bl[j]; /* hessian */ } } t = MAX(t - gt / MAX(ht, 1e-8), 0.); /* newton step */ #if VERBOSITY > 3 scs_printf("iter %i, t_new %1.3e, t_prev %1.3e, gt %1.3e, ht %1.3e\n", iter, @@ -645,10 +658,11 @@ x[j] = t * bl[j]; } /* x[j] unchanged otherwise */ } tx[0] = t; + #if VERBOSITY > 3 scs_printf("box cone iters %i\n", (int)iter + 1); #endif return t; } @@ -716,77 +730,83 @@ v[1] = y; v[2] = (v[2] < 0) ? -(r) : (r); } /* project onto the primal K cone in the paper */ +/* the r_y vector determines the INVERSE metric, ie, project under the + * diag(r_y)^-1 norm. + */ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c, - scs_int normalize) { + scs_int normalize, scs_float *r_y) { scs_int i, status; scs_int count = 0; + scs_float *r_box = SCS_NULL; + scs_float *bu, *bl; - if (k->z) { + if (k->z) { /* doesn't use r_y */ /* project onto primal zero / dual free cone */ memset(x, 0, k->z * sizeof(scs_float)); count += k->z; } - if (k->l) { + if (k->l) { /* doesn't use r_y */ /* project onto positive orthant */ for (i = count; i < count + k->l; ++i) { x[i] = MAX(x[i], 0.0); } count += k->l; } - if (k->bsize) { - /* project onto box cone */ - if (normalize) { - c->box_t_warm_start = proj_box_cone(&(x[count]), c->bl, c->bu, k->bsize, - c->box_t_warm_start); - } else { - c->box_t_warm_start = proj_box_cone(&(x[count]), k->bl, k->bu, k->bsize, - c->box_t_warm_start); + if (k->bsize) { /* DOES use r_y */ + if (r_y) { + r_box = &(r_y[count]); } + /* project onto box cone */ + bu = normalize ? c->bu : k->bu; + bl = normalize ? c->bl : k->bl; + c->box_t_warm_start = proj_box_cone(&(x[count]), bl, bu, k->bsize, + c->box_t_warm_start, r_box); count += k->bsize; /* since b = (t,s), len(s) = bsize - 1 */ } - if (k->qsize && k->q) { + if (k->qsize && k->q) { /* doesn't use r_y */ /* project onto second-order cones */ for (i = 0; i < k->qsize; ++i) { proj_soc(&(x[count]), k->q[i]); count += k->q[i]; } } - if (k->ssize && k->s) { + if (k->ssize && k->s) { /* doesn't use r_y */ /* project onto PSD cones */ for (i = 0; i < k->ssize; ++i) { status = proj_semi_definite_cone(&(x[count]), k->s[i], c); if (status < 0) { return status; } count += get_sd_cone_size(k->s[i]); } } - if (k->ep) { - /* - * exponential cone is not self dual, if s \in K - * then y \in K^* and so if K is the primal cone - * here we project onto K^*, via Moreau - * \Pi_C^*(y) = y + \Pi_C(-y) - */ + if (k->ep) { /* doesn't use r_y */ + /* + * exponential cone is not self dual, if s \in K + * then y \in K^* and so if K is the primal cone + * here we project onto K^*, via Moreau + * \Pi_C^*(y) = y + \Pi_C(-y) + */ #ifdef _OPENMP #pragma omp parallel for #endif for (i = 0; i < k->ep; ++i) { proj_exp_cone(&(x[count + 3 * i])); } count += 3 * k->ep; } - if (k->ed) { /* dual exponential cone */ + /* dual exponential cone */ + if (k->ed) { /* doesn't use r_y */ /* * exponential cone is not self dual, if s \in K * then y \in K^* and so if K is the primal cone * here we project onto K^*, via Moreau * \Pi_C^*(y) = y + \Pi_C(-y) @@ -810,19 +830,19 @@ x[idx + 2] -= t; } count += 3 * k->ed; } - if (k->psize && k->p) { + if (k->psize && k->p) { /* doesn't use r_y */ scs_float v[3]; scs_int idx; /* don't use openmp for power cone ifdef _OPENMP pragma omp parallel for private(v, idx) endif */ - for (i = 0; i < k->psize; ++i) { + for (i = 0; i < k->psize; ++i) { /* doesn't use r_y */ idx = count + 3 * i; if (k->p[i] >= 0) { /* primal power cone */ proj_power_cone(&(x[idx]), k->p[i]); } else { @@ -842,48 +862,79 @@ } /* project onto OTHER cones */ return 0; } -ScsConeWork *SCS(init_cone)(const ScsCone *k, const ScsScaling *scal, - scs_int cone_len) { +ScsConeWork *SCS(init_cone)(const ScsCone *k, scs_int m) { ScsConeWork *c = (ScsConeWork *)scs_calloc(1, sizeof(ScsConeWork)); - c->cone_len = cone_len; - c->s = (scs_float *)scs_calloc(cone_len, sizeof(scs_float)); + c->k = k; + c->m = m; + c->scaled_cones = 0; + set_cone_boundaries(k, c); + c->s = (scs_float *)scs_calloc(m, sizeof(scs_float)); + if (k->ssize && k->s) { + if (set_up_sd_cone_work_space(c, k) < 0) { + SCS(finish_cone)(c); + return SCS_NULL; + } + } + return c; +} + +void scale_box_cone(const ScsCone *k, ScsConeWork *c, ScsScaling *scal) { if (k->bsize && k->bu && k->bl) { c->box_t_warm_start = 1.; if (scal) { c->bu = (scs_float *)scs_calloc(k->bsize - 1, sizeof(scs_float)); c->bl = (scs_float *)scs_calloc(k->bsize - 1, sizeof(scs_float)); memcpy(c->bu, k->bu, (k->bsize - 1) * sizeof(scs_float)); memcpy(c->bl, k->bl, (k->bsize - 1) * sizeof(scs_float)); /* also does some sanitizing */ - normalize_box_cone(c, scal ? &(scal->D[k->z + k->l]) : SCS_NULL, - k->bsize); + normalize_box_cone(c, &(scal->D[k->z + k->l]), k->bsize); } } - if (k->ssize && k->s) { - if (set_up_sd_cone_work_space(c, k) < 0) { - SCS(finish_cone)(c); - return SCS_NULL; - } - } - return c; } -/* outward facing cone projection routine - performs projection in-place - if normalize > 0 then will use normalized (equilibrated) cones if applicable. +/* Outward facing cone projection routine, performs projection in-place. + If normalize > 0 then will use normalized (equilibrated) cones if applicable. + + Moreau decomposition for R-norm projections: + + `x + R^{-1} \Pi_{C^*}^{R^{-1}} ( - R x ) = \Pi_C^R ( x )` + + where \Pi^R_C is the projection onto C under the R-norm: + + `||x||_R = \sqrt{x ' R x}`. + */ -scs_int SCS(proj_dual_cone)(scs_float *x, const ScsCone *k, ScsConeWork *c, - scs_int normalize) { - scs_int status; - /* copy x, s = x */ - memcpy(c->s, x, c->cone_len * sizeof(scs_float)); - /* negate x -> -x */ - SCS(scale_array)(x, -1., c->cone_len); - /* project -x onto cone, x -> Pi_K(-x) */ - status = proj_cone(x, k, c, normalize); - /* return Pi_K*(x) = s + Pi_K(-x) */ - SCS(add_scaled_array)(x, c->s, c->cone_len, 1.); +scs_int SCS(proj_dual_cone)(scs_float *x, ScsConeWork *c, ScsScaling *scal, + scs_float *r_y) { + scs_int status, i; + const ScsCone *k = c->k; + + if (!c->scaled_cones) { + scale_box_cone(k, c, scal); + c->scaled_cones = 1; + } + + /* copy s = x */ + memcpy(c->s, x, c->m * sizeof(scs_float)); + + /* x -> - Rx */ + for (i = 0; i < c->m; ++i) { + x[i] *= r_y ? -r_y[i] : -1; + } + + /* project -x onto cone, x -> \Pi_{C^*}^{R^{-1}}(-x) under r_y metric */ + status = proj_cone(x, k, c, scal ? 1 : 0, r_y); + + /* return x + R^{-1} \Pi_{C^*}^{R^{-1}} ( -x ) */ + for (i = 0; i < c->m; ++i) { + if (r_y) { + x[i] = x[i] / r_y[i] + c->s[i]; + } else { + x[i] += c->s[i]; + } + } + return status; }