/*
Copyright (C) 2011 Diego Darriba, David Posada
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package es.uvigo.darwin.jmodeltest.tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import pal.tree.Tree;
import es.uvigo.darwin.jmodeltest.io.TextOutputStream;
import es.uvigo.darwin.jmodeltest.model.Model;
import es.uvigo.darwin.jmodeltest.utilities.Utilities;
public class TreeSummary {
private int AIC_INDEX = 0;
private int AICC_INDEX = 1;
private int BIC_INDEX = 2;
private int DT_INDEX = 3;
// number of Information Criteria
private int IC_COUNT = 4;
private Tree bestTree[] = new Tree[IC_COUNT];
private TreeDistancesCache rfDistances = TreeRFDistancesCache.getInstance();
private TreeDistancesCache euclideanDistances = TreeEuclideanDistancesCache
.getInstance();
private Hashtable<Tree, SummaryRow> summary;
// sorted topologies by criterion
private List<Tree> topologiesAIC = null, topologiesAICc = null,
topologiesBIC = null, topologiesDT = null, sortedTopologies = null;
public TreeSummary(Tree bestAIC, Tree bestAICc, Tree bestBIC, Tree bestDT,
Model[] models) {
if (bestAIC != null) {
this.bestTree[AIC_INDEX] = bestAIC;
this.topologiesAIC = new ArrayList<Tree>();
}
if (bestAICc != null) {
this.bestTree[AICC_INDEX] = bestAICc;
this.topologiesAICc = new ArrayList<Tree>();
}
if (bestBIC != null) {
this.bestTree[BIC_INDEX] = bestBIC;
this.topologiesBIC = new ArrayList<Tree>();
}
if (bestDT != null) {
this.bestTree[DT_INDEX] = bestDT;
this.topologiesDT = new ArrayList<Tree>();
}
this.summary = new Hashtable<Tree, SummaryRow>();
for (Model model : models) {
boolean done = false;
for (Tree tree : summary.keySet()) {
if (sameTopology(tree, model.getTree())) {
summary.get(tree).addModel(model);
done = true;
break;
}
}
if (!done) {
// new topology
SummaryRow newRow = new SummaryRow(model.getTree());
newRow.addModel(model);
summary.put(model.getTree(), newRow);
if (topologiesAIC != null)
topologiesAIC.add(model.getTree());
if (topologiesAICc != null)
topologiesAICc.add(model.getTree());
if (topologiesBIC != null)
topologiesBIC.add(model.getTree());
if (topologiesDT != null)
topologiesDT.add(model.getTree());
}
}
for (Tree tree : summary.keySet()) {
summary.get(tree).computeEuclideanDistances();
}
// sort by criteria
if (topologiesAIC != null)
Collections.sort(topologiesAIC, new AicComparator());
if (topologiesAICc != null)
Collections.sort(topologiesAICc, new AiccComparator());
if (topologiesBIC != null)
Collections.sort(topologiesBIC, new BicComparator());
if (topologiesDT != null)
Collections.sort(topologiesDT, new DtComparator());
sortedTopologies = topologiesAIC != null ? topologiesAIC
: topologiesBIC != null ? topologiesBIC
: topologiesAICc != null ? topologiesAICc
: topologiesDT != null ? topologiesDT
: new ArrayList<Tree>(summary.keySet());
}
public int getNumberOfTopologies() {
return summary.size();
}
public Tree getTopology (int index) {
return sortedTopologies.get(index);
}
public List<Model> getModelsByTopology (int index) {
return summary.get(getTopology(index)).models;
}
public List<Model> getAICModels(int index) {
Tree key = topologiesAIC.get(index);
return summary.get(key).models;
}
public List<Model> getBICModels(int index) {
Tree key = topologiesBIC.get(index);
return summary.get(key).models;
}
public List<Model> getAICcModels(int index) {
Tree key = topologiesAICc.get(index);
return summary.get(key).models;
}
public List<Model> getDTModels(int index) {
Tree key = topologiesDT.get(index);
return summary.get(key).models;
}
public int aicIndexOf(Tree tree) {
return topologiesAIC.indexOf(tree);
}
public long aicRfOf(Tree tree) {
return summary.get(tree).rfDistance[AIC_INDEX];
}
public double aicAvgDistance(Tree tree) {
return summary.get(tree).avgEuclideanDistance[AIC_INDEX];
}
public double aicVarDistance(Tree tree) {
return summary.get(tree).varEuclideanDistance[AIC_INDEX];
}
public double aiccWeight(Tree tree) {
return summary.get(tree).support[AICC_INDEX];
}
public int aiccIndexOf(Tree tree) {
return topologiesAICc.indexOf(tree);
}
public long aiccRfOf(Tree tree) {
return summary.get(tree).rfDistance[AICC_INDEX];
}
public double aiccAvgDistance(Tree tree) {
return summary.get(tree).avgEuclideanDistance[AICC_INDEX];
}
public double aiccVarDistance(Tree tree) {
return summary.get(tree).varEuclideanDistance[AICC_INDEX];
}
public double aicWeight(Tree tree) {
return summary.get(tree).support[AIC_INDEX];
}
public int bicIndexOf(Tree tree) {
return topologiesBIC.indexOf(tree);
}
public long bicRfOf(Tree tree) {
return summary.get(tree).rfDistance[BIC_INDEX];
}
public double bicAvgDistance(Tree tree) {
return summary.get(tree).avgEuclideanDistance[BIC_INDEX];
}
public double bicVarDistance(Tree tree) {
return summary.get(tree).varEuclideanDistance[BIC_INDEX];
}
public double bicWeight(Tree tree) {
return summary.get(tree).support[BIC_INDEX];
}
public int dtIndexOf(Tree tree) {
return topologiesDT.indexOf(tree);
}
public long dtRfOf(Tree tree) {
return summary.get(tree).rfDistance[DT_INDEX];
}
public double dtAvgDistance(Tree tree) {
return summary.get(tree).avgEuclideanDistance[DT_INDEX];
}
public double dtVarDistance(Tree tree) {
return summary.get(tree).varEuclideanDistance[DT_INDEX];
}
public double dtWeight(Tree tree) {
return summary.get(tree).support[DT_INDEX];
}
private boolean sameTopology(Tree t1, Tree t2) {
return (rfDistances.getDistance(t1, t2) == 0);
}
private long rfDistance(Tree t1, Tree t2) {
if (t1 == null || t2 == null) {
return -1;
} else {
return Math.round(rfDistances.getDistance(t1, t2));
}
}
class SummaryRow {
List<Model> models;
Tree commonTopology;
long rfDistance[] = new long[] { 0l, 0l, 0l, 0l };
double avgEuclideanDistance[] = new double[] { 0.0d, 0.0d, 0.0d, 0.0d };
double varEuclideanDistance[] = new double[] { 0.0d, 0.0d, 0.0d, 0.0d };
double support[] = new double[] { 0.0d, 0.0d, 0.0d, 0.0d };
SummaryRow(Tree commonTopology) {
this.commonTopology = commonTopology;
for (int i = 0; i < IC_COUNT; i++) {
this.rfDistance[i] = rfDistance(commonTopology, bestTree[i]);
}
this.models = new ArrayList<Model>();
}
void addModel(Model model) {
// check same topology
if (checkTopology(model)) {
models.add(model);
support[AIC_INDEX] += model.getAICw();
support[AICC_INDEX] += model.getAICcw();
support[BIC_INDEX] += model.getBICw();
support[DT_INDEX] += model.getDTw();
}
}
void computeEuclideanDistances() {
// set variables to zero
for (int i = 0; i < IC_COUNT; i++) {
if (bestTree[i] != null) {
avgEuclideanDistance[i] = 0.0d;
varEuclideanDistance[i] = 0.0d;
for (Model model : models) {
double distance = euclideanDistances.getDistance(
model.getTree(), bestTree[i]);
avgEuclideanDistance[i] += distance;
varEuclideanDistance[i] += distance * distance;
}
avgEuclideanDistance[i] /= models.size();
varEuclideanDistance[i] /= models.size();
varEuclideanDistance[i] -= avgEuclideanDistance[i]
* avgEuclideanDistance[i];
}
}
}
boolean checkTopology(Model model) {
return (rfDistances.getDistance(commonTopology, model.getTree()) == 0);
}
}
class AicComparator implements Comparator<Tree> {
@Override
public int compare(Tree o1, Tree o2) {
long d1 = rfDistance(o1, bestTree[AIC_INDEX]);
long d2 = rfDistance(o2, bestTree[AIC_INDEX]);
return (int) (d1 - d2);
}
}
class AiccComparator implements Comparator<Tree> {
@Override
public int compare(Tree o1, Tree o2) {
long d1 = rfDistance(o1, bestTree[AICC_INDEX]);
long d2 = rfDistance(o2, bestTree[AICC_INDEX]);
return (int) (d1 - d2);
}
}
class BicComparator implements Comparator<Tree> {
@Override
public int compare(Tree o1, Tree o2) {
long d1 = rfDistance(o1, bestTree[BIC_INDEX]);
long d2 = rfDistance(o2, bestTree[BIC_INDEX]);
return (int) (d1 - d2);
}
}
class DtComparator implements Comparator<Tree> {
@Override
public int compare(Tree o1, Tree o2) {
long d1 = rfDistance(o1, bestTree[DT_INDEX]);
long d2 = rfDistance(o2, bestTree[DT_INDEX]);
return (int) (d1 - d2);
}
}
public void print(TextOutputStream stream) {
int MAX_LEN = 80;
if (topologiesAIC != null || topologiesBIC != null
|| topologiesAICc != null || topologiesDT != null) {
List<Tree> sortedTopologies = topologiesAIC != null ? topologiesAIC
: topologiesBIC != null ? topologiesBIC
: topologiesAICc != null ? topologiesAICc
: topologiesDT;
stream.println("::Optimized Topologies Summary::");
stream.println("");
stream.println("There are " + getNumberOfTopologies()
+ " different topologies.");
stream.println("");
for (int i = 0; i < getNumberOfTopologies(); i++) {
int index = i + 1;
Tree currentTree = sortedTopologies.get(i);
SummaryRow summaryRow = summary.get(currentTree);
int aicIndex = topologiesAIC != null ? aicIndexOf(currentTree) + 1
: -1;
int aiccIndex = topologiesAICc != null ? aiccIndexOf(currentTree) + 1
: -1;
int bicIndex = topologiesBIC != null ? bicIndexOf(currentTree) + 1
: -1;
int dtIndex = topologiesDT != null ? dtIndexOf(currentTree) + 1
: -1;
stream.println("Topology Id: " + index);
stream.println("\tRank\tWeight\t\t RF\tAvgEucl\t\tVarEucl");
if (topologiesAIC != null) {
stream.println(getIcRow("AIC", aicIndex, summaryRow, AIC_INDEX));
}
if (topologiesBIC != null) {
stream.println(getIcRow("BIC", bicIndex, summaryRow, BIC_INDEX));
}
if (topologiesAICc != null) {
stream.println(getIcRow("AICc", aiccIndex, summaryRow, AICC_INDEX));
}
if (topologiesDT != null) {
stream.println(getIcRow("DT", dtIndex, summaryRow, DT_INDEX));
}
stream.println("Models supporting: "
+ summaryRow.models.size());
stream.print(" ");
int chars = 0;
for (Model model : summaryRow.models) {
if (chars >= MAX_LEN) {
stream.println("");
stream.print(" ");
chars = 0;
}
stream.print(model.getName() + " ");
chars += model.getName().length() + 1;
}
stream.println("");
stream.println("");
}
}
}
private String getIcRow(String name, int rankIndex, SummaryRow summaryRow, int criterionIndex) {
return name + "\t"
+ Utilities.format(rankIndex,3,0,false)
+ "\t"
+ Utilities
.asPercent(summaryRow.support[criterionIndex] * 100)
+ "\t\t" + Utilities.format(summaryRow.rfDistance[criterionIndex],3,0,false)
+ "\t" + Utilities.format(summaryRow.avgEuclideanDistance[criterionIndex],8,2,true)
+ "\t" + Utilities.format(summaryRow.varEuclideanDistance[criterionIndex],8,2,true);
}
}