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:
*
- * - {@link #setConvergenceChecker vectorial convergence checker}: null
- * - {@link #setInitialStepBoundFactor initial step bound factor}: 100.0
- * - {@link #setMaxIterations maximal iterations}: 1000
- * - {@link #setCostRelativeTolerance cost relative tolerance}: 1.0e-10
- * - {@link #setParRelativeTolerance parameters relative tolerance}: 1.0e-10
- * - {@link #setOrthoTolerance orthogonality tolerance}: 1.0e-10
+ * - {@link #setConvergenceChecker(VectorialConvergenceChecker) vectorial convergence checker}: null
+ * - {@link #setInitialStepBoundFactor(double) initial step bound factor}: 100.0
+ * - {@link #setMaxIterations(int) maximal iterations}: 1000
+ * - {@link #setCostRelativeTolerance(double) cost relative tolerance}: 1.0e-10
+ * - {@link #setParRelativeTolerance(double) parameters relative tolerance}: 1.0e-10
+ * - {@link #setOrthoTolerance(double) orthogonality tolerance}: 1.0e-10
+ * - {@link #setQRRankingThreshold(double) QR ranking threshold}: {@link MathUtils#SAFE_MIN}
*
*
* 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