diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f3dd93dab..bef0cbfaf 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -51,6 +51,11 @@ If the output is not quite correct, check for invisible trailing spaces!
+ * The {@link SimplexSolver} supports the following {@link OptimizationData} data provided + * as arguments to {@link #optimize(OptimizationData...)}: + *
* Note: Depending on the problem definition, the default convergence criteria * may be too strict, resulting in {@link NoFeasibleSolutionException} or * {@link TooManyIterationsException}. In such a case it is advised to adjust these @@ -42,15 +55,8 @@ import org.apache.commons.math3.util.Precision; * The cut-off value has been introduced to zero out very small numbers in the Simplex tableau, * as these may lead to numerical instabilities due to the nature of the Simplex algorithm * (the pivot element is used as a denominator). If the problem definition is very tight, the - * default cut-off value may be too small, thus it is advised to increase it to a larger value, - * in accordance with the chosen epsilon. - *
- * It may also be counter-productive to provide a too large value for {@link - * org.apache.commons.math3.optim.MaxIter MaxIter} as parameter in the call of {@link - * #optimize(org.apache.commons.math3.optim.OptimizationData...) optimize(OptimizationData...)}, - * as the {@link SimplexSolver} will use different strategies depending on the current iteration - * count. After half of the allowed max iterations has already been reached, the strategy to select - * pivot rows will change in order to break possible cycles due to degenerate problems. + * default cut-off value may be too small for certain problems, thus it is advised to increase it + * to a larger value, in accordance with the chosen epsilon. * * @version $Id$ * @since 2.0 @@ -77,6 +83,9 @@ public class SimplexSolver extends LinearOptimizer { */ private final double cutOff; + /** The pivot selection method to use. */ + private PivotSelectionRule pivotSelection; + /** * The solution callback to access the best solution found so far in case * the optimizer fails to find an optimal solution within the iteration limits. @@ -120,6 +129,7 @@ public class SimplexSolver extends LinearOptimizer { this.epsilon = epsilon; this.maxUlps = maxUlps; this.cutOff = cutOff; + this.pivotSelection = PivotSelectionRule.Dantzig; } /** @@ -130,6 +140,7 @@ public class SimplexSolver extends LinearOptimizer { * LinearOptimizer}, this method will register the following data: *
+ * When applying Bland's rule to select the pivot column, it may happen that
+ * there is no corresponding pivot row. This method will check if the selected
+ * pivot column will return a valid pivot row.
+ *
+ * @param tableau simplex tableau for the problem
+ * @param col the column to test
+ * @return {@code true} if the pivot column is valid, {@code false} otherwise
+ */
+ private boolean isValidPivotColumn(SimplexTableau tableau, int col) {
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getHeight(); i++) {
+ final double entry = tableau.getEntry(i, col);
+
+ if (Precision.compareTo(entry, 0d, maxUlps) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns the row with the minimum ratio as given by the minimum ratio test (MRT).
*
- * @param tableau Simple tableau for the problem.
+ * @param tableau Simplex tableau for the problem.
* @param col Column to test the ratio of (see {@link #getPivotColumn(SimplexTableau)}).
* @return the row with the minimum ratio.
*/
@@ -243,26 +287,21 @@ public class SimplexSolver extends LinearOptimizer {
//
// see http://www.stanford.edu/class/msande310/blandrule.pdf
// see http://en.wikipedia.org/wiki/Bland%27s_rule (not equivalent to the above paper)
- //
- // Additional heuristic: if we did not get a solution after half of maxIterations
- // revert to the simple case of just returning the top-most row
- // This heuristic is based on empirical data gathered while investigating MATH-828.
- if (getEvaluations() < getMaxEvaluations() / 2) {
- Integer minRow = null;
- int minIndex = tableau.getWidth();
- final int varStart = tableau.getNumObjectiveFunctions();
- final int varEnd = tableau.getWidth() - 1;
- for (Integer row : minRatioPositions) {
- for (int i = varStart; i < varEnd && !row.equals(minRow); i++) {
- final Integer basicRow = tableau.getBasicRow(i);
- if (basicRow != null && basicRow.equals(row) && i < minIndex) {
- minIndex = i;
- minRow = row;
- }
+
+ Integer minRow = null;
+ int minIndex = tableau.getWidth();
+ final int varStart = tableau.getNumObjectiveFunctions();
+ final int varEnd = tableau.getWidth() - 1;
+ for (Integer row : minRatioPositions) {
+ for (int i = varStart; i < varEnd && !row.equals(minRow); i++) {
+ final Integer basicRow = tableau.getBasicRow(i);
+ if (basicRow != null && basicRow.equals(row) && i < minIndex) {
+ minIndex = i;
+ minRow = row;
}
}
- return minRow;
}
+ return minRow;
}
return minRatioPositions.get(0);
}
diff --git a/src/test/java/org/apache/commons/math3/optim/linear/SimplexSolverTest.java b/src/test/java/org/apache/commons/math3/optim/linear/SimplexSolverTest.java
index 41e0fc2b8..204132b99 100644
--- a/src/test/java/org/apache/commons/math3/optim/linear/SimplexSolverTest.java
+++ b/src/test/java/org/apache/commons/math3/optim/linear/SimplexSolverTest.java
@@ -32,6 +32,34 @@ import org.junit.Assert;
public class SimplexSolverTest {
private static final MaxIter DEFAULT_MAX_ITER = new MaxIter(100);
+ @Test
+ public void testMath842Cycle() {
+ // from http://www.math.toronto.edu/mpugh/Teaching/APM236_04/bland
+ // maximize 10 x1 - 57 x2 - 9 x3 - 24 x4
+ // subject to
+ // 1/2 x1 - 11/2 x2 - 5/2 x3 + 9 x4 <= 0
+ // 1/2 x1 - 3/2 x2 - 1/2 x3 + x4 <= 0
+ // x1 <= 1
+ // x1,x2,x3,x4 >= 0
+
+ LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 10, -57, -9, -24}, 0);
+
+ ArrayList