scs 0.3.0 → 0.4.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +42 -13
  4. data/lib/scs/ffi.rb +1 -7
  5. data/lib/scs/matrix.rb +72 -0
  6. data/lib/scs/solver.rb +19 -26
  7. data/lib/scs/version.rb +1 -1
  8. data/lib/scs.rb +1 -0
  9. data/vendor/scs/CITATION.cff +1 -1
  10. data/vendor/scs/CMakeLists.txt +55 -7
  11. data/vendor/scs/Makefile +9 -9
  12. data/vendor/scs/README.md +4 -1
  13. data/vendor/scs/include/aa.h +1 -1
  14. data/vendor/scs/include/cones.h +17 -12
  15. data/vendor/scs/include/glbopts.h +27 -66
  16. data/vendor/scs/include/linalg.h +2 -1
  17. data/vendor/scs/include/linsys.h +13 -13
  18. data/vendor/scs/include/normalize.h +7 -5
  19. data/vendor/scs/include/rw.h +3 -3
  20. data/vendor/scs/include/scs.h +85 -106
  21. data/vendor/scs/include/scs_types.h +34 -0
  22. data/vendor/scs/include/scs_work.h +80 -0
  23. data/vendor/scs/include/util.h +3 -1
  24. data/vendor/scs/linsys/cpu/direct/private.c +86 -73
  25. data/vendor/scs/linsys/cpu/direct/private.h +2 -2
  26. data/vendor/scs/linsys/cpu/indirect/private.c +42 -33
  27. data/vendor/scs/linsys/cpu/indirect/private.h +1 -2
  28. data/vendor/scs/linsys/csparse.c +3 -3
  29. data/vendor/scs/linsys/external/amd/LICENSE.txt +0 -897
  30. data/vendor/scs/linsys/external/amd/SuiteSparse_config.c +9 -7
  31. data/vendor/scs/linsys/external/amd/SuiteSparse_config.h +1 -1
  32. data/vendor/scs/linsys/external/amd/amd_order.c +5 -5
  33. data/vendor/scs/linsys/gpu/gpu.h +8 -11
  34. data/vendor/scs/linsys/gpu/indirect/private.c +72 -49
  35. data/vendor/scs/linsys/gpu/indirect/private.h +14 -13
  36. data/vendor/scs/linsys/scs_matrix.c +55 -104
  37. data/vendor/scs/linsys/scs_matrix.h +5 -4
  38. data/vendor/scs/scs.mk +1 -5
  39. data/vendor/scs/src/aa.c +13 -8
  40. data/vendor/scs/src/cones.c +197 -108
  41. data/vendor/scs/src/linalg.c +25 -0
  42. data/vendor/scs/src/normalize.c +75 -26
  43. data/vendor/scs/src/rw.c +74 -30
  44. data/vendor/scs/src/scs.c +300 -264
  45. data/vendor/scs/src/scs_version.c +8 -6
  46. data/vendor/scs/src/util.c +27 -13
  47. data/vendor/scs/test/minunit.h +6 -1
  48. data/vendor/scs/test/problem_utils.h +28 -35
  49. data/vendor/scs/test/problems/degenerate.h +2 -1
  50. data/vendor/scs/test/problems/hs21_tiny_qp.h +2 -1
  51. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +6 -2
  52. data/vendor/scs/test/problems/infeasible_tiny_qp.h +2 -1
  53. data/vendor/scs/test/problems/qafiro_tiny_qp.h +5 -4
  54. data/vendor/scs/test/problems/random_prob.h +6 -2
  55. data/vendor/scs/test/problems/rob_gauss_cov_est.h +9 -2
  56. data/vendor/scs/test/problems/small_lp.h +7 -2
  57. data/vendor/scs/test/problems/small_qp.h +387 -0
  58. data/vendor/scs/test/problems/{test_fails.h → test_validation.h} +7 -4
  59. data/vendor/scs/test/problems/unbounded_tiny_qp.h +4 -4
  60. data/vendor/scs/test/random_socp_prob.c +4 -2
  61. data/vendor/scs/test/run_from_file.c +16 -4
  62. data/vendor/scs/test/run_tests.c +23 -14
  63. metadata +10 -35
  64. data/vendor/scs/linsys/cpu/direct/private.o +0 -0
  65. data/vendor/scs/linsys/cpu/indirect/private.o +0 -0
  66. data/vendor/scs/linsys/csparse.o +0 -0
  67. data/vendor/scs/linsys/external/amd/SuiteSparse_config.o +0 -0
  68. data/vendor/scs/linsys/external/amd/amd_1.o +0 -0
  69. data/vendor/scs/linsys/external/amd/amd_2.o +0 -0
  70. data/vendor/scs/linsys/external/amd/amd_aat.o +0 -0
  71. data/vendor/scs/linsys/external/amd/amd_control.o +0 -0
  72. data/vendor/scs/linsys/external/amd/amd_defaults.o +0 -0
  73. data/vendor/scs/linsys/external/amd/amd_dump.o +0 -0
  74. data/vendor/scs/linsys/external/amd/amd_global.o +0 -0
  75. data/vendor/scs/linsys/external/amd/amd_info.o +0 -0
  76. data/vendor/scs/linsys/external/amd/amd_order.o +0 -0
  77. data/vendor/scs/linsys/external/amd/amd_post_tree.o +0 -0
  78. data/vendor/scs/linsys/external/amd/amd_postorder.o +0 -0
  79. data/vendor/scs/linsys/external/amd/amd_preprocess.o +0 -0
  80. data/vendor/scs/linsys/external/amd/amd_valid.o +0 -0
  81. data/vendor/scs/linsys/external/qdldl/qdldl.o +0 -0
  82. data/vendor/scs/linsys/scs_matrix.o +0 -0
  83. data/vendor/scs/src/aa.o +0 -0
  84. data/vendor/scs/src/cones.o +0 -0
  85. data/vendor/scs/src/ctrlc.o +0 -0
  86. data/vendor/scs/src/linalg.o +0 -0
  87. data/vendor/scs/src/normalize.o +0 -0
  88. data/vendor/scs/src/rw.o +0 -0
  89. data/vendor/scs/src/scs.o +0 -0
  90. data/vendor/scs/src/scs_indir.o +0 -0
  91. data/vendor/scs/src/scs_version.o +0 -0
  92. data/vendor/scs/src/util.o +0 -0
