Author: luc
Date: Thu Sep 13 15:13:03 2012
New Revision: 1384363
URL: http://svn.apache.org/viewvc?rev=1384363&view=rev
Log:
Fixed an error in rectangular Cholesky decomposition.
JIRA: MATH-789
Added:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
(with props)
Modified:
commons/proper/math/trunk/src/changes/changes.xml
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/random/CorrelatedRandomVectorGeneratorTest.java
Modified: commons/proper/math/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/changes/changes.xml?rev=1384363&r1=1384362&r2=1384363&view=diff
==============================================================================
--- commons/proper/math/trunk/src/changes/changes.xml (original)
+++ commons/proper/math/trunk/src/changes/changes.xml Thu Sep 13 15:13:03 2012
@@ -52,8 +52,11 @@ If the output is not quite correct, chec
<body>
<release version="3.1" date="TBD" description="
">
+ <action dev="lus" type="fix" issue="MATH-789">
+ Fixed an error in rectangular Cholesky decomposition.
+ </action>
<action dev="psteitz" type="update" issue="MATH-859">
- Clarified definition of isSupportXxxBoundInclusive in RealDistribution
+ Clarified definition of isSupportXxxBoundInclusive in
RealDistribution
interface, made code consistent with the definition, and deprecated
these methods, marking for removal in 4.0.
</action>
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java?rev=1384363&r1=1384362&r2=1384363&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
Thu Sep 13 15:13:03 2012
@@ -62,11 +62,10 @@ public class RectangularCholeskyDecompos
public RectangularCholeskyDecomposition(RealMatrix matrix, double small)
throws NonPositiveDefiniteMatrixException {
- int order = matrix.getRowDimension();
- double[][] c = matrix.getData();
- double[][] b = new double[order][order];
+ final int order = matrix.getRowDimension();
+ final double[][] c = matrix.getData();
+ final double[][] b = new double[order][order];
- int[] swap = new int[order];
int[] index = new int[order];
for (int i = 0; i < order; ++i) {
index[i] = i;
@@ -76,21 +75,24 @@ public class RectangularCholeskyDecompos
for (boolean loop = true; loop;) {
// find maximal diagonal element
- swap[r] = r;
+ int swapR = r;
for (int i = r + 1; i < order; ++i) {
int ii = index[i];
- int isi = index[swap[i]];
- if (c[ii][ii] > c[isi][isi]) {
- swap[r] = i;
+ int isr = index[swapR];
+ if (c[ii][ii] > c[isr][isr]) {
+ swapR = i;
}
}
// swap elements
- if (swap[r] != r) {
- int tmp = index[r];
- index[r] = index[swap[r]];
- index[swap[r]] = tmp;
+ if (swapR != r) {
+ final int tmpIndex = index[r];
+ index[r] = index[swapR];
+ index[swapR] = tmpIndex;
+ final double[] tmpRow = b[r];
+ b[r] = b[swapR];
+ b[swapR] = tmpRow;
}
// check diagonal element
@@ -118,17 +120,18 @@ public class RectangularCholeskyDecompos
} else {
// transform the matrix
- double sqrt = FastMath.sqrt(c[ir][ir]);
+ final double sqrt = FastMath.sqrt(c[ir][ir]);
b[r][r] = sqrt;
- double inverse = 1 / sqrt;
+ final double inverse = 1 / sqrt;
+ final double inverse2 = 1 / c[ir][ir];
for (int i = r + 1; i < order; ++i) {
- int ii = index[i];
- double e = inverse * c[ii][ir];
+ final int ii = index[i];
+ final double e = inverse * c[ii][ir];
b[i][r] = e;
- c[ii][ii] -= e * e;
+ c[ii][ii] -= c[ii][ir] * c[ii][ir] * inverse2;
for (int j = r + 1; j < i; ++j) {
- int ij = index[j];
- double f = c[ii][ij] - e * b[j][r];
+ final int ij = index[j];
+ final double f = c[ii][ij] - e * b[j][r];
c[ii][ij] = f;
c[ij][ii] = f;
}
Added:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java?rev=1384363&view=auto
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
(added)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
Thu Sep 13 15:13:03 2012
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+public class RectangularCholeskyDecompositionTest {
+
+ @Test
+ public void testDecomposition3x3() {
+
+ RealMatrix m = MatrixUtils.createRealMatrix(new double[][] {
+ { 1, 9, 9 },
+ { 9, 225, 225 },
+ { 9, 225, 625 }
+ });
+
+ RectangularCholeskyDecomposition d =
+ new RectangularCholeskyDecomposition(m, 1.0e-6);
+
+ // as this decomposition permutes lines and columns, the root is NOT
triangular
+ // (in fact here it is the lower right part of the matrix which is
zero and
+ // the upper left non-zero)
+ Assert.assertEquals(0.8, d.getRootMatrix().getEntry(0, 2), 1.0e-15);
+ Assert.assertEquals(25.0, d.getRootMatrix().getEntry(2, 0), 1.0e-15);
+ Assert.assertEquals(0.0, d.getRootMatrix().getEntry(2, 2), 1.0e-15);
+
+ RealMatrix root = d.getRootMatrix();
+ RealMatrix rebuiltM = root.multiply(root.transpose());
+ Assert.assertEquals(0.0, m.subtract(rebuiltM).getNorm(), 1.0e-15);
+
+ }
+
+ @Test
+ public void testFullRank() {
+
+ RealMatrix base = MatrixUtils.createRealMatrix(new double[][] {
+ { 0.1159548705, 0., 0., 0. },
+ { 0.0896442724, 0.1223540781, 0., 0. },
+ { 0.0852155322, 4.558668e-3, 0.1083577299, 0. },
+ { 0.0905486674, 0.0213768077, 0.0128878333, 0.1014155693 }
+ });
+
+ RealMatrix m = base.multiply(base.transpose());
+
+ RectangularCholeskyDecomposition d =
+ new RectangularCholeskyDecomposition(m, 1.0e-10);
+
+ RealMatrix root = d.getRootMatrix();
+ RealMatrix rebuiltM = root.multiply(root.transpose());
+ Assert.assertEquals(0.0, m.subtract(rebuiltM).getNorm(), 1.0e-15);
+
+ // the pivoted Cholesky decomposition is *not* unique. Here, the root
is
+ // not equal to the original trianbular base matrix
+ Assert.assertTrue(root.subtract(base).getNorm() > 0.3);
+
+ }
+
+ @Test
+ public void testMath789() {
+
+ final RealMatrix m1 = MatrixUtils.createRealMatrix(new double[][]{
+ {0.013445532, 0.010394690, 0.009881156, 0.010499559},
+ {0.010394690, 0.023006616, 0.008196856, 0.010732709},
+ {0.009881156, 0.008196856, 0.019023866, 0.009210099},
+ {0.010499559, 0.010732709, 0.009210099, 0.019107243}
+ });
+ RealMatrix root1 = new RectangularCholeskyDecomposition(m1,
1.0e-10).getRootMatrix();
+ RealMatrix rebuiltM1 = root1.multiply(root1.transpose());
+ Assert.assertEquals(0.0, m1.subtract(rebuiltM1).getNorm(), 1.0e-16);
+
+ final RealMatrix m2 = MatrixUtils.createRealMatrix(new double[][]{
+ {0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.013445532, 0.010394690, 0.009881156, 0.010499559},
+ {0.0, 0.010394690, 0.023006616, 0.008196856, 0.010732709},
+ {0.0, 0.009881156, 0.008196856, 0.019023866, 0.009210099},
+ {0.0, 0.010499559, 0.010732709, 0.009210099, 0.019107243}
+ });
+ RealMatrix root2 = new RectangularCholeskyDecomposition(m2,
1.0e-10).getRootMatrix();
+ RealMatrix rebuiltM2 = root2.multiply(root2.transpose());
+ Assert.assertEquals(0.0, m2.subtract(rebuiltM2).getNorm(), 1.0e-16);
+
+ final RealMatrix m3 = MatrixUtils.createRealMatrix(new double[][]{
+ {0.013445532, 0.010394690, 0.0, 0.009881156, 0.010499559},
+ {0.010394690, 0.023006616, 0.0, 0.008196856, 0.010732709},
+ {0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.009881156, 0.008196856, 0.0, 0.019023866, 0.009210099},
+ {0.010499559, 0.010732709, 0.0, 0.009210099, 0.019107243}
+ });
+ RealMatrix root3 = new RectangularCholeskyDecomposition(m3,
1.0e-10).getRootMatrix();
+ RealMatrix rebuiltM3 = root3.multiply(root3.transpose());
+ Assert.assertEquals(0.0, m3.subtract(rebuiltM3).getNorm(), 1.0e-16);
+
+ }
+
+}
Propchange:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/RectangularCholeskyDecompositionTest.java
------------------------------------------------------------------------------
svn:keywords = "Author Date Id Revision"
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/random/CorrelatedRandomVectorGeneratorTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/random/CorrelatedRandomVectorGeneratorTest.java?rev=1384363&r1=1384362&r2=1384363&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/random/CorrelatedRandomVectorGeneratorTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/random/CorrelatedRandomVectorGeneratorTest.java
Thu Sep 13 15:13:03 2012
@@ -17,8 +17,13 @@
package org.apache.commons.math3.random;
+import java.util.Arrays;
+
+import org.apache.commons.math3.TestUtils;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.stat.correlation.StorelessCovariance;
import org.apache.commons.math3.stat.descriptive.moment.VectorialCovariance;
import org.apache.commons.math3.stat.descriptive.moment.VectorialMean;
import org.apache.commons.math3.util.FastMath;
@@ -82,9 +87,19 @@ public class CorrelatedRandomVectorGener
CorrelatedRandomVectorGenerator sg =
new CorrelatedRandomVectorGenerator(mean, covRM, 0.00001, rg);
+ double[] min = new double[mean.length];
+ Arrays.fill(min, Double.POSITIVE_INFINITY);
+ double[] max = new double[mean.length];
+ Arrays.fill(max, Double.NEGATIVE_INFINITY);
for (int i = 0; i < 10; i++) {
double[] generated = sg.nextVector();
- Assert.assertTrue(FastMath.abs(generated[0] - 1) > 0.1);
+ for (int j = 0; j < generated.length; ++j) {
+ min[j] = FastMath.min(min[j], generated[j]);
+ max[j] = FastMath.max(max[j], generated[j]);
+ }
+ }
+ for (int j = 0; j < min.length; ++j) {
+ Assert.assertTrue(max[j] - min[j] > 2.0);
}
}
@@ -123,4 +138,61 @@ public class CorrelatedRandomVectorGener
}
}
+
+ @Test
+ public void testSampleWithZeroCovariance() {
+ final double[][] covMatrix1 = new double[][]{
+ {0.013445532, 0.010394690, 0.009881156, 0.010499559},
+ {0.010394690, 0.023006616, 0.008196856, 0.010732709},
+ {0.009881156, 0.008196856, 0.019023866, 0.009210099},
+ {0.010499559, 0.010732709, 0.009210099, 0.019107243}
+ };
+
+ final double[][] covMatrix2 = new double[][]{
+ {0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.013445532, 0.010394690, 0.009881156, 0.010499559},
+ {0.0, 0.010394690, 0.023006616, 0.008196856, 0.010732709},
+ {0.0, 0.009881156, 0.008196856, 0.019023866, 0.009210099},
+ {0.0, 0.010499559, 0.010732709, 0.009210099, 0.019107243}
+ };
+
+ final double[][] covMatrix3 = new double[][]{
+ {0.013445532, 0.010394690, 0.0, 0.009881156, 0.010499559},
+ {0.010394690, 0.023006616, 0.0, 0.008196856, 0.010732709},
+ {0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.009881156, 0.008196856, 0.0, 0.019023866, 0.009210099},
+ {0.010499559, 0.010732709, 0.0, 0.009210099, 0.019107243}
+ };
+
+ testSampler(covMatrix1, 10000, 0.001);
+ testSampler(covMatrix2, 10000, 0.001);
+ testSampler(covMatrix3, 10000, 0.001);
+
+ }
+
+ private CorrelatedRandomVectorGenerator createSampler(double[][] cov) {
+ RealMatrix matrix = new Array2DRowRealMatrix(cov);
+ double small = 10e-12 * matrix.getNorm();
+ return new CorrelatedRandomVectorGenerator(
+ new double[cov.length],
+ matrix,
+ small,
+ new GaussianRandomGenerator(new JDKRandomGenerator()));
+ }
+
+ private void testSampler(final double[][] covMatrix, int samples, double
epsilon) {
+ CorrelatedRandomVectorGenerator sampler = createSampler(covMatrix);
+
+ StorelessCovariance cov = new StorelessCovariance(covMatrix.length);
+ for (int i = 0; i < samples; ++i) {
+ cov.increment(sampler.nextVector());
+ }
+
+ double[][] sampleCov = cov.getData();
+ for (int r = 0; r < covMatrix.length; ++r) {
+ TestUtils.assertEquals(covMatrix[r], sampleCov[r], epsilon);
+ }
+
+ }
+
}