package beast.evolution.speciation; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import beast.core.BEASTObject; import beast.core.Description; import beast.core.Function; import beast.core.Input; import beast.core.Input.Validate; import beast.core.Loggable; import beast.core.StateNode; import beast.core.parameter.Parameter; import beast.evolution.speciation.SpeciesTreePrior.TreePopSizeFunction; import beast.evolution.tree.Node; import beast.evolution.tree.Tree; @Description("Logs tree annotated with metadata in StarBeast format") public class SpeciesTreeLogger extends BEASTObject implements Loggable { final public Input<Tree> treeInput = new Input<>("tree", "tree to be logged", Validate.REQUIRED); final public Input<Function> parameterInput = new Input<>("popSize", "population size parameter associated with tree nodes", Validate.REQUIRED); final public Input<Function> parameterTopInput = new Input<>("popSizeTop", "population size parameter associated with top of tree branches, only used for non-constant *beast analysis"); final public Input<SpeciesTreePrior> speciesTreePriorInput = new Input<>("speciesTreePrior", "species tree prior, used to find which Population Size Function is used. If not specified, assumes 'constant'"); final public Input<TreeTopFinder> treeTopFinderInput = new Input<>("treetop", "calculates height of species tree", Validate.REQUIRED); final public Input<List<Function>> metadataInput = new Input<>("metadata", "meta data to be logged with the tree nodes",new ArrayList<>()); TreePopSizeFunction popSizeFunction; String metaDataLabel; static final String dmv = "dmv"; static final String dmt = "dmt"; @Override public void initAndValidate() { metaDataLabel = "[&" + dmv + "="; if (speciesTreePriorInput.get() != null) { popSizeFunction = speciesTreePriorInput.get().popFunctionInput.get(); } else { popSizeFunction = TreePopSizeFunction.constant; } } @Override public void init(final PrintStream out) { treeInput.get().init(out); } @Override public void log(final int sample, final PrintStream out) { // make sure we get the current version of the inputs final Tree tree = (Tree) treeInput.get().getCurrent(); Function metadata = parameterInput.get(); if (metadata instanceof StateNode) { metadata = ((StateNode) metadata).getCurrent(); } Function metadataTop = parameterTopInput.get(); if (metadataTop != null && metadataTop instanceof StateNode) { metadataTop = ((StateNode) metadataTop).getCurrent(); } List<Function> metadataList = metadataInput.get(); for (int i = 0; i < metadataList.size(); i++) { if (metadataList.get(i) instanceof StateNode) { metadataList.set(i, ((StateNode) metadataList.get(i)).getCurrent()); } } // write out the log tree with meta data out.print("tree STATE_" + sample + " = "); tree.getRoot().sort(); out.print(toNewick(tree.getRoot(), metadata, metadataTop, metadataList)); //out.print(tree.getRoot().toShortNewick(false)); out.print(";"); } String toNewick(final Node node, final Function metadata, final Function metadataTop, List<Function> metadataList) { final StringBuilder buf = new StringBuilder(); if (node.getLeft() != null) { buf.append("("); buf.append(toNewick(node.getLeft(), metadata, metadataTop, metadataList)); if (node.getRight() != null) { buf.append(','); buf.append(toNewick(node.getRight(), metadata, metadataTop, metadataList)); } buf.append(")"); } else { buf.append(node.getNr()+Tree.taxaTranslationOffset); } buf.append("[&"); switch (popSizeFunction) { case constant: { final double popStart = metadata.getArrayValue(node.getNr()); buf.append(dmv + "=").append(popStart); break; } case linear: case linear_with_constant_root: buf.append(dmt + "="); final double b; if (node.isRoot()) { b = treeTopFinderInput.get().getHighestTreeHeight() - node.getHeight(); } else { b = node.getLength(); } buf.append(b).append("," + dmv + "={"); final double popStart; if (node.isLeaf()) { popStart = metadata.getArrayValue(node.getNr()); } else { popStart = (getMetaDataTopValue(node.getLeft(), metadataTop) + getMetaDataTopValue(node.getRight(), metadataTop)); } buf.append(popStart); final double popEnd; if (node.isRoot() && popSizeFunction == TreePopSizeFunction.linear_with_constant_root) { popEnd = popStart; } else { popEnd = getMetaDataTopValue(node, metadataTop); } buf.append(",").append(popEnd).append("}"); break; } if (metadataList.size() > 0) { for (Function metadata2 : metadataList) { if (metadataList.indexOf(metadata2) > 0 || buf.length() > 1) { buf.append(","); } buf.append(((BEASTObject)metadata2).getID()); buf.append('='); if (metadata2 instanceof Parameter<?>) { Parameter<?> p = (Parameter<?>) metadata2; int dim = p.getMinorDimension1(); if (dim > 1) { buf.append('{'); for (int i = 0; i < dim; i++) { buf.append(p.getMatrixValue(node.getNr(), i)); if (i < dim - 1) { buf.append(','); } } buf.append('}'); } else { buf.append(metadata2.getArrayValue(node.getNr())); } } else { buf.append(metadata2.getArrayValue(node.getNr())); } } } buf.append(']'); if (!node.isRoot()) { buf.append(":").append(node.getLength()); } return buf.toString(); } double getMetaDataTopValue(final Node node, final Function metadataTop) { int nr = node.getNr(); if (nr >= metadataTop.getDimension()) { nr = node.getTree().getRoot().getNr(); } return metadataTop.getArrayValue(nr); } @Override public void close(final PrintStream out) { treeInput.get().close(out); } }