/*
* 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.mahout.math;
import org.apache.mahout.common.RandomUtils;
import org.apache.mahout.math.function.DoubleDoubleFunction;
import org.apache.mahout.math.function.DoubleFunction;
import org.apache.mahout.math.function.Functions;
import org.junit.Assert;
import org.junit.Test;
import java.util.Random;
public class CholeskyDecompositionTest extends MahoutTestCase {
@Test
public void rank1() {
Matrix x = new DenseMatrix(3, 3);
x.viewRow(0).assign(new double[]{1, 2, 3});
x.viewRow(1).assign(new double[]{2, 4, 6});
x.viewRow(2).assign(new double[]{3, 6, 9});
CholeskyDecomposition rr = new CholeskyDecomposition(x.transpose().times(x), false);
assertEquals(0, new DenseVector(new double[]{3.741657, 7.483315, 11.22497}).aggregate(rr.getL().transpose().viewRow(0), Functions.PLUS, new DoubleDoubleFunction() {
@Override
public double apply(double arg1, double arg2) {
return Math.abs(arg1) - Math.abs(arg2);
}
}), 1.0e-5);
assertEquals(0, rr.getL().viewPart(0, 3, 1, 2).aggregate(Functions.PLUS, Functions.ABS), 1.0e-9);
}
@Test
public void test1() {
final Random rand = RandomUtils.getRandom();
Matrix z = new DenseMatrix(100, 100);
z.assign(new DoubleFunction() {
@Override
public double apply(double arg1) {
return rand.nextDouble();
}
});
Matrix A = z.times(z.transpose());
for (boolean type = false; !type; type=true) {
CholeskyDecomposition cd = new CholeskyDecomposition(A, type);
Matrix L = cd.getL();
// Assert.assertTrue("Positive definite", cd.isPositiveDefinite());
Matrix Abar = L.times(L.transpose());
double error = A.minus(Abar).aggregate(Functions.MAX, Functions.ABS);
Assert.assertEquals("type = " + type, 0, error, 1.0e-10);
// L should give us a quick and dirty LQ decomposition
Matrix q = cd.solveLeft(z);
Matrix id = q.times(q.transpose());
for (int i = 0; i < id.columnSize(); i++) {
Assert.assertEquals("type = " + type, 1, id.get(i, i), 1.0e-9);
Assert.assertEquals("type = " + type, 1, id.viewRow(i).norm(1), 1.0e-9);
}
// and QR as well
q = cd.solveRight(z.transpose());
id = q.transpose().times(q);
for (int i = 0; i < id.columnSize(); i++) {
Assert.assertEquals("type = " + type, 1, id.get(i, i), 1.0e-9);
Assert.assertEquals("type = " + type, 1, id.viewRow(i).norm(1), 1.0e-9);
}
}
}
@Test
public void test2() {
// Test matrix from Nicholas Higham's paper at http://eprints.ma.man.ac.uk/1199/01/covered/MIMS_ep2008_116.pdf
double[][] values = new double[3][];
values[0] = new double[]{1, -1, 1};
values[1] = new double[]{-1, 1, -1};
values[2] = new double[]{1, -1, 2};
Matrix A = new DenseMatrix(values);
// without pivoting
CholeskyDecomposition cd = new CholeskyDecomposition(A, false);
assertEquals(0, cd.getL().times(cd.getL().transpose()).minus(A).aggregate(Functions.PLUS, Functions.ABS), 1.0e-10);
// with pivoting
cd = new CholeskyDecomposition(A);
assertEquals(0, cd.getL().times(cd.getL().transpose()).minus(A).aggregate(Functions.PLUS, Functions.ABS), 1.0e-10);
}
@Test
public void testRankDeficient() {
Matrix A = rank4Matrix();
CholeskyDecomposition cd = new CholeskyDecomposition(A);
PivotedMatrix Ax = new PivotedMatrix(A, cd.getPivot());
CholeskyDecomposition cd2 = new CholeskyDecomposition(Ax, false);
assertEquals(0, cd2.getL().times(cd2.getL().transpose()).minus(Ax).aggregate(Functions.PLUS, Functions.ABS), 1.0e-10);
assertEquals(0, cd.getL().times(cd.getL().transpose()).minus(A).aggregate(Functions.PLUS, Functions.ABS), 1.0e-10);
Assert.assertFalse(cd.isPositiveDefinite());
Matrix L = cd.getL();
Matrix Abar = L.times(L.transpose());
double error = A.minus(Abar).aggregate(Functions.MAX, Functions.ABS);
Assert.assertEquals(0, error, 1.0e-10);
}
private static Matrix rank4Matrix() {
final Random rand = RandomUtils.getRandom();
Matrix u = new DenseMatrix(10, 4);
u.assign(new DoubleFunction() {
@Override
public double apply(double arg1) {
return rand.nextDouble();
}
});
Matrix v = new DenseMatrix(10, 4);
v.assign(new DoubleFunction() {
@Override
public double apply(double arg1) {
return rand.nextDouble();
}
});
Matrix z = u.times(v.transpose());
return z.times(z.transpose());
}
}