diff --git a/pom.xml b/pom.xml
index 0f00a5a90..4a8456eae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -252,6 +252,9 @@
Fredrik Norin
+
+ Sean Owen
+
Sujit Pal
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 48d2257b8..82791d1c4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -51,6 +51,9 @@ If the output is not quite correct, check for invisible trailing spaces!
+
+ "EigenDecomposition": Using tolerance for detecting whether a matrix is singular.
+
Added SparseGradient to deal efficiently with first derivatives when the number
of variables is very large but most computations depend only on a few of the
diff --git a/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java b/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java
index 4652f618b..b5e93ce2b 100644
--- a/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java
+++ b/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java
@@ -513,15 +513,32 @@ public class EigenDecomposition {
* @return true if the decomposed matrix is non-singular.
*/
public boolean isNonSingular() {
+ // The eigenvalues are sorted by size, descending
+ double largestEigenvalueNorm = eigenvalueNorm(0);
+ // Corner case: zero matrix, all exactly 0 eigenvalues
+ if (largestEigenvalueNorm == 0.0) {
+ return false;
+ }
for (int i = 0; i < realEigenvalues.length; ++i) {
- if (realEigenvalues[i] == 0 &&
- imagEigenvalues[i] == 0) {
+ // Looking for eigenvalues that are 0, where we consider anything much much smaller
+ // than the largest eigenvalue to be effectively 0.
+ if (Precision.equals(eigenvalueNorm(i) / largestEigenvalueNorm, 0, EPSILON)) {
return false;
}
}
return true;
}
+ /**
+ * @param i which eigenvalue to find the norm of
+ * @return the norm of ith (complex) eigenvalue.
+ */
+ private double eigenvalueNorm(int i) {
+ final double re = realEigenvalues[i];
+ final double im = imagEigenvalues[i];
+ return FastMath.sqrt(re * re + im * im);
+ }
+
/**
* Get the inverse of the decomposed matrix.
*
diff --git a/src/test/java/org/apache/commons/math3/linear/EigenSolverTest.java b/src/test/java/org/apache/commons/math3/linear/EigenSolverTest.java
index f9fe7859f..f55dad9df 100644
--- a/src/test/java/org/apache/commons/math3/linear/EigenSolverTest.java
+++ b/src/test/java/org/apache/commons/math3/linear/EigenSolverTest.java
@@ -27,6 +27,13 @@ import org.junit.Assert;
public class EigenSolverTest {
+ private double[][] bigSingular = {
+ { 1.0, 2.0, 3.0, 4.0 },
+ { 2.0, 5.0, 3.0, 4.0 },
+ { 7.0, 3.0, 256.0, 1930.0 },
+ { 3.0, 7.0, 6.0, 8.0 }
+ }; // 4th row = 1st + 2nd
+
/** test non invertible matrix */
@Test
public void testNonInvertible() {
@@ -86,6 +93,20 @@ public class EigenSolverTest {
}
}
+ @Test(expected=SingularMatrixException.class)
+ public void testNonInvertibleMath1045() {
+ EigenDecomposition eigen =
+ new EigenDecomposition(MatrixUtils.createRealMatrix(bigSingular));
+ eigen.getSolver().getInverse();
+ }
+
+ @Test(expected=SingularMatrixException.class)
+ public void testZeroMatrix() {
+ EigenDecomposition eigen =
+ new EigenDecomposition(MatrixUtils.createRealMatrix(new double[][] {{0}}));
+ eigen.getSolver().getInverse();
+ }
+
/** test solve dimension errors */
@Test
public void testSolveDimensionErrors() {