/*
* TreeLogAnalyser.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.app.tools;
import dr.app.beast.BeastVersion;
import dr.app.util.Arguments;
import dr.app.util.Utils;
import dr.evolution.io.Importer;
import dr.evolution.io.NexusImporter;
import dr.evolution.tree.Tree;
import dr.evomodel.tree.TreeTraceAnalysis;
import dr.util.Version;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
*/
public class TreeLogAnalyser {
private final static Version version = new BeastVersion();
static boolean combine = true;
public TreeLogAnalyser(int burnin, String inputFileName, String outputFileName, String trueTreeFileName,
String exportFileName, double minSupport, double credibleSetProbability, int maxExport, boolean verbose) throws IOException {
List<File> files = new ArrayList<File>();
File inputFile = new File(inputFileName);
if (inputFile.isDirectory()) {
System.out.println("Analysing all tree files below directory: " + inputFileName);
collectFiles(inputFile, files);
} else if (inputFile.isFile()) {
System.out.println("Analysing tree file: " + inputFileName);
files.add(inputFile);
} else {
System.err.println(inputFileName + " does not exist!");
System.exit(0);
}
if( files.size() == 0 ) {
System.err.println("No valid files");
System.exit(0);
}
if (outputFileName != null) {
FileOutputStream outputStream = new FileOutputStream(outputFileName);
System.setOut(new PrintStream(outputStream));
}
Tree trueTree = null;
if (trueTreeFileName != null) {
NexusImporter importer = new NexusImporter(new FileReader(trueTreeFileName));
try {
trueTree = importer.importNextTree();
} catch (Importer.ImportException e) {
throw new IOException(e.getMessage());
}
}
analyze(files, burnin, trueTree, verbose, exportFileName, minSupport, credibleSetProbability, maxExport, new boolean[]{true});
}
private static void collectFiles(File file, List<File> files) {
if (file.isFile()) {
if (file.getName().endsWith(".tre") || file.getName().endsWith(".trees") || file.getName().endsWith(".t")) {
files.add(file);
}
} else {
File[] listFiles = file.listFiles();
for (File listFile : listFiles) {
collectFiles(listFile, files);
}
}
}
private static void analyze(List<File> files, int burnin, Tree tree, boolean verbose, String exportFileName,
double minSupport, double credibleSetProbability, int maxExport, boolean[] drawHeader) {
if (combine) {
try {
Reader[] readers = new Reader[files.size()];
for (int i = 0; i < readers.length; i++) {
readers[i] = new FileReader(files.get(i));
}
TreeTraceAnalysis analysis = TreeTraceAnalysis.analyzeLogFile(readers, burnin, verbose);
if (exportFileName != null) {
PrintStream exportStream = new PrintStream(exportFileName);
//System.err.println("Exporting trees ...");
analysis.export(exportStream, minSupport, maxExport, verbose);
} else {
if (verbose) {
analysis.report(0.05, credibleSetProbability, (int)(minSupport+.5));
} else {
final String name = files.size() > 1 ? "combined" : files.get(0).toString();
analysis.shortReport(name, tree, drawHeader[0]);
drawHeader[0] = false;
}
}
} catch (IOException ioe) {
//
}
} else {
for (File file : files) {
try {
final Reader[] readers = {new FileReader(file)};
TreeTraceAnalysis analysis = TreeTraceAnalysis.analyzeLogFile(readers, burnin, verbose);
if (verbose) {
analysis.report((int)(minSupport+.5));
} else {
analysis.shortReport(file.toString(), tree, drawHeader[0]);
drawHeader[0] = false;
}
} catch (IOException ioe) {
//
}
}
}
}
public static void printTitle() {
System.out.println();
centreLine("TreeLogAnalyser " + version.getVersionString() + ", " + version.getDateString(), 60);
centreLine("MCMC Output analysis", 60);
centreLine("by", 60);
centreLine("Alexei Drummond and Andrew Rambaut", 60);
System.out.println();
centreLine("Department of Computer Science", 60);
centreLine("University of Auckland", 60);
centreLine("alexei@cs.auckland.ac.nz", 60);
System.out.println();
centreLine("Institute of Evolutionary Biology", 60);
centreLine("University of Edinburgh", 60);
centreLine("a.rambaut@ed.ac.uk", 60);
System.out.println();
System.out.println();
}
public static void centreLine(String line, int pageWidth) {
int n = pageWidth - line.length();
int n1 = n / 2;
for (int i = 0; i < n1; i++) {
System.out.print(" ");
}
System.out.println(line);
}
public static void printUsage(Arguments arguments) {
arguments.printUsage("treeloganalyser", "<input-file-name> [<true-tree-file-name> [<output-file-name>]]");
System.out.println();
System.out.println(" Example: treeloganalyser test.trees trueTree.tree out.txt");
System.out.println();
}
//Main method
public static void main(String[] args) throws java.io.IOException {
// There is a major issue with languages that use the comma as a decimal separator.
// To ensure compatibility between programs in the package, enforce the US locale.
Locale.setDefault(Locale.US);
printTitle();
Arguments arguments = new Arguments(
new Arguments.Option[]{
new Arguments.IntegerOption("burnin", "the number of states to be considered as 'burn-in' [default = none]"),
new Arguments.StringOption("export", "file-name", "name of file to export"),
new Arguments.RealOption("limit", "don't export trees with support lower than limit [default = 0.0]"),
new Arguments.RealOption("probability", "credible set probability limit [default = 0.95]"),
new Arguments.IntegerOption("max", "export no more than max trees [default = all]"),
new Arguments.Option("short", "use this option to produce a short report"),
new Arguments.Option("help", "option to print this message")
});
try {
arguments.parseArguments(args);
} catch (Arguments.ArgumentException ae) {
System.out.println(ae);
printUsage(arguments);
System.exit(1);
}
if (arguments.hasOption("help")) {
printUsage(arguments);
System.exit(0);
}
int burnin = -1;
if (arguments.hasOption("burnin")) {
burnin = arguments.getIntegerOption("burnin");
}
boolean shortReport = arguments.hasOption("short");
String exportFileName = null;
if (arguments.hasOption("export")) {
exportFileName = arguments.getStringOption("export");
}
double minSupport = 0.0;
if (arguments.hasOption("limit")) {
minSupport = arguments.getRealOption("limit");
}
double credibleSetProbability = 0.95;
if (arguments.hasOption("probability")) {
credibleSetProbability = arguments.getRealOption("probability");
}
int maxExport = -1;
if (arguments.hasOption("max")) {
maxExport = arguments.getIntegerOption("max");
}
String inputFileName = null;
String trueTreeFileName = null;
String outputFileName = null;
String[] args2 = arguments.getLeftoverArguments();
if (args2.length > 3) {
System.err.println("Unknown option: " + args2[2]);
System.err.println();
printUsage(arguments);
System.exit(1);
}
if (args2.length > 0) {
inputFileName = args2[0];
}
if (args2.length > 1) {
trueTreeFileName = args2[1];
}
if (args2.length > 2) {
outputFileName = args2[2];
}
if (inputFileName == null) {
// No input file name was given so throw up a dialog box...
inputFileName = Utils.getLoadFileName("TreeLogAnalyser " + version.getVersionString() + " - Select log file to analyse");
}
new TreeLogAnalyser(burnin, inputFileName, outputFileName, trueTreeFileName, exportFileName,
minSupport, credibleSetProbability, maxExport, !shortReport);
System.exit(0);
}
}