Skip to content

Commit 3440a45

Browse files
committed
Bug fix for timing not found error for mapped trees
This commit fixes and error that was caused by differences in the result of double double addition vs. subtraction in some cases. As a result, the mapped mascot class didn't find the upwards state probabilities for lineages resulting in an error and runs being aborted. This commit changes how the intermediate results of state probbailities on lineages are retrieved by storing the timings of the intermediate results
1 parent 43100a5 commit 3440a45

2 files changed

Lines changed: 69 additions & 52 deletions

File tree

src/mascot/distribution/MappedMascot.java

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,16 @@ public class MappedMascot extends Mascot implements Loggable {
5656
protected DecimalFormat df;
5757
protected boolean someMetaDataNeedsLogging;
5858
protected boolean substitutions = false;
59+
60+
private enum EventType {
61+
SAMPLE, COALESCENT, RATESHIFT
62+
}
5963

6064
public Tree mappedTree;
6165

6266
List<Integer> activeStates;
67+
List<List<Double>> usedTimes; // keeps track of the timings used for the intermediate results
68+
List<EventType> usedTypes; // keeps track of the timings used for the intermediate results
6369
double[] migrationRates;
6470

6571
long lastLog=-1;
@@ -107,19 +113,26 @@ public void calcForLogging(long sample) {
107113

108114
@Override
109115
public double calculateLogP() {
116+
110117
// System.out.println(treeIntervals.treeInput.get());
111118
// newly calculate tree intervals (already done by swap() below)
112119
treeIntervals.calculateIntervals();
113120
// correctly calculate the daughter nodes at coalescent intervals in the case of
114121
// bifurcation or in case two nodes are at the same height
115122
treeIntervals.swap();
116123

124+
// initialize a new Mapped tree that inclused single child nodes for migration events
117125
mappedTree = new Tree(tree.getRoot().copy());
118126
mappedTree.getRoot().sort();
119-
127+
128+
// Maps that keep track of the state probabilities and times that fall onto an edge
120129
intermediateStateProbs = new HashMap<>();
121130
intermediateTimes = new HashMap<>();
131+
// reset the usedTimes
132+
usedTimes = new ArrayList<>();
133+
usedTypes = new ArrayList<>();
122134

135+
// The maximum step size for storing intermediate results
123136
double maxStepSize = treeIntervals.treeInput.get().getRoot().getHeight() * maxIntegrationStepMappingInput.get();
124137

125138
// Set up ArrayLists for the indices of active lineages and the lineage state
@@ -129,7 +142,7 @@ public double calculateLogP() {
129142
nrLineages = 0;
130143
// linProbs = new double[0];// initialize the tree and rates interval counter
131144
linProbsLength = 0;
132-
int treeInterval = 0, ratesInterval = 0;
145+
int treeInterval = 0, ratesInterval = 0, anyInterval = 0;
133146
double nextEventTime = 0.0;
134147

135148
// Time to the next rate shift or event on the tree
@@ -144,7 +157,6 @@ public double calculateLogP() {
144157
}
145158

146159
// if (first == 0 || !dynamics.areDynamicsKnown()) {
147-
// System.out.println("lalal");
148160
setUpDynamics();
149161
// }
150162

@@ -157,11 +169,16 @@ public double calculateLogP() {
157169
double currTime = 0;
158170

159171
double lastRateShift = currTime;
172+
173+
usedTimes.add(new ArrayList<>());
174+
175+
double maxFloatError = treeIntervals.treeInput.get().getRoot().getHeight()*maxDiffInput.get();
176+
160177

161178
// Calculate the likelihood
162179
do {
163180
nextEventTime = Math.min(nextTreeEvent, nextRateShift);
164-
if (nextEventTime > 0) { // if true, calculate the interval contribution
181+
if (nextEventTime > maxFloatError) { // if true, calculate the interval contribution
165182
if (recalculateLogP) {
166183
System.err.println("ode calculation stuck, reducing tolerance, new tolerance= " + maxTolerance);
167184
maxTolerance *= 0.9;
@@ -170,9 +187,13 @@ public double calculateLogP() {
170187
return calculateLogP();
171188
}
172189

190+
// calculate the intermediate state probabilities until the next event, either a coal, sample or rate shift
173191
if (nextEventTime < maxStepSize) {
174192
logP += doEuler(nextEventTime, ratesInterval);
175193
currTime += nextEventTime;
194+
// add the current time to the used times
195+
usedTimes.get(anyInterval).add(currTime);
196+
// store the intermediate results
176197
storeIntermediateResults(currTime);
177198
} else {
178199
int nrIntermediates = (int) (nextEventTime / maxStepSize);
@@ -183,15 +204,23 @@ public double calculateLogP() {
183204
if (i == nrIntermediates)
184205
currTime = oldCurrTime + nextEventTime;
185206

207+
// add the current time to the used times
208+
usedTimes.get(anyInterval).add(currTime);
209+
// store the intermediate results
186210
storeIntermediateResults(currTime);
187211
}
188212
}
189213
}
190214

215+
usedTimes.add(new ArrayList<>());
216+
anyInterval++;
217+
191218
if (nextTreeEvent <= nextRateShift) {
192219
if (treeIntervals.getIntervalType(treeInterval) == IntervalType.COALESCENT) {
193220
nrLineages--; // coalescent event reduces the number of lineages by one
194221
logP += coalesce(treeInterval, ratesInterval, nextTreeEvent, nextRateShift, currTime); // calculate
222+
usedTimes.get(anyInterval).add(currTime);
223+
usedTypes.add(EventType.COALESCENT);
195224
// the
196225
// likelihood of the
197226
// coalescent event
@@ -202,7 +231,9 @@ public double calculateLogP() {
202231
// logP += normalizeLineages(linProbs); // normalize all lineages before event
203232
nrLineages++; // sampling event increases the number of lineages by one
204233
sample(treeInterval, ratesInterval, nextTreeEvent, nextRateShift, currTime); // calculate the
205-
// likelihood of
234+
usedTimes.get(anyInterval).add(currTime);
235+
usedTypes.add(EventType.SAMPLE);
236+
206237
// the sampling event if
207238
// sampling rate is given
208239
}
@@ -222,6 +253,8 @@ public double calculateLogP() {
222253
nextTreeEvent -= nextRateShift;
223254
nextRateShift = dynamics.getInterval(ratesInterval);
224255
lastRateShift = currTime;
256+
usedTimes.get(anyInterval).add(currTime);
257+
usedTypes.add(EventType.RATESHIFT);
225258
}
226259
if (logP == Double.NEGATIVE_INFINITY) {
227260
return logP;
@@ -232,10 +265,9 @@ public double calculateLogP() {
232265
} while (nextTreeEvent <= Double.POSITIVE_INFINITY);
233266

234267
first++;
235-
268+
236269
resample(treeInterval, ratesInterval, lastRateShift);
237-
// System.out.println(treeIntervals.treeInput.get());
238-
// System.out.println("");
270+
239271
return logP;
240272
}
241273

@@ -255,7 +287,6 @@ protected void setUpDynamics() {
255287
euler.setUpDynamics(coalescentRates, migrationRates, indicators, nextRateShift);
256288
}
257289

258-
259290
protected double coalesce(int currTreeInterval, int currRatesInterval, double nextTreeEvent, double nextRateShift,
260291
double currTime) {
261292
double logP = super.coalesce(currTreeInterval, currRatesInterval, nextTreeEvent, nextRateShift);
@@ -287,50 +318,53 @@ private void addNewLineage(int nr, double time) {
287318
intermediateTimes.get(nr).add(time);
288319
}
289320

321+
290322
private void resample(int treeInterval, int ratesInterval, double lastRateShift) {
291323
treeInterval--;
292324
// start by resampling the root
293325
int rootNr = treeIntervals.getLineagesAdded(treeInterval);
294-
295-
// System.out.println(tree);
296-
326+
// the number of lineages at the root is always 1
297327
nrLineages = 1;
328+
// the active lineages are the ones that are present at the root
298329
activeStates = new ArrayList<>();
299-
300-
// sample rootState
330+
// sample rootState from the state probabilities at the root
301331
int rootState = Randomizer.randomChoicePDF(intermediateStateProbs.get(rootNr).get(0));
302-
332+
// add the rootState to the active states
303333
activeStates.add(rootState);
334+
// keep track of the active lineages
304335
activeLineages.clear();
305336
activeLineages.add(rootNr);
306337
linProbsLength = 0;
307-
338+
// index to keep track of the used times, start with the last one
308339

309340
mappedTree.getRoot().setMetaData("location", rootState);
310341
coalesceDown(treeInterval);
311342

312-
double currTime = treeIntervals.treeInput.get().getRoot().getHeight();
313-
314-
double nextTreeEvent = treeIntervals.getInterval(treeInterval);
343+
int anyInterval = usedTimes.size() - 2; // start with the series of times saved in the last interval before the coalescent event
344+
double currTime = usedTimes.get(anyInterval+1).get(0); // get the time of the root as the time of the coalescent interval of the last usedTimes
345+
double maxFloatError = currTime*maxDiffInput.get();
346+
347+
// get the time until the next tree event and the next rate shift
348+
double nextTreeEvent = treeIntervals.getInterval(treeInterval);
315349
double nextRateShift = currTime - lastRateShift;
316-
350+
317351
double nextEventTime;
318352
// Calculate the likelihood
319353
do {
320354
nextEventTime = Math.min(nextTreeEvent, nextRateShift);
321-
if (nextEventTime > 0) { // if true, calculate the interval contribution
322-
// System.out.println("currentTime " + currTime);
323-
sampleMigrationEvents(currTime, currTime - nextEventTime);
355+
if (nextEventTime > maxFloatError) { // if true, calculate the interval contribution
356+
// sample the migration events that occurred between now and the next event time (sample, coal, or rate shift)
357+
sampleMigrationEvents(usedTimes.get(anyInterval));
324358
currTime -= nextEventTime;
325359
}
326-
327-
if (nextTreeEvent <= nextRateShift) {
328-
if (treeIntervals.getIntervalType(treeInterval - 1) == IntervalType.COALESCENT) {
360+
anyInterval--;
361+
if (usedTypes.get(anyInterval) != EventType.RATESHIFT) {
362+
if (usedTypes.get(anyInterval) == EventType.COALESCENT) {
329363
nrLineages++; // coalescent event reduces the number of lineages by one
330364
coalesceDown(treeInterval - 1); // calculate the
331365
}
332366

333-
if (treeIntervals.getIntervalType(treeInterval - 1) == IntervalType.SAMPLE) {
367+
if (usedTypes.get(anyInterval) == EventType.SAMPLE) {
334368
// if (linProbsLength > 0)
335369
// logP += normalizeLineages(linProbs); // normalize all lineages before event
336370
nrLineages--; // sampling event increases the number of lineages by one
@@ -357,42 +391,25 @@ private void resample(int treeInterval, int ratesInterval, double lastRateShift)
357391
// indicators = dynamics.getIndicators(ratesInterval);
358392
}
359393
} while (treeInterval > 0);
360-
361-
first++;
362-
363394
}
364395

365396

366-
private void sampleMigrationEvents(double startTime, double endTime) {
397+
private void sampleMigrationEvents(List<Double> timesInInterval) {
367398
for (int i = 0; i < activeLineages.size(); i++) {
368-
sampleMigrationEventsLineage(activeLineages.get(i), i, startTime, endTime);
399+
sampleMigrationEventsLineage(activeLineages.get(i), i, timesInInterval);
369400
}
370401
}
371402

372-
private void sampleMigrationEventsLineage(Integer nodeNr, int index, double startTime, double endTime) {
403+
private void sampleMigrationEventsLineage(Integer nodeNr, int index, List<Double> timesInInterval) {
373404
double K = -Math.log(Randomizer.nextDouble());
374405
double I = 0.0;
406+
double startTime = timesInInterval.get(timesInInterval.size() - 1);
407+
double endTime = timesInInterval.get(0);
375408
double currentTime = startTime;
376-
409+
377410
int currTimeInterval = intermediateTimes.get(nodeNr).indexOf(startTime);
378411
if (currTimeInterval == -1) {
379-
boolean cont = false;
380-
for (int i = 0; i < intermediateTimes.get(nodeNr).size(); i++)
381-
if (Math.abs(intermediateTimes.get(nodeNr).get(i) - startTime) < maxDiffInput.get()) {
382-
currTimeInterval = i;
383-
cont = true;
384-
break;
385-
}
386-
387-
if (!cont) {
388-
for (int i = 0; i < intermediateTimes.get(nodeNr).size(); i++)
389-
System.err.println(intermediateTimes.get(nodeNr).get(i) - startTime);
390-
391-
// System.out.println(mappedTree);
392-
393-
394-
throw new IllegalArgumentException("timing not found");
395-
}
412+
throw new IllegalArgumentException("timing not found");
396413
}
397414

398415
double[] prob_start = intermediateStateProbs.get(nodeNr).get(currTimeInterval);

version.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<package name='Mascot' version='3.0.6'>
1+
<package name='Mascot' version='3.0.7'>
22
<depends on='BEAST.base' atleast='2.7.2'/>
33
<depends on='BEAST.app' atleast='2.7.2'/>
44

0 commit comments

Comments
 (0)