Author: tn
Date: Sun Sep 15 19:42:24 2013
New Revision: 1523495

URL: http://svn.apache.org/r1523495
Log:
[MATH-842] Fix Blands rule by applying it also to pivot column selection, add 
getter/setter for cycle prevention, reduce default cut-off threshold to 1e-10.

Modified:
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
    
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/optimization/linear/SimplexSolverTest.java

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java?rev=1523495&r1=1523494&r2=1523495&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
 Sun Sep 15 19:42:24 2013
@@ -47,6 +47,9 @@ public class SimplexSolver extends Abstr
     /** Amount of error to accept in floating point comparisons (as ulps). */
     private final int maxUlps;
 
+    /** Prevent cycles via Bland's rule. */
+    private boolean cyclePrevention;
+
     /**
      * Build a simplex solver with default settings.
      */
@@ -62,6 +65,33 @@ public class SimplexSolver extends Abstr
     public SimplexSolver(final double epsilon, final int maxUlps) {
         this.epsilon = epsilon;
         this.maxUlps = maxUlps;
+        this.cyclePrevention = true;
+    }
+
+    /**
+     * Enables/disables cycle prevention using Bland's rule.
+     * <p>
+     * In case of a degeneracy the simplex algorithm might cycle. This can be 
prevented
+     * by using Bland's pivot selection rule. The drawback of this rule is 
that it may result
+     * in pivot choices that do not significantly improve the objective 
function value,
+     * thus increasing the number of required iterations.
+     * <p>
+     * By default, the cycle prevention is enabled.
+     *
+     * @param cyclePrevention if cycle prevention shall be used
+     *
+     * @see <a 
href="http://www.stanford.edu/class/msande310/blandrule.pdf";>Bland's rule</a>
+     */
+    public void setCyclePrevention(boolean cyclePrevention) {
+        this.cyclePrevention = cyclePrevention;
+    }
+
+    /**
+     * Returns whether cycle prevention (using Bland's rule) is enabled.
+     * @return {@code true} if cycle prevention is enable, {@code false} 
otherwise
+     */
+    public boolean getCyclePrevention() {
+        return cyclePrevention;
     }
 
     /**
@@ -79,14 +109,42 @@ public class SimplexSolver extends Abstr
             if (entry < minValue) {
                 minValue = entry;
                 minPos = i;
+
+                // Bland's rule: chose the entering column with the lowest 
index
+                if (cyclePrevention && isValidPivotColumn(tableau, i)) {
+                    break;
+                }
             }
         }
         return minPos;
     }
 
     /**
+     * Checks whether the given column is valid pivot column, i.e. will result
+     * in a valid pivot row.
+     * <p>
+     * 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 the column to test the ratio of.  See {@link 
#getPivotColumn(SimplexTableau)}
      * @return row with the minimum ratio
      */
@@ -136,11 +194,7 @@ public class SimplexSolver extends Abstr
             //
             // 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 (getIterations() < getMaxIterations() / 2) {
+            if (cyclePrevention) {
                 Integer minRow = null;
                 int minIndex = tableau.getWidth();
                 final int varStart = tableau.getNumObjectiveFunctions();

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java?rev=1523495&r1=1523494&r2=1523495&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
 Sun Sep 15 19:42:24 2013
@@ -73,7 +73,7 @@ class SimplexTableau implements Serializ
     private static final int DEFAULT_ULPS = 10;
 
     /** The cut-off threshold to zero-out entries. */
-    private static final double CUTOFF_THRESHOLD = 1e-12;
+    private static final double CUTOFF_THRESHOLD = 1e-10;
 
     /** Serializable version identifier. */
     private static final long serialVersionUID = -1369660067587938365L;

Modified: 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/optimization/linear/SimplexSolverTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/optimization/linear/SimplexSolverTest.java?rev=1523495&r1=1523494&r2=1523495&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/optimization/linear/SimplexSolverTest.java
 (original)
+++ 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/optimization/linear/SimplexSolverTest.java
 Sun Sep 15 19:42:24 2013
@@ -31,6 +31,31 @@ import org.junit.Test;
 public class SimplexSolverTest {
 
     @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 <LinearConstraint>constraints = new 
ArrayList<LinearConstraint>();
+
+        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, constraints, 
GoalType.MAXIMIZE, true);
+        Assert.assertEquals(1.0d, solution.getValue(), epsilon);
+        Assert.assertTrue(validSolution(solution, constraints, epsilon));
+    }
+
+    @Test
     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);


Reply via email to