Skip to content
This repository was archived by the owner on Jul 26, 2025. It is now read-only.

Commit bc870f3

Browse files
authored
Improve setup precondition checks and documentation (#60)
* Add error message * Rewrite condition similar to ajc * Fix bug * Improve javadoc; Backward compatible change in the method signature: if A and b are provided, they should be non-null, if they are not provided, they should be null; Add more precondition checks and improve existing * Fix tests * Improve javadocs
1 parent 41527f0 commit bc870f3

File tree

2 files changed

+52
-28
lines changed

2 files changed

+52
-28
lines changed

src/main/java/com/ustermetrics/ecos4j/Model.java

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,50 +58,74 @@ public static String version() {
5858
* exponential cone.
5959
*
6060
* @param l the dimension of the positive orthant.
61-
* @param q the dimensions of each cone.
61+
* @param q the dimensions of the second-order cones.
6262
* @param nExC the number of exponential cones.
6363
* @param gpr the sparse G matrix data (Column Compressed Storage CCS).
6464
* @param gjc the sparse G matrix column index (CCS).
6565
* @param gir the sparse G matrix row index (CCS).
6666
* @param c the cost function weights.
6767
* @param h the right-hand-side of the cone constraints.
68-
* @param apr the sparse A matrix data (CCS).
69-
* @param ajc the sparse A matrix column index (CCS).
70-
* @param air the sparse A matrix row index (CCS).
71-
* @param b the right-hand-side of the equalities.
68+
* @param apr the (optional) sparse A matrix data (CCS).
69+
* @param ajc the (optional) sparse A matrix column index (CCS).
70+
* @param air the (optional) sparse A matrix row index (CCS).
71+
* @param b the (optional) right-hand-side of the equalities.
7272
* @see <a href="https://github.com/embotech/ecos">ECOS</a>
7373
*/
7474
public void setup(long l, long @NonNull [] q, long nExC, double @NonNull [] gpr, long @NonNull [] gjc,
75-
long @NonNull [] gir, double @NonNull [] c, double @NonNull [] h, double @NonNull [] apr,
76-
long @NonNull [] ajc, long @NonNull [] air, double @NonNull [] b) {
75+
long @NonNull [] gir, double @NonNull [] c, double @NonNull [] h, double[] apr, long[] ajc,
76+
long[] air, double[] b) {
7777
checkState(stage == Stage.NEW, "Model must be in stage new");
78-
checkArgument(apr.length == 0 && ajc.length == 0 && air.length == 0 && b.length == 0
79-
|| apr.length > 0 && ajc.length > 0 && air.length > 0 && b.length > 0);
80-
val nonNegErrMsg = "%s must be non-negative";
81-
checkArgument(l >= 0, nonNegErrMsg, "l");
82-
checkArgument(nExC >= 0, nonNegErrMsg, "nExC");
8378

79+
checkArgument(l >= 0, "dimension of the positive orthant l must be non-negative");
80+
val nCones = q.length;
81+
checkArgument(nCones == 0 || Arrays.stream(q).allMatch(d -> d > 0),
82+
"second-order cone dimensions q must be empty or each dimension q[i] must be positive");
83+
checkArgument(nExC >= 0, "number of exponential cones nExC must be non-negative");
84+
val nnzG = gpr.length;
85+
checkArgument(nnzG > 0, "number of non-zero elements in G (gpr.length) must be positive");
86+
checkArgument(nnzG == gir.length,
87+
"number of non-zero elements in G (gpr.length) must be equal to the number of elements in the row " +
88+
"index of G (gir.length)");
89+
val nColsG = gjc.length - 1;
90+
checkArgument(nColsG > 0, "number of columns of G (gjc.length - 1) must be positive");
8491
n = c.length;
92+
checkArgument(n > 0, "number of variables x (c.length) must be positive");
8593
m = h.length;
86-
p = b.length;
87-
val nCones = q.length;
88-
94+
checkArgument(m > 0, "dimension of all cones (h.length) must be positive");
8995
checkArgument(m == l + Arrays.stream(q).sum() + 3 * nExC,
90-
"Length of h must be equal to the sum of l, q, and 3*nExC");
91-
checkArgument(n == gjc.length - 1, "Length of c must be equal to the length of gjc minus one");
92-
checkArgument(ajc.length == 0 || ajc.length == n + 1,
93-
"ajc has zero length or must be equal to the length of c plus one");
96+
"dimension of all cones (h.length) must be equal to the sum of the positive orthant dimension l, the " +
97+
"second-order cone dimensions q[i], and three times the number of exponential cones 3 * nExC");
98+
checkArgument(nColsG == n, "number of columns of G (gjc.length - 1) must be equal to the number of variables " +
99+
"x (c.length)");
100+
101+
checkArgument(apr != null && ajc != null && air != null && b != null || apr == null && ajc == null && air == null && b == null,
102+
"A (apr, ajc, air) and b must be supplied (all non-null) or omitted (all null) together");
103+
if (apr != null) {
104+
val nnzA = apr.length;
105+
checkArgument(nnzA > 0, "number of non-zero elements in A (apr.length) must be positive");
106+
checkArgument(nnzA == air.length,
107+
"number of non-zero elements in A (apr.length) must be equal to the number of elements in the row" +
108+
" index of A (air.length)");
109+
val nColsA = ajc.length - 1;
110+
checkArgument(nColsA > 0, "number of columns of A (ajc.length - 1) must be positive");
111+
p = b.length;
112+
checkArgument(p > 0, "number of equalities (b.length) must be positive");
113+
checkArgument(nColsA == n, "number of columns of A (ajc.length - 1) must be equal to the number of " +
114+
"variables x (c.length)");
115+
} else {
116+
p = 0;
117+
}
94118

95119
val qSeg = arena.allocateFrom(C_LONG_LONG, q);
96120
val gprSeg = arena.allocateFrom(C_DOUBLE, gpr);
97121
val gjcSeg = arena.allocateFrom(C_LONG_LONG, gjc);
98122
val girSeg = arena.allocateFrom(C_LONG_LONG, gir);
99-
val aprSeg = apr.length > 0 ? arena.allocateFrom(C_DOUBLE, apr) : NULL;
100-
val ajcSeg = ajc.length > 0 ? arena.allocateFrom(C_LONG_LONG, ajc) : NULL;
101-
val airSeg = air.length > 0 ? arena.allocateFrom(C_LONG_LONG, air) : NULL;
123+
val aprSeg = apr != null ? arena.allocateFrom(C_DOUBLE, apr) : NULL;
124+
val ajcSeg = ajc != null ? arena.allocateFrom(C_LONG_LONG, ajc) : NULL;
125+
val airSeg = air != null ? arena.allocateFrom(C_LONG_LONG, air) : NULL;
102126
val cSeg = arena.allocateFrom(C_DOUBLE, c);
103127
val hSeg = arena.allocateFrom(C_DOUBLE, h);
104-
val bSeg = b.length > 0 ? arena.allocateFrom(C_DOUBLE, b) : NULL;
128+
val bSeg = b != null ? arena.allocateFrom(C_DOUBLE, b) : NULL;
105129

106130
workSeg = ECOS_setup(n, m, p, l, nCones, qSeg, nExC, gprSeg, gjcSeg, girSeg, aprSeg, ajcSeg, airSeg, cSeg,
107131
hSeg, bSeg).reinterpret(pwork.sizeof(), arena, null);
@@ -119,7 +143,7 @@ public void setup(long l, long @NonNull [] q, long nExC, double @NonNull [] gpr,
119143
* without equality constraint, i.e. {@code apr}, {@code ajc}, {@code air}, and {@code b} are empty arrays.
120144
*
121145
* @param l the dimension of the positive orthant.
122-
* @param q the dimensions of each cone.
146+
* @param q the dimensions of the second-order cones.
123147
* @param nExC the number of exponential cones.
124148
* @param gpr the sparse G matrix data (Column Compressed Storage CCS).
125149
* @param gjc the sparse G matrix column index (CCS).
@@ -129,7 +153,7 @@ public void setup(long l, long @NonNull [] q, long nExC, double @NonNull [] gpr,
129153
*/
130154
public void setup(long l, long @NonNull [] q, long nExC, double @NonNull [] gpr, long @NonNull [] gjc,
131155
long @NonNull [] gir, double @NonNull [] c, double @NonNull [] h) {
132-
setup(l, q, nExC, gpr, gjc, gir, c, h, new double[]{}, new long[]{}, new long[]{}, new double[]{});
156+
setup(l, q, nExC, gpr, gjc, gir, c, h, null, null, null, null);
133157
}
134158

135159
/**

src/test/java/com/ustermetrics/ecos4j/ModelTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ static void setup() {
3333
-1.};
3434
gjc = new long[]{0, 2, 5, 9, 14, 16};
3535
gir = new long[]{0, 6, 1, 6, 7, 2, 6, 7, 8, 3, 6, 7, 8, 9, 4, 5};
36-
apr = new double[]{1., 1., 1., 1., 0.};
36+
apr = new double[]{1., 1., 1., 1.};
3737
ajc = new long[]{0, 1, 2, 3, 4, 4};
3838
air = new long[]{0, 0, 0, 0};
3939
c = new double[]{-0.05, -0.06, -0.08, -0.06, 0.};
@@ -160,7 +160,7 @@ void setupWithInvalidPositiveOrthantDimensionThrowsException() {
160160
}
161161
});
162162

163-
assertEquals("l must be non-negative", exception.getMessage());
163+
assertEquals("dimension of the positive orthant l must be non-negative", exception.getMessage());
164164
}
165165

166166
@Test
@@ -171,7 +171,7 @@ void setupWithInvalidNumberOfExponentialConesThrowsException() {
171171
}
172172
});
173173

174-
assertEquals("nExC must be non-negative", exception.getMessage());
174+
assertEquals("number of exponential cones nExC must be non-negative", exception.getMessage());
175175
}
176176

177177
@Test

0 commit comments

Comments
 (0)