/* Copyright (C) 2009 Diego Darriba This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package es.uvigo.darwin.prottest.model; import java.io.PrintWriter; import java.io.Serializable; import pal.alignment.Alignment; import pal.tree.Tree; import pal.tree.TreeUtils; import es.uvigo.darwin.prottest.model.state.ModelEmptyLkState; import es.uvigo.darwin.prottest.model.state.ModelFilledLkState; import es.uvigo.darwin.prottest.model.state.ModelLkState; import es.uvigo.darwin.prottest.util.exception.ProtTestInternalException; import es.uvigo.darwin.prottest.util.printer.ProtTestFormattedOutput; import static es.uvigo.darwin.prottest.util.logging.ProtTestLogger.*; import java.io.StringWriter; /** * Substitution model. */ public abstract class Model implements Serializable { /** The serialVersionUID. */ private static final long serialVersionUID = 20090804L; // distributions /** Useful constant for uniform distribution. */ public static final int DISTRIBUTION_UNIFORM = 0; /** Useful constant for distribution with a proportion of invariable sites. */ public static final int DISTRIBUTION_INVARIABLE = 1; /** Useful constant for gamma distribution. */ public static final int DISTRIBUTION_GAMMA = 2; /** Useful constant for gamma distribution with a proportion of invariable sites. */ public static final int DISTRIBUTION_GAMMA_INV = 3; // frequencies distribution /** The value of Uniform Frequencies Distribution. */ static final int FREQ_DISTRIBUTION_UNIFORM = 1; /** The value of Empirical Frequencies Distribution. */ static final int FREQ_DISTRIBUTION_EMPIRICAL = 2; /** The value of Maximum Likelihood Frequencies Distribution. */ static final int FREQ_DISTRIBUTION_MAXIMUM_LIKELIHOOD = 3; /** The value of any other frequencies distribution. */ static final int FREQ_DISTRIBUTION_OTHER = 4; /** Useful constant for consider observed frequencies. */ public static final String PROP_PLUS_F = "plusF"; /** The matrix name. */ private String matrix; /** The distribution. */ private int distribution; /** Consider observed frequencies. */ private boolean plusF; /** The frequencies distribution. */ protected int frequenciesDistribution; /** The alignment hashcode. */ private int alignment; /** The number of sequences. */ private int numberOfSequences; /** The tree. */ private Tree tree; /** The number of model parameters. */ // private int numModelParameters; /** The number of transition categories. */ private int numOfTransCategories; /** The likelihood calculation state. */ private ModelLkState lkState; /** The external executor command line. */ private String[] commandLine; /** * Gets the likelihood estimated value. * * @return the likelihood estimated value */ public double getLk() { return lkState.getLk(); } /** * Checks if is computed. * * @return true, if is computed */ public boolean isComputed() { return (lkState instanceof ModelFilledLkState); } /** * Sets the likelihood estimated value. * * @param lk the new likelihood estimated value */ public void setLk(double lk) { lkState = lkState.setLk(lk); } /** * Gets the alpha estimated value. * * @return the alpha estimated value */ public double getAlpha() { return lkState.getAlpha(); } /** * Sets the alpha estimated value. * * @param alpha the new alpha estimated value */ public void setAlpha(double alpha) { lkState = lkState.setAlpha(alpha); } /** * Gets the proportion of invariant sites. * * @return the proportion of invariant sites */ public double getInv() { return lkState.getInv(); } /** * Sets the proportion of invariant sites. * * @param inv the new proportion of invariant sites */ public void setInv(double inv) { lkState = lkState.setInv(inv); } /** * Gets the alignment hashcode. * * @return the alignment hashcode */ public int getAlignment() { return alignment; } /** * Sets the alignment only if there wasn't set or it is a different instance of the same object. * * @param alignment the new alignment */ public void setAlignment(Alignment alignment) { if (this.alignment != 0 && this.alignment != alignment.toString().hashCode()) { throw new ProtTestInternalException("cannot set a different alignment"); } this.alignment = alignment.toString().hashCode(); this.numberOfSequences = alignment.getSequenceCount(); } /** * Checks if an alignment matches the internal model alignment * * @return alignment equality */ public boolean checkAlignment(Alignment alignment) { if (alignment == null) { return false; } return (this.alignment == alignment.toString().hashCode() && this.numberOfSequences == alignment.getSequenceCount()); } /** * Gets the tree. * * @return the tree */ public Tree getTree() { return tree; } /** * Sets the tree. * * @param tree the new tree */ public void setTree(Tree tree) { this.tree = tree; } /** * Gets the command line. * * @return the command line */ public String[] getCommandLine() { return commandLine; } /** * Sets the command line. * * @param commandLine the new command line */ public void setCommandLine(String[] commandLine) { this.commandLine = commandLine; } /** * Gets the number of transition categories. * * @return the number of transition categories */ public int getNumberOfTransitionCategories() { return numOfTransCategories; } /** * Instantiates a new substitution model. * * @param matrix the matrix name * @param distribution the distribution value * @param plusF consider observed frequencies * @param alignment the alignment * @param tree the tree * @param ncat the ncat */ public Model(String matrix, int distribution, boolean plusF, Alignment alignment, Tree tree, int ncat) { if (distribution < 0 || distribution > 3) { throw new ProtTestInternalException("Distribution not supported " + distribution); } if (alignment == null) { throw new ProtTestInternalException("Null alignment"); } this.matrix = matrix; this.distribution = distribution; this.plusF = plusF; this.alignment = alignment.toString().hashCode(); this.numberOfSequences = alignment.getSequenceCount(); this.tree = tree; this.lkState = new ModelEmptyLkState(); // numBranches = 2*alignment.getSequenceCount() - 3; switch (distribution) { case DISTRIBUTION_UNIFORM: numOfTransCategories = 1; break; case DISTRIBUTION_INVARIABLE: numOfTransCategories = 1; break; case DISTRIBUTION_GAMMA: numOfTransCategories = ncat; break; case DISTRIBUTION_GAMMA_INV: numOfTransCategories = ncat; break; } } /** * Gets the number of parameters according with the distribution. * * @return the distribution parameters according with the distribution */ public int getDistributionParameters() { int value = -1; switch (distribution) { case DISTRIBUTION_UNIFORM: value = 0; break; case DISTRIBUTION_INVARIABLE: value = 1; break; case DISTRIBUTION_GAMMA: value = 1; break; case DISTRIBUTION_GAMMA_INV: value = 2; break; } return value; } /** * Gets the number of model parameters. * * @return the number of model parameters */ public abstract int getNumberOfModelParameters(); /** * Gets the matrix name. * * @return the matrix name */ public String getMatrix() { return matrix; } /** * Gets the distribution. * * @return the distribution */ public int getDistribution() { return distribution; } /** * Checks if observed frequencies are considered. * * @return true, if the model considers observed frequencies */ public boolean isPlusF() { return plusF; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + distribution; result = prime * result + ((matrix == null) ? 0 : matrix.hashCode()); result = prime * result + (isPlusF() ? 1231 : 1237); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Model other = (Model) obj; if (distribution != other.distribution) { return false; } if (matrix == null) { if (other.matrix != null) { return false; } } else if (!matrix.equals(other.matrix)) { return false; } if (isPlusF() != other.isPlusF()) { return false; } return true; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Model [distribution=" + distribution + ", matrix=" + matrix + ", plusF=" + isPlusF(); // + ", weight=" + weight + "]"; } /** * Gets the complete model name. * * @return the model name */ public abstract String getModelName(); /** * Gets the number of branches. * * @return the number of branches */ public int getNumBranches() { return 2 * numberOfSequences - 3; } /** * Checks if distribution is gamma. * * @return true, if is gamma */ public boolean isGamma() { return (distribution == DISTRIBUTION_GAMMA || distribution == DISTRIBUTION_GAMMA_INV); } /** * Checks if a proportion of invariant sites are considered. * * @return true, if considers a proportion of invariant sites */ public boolean isInv() { return (distribution == DISTRIBUTION_INVARIABLE || distribution == DISTRIBUTION_GAMMA_INV); } /** * Prints the model status report. */ public void printReport() { println("Model................................ : " + getModelName()); print(" Number of parameters............... : " + getNumberOfModelParameters()); println(" (" + (getNumberOfModelParameters() - getNumBranches()) + " + " + getNumBranches() + " branch length estimates)"); if (isComputed()) { if (isGamma()) { println(" gamma shape (" + getNumberOfTransitionCategories() + " rate categories).. = " + getAlpha()); } if (isInv()) { println(" proportion of invariable sites... = " + getInv()); } if (isPlusF()) { println(" aminoacid frequencies............ = observed (see above)"); } print(" -lnL................................ = " + ProtTestFormattedOutput.getDecimalString((-1 * getLk()), 2)); println(""); verboseln("The tree:"); verboseln("---------"); StringWriter ascciiSw = new StringWriter(); TreeUtils.report(getTree(), new PrintWriter(ascciiSw)); ascciiSw.flush(); verboseln(ascciiSw.toString()); StringWriter newickSw = new StringWriter(); verboseln("---------"); TreeUtils.printNH(getTree(), new PrintWriter(newickSw)); newickSw.flush(); verboseln(newickSw.toString()); verboseln(""); } flush(Model.class); } private void print(String message) { info(message, Model.class); } private void println(String message) { infoln(message, Model.class); } private void verbose(String message) { finer(message, Model.class); } private void verboseln(String message) { finerln(message, Model.class); } }