|
| 1 | +package beast.evolution.speciation; |
| 2 | + |
| 3 | + import beast.core.Distribution; |
| 4 | + import beast.core.Input; |
| 5 | + import beast.core.State; |
| 6 | + import beast.core.parameter.IntegerParameter; |
| 7 | + import beast.core.parameter.RealParameter; |
| 8 | + |
| 9 | + import java.util.List; |
| 10 | + import java.util.Random; |
| 11 | + |
| 12 | +/** |
| 13 | + * @author Alexandra Gavryushkina |
| 14 | + */ |
| 15 | +public class ProbabilitySA extends Distribution { |
| 16 | + |
| 17 | + public final Input<IntegerParameter> SAInput = new Input<IntegerParameter>("SA", "A binary parameter that describe " + |
| 18 | + "the fact that a lineage has no sampled descendants (zero) or it has sampled descendants (one)", Input.Validate.REQUIRED); |
| 19 | + |
| 20 | + public Input<RealParameter> timeInput = |
| 21 | + new Input<RealParameter>("time", "The time of the lineage",Input.Validate.REQUIRED); |
| 22 | + |
| 23 | + public Input<RealParameter> originInput = |
| 24 | + new Input<RealParameter>("origin", "The time when the process started",(RealParameter)null); |
| 25 | + |
| 26 | + //'direct' parameters |
| 27 | + public Input<RealParameter> birthRateInput = |
| 28 | + new Input<RealParameter>("birthRate", "Birth rate"); |
| 29 | + public Input<RealParameter> deathRateInput = |
| 30 | + new Input<RealParameter>("deathRate", "Death rate"); |
| 31 | + public Input<RealParameter> samplingRateInput = |
| 32 | + new Input<RealParameter>("samplingRate", "Sampling rate per individual"); |
| 33 | + |
| 34 | + //transformed parameters: |
| 35 | + public Input<RealParameter> diversificationRateInput = |
| 36 | + new Input<RealParameter>("diversificationRate", "Net diversification rate. Birth rate - death rate", Input.Validate.XOR, birthRateInput); |
| 37 | + public Input<RealParameter> turnoverInput = |
| 38 | + new Input<RealParameter>("turnover", "Turnover. Death rate/birth rate", Input.Validate.XOR, deathRateInput); |
| 39 | + public Input<RealParameter> samplingProportionInput = |
| 40 | + new Input<RealParameter>("samplingProportion", "The probability of sampling prior to death. Sampling rate/(sampling rate + death rate)", Input.Validate.XOR, samplingRateInput); |
| 41 | + |
| 42 | + |
| 43 | + // r parameter |
| 44 | + public Input<RealParameter> removalProbability = |
| 45 | + new Input<RealParameter>("removalProbability", "The probability that an individual is removed from the process after the sampling", Input.Validate.REQUIRED); |
| 46 | + |
| 47 | + public Input<RealParameter> rhoProbability = |
| 48 | + new Input<RealParameter>("rho", "Probability of an individual to be sampled at present", (RealParameter)null); |
| 49 | + |
| 50 | + // if the tree likelihood is condition on sampling at least one individual then set to true one of the inputs: |
| 51 | + public Input<Boolean> conditionOnSamplingInput = new Input<Boolean>("conditionOnSampling", "the tree " + |
| 52 | + "likelihood is conditioned on sampling at least one individual", false); |
| 53 | + public Input<Boolean> conditionOnRhoSamplingInput = new Input<Boolean>("conditionOnRhoSampling", "the tree " + |
| 54 | + "likelihood is conditioned on sampling at least one individual in present", false); |
| 55 | + |
| 56 | + public Input<Boolean> conditionOnRootInput = new Input<Boolean>("conditionOnRoot", "the tree " + |
| 57 | + "likelihood is conditioned on the root height otherwise on the time of origin", false); |
| 58 | + |
| 59 | + |
| 60 | + protected double r; |
| 61 | + protected double lambda; |
| 62 | + protected double mu; |
| 63 | + protected double psi; |
| 64 | + protected double c1; |
| 65 | + protected double c2; |
| 66 | + protected double origin; |
| 67 | + protected double rho; |
| 68 | + protected boolean transform; //is true if the model is parametrised through transformed parameters |
| 69 | + private boolean lambdaExceedsMu = false; |
| 70 | + |
| 71 | + @Override |
| 72 | + public List<String> getArguments() { |
| 73 | + return null; |
| 74 | + } |
| 75 | + |
| 76 | + @Override |
| 77 | + public List<String> getConditions() { |
| 78 | + return null; |
| 79 | + } |
| 80 | + |
| 81 | + @Override |
| 82 | + public void sample(State state, Random random) { |
| 83 | + |
| 84 | + } |
| 85 | + |
| 86 | + public void initAndValidate() throws Exception { |
| 87 | + |
| 88 | + if (originInput.get() == null && !conditionOnRootInput.get()) { |
| 89 | + throw new RuntimeException("Either specify origin input or set conditionOnRoot input to \"true\""); |
| 90 | + } |
| 91 | + |
| 92 | + if (originInput.get() != null && conditionOnRootInput.get()){ |
| 93 | + throw new RuntimeException("Either don't specify origin input or set conditionOnRoot input to \"false\""); |
| 94 | + } |
| 95 | + |
| 96 | + |
| 97 | + if (conditionOnSamplingInput.get() && conditionOnRhoSamplingInput.get()){ |
| 98 | + throw new RuntimeException("Either set to \"true\" only one of conditionOnSampling and conditionOnRhoSampling inputs or don't specify both!"); |
| 99 | + } |
| 100 | + |
| 101 | + if (birthRateInput.get() != null && deathRateInput.get() != null && samplingRateInput.get() != null) { |
| 102 | + |
| 103 | + transform = false; |
| 104 | + //mu = deathRateInput.get().getValue(); |
| 105 | + //psi = samplingRateInput.get().getValue(); |
| 106 | + //lambda = birthRateInput.get().getValue(); |
| 107 | + |
| 108 | + } else if (diversificationRateInput.get() != null && turnoverInput.get() != null && samplingProportionInput.get() != null) { |
| 109 | + |
| 110 | + transform = true; |
| 111 | + |
| 112 | + } else { |
| 113 | + throw new RuntimeException("Either specify birthRate, deathRate and samplingRate OR specify diversificationRate, turnover and samplingProportion!"); |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + |
| 118 | + private double oneMinusP0(double t, double c1, double c2) { |
| 119 | + return 1 - (lambda + mu + psi - c1 * ((1 + c2) - Math.exp(-c1 * t) * (1 - c2)) / ((1 + c2) + Math.exp(-c1 * t) * (1 - c2))) / (2 * lambda); |
| 120 | + } |
| 121 | + |
| 122 | + |
| 123 | + private void transformParameters() { |
| 124 | + double d = diversificationRateInput.get().getValue(); |
| 125 | + double r_turnover = turnoverInput.get().getValue(); |
| 126 | + double s = samplingProportionInput.get().getValue(); |
| 127 | + lambda = d/(1-r_turnover); |
| 128 | + mu = r_turnover*lambda; |
| 129 | + psi = mu*s/(1-s); |
| 130 | + } |
| 131 | + |
| 132 | + private void updateParameters() { |
| 133 | + |
| 134 | + if (transform) { |
| 135 | + transformParameters(); |
| 136 | + } else { |
| 137 | + lambda = birthRateInput.get().getValue(); |
| 138 | + mu = deathRateInput.get().getValue(); |
| 139 | + psi = samplingRateInput.get().getValue(); |
| 140 | + } |
| 141 | + |
| 142 | + r = removalProbability.get().getValue(); |
| 143 | + if (rhoProbability.get() != null ) { |
| 144 | + rho = rhoProbability.get().getValue(); |
| 145 | + } else { |
| 146 | + rho = 0.; |
| 147 | + } |
| 148 | + c1 = Math.sqrt((lambda - mu - psi) * (lambda - mu - psi) + 4 * lambda * psi); |
| 149 | + c2 = -(lambda - mu - 2*lambda*rho - psi) / c1; |
| 150 | + if (originInput.get() != null){ |
| 151 | + origin = originInput.get().getValue(); |
| 152 | + } else { |
| 153 | + origin = Double.POSITIVE_INFINITY; |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + @Override |
| 158 | + public double calculateLogP() |
| 159 | + { |
| 160 | + updateParameters(); |
| 161 | + |
| 162 | + if (lambdaExceedsMu && lambda <= mu) { |
| 163 | + return Double.NEGATIVE_INFINITY; |
| 164 | + } |
| 165 | + double t=timeInput.get().getValue(); |
| 166 | + if (SAInput.get().getValue() == 0) { |
| 167 | + logP = Math.log(1 - oneMinusP0(t, c1, c2)); |
| 168 | + } else { |
| 169 | + logP = Math.log(oneMinusP0(t, c1, c2)); |
| 170 | + } |
| 171 | + |
| 172 | + return logP; |
| 173 | + } |
| 174 | + |
| 175 | + @Override |
| 176 | + protected boolean requiresRecalculation() { |
| 177 | + return true; |
| 178 | + } |
| 179 | +} |
0 commit comments