package beast.evolution.substitutionmodel; import java.lang.reflect.InvocationTargetException; import beast.core.Description; import beast.core.Input.Validate; import beast.core.parameter.RealParameter; import beast.evolution.tree.Node; @Description("A substitution model where the rates and frequencies are obtained from " + "empirical evidence. Especially, amino acid models like WAG.") public abstract class EmpiricalSubstitutionModel extends GeneralSubstitutionModel { public EmpiricalSubstitutionModel() { frequenciesInput.setRule(Validate.OPTIONAL); ratesInput.setRule(Validate.OPTIONAL); } double[] m_empiricalRates; @Override public void initAndValidate() { frequencies = getEmpericalFrequencieValues(); m_empiricalRates = getEmpericalRateValues(); int freqs = frequencies.getFreqs().length; if (m_empiricalRates.length != freqs * (freqs - 1)) { throw new IllegalArgumentException("The number of empirical rates (" + m_empiricalRates.length + ") should be " + "equal to #frequencies * (#frequencies-1) = (" + freqs + "*" + (freqs - 1) + ")."); } updateMatrix = true; nrOfStates = frequencies.getFreqs().length; try { eigenSystem = createEigenSystem(); } catch (SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalArgumentException(e.getMessage()); } rateMatrix = new double[nrOfStates][nrOfStates]; relativeRates = new double[m_empiricalRates.length]; storedRelativeRates = new double[m_empiricalRates.length]; } // initAndValidate @Override protected void setupRelativeRates() { System.arraycopy(m_empiricalRates, 0, relativeRates, 0, m_empiricalRates.length); } /** * convert empirical rates into a RealParameter, only off diagonal entries are recorded * */ double[] getEmpericalRateValues() { double[][] matrix = getEmpiricalRates(); int[] order = getEncodingOrder(); int states = matrix.length; double[] rates = new double[states * (states - 1)]; int k = 0; for (int i = 0; i < states; i++) { int u = order[i]; for (int j = 0; j < states; j++) { int v = order[j]; if (i != j) { rates[k++] = matrix[Math.min(u, v)][Math.max(u, v)]; } } } return rates; } /** * convert empirical frequencies into a RealParameter * */ Frequencies getEmpericalFrequencieValues() { double[] freqs = getEmpiricalFrequencies(); int[] order = getEncodingOrder(); int states = freqs.length; Frequencies freqsParam = new Frequencies(); String valuesString = ""; for (int i = 0; i < states; i++) { valuesString += freqs[order[i]] + " "; } RealParameter freqsRParam = new RealParameter(); freqsRParam.initByName( "value", valuesString, "lower", 0.0, "upper", 1.0, "dimension", states ); freqsParam.frequenciesInput.setValue(freqsRParam, freqsParam); freqsParam.initAndValidate(); return freqsParam; } /** * return rate matrix (ie two dimensional array) in upper diagonal form * */ abstract double[][] getEmpiricalRates(); /** * return empirical frequencies * */ abstract double[] getEmpiricalFrequencies(); /** * return character order for getEmpricialRates and getEmpriricalFrequencies * // The rates may be specified assuming that the amino acids are in this order: * // ARNDCQEGHILKMFPSTWYV * // but the AminoAcids dataType wants them in this order: * // ACDEFGHIKLMNPQRSTVWY * // This method returns the proper order */ abstract int[] getEncodingOrder(); @Override public double[] getRateMatrix(Node node) { double[][] matrix = getEmpiricalRates(); int states = matrix.length; double[] rates = new double[states * states]; for (int i = 0; i < states; i++) { for (int j = i + 1; j < states; j++) { rates[i * states + j] = matrix[i][j]; rates[j * states + i] = matrix[i][j]; } } // determine diagonal for (int i = 0; i < states; i++) { double sum = 0; for (int j = i + 1; j < states; j++) { sum += rates[i * states + j]; } rates[i * states + i] = -sum; } return rates; } }