diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4094a4806..2620bc7aa 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -57,6 +57,11 @@ If the output is not quite correct, check for invisible trailing spaces!
Fixed error message for unknown parameter name in ODE.
+
+ The "SimplexSolver" will now throw a "DimensionMismatchException"
+ when calling "optimize(...)" with linear constraints whose dimension
+ does not match the dimension of the objective function.
+
Fixed wrong event detection in case of close events pairs.
@@ -73,10 +78,6 @@ If the output is not quite correct, check for invisible trailing spaces!
Use Double.isNaN rather than x != x in FastMath.
-
- Fix potential branching errors in "FastMath#pow(double, double)" when
- passing special values, i.e. infinity, due to erroneous JIT optimization.
-
Fixed equals/hashcode contract failure for Dfp.
diff --git a/src/main/java/org/apache/commons/math4/optim/linear/SimplexSolver.java b/src/main/java/org/apache/commons/math4/optim/linear/SimplexSolver.java
index d4b4259f7..a835765ea 100644
--- a/src/main/java/org/apache/commons/math4/optim/linear/SimplexSolver.java
+++ b/src/main/java/org/apache/commons/math4/optim/linear/SimplexSolver.java
@@ -146,6 +146,8 @@ public class SimplexSolver extends LinearOptimizer {
*
* @return {@inheritDoc}
* @throws TooManyIterationsException if the maximal number of iterations is exceeded.
+ * @throws org.apache.commons.math4.exception.DimensionMismatchException if the dimension
+ * of the constraints does not match the dimension of the objective function
*/
@Override
public PointValuePair optimize(OptimizationData... optData)
diff --git a/src/main/java/org/apache/commons/math4/optim/linear/SimplexTableau.java b/src/main/java/org/apache/commons/math4/optim/linear/SimplexTableau.java
index e869a7489..f11632bc0 100644
--- a/src/main/java/org/apache/commons/math4/optim/linear/SimplexTableau.java
+++ b/src/main/java/org/apache/commons/math4/optim/linear/SimplexTableau.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Set;
import java.util.TreeSet;
+import org.apache.commons.math4.exception.DimensionMismatchException;
import org.apache.commons.math4.linear.Array2DRowRealMatrix;
import org.apache.commons.math4.linear.MatrixUtils;
import org.apache.commons.math4.linear.RealVector;
@@ -112,6 +113,8 @@ class SimplexTableau implements Serializable {
* or {@link GoalType#MINIMIZE}.
* @param restrictToNonNegative Whether to restrict the variables to non-negative values.
* @param epsilon Amount of error to accept when checking for optimality.
+ * @throws DimensionMismatchException if the dimension of the constraints does not match the
+ * dimension of the objective function
*/
SimplexTableau(final LinearObjectiveFunction f,
final Collection constraints,
@@ -129,13 +132,16 @@ class SimplexTableau implements Serializable {
* @param restrictToNonNegative whether to restrict the variables to non-negative values
* @param epsilon amount of error to accept when checking for optimality
* @param maxUlps amount of error to accept in floating point comparisons
+ * @throws DimensionMismatchException if the dimension of the constraints does not match the
+ * dimension of the objective function
*/
SimplexTableau(final LinearObjectiveFunction f,
final Collection constraints,
final GoalType goalType,
final boolean restrictToNonNegative,
final double epsilon,
- final int maxUlps) {
+ final int maxUlps) throws DimensionMismatchException {
+ checkDimensions(f, constraints);
this.f = f;
this.constraints = normalizeConstraints(constraints);
this.restrictToNonNegative = restrictToNonNegative;
@@ -153,6 +159,23 @@ class SimplexTableau implements Serializable {
initializeColumnLabels();
}
+ /**
+ * Checks that the dimensions of the objective function and the constraints match.
+ * @param objectiveFunction the objective function
+ * @param c the set of constraints
+ * @throws DimensionMismatchException if the constraint dimensions do not match with the
+ * dimension of the objective function
+ */
+ private void checkDimensions(final LinearObjectiveFunction objectiveFunction,
+ final Collection c) {
+ final int dimension = objectiveFunction.getCoefficients().getDimension();
+ for (final LinearConstraint constraint : c) {
+ final int constraintDimension = constraint.getCoefficients().getDimension();
+ if (constraintDimension != dimension) {
+ throw new DimensionMismatchException(constraintDimension, dimension);
+ }
+ }
+ }
/**
* Initialize the labels for the columns.
*/
diff --git a/src/test/java/org/apache/commons/math4/optim/linear/SimplexSolverTest.java b/src/test/java/org/apache/commons/math4/optim/linear/SimplexSolverTest.java
index b112ac78e..987ab6c69 100644
--- a/src/test/java/org/apache/commons/math4/optim/linear/SimplexSolverTest.java
+++ b/src/test/java/org/apache/commons/math4/optim/linear/SimplexSolverTest.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import org.apache.commons.math4.exception.DimensionMismatchException;
import org.apache.commons.math4.exception.TooManyIterationsException;
import org.apache.commons.math4.optim.MaxIter;
import org.apache.commons.math4.optim.PointValuePair;
@@ -52,13 +53,13 @@ public class SimplexSolverTest {
// x1,x2,x3,x4 >= 0
LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 10, -57, -9, -24}, 0);
-
+
ArrayList constraints = new ArrayList();
constraints.add(new LinearConstraint(new double[] {0.5, -5.5, -2.5, 9}, Relationship.LEQ, 0));
constraints.add(new LinearConstraint(new double[] {0.5, -1.5, -0.5, 1}, Relationship.LEQ, 0));
constraints.add(new LinearConstraint(new double[] { 1, 0, 0, 0}, Relationship.LEQ, 1));
-
+
double epsilon = 1e-6;
SimplexSolver solver = new SimplexSolver();
PointValuePair solution = solver.optimize(f, new LinearConstraintSet(constraints),
@@ -73,7 +74,7 @@ public class SimplexSolverTest {
public void testMath828() {
LinearObjectiveFunction f = new LinearObjectiveFunction(
new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, 0.0);
-
+
ArrayList constraints = new ArrayList();
constraints.add(new LinearConstraint(new double[] {0.0, 39.0, 23.0, 96.0, 15.0, 48.0, 9.0, 21.0, 48.0, 36.0, 76.0, 19.0, 88.0, 17.0, 16.0, 36.0,}, Relationship.GEQ, 15.0));
@@ -83,7 +84,7 @@ public class SimplexSolverTest {
constraints.add(new LinearConstraint(new double[] {25.0, -7.0, -99.0, -78.0, -25.0, -14.0, -16.0, -89.0, -39.0, -56.0, -53.0, -9.0, -18.0, -26.0, -11.0, -61.0,}, Relationship.GEQ, 0.0));
constraints.add(new LinearConstraint(new double[] {33.0, -95.0, -15.0, -4.0, -33.0, -3.0, -20.0, -96.0, -27.0, -13.0, -80.0, -24.0, -3.0, -13.0, -57.0, -76.0,}, Relationship.GEQ, 0.0));
constraints.add(new LinearConstraint(new double[] {7.0, -95.0, -39.0, -93.0, -7.0, -94.0, -94.0, -62.0, -76.0, -26.0, -53.0, -57.0, -31.0, -76.0, -53.0, -52.0,}, Relationship.GEQ, 0.0));
-
+
double epsilon = 1e-6;
PointValuePair solution = new SimplexSolver().optimize(DEFAULT_MAX_ITER, f, new LinearConstraintSet(constraints),
GoalType.MINIMIZE, new NonNegativeConstraint(true));
@@ -95,7 +96,7 @@ public class SimplexSolverTest {
public void testMath828Cycle() {
LinearObjectiveFunction f = new LinearObjectiveFunction(
new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, 0.0);
-
+
ArrayList constraints = new ArrayList();
constraints.add(new LinearConstraint(new double[] {0.0, 16.0, 14.0, 69.0, 1.0, 85.0, 52.0, 43.0, 64.0, 97.0, 14.0, 74.0, 89.0, 28.0, 94.0, 58.0, 13.0, 22.0, 21.0, 17.0, 30.0, 25.0, 1.0, 59.0, 91.0, 78.0, 12.0, 74.0, 56.0, 3.0, 88.0,}, Relationship.GEQ, 91.0));
@@ -105,14 +106,14 @@ public class SimplexSolverTest {
constraints.add(new LinearConstraint(new double[] {41.0, -96.0, -41.0, -48.0, -70.0, -43.0, -43.0, -43.0, -97.0, -37.0, -85.0, -70.0, -45.0, -67.0, -87.0, -69.0, -94.0, -54.0, -54.0, -92.0, -79.0, -10.0, -35.0, -20.0, -41.0, -41.0, -65.0, -25.0, -12.0, -8.0, -46.0,}, Relationship.GEQ, 0.0));
constraints.add(new LinearConstraint(new double[] {27.0, -42.0, -65.0, -49.0, -53.0, -42.0, -17.0, -2.0, -61.0, -31.0, -76.0, -47.0, -8.0, -93.0, -86.0, -62.0, -65.0, -63.0, -22.0, -43.0, -27.0, -23.0, -32.0, -74.0, -27.0, -63.0, -47.0, -78.0, -29.0, -95.0, -73.0,}, Relationship.GEQ, 0.0));
constraints.add(new LinearConstraint(new double[] {15.0, -46.0, -41.0, -83.0, -98.0, -99.0, -21.0, -35.0, -7.0, -14.0, -80.0, -63.0, -18.0, -42.0, -5.0, -34.0, -56.0, -70.0, -16.0, -18.0, -74.0, -61.0, -47.0, -41.0, -15.0, -79.0, -18.0, -47.0, -88.0, -68.0, -55.0,}, Relationship.GEQ, 0.0));
-
+
double epsilon = 1e-6;
PointValuePair solution = new SimplexSolver().optimize(DEFAULT_MAX_ITER, f,
new LinearConstraintSet(constraints),
GoalType.MINIMIZE, new NonNegativeConstraint(true),
PivotSelectionRule.BLAND);
Assert.assertEquals(1.0d, solution.getValue(), epsilon);
- Assert.assertTrue(validSolution(solution, constraints, epsilon));
+ Assert.assertTrue(validSolution(solution, constraints, epsilon));
}
@Test
@@ -195,7 +196,7 @@ public class SimplexSolverTest {
SimplexSolver solver = new SimplexSolver();
PointValuePair solution = solver.optimize(DEFAULT_MAX_ITER, f, new LinearConstraintSet(constraints),
GoalType.MINIMIZE, new NonNegativeConstraint(false));
-
+
Assert.assertTrue(Precision.compareTo(solution.getPoint()[0] * 200.d, 1.d, epsilon) >= 0);
Assert.assertEquals(0.0050, solution.getValue(), epsilon);
}
@@ -219,13 +220,13 @@ public class SimplexSolverTest {
SimplexSolver simplex = new SimplexSolver();
PointValuePair solution = simplex.optimize(DEFAULT_MAX_ITER, f, new LinearConstraintSet(constraints),
GoalType.MINIMIZE, new NonNegativeConstraint(false));
-
+
Assert.assertTrue(Precision.compareTo(solution.getPoint()[0], -1e-18d, epsilon) >= 0);
- Assert.assertEquals(1.0d, solution.getPoint()[1], epsilon);
+ Assert.assertEquals(1.0d, solution.getPoint()[1], epsilon);
Assert.assertEquals(0.0d, solution.getPoint()[2], epsilon);
Assert.assertEquals(1.0d, solution.getValue(), epsilon);
}
-
+
@Test
public void testMath272() {
LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 2, 2, 1 }, 0);
@@ -361,12 +362,12 @@ public class SimplexSolverTest {
@Test
public void testMath930() {
Collection constraints = createMath930Constraints();
-
+
double[] objFunctionCoeff = new double[33];
objFunctionCoeff[3] = 1;
LinearObjectiveFunction f = new LinearObjectiveFunction(objFunctionCoeff, 0);
SimplexSolver solver = new SimplexSolver(1e-4, 10, 1e-6);
-
+
PointValuePair solution = solver.optimize(new MaxIter(1000), f, new LinearConstraintSet(constraints),
GoalType.MINIMIZE, new NonNegativeConstraint(true));
Assert.assertEquals(0.3752298, solution.getValue(), 1e-4);
@@ -761,7 +762,7 @@ public class SimplexSolverTest {
public void testSolutionCallback() {
// re-use the problem from testcase for MATH-288
// it normally requires 5 iterations
-
+
LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 7, 3, 0, 0 }, 0 );
List constraints = new ArrayList();
@@ -773,7 +774,7 @@ public class SimplexSolverTest {
final SimplexSolver solver = new SimplexSolver();
final SolutionCallback callback = new SolutionCallback();
-
+
Assert.assertNull(callback.getSolution());
Assert.assertFalse(callback.isSolutionOptimal());
@@ -784,7 +785,7 @@ public class SimplexSolverTest {
} catch (TooManyIterationsException ex) {
// expected
}
-
+
final PointValuePair solution = callback.getSolution();
Assert.assertNotNull(solution);
Assert.assertTrue(validSolution(solution, constraints, 1e-4));
@@ -793,6 +794,31 @@ public class SimplexSolverTest {
Assert.assertEquals(7.0, solution.getValue(), 1e-4);
}
+ @Test(expected=DimensionMismatchException.class)
+ public void testDimensionMatch() {
+ // min 2x1 +15x2 +18x3
+ // Subject to
+ // -x1 +2x2 -6x3 <=-10
+ // x2 +2x3 <= 6
+ // 2x1 +10x3 <= 19
+ // -x1 +x2 <= -2
+ // x1,x2,x3 >= 0
+
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 2, 15, 18 }, 0);
+ Collection constraints = new ArrayList();
+ // this constraint is wrong, the dimension is less than expected one
+ constraints.add(new LinearConstraint(new double[] { -1, 2 - 6 }, Relationship.LEQ, -10));
+ constraints.add(new LinearConstraint(new double[] { 0, 1, 2 }, Relationship.LEQ, 6));
+ constraints.add(new LinearConstraint(new double[] { 2, 0, 10 }, Relationship.LEQ, 19));
+ constraints.add(new LinearConstraint(new double[] { -1, 1, 0 }, Relationship.LEQ, -2));
+
+ SimplexSolver solver = new SimplexSolver();
+ solver.optimize(f,
+ new LinearConstraintSet(constraints),
+ new NonNegativeConstraint(true),
+ PivotSelectionRule.BLAND);
+ }
+
/**
* Converts a test string to a {@link LinearConstraint}.
* Ex: x0 + x1 + x2 + x3 - x12 = 0
@@ -831,20 +857,20 @@ public class SimplexSolverTest {
for (int i = 0; i < vals.length; i++) {
result += vals[i] * coeffs[i];
}
-
+
switch (c.getRelationship()) {
case EQ:
if (!Precision.equals(result, c.getValue(), epsilon)) {
return false;
}
break;
-
+
case GEQ:
if (Precision.compareTo(result, c.getValue(), epsilon) < 0) {
return false;
}
break;
-
+
case LEQ:
if (Precision.compareTo(result, c.getValue(), epsilon) > 0) {
return false;
@@ -852,7 +878,7 @@ public class SimplexSolverTest {
break;
}
}
-
+
return true;
}