/*
* TreeLogger.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evomodel.tree;
import dr.app.tools.NexusExporter;
import dr.evolution.tree.*;
import dr.inference.loggers.LogFormatter;
import dr.inference.loggers.MCLogger;
import java.text.NumberFormat;
import java.util.*;
/**
* A logger that logs tree and clade frequencies.
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: TreeLogger.java,v 1.25 2006/09/05 13:29:34 rambaut Exp $
*/
public class TreeLogger extends MCLogger {
private Tree tree;
private BranchRates branchRates = null;
private TreeAttributeProvider[] treeAttributeProviders;
private TreeTraitProvider[] treeTraitProviders;
private boolean nexusFormat = false;
public boolean usingRates = false;
public boolean substitutions = false;
private final Map<String, Integer> idMap = new HashMap<String, Integer>();
private final List<String> taxaIds = new ArrayList<String>();
private boolean mapNames = true;
/*private double normaliseMeanRateTo = Double.NaN;
boolean normaliseMeanRate = false;*/
private NumberFormat format;
private LogUpon condition = null;
/**
* Interface to indicate when to log a tree
*/
public interface LogUpon {
/**
*
* @param state
* @return True if log tree of this state.
*/
boolean logNow(long state);
}
public TreeLogger(Tree tree, LogFormatter formatter, int logEvery, boolean nexusFormat,
boolean sortTranslationTable, boolean mapNames) {
this(tree, null, null, null, formatter, logEvery, nexusFormat, sortTranslationTable, mapNames, null, null/*, Double.NaN*/);
}
public TreeLogger(Tree tree, LogFormatter formatter, int logEvery, boolean nexusFormat,
boolean sortTranslationTable, boolean mapNames, NumberFormat format) {
this(tree, null, null, null, formatter, logEvery, nexusFormat, sortTranslationTable, mapNames, format, null/*, Double.NaN*/);
}
public TreeLogger(Tree tree, BranchRates branchRates,
TreeAttributeProvider[] treeAttributeProviders,
TreeTraitProvider[] treeTraitProviders,
LogFormatter formatter, int logEvery, boolean nexusFormat,
boolean sortTranslationTable, boolean mapNames, NumberFormat format,
TreeLogger.LogUpon condition) {
super(formatter, logEvery, false);
this.condition = condition;
/*this.normaliseMeanRateTo = normaliseMeanRateTo;
if(!Double.isNaN(normaliseMeanRateTo)) {
normaliseMeanRate = true;
}*/
this.nexusFormat = nexusFormat;
// if not NEXUS, can't map names
this.mapNames = mapNames && nexusFormat;
this.branchRates = branchRates;
this.treeAttributeProviders = treeAttributeProviders;
this.treeTraitProviders = treeTraitProviders;
if (this.branchRates != null) {
this.substitutions = true;
}
this.tree = tree;
for (int i = 0; i < tree.getTaxonCount(); i++) {
taxaIds.add(tree.getTaxon(i).getId());
}
if (sortTranslationTable) {
Collections.sort(taxaIds);
}
int k = 1;
for (String taxaId : taxaIds) {
idMap.put(taxaId, k);
k += 1;
}
this.format = format;
}
public void startLogging() {
if (nexusFormat) {
int taxonCount = tree.getTaxonCount();
logLine("#NEXUS");
logLine("");
logLine("Begin taxa;");
logLine("\tDimensions ntax=" + taxonCount + ";");
logLine("\tTaxlabels");
for (String taxaId : taxaIds) {
logLine("\t\t" + cleanTaxonName(taxaId));
}
logLine("\t\t;");
logLine("End;");
logLine("");
logLine("Begin trees;");
if (mapNames) {
// This is needed if the trees use numerical taxon labels
logLine("\tTranslate");
int k = 1;
for (String taxaId : taxaIds) {
if (k < taxonCount) {
logLine("\t\t" + k + " " + cleanTaxonName(taxaId) + ",");
} else {
logLine("\t\t" + k + " " + cleanTaxonName(taxaId));
}
k += 1;
}
logLine("\t\t;");
}
}
}
private String cleanTaxonName(String taxaId) {
if (taxaId.matches(NexusExporter.SPECIAL_CHARACTERS_REGEX)) {
if (taxaId.contains("\'")) {
if (taxaId.contains("\"")) {
throw new RuntimeException("Illegal taxon name - contains both single and double quotes");
}
return "\"" + taxaId + "\"";
}
return "\'" + taxaId + "\'";
}
return taxaId;
}
public void log(long state) {
/*if(normaliseMeanRate) {
NormaliseMeanTreeRate.analyze(tree, normaliseMeanRateTo);
}*/
final boolean doIt = condition != null ? condition.logNow(state) :
(logEvery < 0 || ((state % logEvery) == 0));
if ( doIt ) {
StringBuffer buffer = new StringBuffer("tree STATE_");
buffer.append(state);
if (treeAttributeProviders != null) {
boolean hasAttribute = false;
for (TreeAttributeProvider tap : treeAttributeProviders) {
String[] attributeLabel = tap.getTreeAttributeLabel();
String[] attributeValue = tap.getAttributeForTree(tree);
for (int i = 0; i < attributeLabel.length; i++) {
if (!hasAttribute) {
buffer.append(" [&");
hasAttribute = true;
} else {
buffer.append(",");
}
buffer.append(attributeLabel[i]);
buffer.append("=");
buffer.append(attributeValue[i]);
}
}
if (hasAttribute) {
buffer.append("]");
}
}
buffer.append(" = [&R] ");
if (substitutions) {
TreeUtils.newick(tree, tree.getRoot(), false, TreeUtils.BranchLengthType.LENGTHS_AS_SUBSTITUTIONS,
format, branchRates, treeTraitProviders, idMap, buffer);
} else {
//System.out.println(treeTraitProviders.length);
TreeUtils.newick(tree, tree.getRoot(), !mapNames, TreeUtils.BranchLengthType.LENGTHS_AS_TIME,
format, null, treeTraitProviders, idMap, buffer);
}
buffer.append(";");
logLine(buffer.toString());
}
}
public void stopLogging() {
logLine("End;");
super.stopLogging();
}
public Tree getTree() {
return tree;
}
public void setTree(Tree tree) {
this.tree = tree;
}
public TreeAttributeProvider[] getTreeAttributeProviders() {
return this.treeAttributeProviders;
}
public TreeTraitProvider[] getTreeTraitProviders() {
return this.treeTraitProviders;
}
public BranchRates getBranchRates() {
return this.branchRates;
}
}