[MATH-930] Add new constructors to override the hard-coded cut-off value, further improve javadoc and update failing unit test.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1435810 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2013-01-20 10:04:45 +00:00
parent 573c806be0
commit 0c0822d658
4 changed files with 69 additions and 22 deletions

View File

@ -56,9 +56,9 @@ This is a minor release: It combines bug fixes and new features.
way such as to allow drop-in replacement of the v3.1[.1] JAR file.
">
<action dev="tn" type="fix" issue="MATH-930">
Improved class javadoc wrt convergence criteria and added an
additional constructor to override the default epsilon value in class
"SimplexSolver".
Improved class javadoc wrt convergence criteria and added
additional constructors to override the default epsilon and cut-off
values in class "SimplexSolver".
</action>
<action dev="erans" type="fix" issue="MATH-929" due-to="Piotr Wydrych">
Fixed truncated value in "MultivariateNormalDistribution".

View File

@ -34,8 +34,15 @@ import org.apache.commons.math3.util.Precision;
* <ul>
* <li>Algorithm convergence: 1e-6</li>
* <li>Floating-point comparisons: 10 ulp</li>
* <li>Cut-Off value: 1e-12</li>
* </ul>
* <p>
* 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.
* <p>
* It may also be counter-productive to provide a too large value for {@link MaxIter}
* as parameter in the call of {@link #optimize(org.apache.commons.math3.optim.OptimizationData...)},
* as the {@link SimplexSolver} will use different strategies depending on the current iteration
@ -46,23 +53,32 @@ import org.apache.commons.math3.util.Precision;
* @since 2.0
*/
public class SimplexSolver extends LinearOptimizer {
/** Default amount of error to accept in floating point comparisons (as ulps). */
static final int DEFAULT_ULPS = 10;
/** Default cut-off value. */
static final double DEFAULT_CUT_OFF = 1e-12;
/** Default amount of error to accept for algorithm convergence. */
private static final double DEFAULT_EPSILON = 1.0e-6;
/** Default amount of error to accept in floating point comparisons (as ulps). */
private static final int DEFAULT_ULPS = 10;
/** Amount of error to accept for algorithm convergence. */
private final double epsilon;
/** Amount of error to accept in floating point comparisons (as ulps). */
private final int maxUlps;
/**
* Cut-off value for entries in the tableau: values smaller than the cut-off
* are treated as zero to improve numerical stability.
*/
private final double cutOff;
/**
* Builds a simplex solver with default settings.
*/
public SimplexSolver() {
this(DEFAULT_EPSILON, DEFAULT_ULPS);
this(DEFAULT_EPSILON, DEFAULT_ULPS, DEFAULT_CUT_OFF);
}
/**
@ -71,7 +87,7 @@ public class SimplexSolver extends LinearOptimizer {
* @param epsilon Amount of error to accept for algorithm convergence.
*/
public SimplexSolver(final double epsilon) {
this(epsilon, DEFAULT_ULPS);
this(epsilon, DEFAULT_ULPS, DEFAULT_CUT_OFF);
}
/**
@ -80,10 +96,21 @@ public class SimplexSolver extends LinearOptimizer {
* @param epsilon Amount of error to accept for algorithm convergence.
* @param maxUlps Amount of error to accept in floating point comparisons.
*/
public SimplexSolver(final double epsilon,
final int maxUlps) {
public SimplexSolver(final double epsilon, final int maxUlps) {
this(epsilon, maxUlps, DEFAULT_CUT_OFF);
}
/**
* Builds a simplex solver with a specified accepted amount of error.
*
* @param epsilon Amount of error to accept for algorithm convergence.
* @param maxUlps Amount of error to accept in floating point comparisons.
* @param cutOff Values smaller than the cutOff are treated as zero.
*/
public SimplexSolver(final double epsilon, final int maxUlps, final double cutOff) {
this.epsilon = epsilon;
this.maxUlps = maxUlps;
this.cutOff = cutOff;
}
/**
@ -258,7 +285,8 @@ public class SimplexSolver extends LinearOptimizer {
getGoalType(),
isRestrictedToNonNegative(),
epsilon,
maxUlps);
maxUlps,
cutOff);
solvePhase1(tableau);
tableau.dropPhase1Objective();

View File

@ -66,12 +66,6 @@ class SimplexTableau implements Serializable {
/** Column label for negative vars. */
private static final String NEGATIVE_VAR_COLUMN_LABEL = "x-";
/** Default amount of error to accept in floating point comparisons (as ulps). */
private static final int DEFAULT_ULPS = 10;
/** The cut-off threshold to zero-out entries. */
private static final double CUTOFF_THRESHOLD = 1e-12;
/** Serializable version identifier. */
private static final long serialVersionUID = -1369660067587938365L;
@ -105,6 +99,9 @@ class SimplexTableau implements Serializable {
/** Amount of error to accept in floating point comparisons. */
private final int maxUlps;
/** Cut-off value for entries in the tableau. */
private final double cutOff;
/**
* Builds a tableau for a linear problem.
*
@ -120,7 +117,8 @@ class SimplexTableau implements Serializable {
final GoalType goalType,
final boolean restrictToNonNegative,
final double epsilon) {
this(f, constraints, goalType, restrictToNonNegative, epsilon, DEFAULT_ULPS);
this(f, constraints, goalType, restrictToNonNegative, epsilon,
SimplexSolver.DEFAULT_ULPS, SimplexSolver.DEFAULT_CUT_OFF);
}
/**
@ -138,11 +136,32 @@ class SimplexTableau implements Serializable {
final boolean restrictToNonNegative,
final double epsilon,
final int maxUlps) {
this(f, constraints, goalType, restrictToNonNegative, epsilon, maxUlps, SimplexSolver.DEFAULT_CUT_OFF);
}
/**
* Build a tableau for a linear problem.
* @param f linear objective function
* @param constraints linear constraints
* @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} 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
* @param maxUlps amount of error to accept in floating point comparisons
* @param cutOff the cut-off value for tableau entries
*/
SimplexTableau(final LinearObjectiveFunction f,
final Collection<LinearConstraint> constraints,
final GoalType goalType,
final boolean restrictToNonNegative,
final double epsilon,
final int maxUlps,
final double cutOff) {
this.f = f;
this.constraints = normalizeConstraints(constraints);
this.restrictToNonNegative = restrictToNonNegative;
this.epsilon = epsilon;
this.maxUlps = maxUlps;
this.cutOff = cutOff;
this.numDecisionVariables = f.getCoefficients().getDimension() +
(restrictToNonNegative ? 0 : 1);
this.numSlackVariables = getConstraintTypeCounts(Relationship.LEQ) +
@ -462,8 +481,8 @@ class SimplexTableau implements Serializable {
final double multiple) {
for (int i = 0; i < getWidth(); i++) {
double result = tableau.getEntry(minuendRow, i) - tableau.getEntry(subtrahendRow, i) * multiple;
// cut-off values smaller than the CUTOFF_THRESHOLD, otherwise may lead to numerical instabilities
if (FastMath.abs(result) < CUTOFF_THRESHOLD) {
// cut-off values smaller than the cut-off threshold, otherwise may lead to numerical instabilities
if (FastMath.abs(result) < cutOff) {
result = 0.0;
}
tableau.setEntry(minuendRow, i, result);

View File

@ -420,11 +420,11 @@ public class SimplexSolverTest {
double[] objFunctionCoeff = new double[33];
objFunctionCoeff[3] = 1;
LinearObjectiveFunction f = new LinearObjectiveFunction(objFunctionCoeff, 0);
SimplexSolver solver = new SimplexSolver(1e-4, 10);
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-6);
Assert.assertEquals(0.3752298, solution.getValue(), 1e-4);
}
@Test