Skip to content

Commit 27542a6

Browse files
authored
New exp cone projection (#247)
* New exponential cone projection, about 50x faster than the original one. * Based on "Projection onto the exponential cone: a univariate root-finding problem", Friberg, 2021. * Add test covering exp cone projection.
1 parent 986a200 commit 27542a6

9 files changed

+506
-163
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ set(${PROJECT_NAME}_SRC
161161
src/aa.c
162162
src/cones.c
163163
src/ctrlc.c
164+
src/exp_cone.c
164165
src/linalg.c
165166
src/normalize.c
166167
src/rw.c

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# MAKEFILE for scs
22
include scs.mk
33

4-
SCS_OBJECTS = src/util.o src/cones.o src/aa.o src/rw.o src/linalg.o src/ctrlc.o src/scs_version.o src/normalize.o
4+
SCS_OBJECTS = src/util.o src/cones.o src/exp_cone.o src/aa.o src/rw.o src/linalg.o src/ctrlc.o src/scs_version.o src/normalize.o
55
SCS_O = src/scs.o
66
SCS_INDIR_O = src/scs_indir.o
77

@@ -41,6 +41,7 @@ $(SCS_INDIR_O): src/scs.c $(INC_FILES)
4141

4242
src/util.o : src/util.c $(INC_FILES)
4343
src/cones.o : src/cones.c $(INC_FILES)
44+
src/exp_cone.o : src/exp_cone.c $(INC_FILES)
4445
src/aa.o : src/aa.c $(INC_FILES)
4546
src/rw.o : src/rw.c $(INC_FILES)
4647
src/linalg.o: src/linalg.c $(INC_FILES)

src/cones.c

+14-158
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
#include "scs_blas.h" /* contains BLAS(X) macros and type info */
55
#include "util.h"
66

7-
#define CONE_TOL (1e-9)
8-
#define CONE_THRESH (1e-8)
9-
#define EXP_CONE_MAX_ITERS (100)
107
#define BOX_CONE_MAX_ITERS (25)
8+
#define POW_CONE_TOL (1e-9)
119
#define POW_CONE_MAX_ITERS (20)
1210

1311
/* Box cone limits (+ or -) taken to be INF */
@@ -35,6 +33,11 @@ void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
3533

3634
#endif
3735

36+
/* Forward declare exponential cone projection routine.
37+
* Implemented in exp_cone.c.
38+
*/
39+
scs_float SCS(proj_pd_exp_cone)(scs_float *v0, scs_int primal);
40+
3841
void SCS(free_cone)(ScsCone *k) {
3942
if (k) {
4043
if (k->bu)
@@ -325,118 +328,6 @@ char *SCS(get_cone_header)(const ScsCone *k) {
325328
return tmp;
326329
}
327330

328-
static scs_float exp_newton_one_d(scs_float rho, scs_float y_hat,
329-
scs_float z_hat, scs_float w) {
330-
scs_float t_prev, t = MAX(w - z_hat, MAX(-z_hat, 1e-9));
331-
scs_float f = 1., fp = 1.;
332-
scs_int i;
333-
for (i = 0; i < EXP_CONE_MAX_ITERS; ++i) {
334-
t_prev = t;
335-
f = t * (t + z_hat) / rho / rho - y_hat / rho + log(t / rho) + 1;
336-
fp = (2 * t + z_hat) / rho / rho + 1 / t;
337-
338-
t = t - f / fp;
339-
340-
if (t <= -z_hat) {
341-
t = -z_hat;
342-
break;
343-
} else if (t <= 0) {
344-
t = 0;
345-
break;
346-
} else if (ABS(t - t_prev) < CONE_TOL) {
347-
break;
348-
} else if (SQRTF(f * f / fp) < CONE_TOL) {
349-
break;
350-
}
351-
}
352-
if (i == EXP_CONE_MAX_ITERS) {
353-
scs_printf("warning: exp cone newton step hit maximum %i iters\n", (int)i);
354-
scs_printf("rho=%1.5e; y_hat=%1.5e; z_hat=%1.5e; w=%1.5e; f=%1.5e, "
355-
"fp=%1.5e, t=%1.5e, t_prev= %1.5e\n",
356-
rho, y_hat, z_hat, w, f, fp, t, t_prev);
357-
}
358-
return t + z_hat;
359-
}
360-
361-
static void exp_solve_for_x_with_rho(const scs_float *v, scs_float *x,
362-
scs_float rho, scs_float w) {
363-
x[2] = exp_newton_one_d(rho, v[1], v[2], w);
364-
x[1] = (x[2] - v[2]) * x[2] / rho;
365-
x[0] = v[0] - rho;
366-
}
367-
368-
static scs_float exp_calc_grad(const scs_float *v, scs_float *x, scs_float rho,
369-
scs_float w) {
370-
exp_solve_for_x_with_rho(v, x, rho, w);
371-
if (x[1] <= 1e-12) {
372-
return x[0];
373-
}
374-
return x[0] + x[1] * log(x[1] / x[2]);
375-
}
376-
377-
static void exp_get_rho_ub(const scs_float *v, scs_float *x, scs_float *ub,
378-
scs_float *lb) {
379-
*lb = 0;
380-
*ub = 0.125;
381-
while (exp_calc_grad(v, x, *ub, v[1]) > 0) {
382-
*lb = *ub;
383-
(*ub) *= 2;
384-
}
385-
}
386-
387-
/* project onto the exponential cone, v has dimension *exactly* 3 */
388-
static scs_int proj_exp_cone(scs_float *v) {
389-
scs_int i;
390-
scs_float ub, lb, rho, g, x[3];
391-
scs_float r = v[0], s = v[1], t = v[2];
392-
393-
/* v in cl(Kexp) */
394-
if ((s * exp(r / s) - t <= CONE_THRESH && s > 0) ||
395-
(r <= 0 && s == 0 && t >= 0)) {
396-
return 0;
397-
}
398-
399-
/* -v in Kexp^* */
400-
if ((r > 0 && r * exp(s / r) + exp(1) * t <= CONE_THRESH) ||
401-
(r == 0 && s <= 0 && t <= 0)) {
402-
memset(v, 0, 3 * sizeof(scs_float));
403-
return 0;
404-
}
405-
406-
/* special case with analytical solution */
407-
if (r < 0 && s < 0) {
408-
v[1] = 0.0;
409-
v[2] = MAX(v[2], 0);
410-
return 0;
411-
}
412-
413-
/* iterative procedure to find projection, bisects on dual variable: */
414-
exp_get_rho_ub(v, x, &ub, &lb); /* get starting upper and lower bounds */
415-
for (i = 0; i < EXP_CONE_MAX_ITERS; ++i) {
416-
rho = (ub + lb) / 2; /* halfway between upper and lower bounds */
417-
g = exp_calc_grad(v, x, rho, x[1]); /* calculates gradient wrt dual var */
418-
if (g > 0) {
419-
lb = rho;
420-
} else {
421-
ub = rho;
422-
}
423-
if (ub - lb < CONE_TOL) {
424-
break;
425-
}
426-
}
427-
#if VERBOSITY > 10
428-
scs_printf("exponential cone proj iters %i\n", (int)i);
429-
#endif
430-
if (i == EXP_CONE_MAX_ITERS) {
431-
scs_printf("warning: exp cone outer step hit maximum %i iters\n", (int)i);
432-
scs_printf("r=%1.5e; s=%1.5e; t=%1.5e\n", r, s, t);
433-
}
434-
v[0] = x[0];
435-
v[1] = x[1];
436-
v[2] = x[2];
437-
return 0;
438-
}
439-
440331
static scs_int set_up_sd_cone_work_space(ScsConeWork *c, const ScsCone *k) {
441332
scs_int i;
442333
#ifdef USE_LAPACK
@@ -741,13 +632,13 @@ static void proj_power_cone(scs_float *v, scs_float a) {
741632
scs_int i;
742633
/* v in K_a */
743634
if (xh >= 0 && yh >= 0 &&
744-
CONE_THRESH + POWF(xh, a) * POWF(yh, (1 - a)) >= rh) {
635+
POW_CONE_TOL + POWF(xh, a) * POWF(yh, (1 - a)) >= rh) {
745636
return;
746637
}
747638

748639
/* -v in K_a^* */
749640
if (xh <= 0 && yh <= 0 &&
750-
CONE_THRESH + POWF(-xh, a) * POWF(-yh, 1 - a) >=
641+
POW_CONE_TOL + POWF(-xh, a) * POWF(-yh, 1 - a) >=
751642
rh * POWF(a, a) * POWF(1 - a, 1 - a)) {
752643
v[0] = v[1] = v[2] = 0;
753644
return;
@@ -760,7 +651,7 @@ static void proj_power_cone(scs_float *v, scs_float a) {
760651
y = pow_calc_x(r, yh, rh, 1 - a);
761652

762653
f = pow_calc_f(x, y, r, a);
763-
if (ABS(f) < CONE_TOL) {
654+
if (ABS(f) < POW_CONE_TOL) {
764655
break;
765656
}
766657

@@ -829,51 +720,16 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
829720
}
830721
}
831722

832-
if (k->ep) { /* doesn't use r_y */
833-
/*
834-
* exponential cone is not self dual, if s \in K
835-
* then y \in K^* and so if K is the primal cone
836-
* here we project onto K^*, via Moreau
837-
* \Pi_C^*(y) = y + \Pi_C(-y)
838-
*/
723+
if (k->ep || k->ed) { /* doesn't use r_y */
839724
#ifdef _OPENMP
840725
#pragma omp parallel for
841726
#endif
842-
for (i = 0; i < k->ep; ++i) {
843-
proj_exp_cone(&(x[count + 3 * i]));
727+
for (i = 0; i < k->ep + k->ed; ++i) {
728+
/* provided in exp_cone.c */
729+
SCS(proj_pd_exp_cone)(&(x[count + 3 * i]), i < k->ep);
844730
}
845-
count += 3 * k->ep;
731+
count += 3 * (k->ep + k->ed);
846732
}
847-
848-
/* dual exponential cone */
849-
if (k->ed) { /* doesn't use r_y */
850-
/*
851-
* exponential cone is not self dual, if s \in K
852-
* then y \in K^* and so if K is the primal cone
853-
* here we project onto K^*, via Moreau
854-
* \Pi_C^*(y) = y + \Pi_C(-y)
855-
*/
856-
scs_int idx;
857-
scs_float r, s, t;
858-
SCS(scale_array)(&(x[count]), -1, 3 * k->ed); /* x = -x; */
859-
#ifdef _OPENMP
860-
#pragma omp parallel for private(r, s, t, idx)
861-
#endif
862-
for (i = 0; i < k->ed; ++i) {
863-
idx = count + 3 * i;
864-
r = x[idx];
865-
s = x[idx + 1];
866-
t = x[idx + 2];
867-
868-
proj_exp_cone(&(x[idx]));
869-
870-
x[idx] -= r;
871-
x[idx + 1] -= s;
872-
x[idx + 2] -= t;
873-
}
874-
count += 3 * k->ed;
875-
}
876-
877733
if (k->psize && k->p) { /* doesn't use r_y */
878734
scs_float v[3];
879735
scs_int idx;

0 commit comments

Comments
 (0)