/* * SymmetricMatrix.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.math.matrixAlgebra; /** * Symmetric matrix * * @author Didier H. Besset */ public class SymmetricMatrix extends Matrix { private static int lupCRLCriticalDimension = 36; /** * Creates a symmetric matrix with given components. * * @param a double[][] */ public SymmetricMatrix(double[][] a) { super(a); } /** * @param n int * @throws java.lang.NegativeArraySizeException * if n <= 0 */ public SymmetricMatrix(int n) throws NegativeArraySizeException { super(n, n); } /** * Constructor method. * * @param n int * @param m int * @throws java.lang.NegativeArraySizeException * if n,m <= 0 */ public SymmetricMatrix(int n, int m) throws NegativeArraySizeException { super(n, m); } /** * @param a Matrix * @return SymmetricMatrix sum of the matrix with the supplied matrix. * @throws IllegalDimension if the supplied matrix does not * have the same dimensions. */ public SymmetricMatrix add(SymmetricMatrix a) throws IllegalDimension { return new SymmetricMatrix(addComponents(a)); } /** * Answers the inverse of the receiver computed via the CRL algorithm. * * @return MatrixAlgebra.SymmetricMatrix * @throws java.lang.ArithmeticException if the matrix is singular. */ private SymmetricMatrix crlInverse() throws ArithmeticException { if (rows() == 1) return inverse1By1(); else if (rows() == 2) return inverse2By2(); Matrix[] splitMatrices = split(); SymmetricMatrix b1 = (SymmetricMatrix) splitMatrices[0].inverse(); Matrix cb1 = splitMatrices[2].secureProduct(b1); SymmetricMatrix cb1cT = new SymmetricMatrix( cb1.productWithTransposedComponents(splitMatrices[2])); splitMatrices[1] = ((SymmetricMatrix) splitMatrices[1]).secureSubtract(cb1cT).inverse(); splitMatrices[2] = splitMatrices[1].secureProduct(cb1); splitMatrices[0] = b1.secureAdd(new SymmetricMatrix( cb1.transposedProductComponents(splitMatrices[2]))); return SymmetricMatrix.join(splitMatrices); } /** * @return MatrixAlgebra.SymmetricMatrix * @throws matrixAlgebra.IllegalDimension The supplied components are not those of a square matrix. * @throws matrixAlgebra.NonSymmetricComponents * The supplied components are not symmetric. * @param comp double[][] components of the matrix */ public static SymmetricMatrix fromComponents(double[][] comp) throws IllegalDimension, NonSymmetricComponents { if (comp.length != comp[0].length) throw new IllegalDimension("Non symmetric components: a " + comp.length + " by " + comp[0].length + " matrix cannot be symmetric"); for (int i = 0; i < comp.length; i++) { for (int j = 0; j < i; j++) { if (comp[i][j] != comp[j][i]) throw new NonSymmetricComponents( "Non symmetric components: a[" + i + "][" + j + "]= " + comp[i][j] + ", a[" + j + "][" + i + "]= " + comp[j][i]); } } return new SymmetricMatrix(comp); } /** * @param n int * @return SymmetricMatrix an identity matrix of size n */ public static SymmetricMatrix identityMatrix(int n) { double[][] a = new double[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) a[i][j] = 0; a[i][i] = 1; } return new SymmetricMatrix(a); } /** * @return Matrix inverse of the receiver. * @throws java.lang.ArithmeticException if the receiver is * a singular matrix. */ public Matrix inverse() throws ArithmeticException { return rows() < lupCRLCriticalDimension ? new SymmetricMatrix( (new LUPDecomposition(this)).inverseMatrixComponents()) : crlInverse(); } /** * Compute the inverse of the receiver in the case of a 1 by 1 matrix. * Internal use only: no check is made. * * @return MatrixAlgebra.SymmetricMatrix */ private SymmetricMatrix inverse1By1() { double[][] newComponents = new double[1][1]; newComponents[0][0] = 1 / components[0][0]; return new SymmetricMatrix(newComponents); } /** * Compute the inverse of the receiver in the case of a 2 by 2 matrix. * Internal use only: no check is made. * * @return MatrixAlgebra.SymmetricMatrix */ private SymmetricMatrix inverse2By2() { double[][] newComponents = new double[2][2]; double inverseDeterminant = 1 / (components[0][0] * components[1][1] - components[0][1] * components[1][0]); newComponents[0][0] = inverseDeterminant * components[1][1]; newComponents[1][1] = inverseDeterminant * components[0][0]; newComponents[0][1] = newComponents[1][0] = -inverseDeterminant * components[1][0]; return new SymmetricMatrix(newComponents); } /** * Build a matrix from 3 parts (inverse of split). * * @param a MatrixAlgebra.Matrix[] * @return MatrixAlgebra.SymmetricMatrix */ private static SymmetricMatrix join(Matrix[] a) { int p = a[0].rows(); int n = p + a[1].rows(); double[][] newComponents = new double[n][n]; for (int i = 0; i < p; i++) { for (int j = 0; j < p; j++) newComponents[i][j] = a[0].components[i][j]; for (int j = p; j < n; j++) newComponents[i][j] = newComponents[j][i] = -a[2].components[j - p][i]; } for (int i = p; i < n; i++) { for (int j = p; j < n; j++) newComponents[i][j] = a[1].components[i - p][j - p]; } return new SymmetricMatrix(newComponents); } /** * @param n int * @return int */ private int largestPowerOf2SmallerThan(int n) { int m = 2; int m2; while (true) { m2 = 2 * m; if (m2 >= n) return m; m = m2; } } /** * @param a double * @return MatrixAlgebra.SymmetricMatrix */ public Matrix product(double a) { return new SymmetricMatrix(productComponents(a)); } /** * @param a Matrix * @return Matrix product of the receiver with the supplied matrix * @throws IllegalDimension If the number of columns of * the receivers are not equal to the number of rows * of the supplied matrix. */ public SymmetricMatrix product(SymmetricMatrix a) throws IllegalDimension { return new SymmetricMatrix(productComponents(a)); } /** * @param a MatrixAlgebra.Matrix * @return MatrixAlgebra.Matrix product of the receiver with * the transpose of the supplied matrix * @throws MatrixAlgebra.IllegalDimension If the number of * columns of the receiver are not equal to the number of * columns of the supplied matrix. */ public SymmetricMatrix productWithTransposed(SymmetricMatrix a) throws IllegalDimension { if (a.columns() != columns()) throw new IllegalDimension( "Operation error: cannot multiply a " + rows() + " by " + columns() + " matrix with the transpose of a " + a.rows() + " by " + a.columns() + " matrix"); return new SymmetricMatrix(productWithTransposedComponents(a)); } /** * Same as add ( SymmetricMatrix a), but without dimension checking. * * @param a MatrixAlgebra.SymmetricMatrix * @return MatrixAlgebra.SymmetricMatrix */ protected SymmetricMatrix secureAdd(SymmetricMatrix a) { return new SymmetricMatrix(addComponents(a)); } /** * Same as product(SymmetricMatrix a), but without dimension checking. * * @param a MatrixAlgebra.SymmetricMatrix * @return MatrixAlgebra.SymmetricMatrix */ protected SymmetricMatrix secureProduct(SymmetricMatrix a) { return new SymmetricMatrix(productComponents(a)); } /** * Same as subtract ( SymmetricMatrix a), but without dimension checking. * * @param a MatrixAlgebra.SymmetricMatrix * @return MatrixAlgebra.SymmetricMatrix */ protected SymmetricMatrix secureSubtract(SymmetricMatrix a) { return new SymmetricMatrix(subtractComponents(a)); } /** * Divide the receiver into 3 matrices of approximately equal dimension. * * @return MatrixAlgebra.Matrix[] Array of splitted matrices */ private Matrix[] split() { int n = rows(); int p = largestPowerOf2SmallerThan(n); int q = n - p; double[][] a = new double[p][p]; double[][] b = new double[q][q]; double[][] c = new double[q][p]; for (int i = 0; i < p; i++) { for (int j = 0; j < p; j++) a[i][j] = components[i][j]; for (int j = p; j < n; j++) c[j - p][i] = components[i][j]; } for (int i = p; i < n; i++) { for (int j = p; j < n; j++) b[i - p][j - p] = components[i][j]; } Matrix[] answer = new Matrix[3]; answer[0] = new SymmetricMatrix(a); answer[1] = new SymmetricMatrix(b); answer[2] = new Matrix(c); return answer; } /** * @param a matrixAlgebra.SymmetricMatrix * @return matrixAlgebra.SymmetricMatrix * @throws matrixAlgebra.IllegalDimension (from constructor). */ public SymmetricMatrix subtract(SymmetricMatrix a) throws IllegalDimension { return new SymmetricMatrix(subtractComponents(a)); } /** * @return matrixAlgebra.Matrix the same matrix */ public Matrix transpose() { return this; } /** * @param a MatrixAlgebra.SymmetricMatrix * @return MatrixAlgebra.SymmetricMatrix product of the tranpose * of the receiver with the supplied matrix * @throws MatrixAlgebra.IllegalDimension If the number of * rows of the receiver are not equal to * the number of rows of the supplied matrix. */ public SymmetricMatrix transposedProduct(SymmetricMatrix a) throws IllegalDimension { if (a.rows() != rows()) throw new IllegalDimension( "Operation error: cannot multiply a tranposed " + rows() + " by " + columns() + " matrix with a " + a.rows() + " by " + a.columns() + " matrix"); return new SymmetricMatrix(transposedProductComponents(a)); } }