diff --git a/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java b/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java index 052a9709b..a603e2b3b 100644 --- a/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java +++ b/src/main/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizer.java @@ -21,6 +21,7 @@ import java.util.Arrays; import org.apache.commons.math.FunctionEvaluationException; import org.apache.commons.math.optimization.OptimizationException; import org.apache.commons.math.optimization.VectorialPointValuePair; +import org.apache.commons.math.util.MathUtils; /** @@ -140,16 +141,20 @@ public class LevenbergMarquardtOptimizer extends AbstractLeastSquaresOptimizer { * and the columns of the jacobian. */ private double orthoTolerance; + /** Threshold for QR ranking. */ + private double qrRankingThreshold; + /** * Build an optimizer for least squares problems. *

The default values for the algorithm settings are: *

*

*

These default values may be overridden after construction. If the {@link @@ -168,6 +173,7 @@ public class LevenbergMarquardtOptimizer extends AbstractLeastSquaresOptimizer { setCostRelativeTolerance(1.0e-10); setParRelativeTolerance(1.0e-10); setOrthoTolerance(1.0e-10); + setQRRankingThreshold(MathUtils.SAFE_MIN); } @@ -216,6 +222,19 @@ public class LevenbergMarquardtOptimizer extends AbstractLeastSquaresOptimizer { this.orthoTolerance = orthoTolerance; } + /** + * Set the desired threshold for QR ranking. + *

+ * If the squared norm of a column vector is smaller or equal to this threshold + * during QR decomposition, it is considered to be a zero vector and hence the + * rank of the matrix is reduced. + *

+ * @param qrRankingThreshold threshold for QR ranking + */ + public void setQRRankingThreshold(final double qrRankingThreshold) { + this.qrRankingThreshold = qrRankingThreshold; + } + /** {@inheritDoc} */ @Override protected VectorialPointValuePair doOptimize() @@ -805,7 +824,7 @@ public class LevenbergMarquardtOptimizer extends AbstractLeastSquaresOptimizer { ak2 = norm2; } } - if (ak2 == 0) { + if (ak2 <= qrRankingThreshold) { rank = k; return; } diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index f63cb60b4..f8b8a49c9 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -52,6 +52,10 @@ The type attribute can be add,update,fix,remove. If the output is not quite correct, check for invisible trailing spaces! --> + + Added a setQRRankingThreshold method to Levenberg-Marquardt optimizer to improve robustness + of rank determination. + Added random data generation methods to RandomDataImpl for the remaining distributions in the distributions package. Added a generic nextInversionDeviate method that takes a discrete diff --git a/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java b/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java index 06f1721fb..f2b850f48 100644 --- a/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java +++ b/src/test/java/org/apache/commons/math/optimization/general/LevenbergMarquardtOptimizerTest.java @@ -505,10 +505,12 @@ public class LevenbergMarquardtOptimizerTest problem.addPoint (2, -2.1488478161387325); problem.addPoint (3, -1.9122489313410047); problem.addPoint (4, 1.7785661310051026); - new LevenbergMarquardtOptimizer().optimize(problem, - new double[] { 0, 0, 0, 0, 0 }, - new double[] { 0.0, 4.4e-323, 1.0, 4.4e-323, 0.0 }, - new double[] { 0, 0, 0 }); + LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer(); + optimizer.setQRRankingThreshold(0); + optimizer.optimize(problem, + new double[] { 0, 0, 0, 0, 0 }, + new double[] { 0.0, 4.4e-323, 1.0, 4.4e-323, 0.0 }, + new double[] { 0, 0, 0 }); fail("an exception should have been thrown"); } catch (OptimizationException ee) { // expected behavior