/* * DependentProductChainSubstitutionModel.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.evomodel.substmodel; import dr.evomodel.siteratemodel.SiteRateModel; import dr.inference.model.Parameter; import dr.math.KroneckerOperation; import java.util.List; /** * A derived class from ProductChainSubstitutionModel that introduces dependence between sites. * * @author Marc A. Suchard * @author Yu-Nong Gong * @version 08/01/2010 */ public class DependentProductChainSubstitutionModel extends ProductChainSubstitutionModel { public DependentProductChainSubstitutionModel(String name, List<SubstitutionModel> baseModels, Parameter dependenceParameter) { this(name, baseModels, null, dependenceParameter); } public DependentProductChainSubstitutionModel(String name, List<SubstitutionModel> baseModels, List<SiteRateModel> rateModels, Parameter dependenceParameter) { super(name, baseModels, rateModels); this.dependenceParameter = dependenceParameter; eigenSystemCES = new ColtEigenSystem(stateCount); } public EigenDecomposition getEigenDecomposition() { synchronized (this) { if (updateMatrix) { computeKroneckerSumsAndProducts(); } } return eigenDecomposition; } public void getInfinitesimalMatrix(double[] out) { getEigenDecomposition(); // Updates rate matrix if necessary System.arraycopy(rateMatrix, 0, out, 0, stateCount * stateCount); } private void computeKroneckerSumsAndProducts() { int currentStateSize = stateSizes[0]; double[] currentRate = new double[currentStateSize * currentStateSize]; baseModels.get(0).getInfinitesimalMatrix(currentRate); currentRate = scaleForProductChain(currentRate, 0); for (int i = 1; i < numBaseModel; i++) { SubstitutionModel nextModel = baseModels.get(i); int nextStateSize = stateSizes[i]; double[] nextRate = new double[nextStateSize * nextStateSize]; nextModel.getInfinitesimalMatrix(nextRate); nextRate = scaleForProductChain(nextRate, i); currentRate = KroneckerOperation.sum(currentRate, currentStateSize, nextRate, nextStateSize); currentStateSize *= nextStateSize; } double dependence = dependenceParameter.getParameterValue(0); /* put dependence parameter into anti-diagonal elements*/ for (int j = 0; j < stateCount; j++) { currentRate[(j + 1) * stateCount - (j + 1)] = dependence; } /* put dependence parameter into co-varying sites */ /* take nucleotide for example, A, T, C, and G as 0, 1, 2, and 3 */ String[] letA = new String[(int) Math.sqrt(stateCount)]; String[] letB = new String[(int) Math.sqrt(stateCount)]; String[] letAB = new String[stateCount]; int count = 0; for (int letI = 0; letI < letA.length; letI++) { letA[letI] = String.valueOf(count); letB[letI] = String.valueOf(count); count++; } /* generate two-state array {(00), (01), (02), (03), (10), ..., (33)} */ int letABi = 0; for (int letAi = 0; letAi < letA.length; letAi++) { for (int letBi = 0; letBi < letB.length; letBi++) { letAB[letABi] = letA[letAi] + " " + letB[letBi]; letABi++; } } /* assign dependence parameter to co-varying sites */ for (int j = 0; j < letAB.length; j++) { for (int k = 0; k < letAB.length; k++) { String[] Spl1 = letAB[j].split(" "); String[] Spl2 = letAB[k].split(" "); if (!Spl1[0].equals(Spl2[0]) && !Spl1[1].equals(Spl2[1])) { /* Is co-varying site */ currentRate[j * stateCount + k] = dependence; } } } /* Rescale diagonal elements in currentRate */ double diagonalSum = 0; for (int k = 0; k < stateCount; k++) { for (int l = k * stateCount; l < ((k + 1) * stateCount); l++) { if (l != (k * stateCount + k)) { diagonalSum += currentRate[l]; } } /* assign value to diagonal element */ currentRate[k * stateCount + k] = -(diagonalSum); diagonalSum = 0; } rateMatrix = currentRate; // Call standard eigendecomposition on currentRate decompose(); } private void decompose() { double[][] q = new double[stateCount][stateCount]; for (int i = 0; i < stateCount; i++) { System.arraycopy(rateMatrix, i * stateCount, q[i], 0, stateCount); } eigenDecomposition = eigenSystemCES.decomposeMatrix(q); //System.err.println("eigenSystem null? " + (eigenSystem == null ? "yes" : "no")); //eigenDecomposition = eigenSystem.decomposeMatrix(q); updateMatrix = false; } private final Parameter dependenceParameter; private final EigenSystem eigenSystemCES; //= new ColtEigenSystem(); }