@@ -10,18 +10,15 @@
10
10
  #define BOX_CONE_MAX_ITERS (25)
11
11
  #define POW_CONE_MAX_ITERS (20)
12
12
 
13
- /* In the box cone projection we penalize the `t` term additionally by this
14
- * factor. This encourages the `t` term to stay close to the incoming `t` term,
15
- * which should provide better convergence since typically the `t` term does
16
- * not appear in the linear system other than `t = 1`. Setting to 1 is
17
- * the vanilla projection.
18
- */
19
- #define BOX_T_SCALE (1.)
20
-
21
13
  /* Box cone limits (+ or -) taken to be INF */
22
14
  #define MAX_BOX_VAL (1e15)
23
15
 
24
16
  #ifdef USE_LAPACK
17
+
18
+ #ifdef __cplusplus
19
+ extern "C" {
20
+ #endif
21
+
25
22
  void BLAS(syev)(const char *jobz, const char *uplo, blas_int *n, scs_float *a,
26
23
  blas_int *lda, scs_float *w, scs_float *work, blas_int *lwork,
27
24
  blas_int *info);
@@ -31,34 +28,94 @@ blas_int BLAS(syrk)(const char *uplo, const char *trans, const blas_int *n,
31
28
  const scs_float *beta, scs_float *c, const blas_int *ldc);
32
29
  void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
33
30
  const blas_int *incx);
31
+
32
+ #ifdef __cplusplus
33
+ }
34
+ #endif
35
+
34
36
  #endif
35
37
 
38
+ void SCS(free_cone)(ScsCone *k) {
39
+ if (k) {
40
+ if (k->bu)
41
+ scs_free(k->bu);
42
+ if (k->bl)
43
+ scs_free(k->bl);
44
+ if (k->q)
45
+ scs_free(k->q);
46
+ if (k->s)
47
+ scs_free(k->s);
48
+ if (k->p)
49
+ scs_free(k->p);
50
+ scs_free(k);
51
+ }
52
+ }
53
+
54
+ void SCS(deep_copy_cone)(ScsCone *dest, const ScsCone *src) {
55
+ memcpy(dest, src, sizeof(ScsCone));
56
+ /* copy bu, bl */
57
+ if (src->bsize > 1) {
58
+ dest->bu = (scs_float *)scs_calloc(src->bsize - 1, sizeof(scs_float));
59
+ memcpy(dest->bu, src->bu, (src->bsize - 1) * sizeof(scs_float));
60
+ dest->bl = (scs_float *)scs_calloc(src->bsize - 1, sizeof(scs_float));
61
+ memcpy(dest->bl, src->bl, (src->bsize - 1) * sizeof(scs_float));
62
+ } else {
63
+ dest->bu = SCS_NULL;
64
+ dest->bl = SCS_NULL;
65
+ }
66
+ /* copy SOC */
67
+ if (src->qsize > 0) {
68
+ dest->q = (scs_int *)scs_calloc(src->qsize, sizeof(scs_int));
69
+ memcpy(dest->q, src->q, src->qsize * sizeof(scs_int));
70
+ } else {
71
+ dest->q = SCS_NULL;
72
+ }
73
+ /* copy PSD cone */
74
+ if (src->ssize > 0) {
75
+ dest->s = (scs_int *)scs_calloc(src->ssize, sizeof(scs_int));
76
+ memcpy(dest->s, src->s, src->ssize * sizeof(scs_int));
77
+ } else {
78
+ dest->s = SCS_NULL;
79
+ }
80
+ /* copy power cone */
81
+ if (src->psize > 0) {
82
+ dest->p = (scs_float *)scs_calloc(src->psize, sizeof(scs_float));
83
+ memcpy(dest->p, src->p, src->psize * sizeof(scs_float));
84
+ } else {
85
+ dest->p = SCS_NULL;
86
+ }
87
+ }
88
+
36
89
  /* set the vector of rho y terms, based on scale and cones */
