From dbdff0758b40601238e88b2cffbf7ceb58ed8977 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe Date: Fri, 21 Aug 2009 23:07:42 +0000 Subject: [PATCH] fixed an error leading the simplex solver to compute the right solution but return another one JIRA: MATH-286 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@806753 13f79535-47bb-0310-9956-ffa450edef68 --- .../optimization/linear/SimplexTableau.java | 65 +++++++++++++------ src/site/xdoc/changes.xml | 4 ++ .../linear/SimplexSolverTest.java | 12 +++- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java b/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java index c228ad62d..b38776718 100644 --- a/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java +++ b/src/main/java/org/apache/commons/math/optimization/linear/SimplexTableau.java @@ -270,8 +270,27 @@ class SimplexTableau implements Serializable { * @return the row that the variable is basic in. null if the column is not basic */ private Integer getBasicRow(final int col) { + return getBasicRow(col, true); + } + + /** + * Checks whether the given column is basic. + * @param col index of the column to check + * @return the row that the variable is basic in. null if the column is not basic + */ + private Integer getBasicRowForSolution(final int col) { + return getBasicRow(col, false); + } + + /** + * Checks whether the given column is basic. + * @param col index of the column to check + * @return the row that the variable is basic in. null if the column is not basic + */ + private Integer getBasicRow(final int col, boolean ignoreObjectiveRows) { Integer row = null; - for (int i = getNumObjectiveFunctions(); i < getHeight(); i++) { + int start = ignoreObjectiveRows ? getNumObjectiveFunctions() : 0; + for (int i = start; i < getHeight(); i++) { if (MathUtils.equals(getEntry(i, col), 1.0, epsilon) && (row == null)) { row = i; } else if (!MathUtils.equals(getEntry(i, col), 0.0, epsilon)) { @@ -318,24 +337,23 @@ class SimplexTableau implements Serializable { * @return current solution */ protected RealPointValuePair getSolution() { - double[] coefficients = new double[getOriginalNumDecisionVariables()]; - Integer basicRow = - getBasicRow(getNumObjectiveFunctions() + getOriginalNumDecisionVariables()); - double mostNegative = basicRow == null ? 0 : getEntry(basicRow, getRhsOffset()); - Set basicRows = new HashSet(); - for (int i = 0; i < coefficients.length; i++) { - basicRow = getBasicRow(getNumObjectiveFunctions() + i); - if (basicRows.contains(basicRow)) { - // if multiple variables can take a given value - // then we choose the first and set the rest equal to 0 - coefficients[i] = 0; - } else { - basicRows.add(basicRow); - coefficients[i] = - (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) - - (restrictToNonNegative ? 0 : mostNegative); - } - } + double[] coefficients = new double[getOriginalNumDecisionVariables()]; + Integer negativeVarBasicRow = getBasicRowForSolution(getNegativeDecisionVariableOffset()); + double mostNegative = negativeVarBasicRow == null ? 0 : getEntry(negativeVarBasicRow, getRhsOffset()); + Set basicRows = new HashSet(); + for (int i = 0; i < coefficients.length; i++) { + Integer basicRow = getBasicRowForSolution(getNumObjectiveFunctions() + i); + if (basicRows.contains(basicRow)) { + // if multiple variables can take a given value + // then we choose the first and set the rest equal to 0 + coefficients[i] = 0; + } else { + basicRows.add(basicRow); + coefficients[i] = + (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) - + (restrictToNonNegative ? 0 : mostNegative); + } + } return new RealPointValuePair(coefficients, f.getValue(coefficients)); } @@ -430,6 +448,15 @@ class SimplexTableau implements Serializable { protected final int getRhsOffset() { return getWidth() - 1; } + + /** + * Returns the offset of the extra decision variable added when there is a + * negative decision variable in the original problem. + * @return the offset of x- + */ + protected final int getNegativeDecisionVariableOffset() { + return getNumObjectiveFunctions() + getOriginalNumDecisionVariables(); + } /** * Get the number of decision variables. diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 504b8e79f..a48e1da07 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -39,6 +39,10 @@ The type attribute can be add,update,fix,remove. + + Fixed an error leading the simplex solver to compute the right solution + but return another one + Prevent infinite loops in multi-directional direct optimization method when the start point is exactly at the optimal point diff --git a/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java b/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java index 3a082a7b0..9d43a5405 100644 --- a/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java +++ b/src/test/java/org/apache/commons/math/optimization/linear/SimplexSolverTest.java @@ -46,8 +46,18 @@ public class SimplexSolverTest { assertEquals(1.0, solution.getPoint()[1], .0000001); assertEquals(1.0, solution.getPoint()[2], .0000001); assertEquals(3.0, solution.getValue(), .0000001); - } + } + @Test + public void testMath286() throws OptimizationException { + LinearObjectiveFunction f = new LinearObjectiveFunction(new double[] { 0.2, 0.3 }, 0 ); + Collection constraints = new ArrayList(); + constraints.add(new LinearConstraint(new double[] { 1, 1 }, Relationship.EQ, 23.0)); + + RealPointValuePair solution = new SimplexSolver().optimize(f, constraints, GoalType.MAXIMIZE, true); + assertEquals(6.9, solution.getValue(), .0000001); + } + @Test public void testSimplexSolver() throws OptimizationException { LinearObjectiveFunction f =