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();
// }
}