37
- void SCS(set_rho_y_vec)(const ScsCone *k, scs_float scale, scs_float *rho_y_vec,
38
- scs_int m) {
39
- scs_int i, count = 0;
40
- /* f cone */
41
- for (i = 0; i < k->z; ++i) {
90
+ void SCS(set_r_y)(const ScsConeWork *c, scs_float scale, scs_float *r_y) {
91
+ scs_int i;
92
+ /* z cone */
93
+ for (i = 0; i < c->k->z; ++i) {
42
94
  /* set rho_y small for z, similar to rho_x term, since z corresponds to
43
95
  * dual free cone, this effectively decreases penalty on those entries
44
96
  * and lets them be determined almost entirely by the linear system solve
45
97
  */
46
- rho_y_vec[i] = 1.0 / (1000. * scale);
98
+ r_y[i] = 1.0 / (1000. * scale);
47
99
  }
48
- count += k->z;
49
100
  /* others */
50
- for (i = count; i < m; ++i) {
51
- rho_y_vec[i] = 1.0 / scale;
101
+ for (i = c->k->z; i < c->m; ++i) {
102
+ r_y[i] = 1.0 / scale;
52
103
  }
104
+ }
53
105
 
54
- /* Note, if updating this to use different scales for other cones (e.g. box)
55
- * then you must be careful to also include the effect of the rho_y_vec
56
- * in the cone projection operator.
57
- */
58
-
59
- /* Increase rho_y_vec for the t term in the box cone */
60
- if (k->bsize) {
61
- rho_y_vec[k->z + k->l] *= BOX_T_SCALE;
106
+ /* the function f aggregates the entries within each cone */
107
+ void SCS(enforce_cone_boundaries)(const ScsConeWork *c, scs_float *vec,
108
+ scs_float (*f)(const scs_float *, scs_int)) {
109
+ scs_int i, j, delta;
110
+ scs_int count = c->cone_boundaries[0];
111
+ scs_float wrk;
112
+ for (i = 1; i < c->cone_boundaries_len; ++i) {
113
+ delta = c->cone_boundaries[i];
114
+ wrk = f(&(vec[count]), delta);
115
+ for (j = count; j < count + delta; ++j) {
116
+ vec[j] = wrk;
117
+ }
118
+ count += delta;
62
119
  }
63
120
  }
64
121
 
@@ -71,7 +128,7 @@ static inline scs_int get_sd_cone_size(scs_int s) {
71
128
  * cone boundaries, boundaries[0] is starting index for cones of size strictly
72
129
  * larger than 1, boundaries malloc-ed here so should be freed.
73
130
  */
74
- scs_int SCS(set_cone_boundaries)(const ScsCone *k, scs_int **cone_boundaries) {
131
+ void set_cone_boundaries(const ScsCone *k, ScsConeWork *c) {
75
132
  scs_int i, s_cone_sz, count = 0;
76
133
  scs_int cone_boundaries_len =
77
134
  1 + k->qsize + k->ssize + k->ed + k->ep + k->psize;
@@ -99,8 +156,8 @@ scs_int SCS(set_cone_boundaries)(const ScsCone *k, scs_int **cone_boundaries) {
99
156
  }
100
157
  count += k->psize;
101
158
  /* other cones */
102
- *cone_boundaries = b;
103
- return cone_boundaries_len;
159
+ c->cone_boundaries = b;
160
+ c->cone_boundaries_len = cone_boundaries_len;
104
161
  }
105
162
 
106
163
  static scs_int get_full_cone_dims(const ScsCone *k) {
@@ -121,7 +178,7 @@ static scs_int get_full_cone_dims(const ScsCone *k) {
121
178
  if (k->ep) {
122
179
  c += 3 * k->ep;
123
180
  }
124
- if (k->p) {
181
+ if (k->psize) {
125
182
  c += 3 * k->psize;
126
183
  }
127
184
  return c;
@@ -216,15 +273,12 @@ void SCS(finish_cone)(ScsConeWork *c) {
216
273
  scs_free(c->work);
217
274
  }
218
275
  #endif
276
+ if (c->cone_boundaries) {
277
+ scs_free(c->cone_boundaries);
278
+ }
219
279
  if (c->s) {
220
280
  scs_free(c->s);
221
281
  }
222
- if (c->bu) {
223
- scs_free(c->bu);
224
- }
225
- if (c->bl) {
226
- scs_free(c->bl);
227
- }
228
282
  if (c) {
229
283
  scs_free(c);
230
284
  }
@@ -484,7 +538,7 @@ static scs_int proj_semi_definite_cone(scs_float *X, const scs_int n,
484
538
  /* Solve eigenproblem, reuse workspaces */
485
539
  BLAS(syev)("Vectors", "Lower", &nb, Xs, &nb, e, work, &lwork, &info);
486
540
  if (info != 0) {
487
- scs_printf("WARN: LAPACK syev error, info = %i\n", info);
541
+ scs_printf("WARN: LAPACK syev error, info = %i\n", (int)info);
488
542
  if (info < 0) {
489
543
  return info;
490
544
  }
@@ -571,52 +625,56 @@ static scs_float pow_calc_fp(scs_float x, scs_float y, scs_float dxdr,
571
625
  * { (t', s') | t' * l' <= s' <= t' u', t >= 0 } = K'
572
626
  * where l' = D l / d0, u' = D u / d0.
573
627
  */
574
- static void normalize_box_cone(ScsConeWork *c, scs_float *D, scs_int bsize) {
628
+ static void normalize_box_cone(ScsCone *k, scs_float *D, scs_int bsize) {
575
629
  scs_int j;
576
630
  for (j = 0; j < bsize - 1; j++) {
577
- if (c->bu[j] >= MAX_BOX_VAL) {
578
- c->bu[j] = INFINITY;
631
+ if (k->bu[j] >= MAX_BOX_VAL) {
632
+ k->bu[j] = INFINITY;
579
633
  } else {
580
- c->bu[j] = D ? D[j + 1] * c->bu[j] / D[0] : c->bu[j];
634
+ k->bu[j] = D ? D[j + 1] * k->bu[j] / D[0] : k->bu[j];
581
635
  }
582
- if (c->bl[j] <= -MAX_BOX_VAL) {
583
- c->bl[j] = -INFINITY;
636
+ if (k->bl[j] <= -MAX_BOX_VAL) {
637
+ k->bl[j] = -INFINITY;
584
638
  } else {
585
- c->bl[j] = D ? D[j + 1] * c->bl[j] / D[0] : c->bl[j];
639
+ k->bl[j] = D ? D[j + 1] * k->bl[j] / D[0] : k->bl[j];
586
640
  }
587
641
  }
588
642
  }
589
643
 
590
- /* project onto { (t, s) | t * l <= s <= t * u, t >= 0 }, Newton's method on t
591
- tx = [t; s], total length = bsize
592
- uses Moreau since \Pi_K*(tx) = \Pi_K(-tx) + tx
644
+ /* Project onto { (t, s) | t * l <= s <= t * u, t >= 0 }, Newton's method on t
645
+ tx = [t; s], total length = bsize, under Euclidean metric 1/r_box.
593
646
  */
594
647
  static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
595
648
  const scs_float *bu, scs_int bsize,
596
- scs_float t_warm_start) {
649
+ scs_float t_warm_start, scs_float *r_box) {
597
650
  scs_float *x, gt, ht, t_prev, t = t_warm_start;
651
+ scs_float rho_t = 1, *rho = SCS_NULL, r;
598
652
  scs_int iter, j;
599
653
 
600
654
  if (bsize == 1) { /* special case */
601
655
  tx[0] = MAX(tx[0], 0.0);
602
656
  return tx[0];
603
657
  }
604
-
605
658
  x = &(tx[1]);
606
659
 
660
+ if (r_box) {
661
+ rho_t = 1.0 / r_box[0];
662
+ rho = &(r_box[1]);
663
+ }
664
+
607
665
  /* should only require about 5 or so iterations, 1 or 2 if warm-started */
608
666
  for (iter = 0; iter < BOX_CONE_MAX_ITERS; iter++) {
609
667
  t_prev = t;
610
- /* incorporate the additional BOX_T_SCALE factor into the projection */
611
- gt = BOX_T_SCALE * (t - tx[0]); /* gradient */
612
- ht = BOX_T_SCALE; /* hessian */
668
+ gt = rho_t * (t - tx[0]); /* gradient */
669
+ ht = rho_t; /* hessian */
613
670
  for (j = 0; j < bsize - 1; j++) {
671
+ r = rho ? 1.0 / rho[j] : 1.;
614
672
  if (x[j] > t * bu[j]) {
615
- gt += (t * bu[j] - x[j]) * bu[j]; /* gradient */
616
- ht += bu[j] * bu[j]; /* hessian */
673
+ gt += r * (t * bu[j] - x[j]) * bu[j]; /* gradient */
674
+ ht += r * bu[j] * bu[j]; /* hessian */
617
675
  } else if (x[j] < t * bl[j]) {
618
- gt += (t * bl[j] - x[j]) * bl[j]; /* gradient */
619
- ht += bl[j] * bl[j]; /* hessian */
676
+ gt += r * (t * bl[j] - x[j]) * bl[j]; /* gradient */
677
+ ht += r * bl[j] * bl[j]; /* hessian */
620
678
  }
621
679
  }
622
680
  t = MAX(t - gt / MAX(ht, 1e-8), 0.); /* newton step */
@@ -647,6 +705,7 @@ static scs_float proj_box_cone(scs_float *tx, const scs_float *bl,
647
705
  /* x[j] unchanged otherwise */
648
706
  }
649
707
  tx[0] = t;
708
+
650
709
  #if VERBOSITY > 3
651
710
  scs_printf("box cone iters %i\n", (int)iter + 1);
652
711
  #endif
@@ -718,18 +777,22 @@ static void proj_power_cone(scs_float *v, scs_float a) {
718
777
  }
719
778
 
720
779
  /* project onto the primal K cone in the paper */
780
+ /* the r_y vector determines the INVERSE metric, ie, project under the
781
+ * diag(r_y)^-1 norm.
782
+ */
721
783
  static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
722
- scs_int normalize) {
784
+ scs_int normalize, scs_float *r_y) {
723
785
  scs_int i, status;
724
786
  scs_int count = 0;
787
+ scs_float *r_box = SCS_NULL;
725
788
 
726
- if (k->z) {
789
+ if (k->z) { /* doesn't use r_y */
727
790
  /* project onto primal zero / dual free cone */
728
791
  memset(x, 0, k->z * sizeof(scs_float));
729
792
  count += k->z;
730
793
  }
731
794
 
732
- if (k->l) {
795
+ if (k->l) { /* doesn't use r_y */
733
796
  /* project onto positive orthant */
734
797
  for (i = count; i < count + k->l; ++i) {
735
798
  x[i] = MAX(x[i], 0.0);
@@ -737,19 +800,17 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
737
800
  count += k->l;
738
801
  }
739
802
 
740
- if (k->bsize) {
741
- /* project onto box cone */
742
- if (normalize) {
743
- c->box_t_warm_start = proj_box_cone(&(x[count]), c->bl, c->bu, k->bsize,
744
- c->box_t_warm_start);
745
- } else {
746
- c->box_t_warm_start = proj_box_cone(&(x[count]), k->bl, k->bu, k->bsize,
747
- c->box_t_warm_start);
803
+ if (k->bsize) { /* DOES use r_y */
804
+ if (r_y) {
805
+ r_box = &(r_y[count]);
748
806
  }
807
+ /* project onto box cone */
808
+ c->box_t_warm_start = proj_box_cone(&(x[count]), k->bl, k->bu, k->bsize,
809
+ c->box_t_warm_start, r_box);
749
810
  count += k->bsize; /* since b = (t,s), len(s) = bsize - 1 */
750
811
  }
751
812
 
752
- if (k->qsize && k->q) {
813
+ if (k->qsize && k->q) { /* doesn't use r_y */
753
814
  /* project onto second-order cones */
754
815
  for (i = 0; i < k->qsize; ++i) {
755
816
  proj_soc(&(x[count]), k->q[i]);
@@ -757,7 +818,7 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
757
818
  }
758
819
  }
759
820
 
760
- if (k->ssize && k->s) {
821
+ if (k->ssize && k->s) { /* doesn't use r_y */
761
822
  /* project onto PSD cones */
762
823
  for (i = 0; i < k->ssize; ++i) {
763
824
  status = proj_semi_definite_cone(&(x[count]), k->s[i], c);
@@ -768,13 +829,13 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
768
829
  }
769
830
  }
770
831
 
771
- if (k->ep) {
772
- /*
773
- * exponential cone is not self dual, if s \in K
774
- * then y \in K^* and so if K is the primal cone
775
- * here we project onto K^*, via Moreau
776
- * \Pi_C^*(y) = y + \Pi_C(-y)
777
- */
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
+ */
778
839
  #ifdef _OPENMP
779
840
  #pragma omp parallel for
780
841
  #endif
@@ -784,7 +845,8 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
784
845
  count += 3 * k->ep;
785
846
  }
786
847
 
787
- if (k->ed) { /* dual exponential cone */
848
+ /* dual exponential cone */
849
+ if (k->ed) { /* doesn't use r_y */
788
850
  /*
789
851
  * exponential cone is not self dual, if s \in K
790
852
  * then y \in K^* and so if K is the primal cone
@@ -812,7 +874,7 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
812
874
  count += 3 * k->ed;
813
875
  }
814
876
 
815
- if (k->psize && k->p) {
877
+ if (k->psize && k->p) { /* doesn't use r_y */
816
878
  scs_float v[3];
817
879
  scs_int idx;
818
880
  /* don't use openmp for power cone
@@ -820,7 +882,7 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
820
882
  pragma omp parallel for private(v, idx)
821
883
  endif
822
884
  */
823
- for (i = 0; i < k->psize; ++i) {
885
+ for (i = 0; i < k->psize; ++i) { /* doesn't use r_y */
824
886
  idx = count + 3 * i;
825
887
  if (k->p[i] >= 0) {
826
888
  /* primal power cone */
@@ -844,23 +906,13 @@ static scs_int proj_cone(scs_float *x, const ScsCone *k, ScsConeWork *c,
844
906
  return 0;
845
907
  }
846
908
 
847
- ScsConeWork *SCS(init_cone)(const ScsCone *k, const ScsScaling *scal,
848
- scs_int cone_len) {
909
+ ScsConeWork *SCS(init_cone)(ScsCone *k, scs_int m) {
849
910
  ScsConeWork *c = (ScsConeWork *)scs_calloc(1, sizeof(ScsConeWork));
850
- c->cone_len = cone_len;
851
- c->s = (scs_float *)scs_calloc(cone_len, sizeof(scs_float));
852
- if (k->bsize && k->bu && k->bl) {
853
- c->box_t_warm_start = 1.;
854
- if (scal) {
855
- c->bu = (scs_float *)scs_calloc(k->bsize - 1, sizeof(scs_float));
856
- c->bl = (scs_float *)scs_calloc(k->bsize - 1, sizeof(scs_float));
857
- memcpy(c->bu, k->bu, (k->bsize - 1) * sizeof(scs_float));
858
- memcpy(c->bl, k->bl, (k->bsize - 1) * sizeof(scs_float));
859
- /* also does some sanitizing */
860
- normalize_box_cone(c, scal ? &(scal->D[k->z + k->l]) : SCS_NULL,
861
- k->bsize);
862
- }
863
- }
911
+ c->k = k;
912
+ c->m = m;
913
+ c->scaled_cones = 0;
914
+ set_cone_boundaries(k, c);
915
+ c->s = (scs_float *)scs_calloc(m, sizeof(scs_float));
864
916
  if (k->ssize && k->s) {
865
917
  if (set_up_sd_cone_work_space(c, k) < 0) {
866
918
  SCS(finish_cone)(c);
@@ -870,20 +922,57 @@ ScsConeWork *SCS(init_cone)(const ScsCone *k, const ScsScaling *scal,
870
922
  return c;
871
923
  }
872
924
 
873
- /* outward facing cone projection routine
874
- performs projection in-place
875
- if normalize > 0 then will use normalized (equilibrated) cones if applicable.
925
+ void scale_box_cone(ScsCone *k, ScsConeWork *c, ScsScaling *scal) {
926
+ if (k->bsize && k->bu && k->bl) {
927
+ c->box_t_warm_start = 1.;
928
+ if (scal) {
929
+ /* also does some sanitizing */
930
+ normalize_box_cone(k, &(scal->D[k->z + k->l]), k->bsize);
931
+ }
932
+ }
933
+ }
934
+
935
+ /* Outward facing cone projection routine, performs projection in-place.
936
+ If normalize > 0 then will use normalized (equilibrated) cones if applicable.
937
+
938
+ Moreau decomposition for R-norm projections:
939
+
940
+ `x + R^{-1} \Pi_{C^*}^{R^{-1}} ( - R x ) = \Pi_C^R ( x )`
941
+
942
+ where \Pi^R_C is the projection onto C under the R-norm:
943
+
944
+ `||x||_R = \sqrt{x ' R x}`.
945
+
876
946
  */
877
- scs_int SCS(proj_dual_cone)(scs_float *x, const ScsCone *k, ScsConeWork *c,
878
- scs_int normalize) {
879
- scs_int status;
880
- /* copy x, s = x */
881
- memcpy(c->s, x, c->cone_len * sizeof(scs_float));
882
- /* negate x -> -x */
883
- SCS(scale_array)(x, -1., c->cone_len);
884
- /* project -x onto cone, x -> Pi_K(-x) */
885
- status = proj_cone(x, k, c, normalize);
886
- /* return Pi_K*(x) = s + Pi_K(-x) */
887
- SCS(add_scaled_array)(x, c->s, c->cone_len, 1.);
947
+ scs_int SCS(proj_dual_cone)(scs_float *x, ScsConeWork *c, ScsScaling *scal,
948
+ scs_float *r_y) {
949
+ scs_int status, i;
950
+ ScsCone *k = c->k;
951
+
952
+ if (!c->scaled_cones) {
953
+ scale_box_cone(k, c, scal);
954
+ c->scaled_cones = 1;
955
+ }
956
+
957
+ /* copy s = x */
958
+ memcpy(c->s, x, c->m * sizeof(scs_float));
959
+
960
+ /* x -> - Rx */
961
+ for (i = 0; i < c->m; ++i) {
962
+ x[i] *= r_y ? -r_y[i] : -1;
963
+ }
964
+
965
+ /* project -x onto cone, x -> \Pi_{C^*}^{R^{-1}}(-x) under r_y metric */
966
+ status = proj_cone(x, k, c, scal ? 1 : 0, r_y);
967
+
968
+ /* return x + R^{-1} \Pi_{C^*}^{R^{-1}} ( -x ) */
969
+ for (i = 0; i < c->m; ++i) {
970
+ if (r_y) {
971
+ x[i] = x[i] / r_y[i] + c->s[i];
972
+ } else {
973
+ x[i] += c->s[i];
974
+ }
975
+ }
976
+
888
977
  return status;
889
978
  }
@@ -83,9 +83,22 @@ void SCS(add_scaled_array)(scs_float *a, const scs_float *b, scs_int n,
83
83
  }
84
84
  }
85
85
 
86
+ scs_float SCS(mean)(const scs_float *x, scs_int n) {
87
+ scs_int i;
88
+ scs_float mean = 0.;
89
+ for (i = 0; i < n; ++i) {
90
+ mean += x[i];
91
+ }
92
+ return mean / n;
93
+ }
94
+
86
95
  #else
87
96
  /* If we have BLAS / LAPACK we may as well use them */
88
97
 
98
+ #ifdef __cplusplus
99
+ extern "C" {
100
+ #endif
101
+
89
102
  scs_float BLAS(nrm2)(blas_int *n, const scs_float *x, blas_int *incx);
90
103
  scs_float BLAS(dot)(const blas_int *n, const scs_float *x, const blas_int *incx,
91
104
  const scs_float *y, const blas_int *incy);
@@ -96,6 +109,10 @@ void BLAS(axpy)(blas_int *n, const scs_float *a, const scs_float *x,
96
109
  void BLAS(scal)(const blas_int *n, const scs_float *sa, scs_float *sx,
97
110
  const blas_int *incx);
98
111
 
112
+ #ifdef __cplusplus
113
+ }
114
+ #endif
115
+
99
116
  /* a *= b */
100
117
  void SCS(scale_array)(scs_float *a, const scs_float b, scs_int len) {
101
118
  blas_int bone = 1;
@@ -137,4 +154,12 @@ void SCS(add_scaled_array)(scs_float *a, const scs_float *b, scs_int len,
137
154
  BLAS(axpy)(&blen, &sc, b, &bone, a, &bone);
138
155
  }
139
156
 
157
+ scs_float SCS(mean)(const scs_float *x, scs_int n) {
158
+ blas_int bone = 1;
159
+ blas_int bzero = 0;
160
+ blas_int blen = (blas_int)n;
161
+ scs_float y = 1.0;
162
+ return BLAS(dot)(&blen, x, &bone, &y, &bzero) / n;
163
+ }
164
+
140
165
  #endif
@@ -3,49 +3,98 @@
3
3
  #include "linalg.h"
4
4
  #include "scs.h"
5
5
 
6
+ /* copied from linsys/scs_matrix.c */
7
+ #define MIN_NORMALIZATION_FACTOR (1e-4)
8
+ #define MAX_NORMALIZATION_FACTOR (1e4)
9
+
10
+ /* Given D, E in scaling normalize b, c and compute primal / dual scales.
11
+ *
12
+ * Recall that the normalization routine is performing:
13
+ *
14
+ * [P A' c] with [E 0 0] on both sides (D, E diagonal)
15
+ * [A 0 b] [0 D 0]
16
+ * [c' b' 0] [0 0 s]
17
+ *
18
+ * which results in:
19
+ *
20
+ * [ EPE EA'D sEc ]
21
+ * [ DAE 0 sDb ]
22
+ * [ sc'E sb'D 0 ]
23
+ *
24
+ * `s` is incorporated into dual_scale and primal_scale
25
+ *
26
+ */
27
+ void SCS(normalize_b_c)(ScsScaling *scal, scs_float *b, scs_float *c) {
28
+ scs_int i;
29
+ scs_float sigma;
30
+
31
+ /* scale c */
32
+ for (i = 0; i < scal->n; ++i) {
33
+ c[i] *= scal->E[i];
34
+ }
35
+ /* scale b */
36
+ for (i = 0; i < scal->m; ++i) {
37
+ b[i] *= scal->D[i];
38
+ }
39
+
40
+ /* calculate primal and dual scales */
41
+ sigma = MAX(SCS(norm_inf)(c, scal->n), SCS(norm_inf)(b, scal->m));
42
+ sigma = sigma < MIN_NORMALIZATION_FACTOR ? 1.0 : sigma;
43
+ sigma = sigma > MAX_NORMALIZATION_FACTOR ? MAX_NORMALIZATION_FACTOR : sigma;
44
+ sigma = SAFEDIV_POS(1.0, sigma);
45
+
46
+ /* Scale b, c */
47
+ SCS(scale_array)(c, sigma, scal->n);
48
+ SCS(scale_array)(b, sigma, scal->m);
49
+
50
+ /* We assume that primal_scale = dual_scale, otherwise need to refactorize */
51
+ scal->primal_scale = sigma;
52
+ scal->dual_scale = sigma;
53
+ }
54
+
6
55
  /* needed for normalizing the warm-start */
7
- void SCS(normalize_sol)(ScsWork *w, ScsSolution *sol) {
56
+ void SCS(normalize_sol)(ScsScaling *scal, ScsSolution *sol) {
8
57
  scs_int i;
9
- scs_float *D = w->scal->D;
10
- scs_float *E = w->scal->E;
11
- for (i = 0; i < w->n; ++i) {
12
- sol->x[i] /= (E[i] / w->scal->dual_scale);
58
+ scs_float *D = scal->D;
59
+ scs_float *E = scal->E;
60
+ for (i = 0; i < scal->n; ++i) {
61
+ sol->x[i] /= (E[i] / scal->dual_scale);
13
62
  }
14
- for (i = 0; i < w->m; ++i) {
15
- sol->y[i] /= (D[i] / w->scal->primal_scale);
63
+ for (i = 0; i < scal->m; ++i) {
64
+ sol->y[i] /= (D[i] / scal->primal_scale);
16
65
  }
17
- for (i = 0; i < w->m; ++i) {
18
- sol->s[i] *= (D[i] * w->scal->dual_scale);
66
+ for (i = 0; i < scal->m; ++i) {
67
+ sol->s[i] *= (D[i] * scal->dual_scale);
19
68
  }
20
69
  }
21
70
 
22
- void SCS(un_normalize_sol)(ScsWork *w, ScsSolution *sol) {
71
+ void SCS(un_normalize_sol)(ScsScaling *scal, ScsSolution *sol) {
23
72
  scs_int i;
24
- scs_float *D = w->scal->D;
25
- scs_float *E = w->scal->E;
26
- for (i = 0; i < w->n; ++i) {
27
- sol->x[i] *= (E[i] / w->scal->dual_scale);
73
+ scs_float *D = scal->D;
74
+ scs_float *E = scal->E;
75
+ for (i = 0; i < scal->n; ++i) {
76
+ sol->x[i] *= (E[i] / scal->dual_scale);
28
77
  }
29
- for (i = 0; i < w->m; ++i) {
30
- sol->y[i] *= (D[i] / w->scal->primal_scale);
78
+ for (i = 0; i < scal->m; ++i) {
79
+ sol->y[i] *= (D[i] / scal->primal_scale);
31
80
  }
32
- for (i = 0; i < w->m; ++i) {
33
- sol->s[i] /= (D[i] * w->scal->dual_scale);
81
+ for (i = 0; i < scal->m; ++i) {
82
+ sol->s[i] /= (D[i] * scal->dual_scale);
34
83
  }
35
84
  }
36
85
 
37
- void SCS(un_normalize_primal)(ScsWork *w, scs_float *r) {
86
+ void SCS(un_normalize_primal)(ScsScaling *scal, scs_float *r) {
38
87
  scs_int i;
39
- scs_float *D = w->scal->D;
40
- for (i = 0; i < w->m; ++i) {
41
- r[i] /= (D[i] * w->scal->dual_scale);
88
+ scs_float *D = scal->D;
89
+ for (i = 0; i < scal->m; ++i) {
90
+ r[i] /= (D[i] * scal->dual_scale);
42
91
  }
43
92
  }
44
93
 
45
- void SCS(un_normalize_dual)(ScsWork *w, scs_float *r) {
94
+ void SCS(un_normalize_dual)(ScsScaling *scal, scs_float *r) {
46
95
  scs_int i;
47
- scs_float *E = w->scal->E;
48
- for (i = 0; i < w->n; ++i) {
49
- r[i] /= (E[i] * w->scal->primal_scale);
96
+ scs_float *E = scal->E;
97
+ for (i = 0; i < scal->n; ++i) {
98
+ r[i] /= (E[i] * scal->primal_scale);
50
99
  }
51
100
  }