diff --git a/README.md b/README.md
index ad558d2..f19194c 100644
--- a/README.md
+++ b/README.md
@@ -80,12 +80,12 @@ where `/path/to` the path to where BEAST is installed. For Windows, use
For CCD1 based point estimates select `MAP (CCD1)` from the drop down box in the GUI, or use `CCD1` instead of `CCD0` for the command line version.
-### Phylogenetic Entropy, Rogue & Skeleton Analysis
+### AppLauncher Tools
-The CCD package has three tools (small apps) to compute the phylogenetic entropy of a tree set, compute rogues scores for each clade, and conduct a skeleton analysis
-that can each be executed with BEAST's AppLauncher.
+The CCD package comes with a set of tools (small apps) that can each be executed with BEAST's AppLauncher.
Note that the given trees *need to be binary* and are assumed to be rooted; they are typically given by a NEXUS `.tree` file, but a list of Newick strings also works.
-For more information on the concepts see the [paper](https://www.biorxiv.org/content/10.1101/2024.09.25.615070v1)
+When a tool finishes, it prints the reference(s) to cite for the method it implements; this is written to stderr, so it does not interfere with results written to stdout.
+For more information on the concepts behind the entropy, rogue and skeleton tools see the [paper](https://www.biorxiv.org/content/10.1101/2024.09.25.615070v1)
and for further information and example data see the [research paper repository](https://github.com/CompEvol/CCD-Research/tree/main/skeletonsAndRogues).
@@ -101,6 +101,22 @@ It has three parameters:
- `ccdType`: either `CCD0`, `CCD1`, or `CCD2` (default: `CCD0`)
+#### Dissonance
+
+The tool `DissonanceCalculator` computes the phylogenetic entropy (via CCD0) of one or more tree sets and, with the `dissonance` flag, also reports a dissonance value for each of them.
+The dissonance is computed by splitting a tree set into its first and second half and subtracting the mean entropy of the two halves from the entropy of the whole set.
+It is thus a diagnostic *within* a single tree set and not a comparison *between* the given tree files.
+```
+/path/to/applauncher DissonanceCalculator -trees /path/to/treeInputFile.trees -burnin 10 -dissonance true
+```
+The app has the following parameters:
+- `trees`: trees file to analyse; can be given more than once to process several tree sets in one run
+- `burnin`: percentage of trees to be used as burn-in (integer, default: `10%`)
+- `dissonance`: `true` to also compute the dissonance of each tree set (default: `false`)
+- `summarise`: `true` to print the mean and standard deviation of the entropies across all given tree sets (default: `false`)
+- `quiet`: `true` to only output the entropy (and dissonance) values and nothing else (default: `false`)
+
+
#### Rogue Analysis
A rogue analysis computes a **rogue score** for each clade, including each taxon, based on a given posterior sample of *binary* trees and some parameters and prints it to a csv file.
@@ -147,17 +163,25 @@ If you further specify an output file, then the given tree set will be reduced/f
- `out`: reduced tree output file; the given tree set will not be filtered if not specified
- `exclude`: file name of text file containing taxa to exclude from filtering - can be comma, tab or newline delimited
-### Credible Level Evaluation
+#### Credible Level Evaluation
-The credible level of a tree within a credible CCD or a probability-based credible set (on a CCD) can be computed with the following tool.
+The **credible level** of a tree is the probability mass of the smallest credible set containing it,
+that is, the smallest α for which the tree lies in an α credible set.
+The tool `TreeCredibleLevel` computes it, together with the probability of the tree, from a CCD estimated from a posterior tree sample.
```
-/path/to/applauncher TreeCredibleLevel -trees /path/to/treeInputFile.trees -tree /path/to/treeInputFile.tree(s) -burnin 10 -out path/to/outputTreeFile
+/path/to/applauncher TreeCredibleLevel -trees /path/to/treeInputFile.trees -tree /path/to/testTreeFile.tree -burnin 10 -out path/to/outputFile
```
The app has the following parameters:
-- `trees`: trees file to construct CCD with and analyse (required)
+- `trees`: trees file to construct the CCD with (required)
+- `tree`: tree file containing the tree for which the probability and credible level are computed (required); only the first tree is used
+- `out`: output file (required); the probability of the tree is written to the first line and its credible level to the second
- `burnin`: percentage of trees to be used as burn-in (integer, default: `10%`)
-- `ccdType`: either `CCD0` or `CCD1` or `CCD2` (default: `CCD1`)
-- `tree`: tree for which the credible level is computed
-- `method`: whether to use probability-based method (`probability`, default) or a credible CCD (`credibleCCD`)
-For the probability-based method the following two parameters can be set:
-- `numsamples`: the number of trees sampled from the CCD to compute the credible level thresholds (default: `10000`)
+- `ccdType`: either `CCD0`, `CCD1`, or `CCD2` (default: `CCD0`)
+- `method`: whether to use the probability-based method (`probability`, default) or a credible CCD (`credibleCCD`)
+- `quiet`: `true` to only output the credible level and nothing else (default: `false`)
+
+For the probability-based method one further parameter can be set:
+- `numsamples`: the number of trees sampled from the CCD to compute the credible level thresholds (default: `100000`)
+
+Both methods are described in the [credible sets paper](https://doi.org/10.1093/molbev/msag141):
+Klawitter J, Drummond AJ (2026), *Bayesian credible sets for phylogenetic tree topologies with applications to coverage analysis and cross-model comparison*, Molecular Biology and Evolution 43(7), msag141.
diff --git a/pom.xml b/pom.xml
index 20924e6..8ca9b90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,7 @@
UTF-8
25
- 2.8.0-beta7
+ 2.8.0-beta8
25.0.2
beast.base
beast.base.minimal.BeastMain
diff --git a/src/main/java/ccd/tools/CCDSampler.java b/src/main/java/ccd/tools/CCDSampler.java
index 63c2570..4fea2fc 100644
--- a/src/main/java/ccd/tools/CCDSampler.java
+++ b/src/main/java/ccd/tools/CCDSampler.java
@@ -1,5 +1,6 @@
package ccd.tools;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Log;
@@ -19,6 +20,9 @@
import ccd.model.HeightSettingStrategy;
import ccd.model.KRegCCD;
+@Citation(value = "Berling and Klawitter et al. (2025). PLOS Computational Biology.\n" +
+ "Accurate Bayesian phylogenetic point estimation using a tree distribution parameterized by clade probabilities.",
+ DOI = "https://doi.org/10.1371/journal.pcbi.1012789")
@Description("Allows to sample from a CCD{0,1} based on a input set of trees")
public class CCDSampler extends Runnable {
final public Input treeInput = new Input<>("trees", "trees file to construct CCD with and analyse", Input.Validate.REQUIRED);
@@ -67,6 +71,8 @@ public void run() throws Exception {
bufferedOutputWriter.newLine();
}
}
+
+ CCDToolUtil.printCitations(this);
}
public static void main(String[] args) throws Exception {
diff --git a/src/main/java/ccd/tools/CCDToolUtil.java b/src/main/java/ccd/tools/CCDToolUtil.java
index ab6ec19..5b74db6 100644
--- a/src/main/java/ccd/tools/CCDToolUtil.java
+++ b/src/main/java/ccd/tools/CCDToolUtil.java
@@ -1,5 +1,7 @@
package ccd.tools;
+import beast.base.core.Citable;
+import beast.base.core.Citation;
import beast.base.core.Input;
import beast.base.core.Log;
import beastfx.app.treeannotator.TreeAnnotator;
@@ -14,6 +16,7 @@
import ccd.model.RegCCD;
import java.io.IOException;
+import java.util.List;
/**
* Static methods provided for tools {@link beastfx.app.tools.Application} in the CCD package
@@ -110,4 +113,30 @@ public static AbstractCCD getCCDTypeByName(TreeAnnotator.MemoryFriendlyTreeSet t
}
return ccd;
}
+
+ /**
+ * Print the {@link Citation}s of the given tool, if it has any, in the style used by
+ * TreeAnnotator. Output goes to {@link Log#warning} (stderr) so that it does not interfere
+ * with results written to stdout.
+ *
+ * @param tool the tool whose citations are printed
+ */
+ public static void printCitations(Citable tool) {
+ List citations = tool.getCitationList();
+ if (citations.isEmpty()) {
+ return;
+ }
+
+ StringBuilder buf = new StringBuilder();
+ buf.append("\n======================================================\n");
+ buf.append("Please cite the following when using these results:\n");
+ for (Citation citation : citations) {
+ buf.append("\n").append(citation.value()).append("\n");
+ if (!citation.DOI().isEmpty()) {
+ buf.append(citation.DOI()).append("\n");
+ }
+ }
+ buf.append("======================================================");
+ Log.warning(buf.toString());
+ }
}
diff --git a/src/main/java/ccd/tools/DissonanceCalculator.java b/src/main/java/ccd/tools/DissonanceCalculator.java
index 4ec3b72..8b479df 100644
--- a/src/main/java/ccd/tools/DissonanceCalculator.java
+++ b/src/main/java/ccd/tools/DissonanceCalculator.java
@@ -1,5 +1,6 @@
package ccd.tools;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Log;
@@ -13,6 +14,9 @@
import java.util.ArrayList;
import java.util.List;
+@Citation(value = "Klawitter, Bouckaert and Drummond (2024). bioRxiv.\n" +
+ "Skeletons in the forest: using entropy-based rogue detection on Bayesian phylogenetic tree distributions.",
+ DOI = "https://doi.org/10.1101/2024.09.25.615070")
@Description("Calculates the phylogenetic entropies of the posterior tree distribution (estimated via CCD0s) of given tree sets")
public class DissonanceCalculator extends beast.base.inference.Runnable {
final public Input> treeInput = new Input<>("trees", "trees to include in dissonance calculation", new ArrayList<>());
@@ -32,7 +36,7 @@ public void run() throws Exception {
long start = System.currentTimeMillis();
if (!quiet) {
- Log.info("# Starting Entropy Calculator");
+ Log.info("# Starting Dissonance Calculator");
}
List entropies = new ArrayList<>();
@@ -115,11 +119,12 @@ public void run() throws Exception {
long end = System.currentTimeMillis();
if (!quiet) {
Log.info("Done in " + (end - start) / 1000.0 + " seconds");
+ CCDToolUtil.printCitations(this);
}
}
public static void main(String[] args) throws Exception {
- new Application(new DissonanceCalculator(), "Entropy Calculator", args);
+ new Application(new DissonanceCalculator(), "Dissonance Calculator", args);
}
}
diff --git a/src/main/java/ccd/tools/EntropyCalculator.java b/src/main/java/ccd/tools/EntropyCalculator.java
index 532cb7a..bfd11a1 100644
--- a/src/main/java/ccd/tools/EntropyCalculator.java
+++ b/src/main/java/ccd/tools/EntropyCalculator.java
@@ -4,6 +4,7 @@
import java.util.ArrayList;
import java.util.List;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Log;
@@ -14,6 +15,9 @@
import ccd.model.AbstractCCD;
import ccd.model.CCDType;
+@Citation(value = "Klawitter, Bouckaert and Drummond (2024). bioRxiv.\n" +
+ "Skeletons in the forest: using entropy-based rogue detection on Bayesian phylogenetic tree distributions.",
+ DOI = "https://doi.org/10.1101/2024.09.25.615070")
@Description("Calculates the phylogenetic entropy of the posterior tree distribution")
public class EntropyCalculator extends beast.base.inference.Runnable {
// input
@@ -64,6 +68,10 @@ public void run() throws Exception {
}
k++;
}
+
+ if (verboseInput.get()) {
+ CCDToolUtil.printCitations(this);
+ }
}
public static void main(String[] args) throws Exception {
diff --git a/src/main/java/ccd/tools/RogueAnalysis.java b/src/main/java/ccd/tools/RogueAnalysis.java
index 867e5cc..aa23bb0 100644
--- a/src/main/java/ccd/tools/RogueAnalysis.java
+++ b/src/main/java/ccd/tools/RogueAnalysis.java
@@ -1,5 +1,6 @@
package ccd.tools;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Input.Validate;
@@ -23,6 +24,9 @@
import java.util.Arrays;
import java.util.Map;
+@Citation(value = "Klawitter, Bouckaert and Drummond (2024). bioRxiv.\n" +
+ "Skeletons in the forest: using entropy-based rogue detection on Bayesian phylogenetic tree distributions.",
+ DOI = "https://doi.org/10.1101/2024.09.25.615070")
@Description("Analyse the clades in a CCD for given trees based on their clade rogue score (based on entropy)")
public class RogueAnalysis extends Runnable {
@@ -141,6 +145,8 @@ public void run() throws Exception {
createNexusTreeFile(infoOutputFileName, tree);
Log.warning("Done");
+
+ CCDToolUtil.printCitations(this);
}
private boolean cladeConditionSatisfied(Clade clade, int maxCladeSize, double minProbability) {
diff --git a/src/main/java/ccd/tools/SkeletonAnalysis.java b/src/main/java/ccd/tools/SkeletonAnalysis.java
index 8fad9d0..bb56c8d 100644
--- a/src/main/java/ccd/tools/SkeletonAnalysis.java
+++ b/src/main/java/ccd/tools/SkeletonAnalysis.java
@@ -1,6 +1,7 @@
package ccd.tools;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Input.Validate;
@@ -30,6 +31,9 @@
import static ccd.algorithms.RogueDetection.TerminationStrategy.*;
+@Citation(value = "Klawitter, Bouckaert and Drummond (2024). bioRxiv.\n" +
+ "Skeletons in the forest: using entropy-based rogue detection on Bayesian phylogenetic tree distributions.",
+ DOI = "https://doi.org/10.1101/2024.09.25.615070")
@Description("Analyses the skeleton of a CCD for given trees based on the total rogue scores (based on entropy)")
public class SkeletonAnalysis extends Runnable {
// input
@@ -145,6 +149,8 @@ public void run() throws Exception {
RogueDetection.annotateRoguePlacements(ccd, lastCCD, rogues, tree);
System.out.println("The resulting annotated tree is: ");
System.out.println(tree.getRoot().toNewick());
+
+ CCDToolUtil.printCitations(this);
}
/* Extracted code that filters source treeset. */
diff --git a/src/main/java/ccd/tools/TreeCredibleLevel.java b/src/main/java/ccd/tools/TreeCredibleLevel.java
index d9aad0a..8487f5f 100644
--- a/src/main/java/ccd/tools/TreeCredibleLevel.java
+++ b/src/main/java/ccd/tools/TreeCredibleLevel.java
@@ -1,5 +1,6 @@
package ccd.tools;
+import beast.base.core.Citation;
import beast.base.core.Description;
import beast.base.core.Input;
import beast.base.core.Log;
@@ -21,6 +22,9 @@
import static ccd.algorithms.credibleSets.ProbabilityBasedCredibleSetComputer.DEFAULT_NUM_SAMPLES;
+@Citation(value = "Klawitter and Drummond (2026). Molecular Biology and Evolution 43(7), msag141.\n" +
+ "Bayesian credible sets for phylogenetic tree topologies with applications to coverage analysis and cross-model comparison.",
+ DOI = "https://doi.org/10.1093/molbev/msag141")
@Description("Compute probability and credible level of given tree in CCD of given tree set")
public class TreeCredibleLevel extends beast.base.inference.Runnable {
// input
@@ -99,6 +103,7 @@ public void run() throws Exception {
if (!quiet) {
Log.info("Done.");
+ CCDToolUtil.printCitations(this);
}
}
diff --git a/version.xml b/version.xml
index aa63cfb..06ca773 100644
--- a/version.xml
+++ b/version.xml
@@ -23,5 +23,7 @@
+
+