package statalign.model.ext; import java.io.File; import java.util.List; import java.util.ArrayList; import javax.swing.JComponent; import statalign.base.InputData; import statalign.base.Mcmc; import statalign.base.Tree; import statalign.base.Utils; import statalign.base.Vertex; import statalign.base.hmm.Hmm; import statalign.io.DataType; import statalign.mcmc.McmcModule; import statalign.mcmc.McmcMove; import statalign.model.subst.SubstitutionModel; /** * Ancestral class for model extension plugins. * * @author novak, herman */ public abstract class ModelExtension extends McmcModule { /** Private access to model extension manager */ private ModelExtManager manager; protected boolean active; /** Whether the plugin's <code>initRun</code> method has already been run. */ protected boolean initComplete = false; /** Determines whether the plugin is selectable by default in GUI mode.*/ protected boolean selectable = true; public boolean isSelectable() { return selectable; } public void addToFilenameExtension(String s) { manager.addToFilenameExtension(s); } public String getFilenameExtension() { return manager.getFilenameExtension(); } /** * If <code>true</code>, then this plugin is able to compute a column-wise * contribution to the likelihood, and can be used to influence the alignment * proposals that are carried out by functions inside statalign.base.Vertex * By default this is false. */ public boolean useInAlignmentProposals() { return false; } /** * This function is called when command line arguments are specified for * a plugin. If the specific {@link ModelExtension} does not override these methods * then it cannot handle any command line arguments, and so the default * error is returned. The overriding method should call * <code>super.setParam(paramName,paramValue)</code> * in the case that <code>paramName</code> is unrecognised. * An overriding method can also include a call such as * <code>addToFilenameExtension(paramName+"_"+paramValue);</code> * which will result in an informative string being appended to the names * of any output files generated by the {@link Postprocess} plugins. * * @param paramName The name of a parameter that is to be set. * @param paramValue The value to which it will be set. */ public void setParam(String paramName, String paramValue) { throw new IllegalArgumentException("Unable to set parameter "+paramName+" for plugin "+this.getPluginID()+"."); } public void setParam(String paramName, boolean paramValue) { throw new IllegalArgumentException("Unable to set parameter "+paramName+" for plugin "+this.getPluginID()+"."); } public void setParam(String paramName, Number paramValue) { throw new IllegalArgumentException("Unable to set parameter "+paramName+" for plugin "+this.getPluginID()+"."); } public void setManager(ModelExtManager manager) { this.manager = manager; } /** * Prints the usage information for this plugin. */ public String getUsageInfo() { return "Currently no information available for this plugin"; } /** * @return The command line identifier string for the plugin. */ public String getPluginID() { return null; } /** * @return <code>true</code> if this plugin is active */ public boolean isActive() { return active; } // /** // * Resets all the fields that are initialised during {@link #initRun(InputData)}. // */ // public ModelExtension reset() { // return null; // } /** * Enables or disables this plugin. * @param active <code>true</code> if plugin should be enabled */ public void setActive(boolean active) { this.active = active; if (useInAlignmentProposals()) { Utils.USE_MODEXT_EM = true; Utils.USE_MODEXT_UPP = true; if (active) { System.out.println("Using information from '"+getPluginID()+ "' in alignment proposals."); } } } /** * Returns the (unmodifiable) list of recognised {@link ModelExtension} plugins. */ public final List<ModelExtension> getPluginList() { return manager.getPluginList(); } /** * Override this to return the list of toolbar items to be added. By default returns <code>null</code>. */ public List<JComponent> getToolBarItems() { return null; } public void dataAdded(File file, DataType data) {} /** * Called during StatAlign startup, after all ModelExtension plugins have been loaded and command line * arguments have been processed (if present). * */ public void init() {} /** * Called during the initialisation of a run if plugin is active. Override to process input data. * * @param inputData the input data * @exception IllegalArgumentException if the run should be terminated as input data is illegal */ public void initRun(InputData inputData) throws IllegalArgumentException { // If this is not the first run then reset first if (initComplete) resetData(); initComplete = true; } /** * Called after initRun, to set up the MCMC moves for the plugin. * * @param inputData */ protected void initMcmc(InputData inputData) { } protected void resetData() { mcmcMoves = new ArrayList<McmcMove>(); mcmcMoveWeights = new ArrayList<Integer>(); mcmcMoveWeightIncrements = new ArrayList<Integer>(); setAllMovesNotProposed(); zeroAllMoveCounts(); } /** * Returns the weight for choosing a parameter change for this model extension in the MCMC kernel. * By default, returns 0, preventing {@link #proposeParamChange(Tree)} from ever being called. */ public int getParamChangeWeight() { return 0; } /** * Called when a model extension plugin is to propose a parameter change. Plugins must check whether * they are selected for the parameter change and propose a change if necessary. Will be called * for the selected plugin first. {@link #logLikeFactor(Tree)} will be called afterwards to get * the likelihood contribution after the change, and the move will be accepted or rejected by the * framework. All plugins will be notified about its outcome by * {@link #afterModExtParamChange(Tree, ModelExtension, boolean)}. * @param tree the current tree * @param ext the model extension plugin that is selected to propose a parameter change */ public void beforeModExtParamChange(Tree tree, ModelExtension ext) {} /** * Called when this plugin was selected to attempt a model parameter change. Plugin should call * {@link #isParamChangeAccepted(double)} with the log of a Metropolis-Hastings-like ratio (see the * method for details) to find out whether the change was accepted and then act * accordingly, ie. keep or restore the state. * * <p>The call to the above method will result in a call to {@link #logLikeModExtParamChange(Tree, ModelExtension)} * with ModelExtension set to <code>this</code> and the plugin should return the log-likelihood factor * calculated after the proposed change. * * <p>All plugins will later be notified about the acceptance/rejection of the change through * {@link #afterModExtParamChange(Tree, ModelExtension, boolean)}. * * @param tree the current tree */ /** * Should be called from {@link #proposeParamChange(Tree)} to find out whether a proposed parameter * change was accepted. * * <p>Parameter <code>logProposalRatio</code> must be the log of P(x|x')/P(x'|x) * Pr(x')/Pr(x) where x is the * old value of the model parameter, x' is new value, P(x'|x) is the probability of the proposed change * (proposal probability), P(x|x') is the backproposal probability, Pr(x') is the prior probability * of the new parameter value (new prior), Pr(x) is the old prior. The remaining factor * Pi(new state)/Pi(old state) of the Metropolis-Hastings ratio will be calculated by calls to * {@link #logLikeModExtParamChange(Tree, ModelExtension)}. * * @param logProposalRatio Ratio of proposal and prior densities as explained above * @return <code>true</code> if the change was accepted */ @Override public final boolean isParamChangeAccepted(double logProposalRatio,McmcMove m) { return manager.isParamChangeAccepted(logProposalRatio,m); } /** * Computes the change in likelihood to this ModelExtension, after a change * to <code>ext</code>. * @param tree * @param ext The <code>ModelExtension</code> that just changed. * @return */ public double logLikeModExtParamChange(Tree tree, ModelExtension ext) { return logLikeFactor(tree); } /** * Called after a proposed model extension parameter change (accepted or rejected). * @param tree the current tree * @param ext the model extension plugin that was selected to propose a parameter change * @param accepted <code>true</code> if the change was accepted */ public void afterModExtParamChange(Tree tree, ModelExtension ext, boolean accepted) {} /** * Called before an alignment change is proposed, but after the affected subtree has been selected. * May change later to be called after subalignment (window) has also been selected. * @param tree the current tree * @param selectRoot root of the selected subtree */ public void beforeAlignChange(Tree tree, Vertex selectRoot) {} /** * * @param tree * @param selectRoot * @return */ public double logLikeAlignChange(Tree tree, Vertex selectRoot) { return logLikeFactor(tree); } /** * Called after an alignment change proposal (accepted or rejected). * @param tree the current tree * @param selectRoot root of the selected subtree * @param accepted true if the change was accepted */ public void afterAlignChange(Tree tree, Vertex selectRoot, boolean accepted) {} /** * Called before a topology change is proposed, but after the affected branches are * selected. * @param tree the current tree * @param nephew node that is proposed to be swapped with its "uncle" */ public void beforeTreeChange(Tree tree, Vertex nephew) {} public double logLikeTreeChange(Tree tree, Vertex nephew) { return logLikeFactor(tree); } /** * Called after a proposed topology change (accepted or rejected). * @param tree the tree after the change * @param nephew the new nephew (this is the deepest lying node that was affected by the change if there was one) * @param accepted true if the change was accepted */ public void afterTreeChange(Tree tree, Vertex nephew, boolean accepted) {} /** * Called before an edge length change is proposed, but after the affected edge is selected. * @param tree the current tree * @param vertex the node whose edge to its parent is selected to be changed */ public void beforeEdgeLenChange(Tree tree, Vertex vertex) {} public double logLikeEdgeLenChange(Tree tree, Vertex vertex) { return logLikeFactor(tree); } /** * Called after a proposed edge length change (accepted or rejected). * @param tree the current tree * @param vertex the node whose edge to its parent was selected * @param accepted true if the change was accepted */ public void afterEdgeLenChange(Tree tree, Vertex vertex, boolean accepted) {} /** * Called before an indel parameter change is proposed, but after the affected parameter is selected. * @param tree the current tree * @param hmm the TKF92 HMM containing its parameters * @param ind the index of the selected parameter */ public void beforeIndelParamChange(Tree tree, Hmm hmm, McmcMove m) {} public double logLikeIndelParamChange(Tree tree, Hmm hmm, McmcMove m) { return logLikeFactor(tree); } /** * Called after a proposed indel parameter change (accepted or rejected). * @param tree the current tree * @param hmm the TKF92 HMM containing its parameters * @param ind the index of the selected parameter * @param accepted true if the change was accepted */ public void afterIndelParamChange(Tree tree, Hmm hmm, McmcMove m, boolean accepted) {} /** * Called before a substitution parameter change is proposed. * @param tree the current tree * @param model the active substitution model * @param ind the index of the substitution parameter selected to be changed or -1 if unknown */ public void beforeSubstParamChange(Tree tree, SubstitutionModel model, int ind) {} public double logLikeSubstParamChange(Tree tree, SubstitutionModel model, int ind) { return logLikeFactor(tree); } /** * Called after a proposed indel parameter change (accepted or rejected). * @param tree the current tree * @param model the active substitution model * @param ind the index of the substitution parameter selected to be changed or -1 if unknown * @param accepted true if the change was accepted */ public void afterSubstParamChange(Tree tree, SubstitutionModel model, int ind, boolean accepted) {} /** * * @param aligned Vector indicating which characters are aligned to the current * column in the subtrees below. * @return Logarithm of emission probability for subtrees */ public double calcLogEm(int[] aligned) { return 0; } /** * Allows access to the Mcmc class. Generally not recommended. */ // protected Mcmc getMcmc() { // return manager.getMcmc(); // } }