diff --git a/examples/bears-not-all-leaves.xml b/examples/bears-not-all-leaves.xml
new file mode 100644
index 0000000..2a5d225
--- /dev/null
+++ b/examples/bears-not-all-leaves.xml
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Canis_lupus_0.0=0.0,
+Phoca_largha_0.0=0.0,
+Ailuropoda_melanoleuca_0.0=0.0,
+Tremarctos_ornatus_0.0=0.0,
+Helarctos_malayanus_0.0=0.0,
+Melursus_ursinus_0.0=0.0,
+Ursus_americanus_0.0=0.0,
+Ursus_arctos_0.0=0.0,
+Ursus_thibetanus_0.0=0.0,
+Ursus_maritimus_0.0=0.0,
+Hesperocyon_gregarius_39.07=39.07,
+Caedocyon_tedfordi_25.88=25.88,
+Osbornodon_sesnoni_30.08=30.08,
+Cormocyon_copei_26.14=26.14,
+Borophagus_diversidens_4.284=4.284,
+Enaliarctos_tedfordi_27.11=27.11,
+Proneotherium_repenningi_17.92=17.92,
+Leptophoca_lenis_14.99=14.99,
+Acrophoca_6.695=6.695,
+Phoca_vitulina_0.805=0.805,
+Parictis_montanus_36.6=36.6,
+Zaragocyon_daamsi_21.86=21.86,
+Ballusia_elmensis_14.01=14.01,
+Ursavus_primaevus_14.41=14.41,
+Ursavus_brevihinus_16.2=16.2,
+Indarctos_vireti_8.68=8.68,
+Indarctos_arctoides_9.54=9.54,
+Indarctos_punjabiensis_4.996=4.996,
+Ailurarctos_lufengensis_7.652=7.652,
+Agriarctos_spp_5.006=5.006,
+Kretzoiarctos_beatrix_11.69=11.69,
+Arctodus_simus_0.487=0.487,
+Ursus_abstrusus_4.27=4.27,
+Ursus_spelaeus_0.054=0.054
+
+
+
+
+
+ 1.0
+ 0.1
+ 1
+ 100.0
+ 1.0
+ 0.5
+ 0.5
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ 0.0
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.5396
+ 0.3819
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.0
+ 1.0
+ 0.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/beast/evolution/operators/LeafToSampledAncestorJump.java b/src/beast/evolution/operators/LeafToSampledAncestorJump.java
index e9f7642..d768656 100644
--- a/src/beast/evolution/operators/LeafToSampledAncestorJump.java
+++ b/src/beast/evolution/operators/LeafToSampledAncestorJump.java
@@ -1,9 +1,14 @@
package beast.evolution.operators;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import beast.core.Description;
import beast.core.Input;
import beast.core.parameter.IntegerParameter;
import beast.core.parameter.RealParameter;
+import beast.evolution.alignment.Taxon;
import beast.evolution.tree.Node;
import beast.evolution.tree.Tree;
import beast.util.Randomizer;
@@ -24,15 +29,46 @@ public class LeafToSampledAncestorJump extends TreeOperator {
public Input rInput =
new Input("removalProbability", "The probability of an individual to be removed from the process immediately after the sampling");
-
+ public Input> sampledTaxa =
+ new Input>(
+ "sampledTaxa",
+ "Taxa that this operator should be allowed to let jump between sampled ancestor and leaf. Default: All non-recent leaves.",
+ new ArrayList<>());
+
+ protected List validLeaves = new ArrayList();
+
@Override
public void initAndValidate() {
+ if (sampledTaxa.get().size() == 0) {
+ validLeaves = new ArrayList(treeInput.get().getLeafNodeCount());
+ for (Node leaf: treeInput.get().getExternalNodes()) {
+ if (leaf.getHeight() > 1e-6) {
+ validLeaves.add(leaf.getNr());
+ }
+ }
+ } else {
+ List taxa = sampledTaxa.get();
+ List taxaNames = new ArrayList(taxa.size());
+ for (Taxon taxon: taxa) {
+ taxaNames.add(taxon.getID());
+ }
+ validLeaves = new ArrayList(taxa.size());
+ Integer i = 0;
+ for (String leaf: treeInput.get().getTaxaNames()) {
+ if (taxaNames.contains(leaf)) {
+ validLeaves.add(i);
+ }
+ i += 1;
+ }
+ }
+ // System.out.println("Nodes to be jumped:");
+ // System.out.println(Arrays.toString(validLeaves.toArray()));
}
@Override
public double proposal() {
- double newHeight, newRange, oldRange;
+ double newHeight, logNewRange, logOldRange;
int categoryCount = 1;
if (categoriesInput.get() != null) {
@@ -41,20 +77,21 @@ public double proposal() {
Tree tree = treeInput.get();
- int leafNodeCount = tree.getLeafNodeCount();
+ int leafNodeCount = validLeaves.size();
- Node leaf = tree.getNode(Randomizer.nextInt(leafNodeCount));
+ Node leaf = tree.getNode(validLeaves.get(Randomizer.nextInt(leafNodeCount)));
Node parent = leaf.getParent();
if (leaf.isDirectAncestor()) {
- oldRange = (double) 1;
+ logOldRange = (double) 0;
if (parent.isRoot()) {
final double randomNumber = Randomizer.nextExponential(1);
newHeight = parent.getHeight() + randomNumber;
- newRange = Math.exp(randomNumber);
+ logNewRange = randomNumber;
} else {
- newRange = parent.getParent().getHeight() - parent.getHeight();
+ double newRange = parent.getParent().getHeight() - parent.getHeight();
newHeight = parent.getHeight() + Randomizer.nextDouble() * newRange;
+ logNewRange = Math.log(newRange);
}
if (categoriesInput.get() != null) {
@@ -63,15 +100,15 @@ public double proposal() {
categoriesInput.get().setValue(index, newValue);
}
} else {
- newRange = (double) 1;
+ logNewRange = (double) 0;
//make sure that the branch where a new sampled node to appear is not above that sampled node
if (getOtherChild(parent, leaf).getHeight() >= leaf.getHeight()) {
return Double.NEGATIVE_INFINITY;
}
if (parent.isRoot()) {
- oldRange = Math.exp(parent.getHeight() - leaf.getHeight());
+ logOldRange = parent.getHeight() - leaf.getHeight();
} else {
- oldRange = parent.getParent().getHeight() - leaf.getHeight();
+ logOldRange = Math.log(parent.getParent().getHeight() - leaf.getHeight());
}
newHeight = leaf.getHeight();
if (categoriesInput.get() != null) {
@@ -86,6 +123,6 @@ public double proposal() {
return Double.NEGATIVE_INFINITY;
}
- return Math.log(newRange/oldRange);
+ return logNewRange - logOldRange;
}
}
diff --git a/src/beast/evolution/operators/SAWilsonBalding.java b/src/beast/evolution/operators/SAWilsonBalding.java
index 7b50fb3..7228be1 100644
--- a/src/beast/evolution/operators/SAWilsonBalding.java
+++ b/src/beast/evolution/operators/SAWilsonBalding.java
@@ -7,179 +7,188 @@
import beast.util.Randomizer;
/**
- *@author Alexandra Gavryushkina
+ * @author Alexandra Gavryushkina
*/
public class SAWilsonBalding extends TreeOperator {
- public Input rInput =
- new Input("removalProbability", "The probability of an individual to become noninfectious immediately after the sampling");
-
- @Override
- public void initAndValidate() {
- }
-
- /**
- * @return log of Hastings Ratio, or Double.NEGATIVE_INFINITY if proposal should not be accepted *
- */
- @Override
- public double proposal() {
-
- Tree tree = treeInput.get(this);
-
- //double x0 = 10;
-
- double oldMinAge, newMinAge, newRange, oldRange, newAge, fHastingsRatio, DimensionCoefficient;
- int newDimension, oldDimension;
-
- // choose a random node avoiding root and leaves that are direct ancestors
- int nodeCount = tree.getNodeCount();
- Node i;
-
- do {
- i = tree.getNode(Randomizer.nextInt(nodeCount));
- } while (i.isRoot() || i.isDirectAncestor());
-
- Node iP = i.getParent();
- Node CiP;
- if (iP.getLeft().getNr() == i.getNr()) {
- CiP = iP.getRight();
- } else {
- CiP = iP.getLeft();
- }
-
- // make sure that there is at least one candidate edge to attach node iP to
- if (iP.getParent() == null && CiP.getHeight() <= i.getHeight()) {
- return Double.NEGATIVE_INFINITY;
- }
-
- // choose another random node to insert i above or to attach i to this node if it is a leaf
- Node j;
- Node jP;
-
- final int leafNodeCount = tree.getLeafNodeCount();
-
- if (leafNodeCount != tree.getExternalNodes().size()) {
- System.out.println("node counts are incorrect. NodeCount = " + nodeCount + " leafNodeCount = " + leafNodeCount + " exteranl node count = " + tree.getExternalNodes().size());
- }
-
- // make sure that the target branch or target leaf j is above the subtree being moved
-
- int nodeNumber;
- double newParentHeight;
- boolean attachingToLeaf;
- boolean adjacentEdge;
- //boolean adjacentLeaf;
- do {
- adjacentEdge = false;
- //adjacentLeaf = false;
- nodeNumber = Randomizer.nextInt(nodeCount + leafNodeCount);
- if (nodeNumber < nodeCount) {
- j = tree.getNode(nodeNumber);
- jP = j.getParent();
- if (jP != null)
- newParentHeight = jP.getHeight();
- else newParentHeight = Double.POSITIVE_INFINITY;
- if (!CiP.isDirectAncestor())
- adjacentEdge = (CiP.getNr() == j.getNr() || iP.getNr() == j.getNr());
- attachingToLeaf = false;
- } else {
- j = tree.getExternalNodes().get(nodeNumber - nodeCount);
- jP = j.getParent();
- newParentHeight = j.getHeight();
- attachingToLeaf = true;
- //adjacentLeaf = (iP.getNr() == j.getNr());
- }
- } while (j.isDirectAncestor() || (newParentHeight <= i.getHeight()) || (i.getNr() == j.getNr()) || adjacentEdge /*|| adjacentLeaf */);
-
-
- if (attachingToLeaf && iP.getNr() == j.getNr()) {
- System.out.println("Proposal failed because j = iP");
- return Double.NEGATIVE_INFINITY;
- }
-
- if (jP != null && jP.getNr() == i.getNr()) {
- System.out.println("Proposal failed because jP = i. Heights of i = " + i.getHeight() + " Height of jP = " + jP.getHeight());
- return Double.NEGATIVE_INFINITY;
- }
-
- oldDimension = nodeCount - tree.getDirectAncestorNodeCount() - 1;
-
- //Hastings numerator calculation + newAge of iP
- if (attachingToLeaf) {
- newRange = 1;
- newAge = j.getHeight();
- } else {
- if (jP != null) {
- newMinAge = Math.max(i.getHeight(), j.getHeight());
- newRange = jP.getHeight() - newMinAge;
- newAge = newMinAge + (Randomizer.nextDouble() * newRange);
- } else {
- double randomNumberFromExponential;
- randomNumberFromExponential = Randomizer.nextExponential(1);
- //newRange = x0 - j.getHeight();
- //randomNumberFromExponential = Randomizer.nextDouble() * newRange;
- newRange = Math.exp(randomNumberFromExponential);
- newAge = j.getHeight() + randomNumberFromExponential;
- }
- }
-
- Node PiP = iP.getParent();
-
- //Hastings denominator calculation
- if (CiP.isDirectAncestor()) {
- oldRange = 1;
- }
- else {
- oldMinAge = Math.max(i.getHeight(), CiP.getHeight());
- if (PiP != null) {
- oldRange = PiP.getHeight() - oldMinAge;
- } else {
- oldRange = Math.exp(iP.getHeight() - oldMinAge);
- //oldRange = x0 - oldMinAge;
- }
- }
-
- //update
- if (iP.getNr() != j.getNr() && CiP.getNr() != j.getNr()) {
- iP.removeChild(CiP); //remove
-
- if (PiP != null) {
- PiP.removeChild(iP); // remove
- PiP.addChild(CiP); // add
- PiP.makeDirty(Tree.IS_FILTHY);
- CiP.makeDirty(Tree.IS_FILTHY);
- } else {
- CiP.setParent(null); // completely remove
- tree.setRootOnly(CiP);
- }
-
- if (jP != null) {
- jP.removeChild(j); // remove
- jP.addChild(iP); // add
- jP.makeDirty(Tree.IS_FILTHY);
- } else {
- iP.setParent(null); // completely remove
- tree.setRootOnly(iP);
- }
- iP.addChild(j);
- iP.makeDirty(Tree.IS_FILTHY);
- j.makeDirty(Tree.IS_FILTHY);
- }
- iP.setHeight(newAge);
-
- //make sure that either there are no direct ancestors or r<1
- if ((rInput.get() != null) && (tree.getDirectAncestorNodeCount() > 0 && rInput.get().getValue() == 1)) {
- return Double.NEGATIVE_INFINITY;
- }
-
- newDimension = nodeCount - tree.getDirectAncestorNodeCount() - 1;
- DimensionCoefficient = (double) oldDimension / newDimension;
-
- fHastingsRatio = Math.abs(DimensionCoefficient * newRange / oldRange);
-
- return Math.log(fHastingsRatio);
-
- }
-
+ public Input rInput = new Input("removalProbability",
+ "The probability of an individual to become noninfectious immediately after the sampling");
+ public Input keepSAInput = new Input("keepSA",
+ "Avoid moving the descendant of a sampled ancestor away, turning it into a non-SA tip", false);
+
+ @Override
+ public void initAndValidate() {
+ }
+
+ /**
+ * @return log of Hastings Ratio, or Double.NEGATIVE_INFINITY if proposal should
+ * not be accepted *
+ */
+ @Override
+ public double proposal() {
+
+ Tree tree = treeInput.get(this);
+
+ // double x0 = 10;
+
+ double oldMinAge, newMinAge, newRange, oldRange, newAge, fHastingsRatio, DimensionCoefficient;
+ int newDimension, oldDimension;
+
+ // choose a random node avoiding root and leaves that are direct ancestors
+ int nodeCount = tree.getNodeCount();
+ Node i;
+
+ // There have been issues with this assumption in this package, so let's assert
+ // it explicitly.
+ assert tree.getNode(nodeCount - 1).isRoot();
+
+ do {
+ i = tree.getNode(Randomizer.nextInt(nodeCount - 1));
+ } while (i.isDirectAncestor());
+
+ Node iP = i.getParent();
+ Node CiP = getOtherChild(iP, i);
+ if (keepSAInput.get() && CiP.isDirectAncestor()) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ // make sure that there is at least one candidate edge to attach node iP to
+ if (iP.getParent() == null && CiP.getHeight() <= i.getHeight()) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ // choose another random node to insert i above or to attach i to this node if
+ // it is a leaf
+ Node j;
+ Node jP;
+
+ final int leafNodeCount = tree.getLeafNodeCount();
+
+ if (leafNodeCount != tree.getExternalNodes().size()) {
+ System.out.println("node counts are incorrect. NodeCount = " + nodeCount + " leafNodeCount = "
+ + leafNodeCount + " external node count = " + tree.getExternalNodes().size());
+ }
+
+ // make sure that the target branch or target leaf j is above the
+ // subtree being moved
+
+ int nodeNumber;
+ double newParentHeight;
+ boolean attachingToLeaf;
+ boolean adjacentEdge;
+ // boolean adjacentLeaf;
+ do {
+ adjacentEdge = false;
+ // adjacentLeaf = false;
+ nodeNumber = Randomizer.nextInt(nodeCount + leafNodeCount);
+ if (nodeNumber < nodeCount) {
+ j = tree.getNode(nodeNumber);
+ jP = j.getParent();
+ if (jP != null)
+ newParentHeight = jP.getHeight();
+ else
+ newParentHeight = Double.POSITIVE_INFINITY;
+ if (!CiP.isDirectAncestor())
+ adjacentEdge = (CiP.getNr() == j.getNr() || iP.getNr() == j.getNr());
+ attachingToLeaf = false;
+ } else {
+ j = tree.getExternalNodes().get(nodeNumber - nodeCount);
+ jP = j.getParent();
+ newParentHeight = j.getHeight();
+ attachingToLeaf = true;
+ // adjacentLeaf = (iP.getNr() == j.getNr());
+ }
+ } while (j.isDirectAncestor() || (newParentHeight <= i.getHeight()) || (i.getNr() == j.getNr())
+ || adjacentEdge /* || adjacentLeaf */);
+
+ if (attachingToLeaf && iP.getNr() == j.getNr()) {
+ System.out.println("Proposal failed because j = iP");
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ if (jP != null && jP.getNr() == i.getNr()) {
+ System.out.println("Proposal failed because jP = i. Heights of i = " + i.getHeight() + " Height of jP = "
+ + jP.getHeight());
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ oldDimension = nodeCount - tree.getDirectAncestorNodeCount() - 1;
+
+ // Hastings numerator calculation + newAge of iP
+ if (attachingToLeaf) {
+ newRange = 1;
+ newAge = j.getHeight();
+ } else {
+ if (jP != null) {
+ newMinAge = Math.max(i.getHeight(), j.getHeight());
+ newRange = jP.getHeight() - newMinAge;
+ newAge = newMinAge + (Randomizer.nextDouble() * newRange);
+ } else {
+ double randomNumberFromExponential;
+ randomNumberFromExponential = Randomizer.nextExponential(1);
+ // newRange = x0 - j.getHeight();
+ // randomNumberFromExponential = Randomizer.nextDouble() * newRange;
+ newRange = Math.exp(randomNumberFromExponential);
+ newAge = j.getHeight() + randomNumberFromExponential;
+ }
+ }
+
+ Node PiP = iP.getParent();
+
+ // Hastings denominator calculation
+ if (CiP.isDirectAncestor()) {
+ oldRange = 1;
+ } else {
+ oldMinAge = Math.max(i.getHeight(), CiP.getHeight());
+ if (PiP != null) {
+ oldRange = PiP.getHeight() - oldMinAge;
+ } else {
+ oldRange = Math.exp(iP.getHeight() - oldMinAge);
+ // oldRange = x0 - oldMinAge;
+ }
+ }
+
+ // update
+ if (iP.getNr() != j.getNr() && CiP.getNr() != j.getNr()) {
+ iP.removeChild(CiP); // remove
+
+ if (PiP != null) {
+ PiP.removeChild(iP); // remove
+ PiP.addChild(CiP); // add
+ PiP.makeDirty(Tree.IS_FILTHY);
+ CiP.makeDirty(Tree.IS_FILTHY);
+ } else {
+ CiP.setParent(null); // completely remove
+ tree.setRootOnly(CiP);
+ }
+
+ if (jP != null) {
+ jP.removeChild(j); // remove
+ jP.addChild(iP); // add
+ jP.makeDirty(Tree.IS_FILTHY);
+ iP.addChild(j);
+ } else {
+ iP.setParent(null); // completely remove
+ iP.addChild(j);
+ tree.setRoot(iP);
+ }
+ iP.makeDirty(Tree.IS_FILTHY);
+ j.makeDirty(Tree.IS_FILTHY);
+ }
+ iP.setHeight(newAge);
+
+ // make sure that either there are no direct ancestors or r<1
+ if ((rInput.get() != null) && (tree.getDirectAncestorNodeCount() > 0 && rInput.get().getValue() == 1)) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ newDimension = nodeCount - tree.getDirectAncestorNodeCount() - 1;
+ DimensionCoefficient = (double) oldDimension / newDimension;
+
+ fHastingsRatio = Math.abs(DimensionCoefficient * newRange / oldRange);
+
+ return Math.log(fHastingsRatio);
+
+ }
}
diff --git a/src/beast/evolution/tree/RandomTreeWithSA.java b/src/beast/evolution/tree/RandomTreeWithSA.java
new file mode 100644
index 0000000..73614a6
--- /dev/null
+++ b/src/beast/evolution/tree/RandomTreeWithSA.java
@@ -0,0 +1,88 @@
+package beast.evolution.tree;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import beast.core.Input;
+import beast.core.Input.Validate;
+import beast.core.util.Log;
+import beast.evolution.alignment.Taxon;
+import beast.evolution.alignment.TaxonSet;
+
+public class RandomTreeWithSA extends RandomTree {
+
+ final public Input> startAsSampledAncestors = new Input<>("sampledAncestor",
+ "Taxa to start as sampled ancestors", new ArrayList());
+
+ @Override
+ public void initAndValidate() {
+ taxa = new LinkedHashSet<>();
+ if (taxaInput.get() != null) {
+ taxa.addAll(taxaInput.get().getTaxaNames());
+ } else {
+ taxa.addAll(m_taxonset.get().asStringList());
+ }
+
+ Set startTaxa = new LinkedHashSet<>();
+ for (Taxon taxon: startAsSampledAncestors.get()) {
+ startTaxa.add(taxon.getID());
+ }
+ if (!taxa.containsAll(startTaxa)) {
+ Set remaining = startTaxa;
+ remaining.removeAll(taxa);
+ Log.warning("WARNING: Not all sampledAncestor taxa were part of the alignment taxa. Missing taxa "
+ + remaining.toString() + " will not be forced to be sampled ancestors.");
+ }
+ super.initAndValidate();
+ }
+
+ @Override
+ public void initStateNodes() {
+ super.initStateNodes();
+
+ List sa = new ArrayList();
+
+ for (Taxon taxon: startAsSampledAncestors.get()) {
+ sa.add(taxon.getID());
+ }
+
+ for (Node leaf : root.getAllLeafNodes()) {
+
+ if (!sa.contains(getTaxonId(leaf))) {
+ continue;
+ }
+ Node parent = leaf.getParent();
+
+ double newHeight = leaf.getHeight();
+ parent.setHeight(newHeight);
+ // setHeight does not check temporal order of nodes, so we nudge all children
+ // that are older than their parent down a bit. This can mess with other
+ // assumptions.
+ for (Node node : parent.getAllChildNodesAndSelf()) {
+ if (node.getHeight() > node.parent.getHeight()) {
+ if (!node.isLeaf()) {
+ // Not a leaf, so cannot be a sampled ancestor which needs to be handled with
+ // care
+ node.setHeight(node.parent.getHeight() - 1e-9);
+ } else if (sa.contains(getTaxonId(node))) {
+ // Keep sampled ancestors as sampled ancestors. Yes, this is a bit redundant,
+ // but at least it should be correct. The alternative is to recursively walk the
+ // tree in a somewhat erratic order.
+ node.setHeight(node.parent.getHeight());
+ } else {
+ // Put everything else down a notch.
+ node.setHeight(node.parent.getHeight() - 1e-9);
+ }
+ }
+ }
+ assert leaf.isDirectAncestor();
+ }
+
+ if (m_initial.get() != null) {
+ m_initial.get().assignFromWithoutID(this);
+ }
+ }
+}
diff --git a/test-reports/TEST-test.beast.evolution.operators.SampledNodeDateRandomWalkerTest.txt b/test-reports/TEST-test.beast.evolution.operators.SampledNodeDateRandomWalkerTest.txt
new file mode 100644
index 0000000..bf03ecd
--- /dev/null
+++ b/test-reports/TEST-test.beast.evolution.operators.SampledNodeDateRandomWalkerTest.txt
@@ -0,0 +1,5 @@
+Testsuite: test.beast.evolution.operators.SampledNodeDateRandomWalkerTest
+Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.141 sec
+
+Testcase: testSwitchWOffset took 0.083 sec
+Testcase: testHeightWOffset took 0.025 sec
diff --git a/test-reports/TEST-test.beast.evolution.operators.TreeDimensionJumpTest.txt b/test-reports/TEST-test.beast.evolution.operators.TreeDimensionJumpTest.txt
new file mode 100644
index 0000000..e5b5eca
--- /dev/null
+++ b/test-reports/TEST-test.beast.evolution.operators.TreeDimensionJumpTest.txt
@@ -0,0 +1,13 @@
+Testsuite: test.beast.evolution.operators.TreeDimensionJumpTest
+Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.079 sec
+------------- Standard Output ---------------
+Tree was = ((0:1.0,1:0.0):3.0,2:2.0):0.0
+Proposed tree = ((0:1.5583203577630607,1:0.5583203577630607):2.4416796422369393,2:2.0):0.0
+Log Hastings ratio = 1.0986122886681098
+Tree was = ((0:1.0,1:0.0):1.0,2:0.0):0.0
+Proposed tree = ((0:1.0,1:0.0):1.0,2:0.0):0.0
+Log Hastings ratio = -Infinity
+------------- ---------------- ---------------
+
+Testcase: testOperator2 took 0.051 sec
+Testcase: testOperator1 took 0.005 sec
diff --git a/test-reports/TEST-test.beast.evolution.speciation.SABirthDeathModelTest.txt b/test-reports/TEST-test.beast.evolution.speciation.SABirthDeathModelTest.txt
new file mode 100644
index 0000000..1301faf
--- /dev/null
+++ b/test-reports/TEST-test.beast.evolution.speciation.SABirthDeathModelTest.txt
@@ -0,0 +1,6 @@
+Testsuite: test.beast.evolution.speciation.SABirthDeathModelTest
+Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.15 sec
+
+Testcase: testLikelihoodCalculationSimple took 0.095 sec
+Testcase: testLikelihoodCalculation1 took 0.011 sec
+Testcase: testLikelihoodCalculation2 took 0.026 sec
diff --git a/test-reports/TEST-test.beast.evolution.tree.TreeWOffsetTest.txt b/test-reports/TEST-test.beast.evolution.tree.TreeWOffsetTest.txt
new file mode 100644
index 0000000..7496784
--- /dev/null
+++ b/test-reports/TEST-test.beast.evolution.tree.TreeWOffsetTest.txt
@@ -0,0 +1,4 @@
+Testsuite: test.beast.evolution.tree.TreeWOffsetTest
+Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 sec
+
+Testcase: testTree took 0.049 sec