package beast.evolution.substitutionmodel; import beast.core.Description; import beast.core.Input; import beast.core.Input.Validate; import beast.core.parameter.RealParameter; import beast.evolution.datatype.DataType; import beast.evolution.tree.Node; @Description("Mutation Death substitution model, can be used as Stochastic Dollo model.") public class MutationDeathModel extends SubstitutionModel.Base { final public Input<RealParameter> delParameter = new Input<>("deathprob", "rate of death, used to calculate death probability", Validate.REQUIRED); // mutation rate is already provided in SiteModel, so no need to duplicate it here //public Input<RealParameter> mutationRate = new Input<>("mu", "mutation rate, default 1"); final public Input<SubstitutionModel.Base> CTMCModelInput = new Input<>("substmodel", "CTMC Model for the life states, so should have " + "a state-space one less than this model. If not specified, ..."); // TODO: figure out the end of the last sentence /** * transition matrix for live states * */ protected double[] trMatrix; /** * number of states * */ int nrOfStates; @Override public void initAndValidate() { super.initAndValidate(); double[] freqs = getFrequencies(); nrOfStates = freqs.length; trMatrix = new double[(nrOfStates - 1) * (nrOfStates - 1)]; if (CTMCModelInput.get() != null) { if (CTMCModelInput.get().frequenciesInput.get().freqs.length != nrOfStates - 1) { throw new IllegalArgumentException("substmodel does not have the correct state space: should be " + (nrOfStates - 1)); } } } @Override public EigenDecomposition getEigenDecomposition(Node node) { return null; } @Override public void getTransitionProbabilities(Node node, double startTime, double endTime, double rate, double[] matrix) { double distance = (startTime - endTime) * rate; int i, j; // assuming that expected number of changes in CTMCModel is 1 per unit time // we are contributing s*deathRate number of changes per unit of time double deathProb = Math.exp(-distance * delParameter.get().getValue()); double mutationR = 2; // if (mutationRate.get() != null) { // mutationR *= mutationRate.get().getValue(); // } double freqs[] = getFrequencies(); for (i = 0; i < freqs.length - 1; ++i) { mutationR *= freqs[i]; } SubstitutionModel.Base CTMCModel = CTMCModelInput.get(); if (CTMCModel != null) { CTMCModel.getTransitionProbabilities(node, startTime, endTime, mutationR * rate, trMatrix); } else { trMatrix[0] = 1.0; } for (i = 0; i < nrOfStates - 1; ++i) { for (j = 0; j < nrOfStates - 1; j++) { matrix[i * (nrOfStates) + j] = trMatrix[i * (nrOfStates - 1) + j] * deathProb; } matrix[i * (nrOfStates) + j] = (1.0 - deathProb); } for (j = 0; j < nrOfStates - 1; ++j) { matrix[nrOfStates * (nrOfStates - 1) + j] = 0.0; } matrix[nrOfStates * nrOfStates - 1] = 1.0; } // getTransitionProbabilities /** * CalculationNode implementation * */ @Override protected boolean requiresRecalculation() { // we only get here if delParameter or mutationRate is dirty return true; } @Override public boolean canHandleDataType(DataType dataType) { if (CTMCModelInput.get() == null) { return dataType.getStateCount() == 2; } else { int states = CTMCModelInput.get().nrOfStates; return dataType.getStateCount() == states + 1; } } } // class MutationDeathModel