/*
* CheckPointUpdater.java
*
* Copyright (c) 2002-2017 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.realtime;
import dr.evolution.alignment.PatternList;
import dr.evolution.alignment.Patterns;
import dr.evolution.tree.BranchRates;
import dr.evolution.tree.NodeRef;
import dr.evolution.tree.Tree;
import dr.evolution.util.Taxon;
import dr.evomodel.tree.TreeModel;
import dr.evomodel.tree.TreeParameterModel;
import dr.evomodel.treedatalikelihood.BeagleDataLikelihoodDelegate;
import dr.evomodel.treedatalikelihood.DataLikelihoodDelegate;
import dr.evomodel.treedatalikelihood.MultiPartitionDataLikelihoodDelegate;
import dr.evomodel.treedatalikelihood.TreeDataLikelihood;
import dr.inference.model.Likelihood;
import java.util.ArrayList;
import java.util.List;
/**
* @author Guy Baele
*/
public class CheckPointTreeModifier {
public final static String TREE_UPDATE_OPTION = "JC69Distance";
public final static Double EPSILON = 0.10;
private TreeModel treeModel;
private ArrayList<String> newTaxaNames;
private int[] nodeMap;
private int additionalTaxa;
public CheckPointTreeModifier(TreeModel treeModel) {
this.treeModel = treeModel;
}
/**
* Modifies the current tree by adopting the provided collection of edges and the stored update criterion.
* @param edges Edges are provided as index: child number; parent: array entry
* @param nodeHeights Also sets the node heights to the provided values
* @param childOrder Array that contains whether a child node is left or right child
* @param taxaNames Taxa names as stored in the checkpoint file
*/
public void adoptTreeStructure(int[] edges, double[] nodeHeights, int[] childOrder, String[] taxaNames) {
this.additionalTaxa = treeModel.getExternalNodeCount() - taxaNames.length;
//check if the taxa are in the right order, i.e. for now only allow adding taxa at the end of the list
boolean correctOrder = checkTaxaOrder(taxaNames);
if (!correctOrder) {
//create a map between the old node order and the new node order
createNodeMap(taxaNames);
}
treeModel.beginTreeEdit();
int currentTaxa = treeModel.getExternalNodeCount();
System.out.println("#external nodes = " + currentTaxa);
//first remove all the child nodes of the internal nodes
for (int i = treeModel.getExternalNodeCount(); i < treeModel.getNodeCount(); i++) {
int childCount = treeModel.getChildCount(treeModel.getNodes()[i]);
for (int j = 0; j < childCount; j++) {
treeModel.removeChild(treeModel.getNodes()[i], treeModel.getChild(treeModel.getNodes()[i], j));
}
}
//use node mapping to reconstruct the checkpointed tree
//disregard the added taxa for now
//start with setting the external node heights
//this.additionalTaxa can function as the offset in the internal node array
for (int i = 0; i < (treeModel.getExternalNodeCount()-additionalTaxa); i++) {
treeModel.setNodeHeight(treeModel.getExternalNode(nodeMap[i]), nodeHeights[i]);
}
//set the internal node heights
//this.additionalTaxa can function as the offset in the internal node array
for (int i = 0; i < (treeModel.getExternalNodeCount()-additionalTaxa-1); i++) {
//No just restart counting, will fix later on in the code by adding additionalTaxa variable
treeModel.setNodeHeight(treeModel.getInternalNode(i), nodeHeights[treeModel.getExternalNodeCount()-additionalTaxa+i]);
}
int newRootIndex = -1;
//now add the parent-child links again to ALL the nodes
for (int i = 0; i < edges.length; i++) {
if (edges[i] != -1) {
//make distinction between external nodes and internal nodes
if (i < (treeModel.getExternalNodeCount()-additionalTaxa)) {
//external node
treeModel.addChild(treeModel.getNode(edges[i]+additionalTaxa), treeModel.getExternalNode(nodeMap[i]));
System.out.println("external: " + (edges[i]+additionalTaxa) + " > " + nodeMap[i]);
} else {
//internal node
treeModel.addChild(treeModel.getNode(edges[i]+additionalTaxa), treeModel.getNode(i+additionalTaxa));
System.out.println("internal: " + (edges[i]+additionalTaxa) + " > " + (i+additionalTaxa));
}
} else {
newRootIndex = i;
}
}
//not possible to determine correct ordering of child nodes in the loop where they're being assigned
//hence perform possible swaps in a separate loop
for (int i = 0; i < edges.length; i++) {
if (edges[i] != -1) {
if (i < (treeModel.getExternalNodeCount()-additionalTaxa)) {
if (childOrder[i] == 0 && treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0) != treeModel.getExternalNode(nodeMap[i])) {
NodeRef childOne = treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0);
NodeRef childTwo = treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 1);
treeModel.removeChild(treeModel.getNode(edges[i] + additionalTaxa), childOne);
treeModel.removeChild(treeModel.getNode(edges[i] + additionalTaxa), childTwo);
treeModel.addChild(treeModel.getNode(edges[i] + additionalTaxa), childTwo);
treeModel.addChild(treeModel.getNode(edges[i] + additionalTaxa), childOne);
System.out.println(i + ": " + edges[i]);
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0));
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 1));
}
} else {
if (childOrder[i] == 0 && treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0) != treeModel.getNode(i + additionalTaxa)) {
NodeRef childOne = treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0);
NodeRef childTwo = treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 1);
treeModel.removeChild(treeModel.getNode(edges[i] + additionalTaxa), childOne);
treeModel.removeChild(treeModel.getNode(edges[i] + additionalTaxa), childTwo);
treeModel.addChild(treeModel.getNode(edges[i] + additionalTaxa), childTwo);
treeModel.addChild(treeModel.getNode(edges[i] + additionalTaxa), childOne);
System.out.println(i + ": " + edges[i]);
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0));
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 1));
}
}
}
}
System.out.println("new root index: " + newRootIndex);
treeModel.setRoot(treeModel.getNode(newRootIndex+additionalTaxa));
System.out.println(treeModel.toString());
for (int i = 0; i < edges.length; i++) {
if (edges[i] != -1) {
System.out.println(i + ": " + edges[i]);
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 0));
System.out.println(" " + treeModel.getChild(treeModel.getNode(edges[i] + additionalTaxa), 1));
}
}
treeModel.endTreeEdit();
}
/**
* Imports trait information from a file
* @param edges Edges are provided as index: child number; parent: array entry
* @param traitModels List of TreeParameterModel object that contain trait information
* @param traitValues Values to be copied into the List of TreeParameterModel objects
*/
//TODO Small difference in reconstructed log likelihood, probably due to increased number of rate categories
public void adoptTraitData(int[] edges, ArrayList<TreeParameterModel> traitModels, double[][] traitValues) {
int index = 0;
for (TreeParameterModel tpm : traitModels) {
int k = 0;
for (int i = 0; i < edges.length; i++) {
System.out.println(i + " " + edges[i]);
if (edges[i] != -1) {
//TODO Seems like I messed up here
if (i < (treeModel.getExternalNodeCount()-additionalTaxa)) {
tpm.setNodeValue(this.treeModel, this.treeModel.getExternalNode(nodeMap[i]), traitValues[index][k]);
System.out.println("Setting external node " + this.treeModel.getExternalNode(nodeMap[i]) + " to " + traitValues[index][k]);
} else {
tpm.setNodeValue(this.treeModel, this.treeModel.getNode(i + additionalTaxa), traitValues[index][k]);
System.out.println("Setting internal node " + this.treeModel.getNode(i + additionalTaxa) + " to " + traitValues[index][k]);
}
} else {
k--;
}
k++;
}
//set trait of remaining internal nodes to -1.0
for (int i = 0; i < additionalTaxa; i++) {
tpm.setNodeValue(this.treeModel, treeModel.getNode(treeModel.getNodeCount()-1-i), -1.0);
}
//set trait of newly added external taxa to -1.0
int shift = 0;
for (int i = 0; i < (treeModel.getExternalNodeCount()-additionalTaxa); i++) {
System.out.println("i = " + i + " ; nodeMap[i] = " + nodeMap[i]);
}
System.out.println();
for (String name : newTaxaNames) {
System.out.println("new taxon: " + name);
}
int externalMissing = 0;
for (String name : newTaxaNames) {
for (int i = 0; i < treeModel.getExternalNodeCount(); i++) {
if (treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId().equals(name)) {
externalMissing++;
tpm.setNodeValue(this.treeModel, this.treeModel.getExternalNode(i), -1.0);
}
}
}
System.out.println("External nodes with trait set to -1.0 = " + externalMissing + "\n");
/*for (int i = 0; i < (treeModel.getExternalNodeCount()-additionalTaxa); i++) {
System.out.println("i = " + i + " ; nodeMap[i] = " + nodeMap[i]);
if (i != (nodeMap[i]-shift)) {
int difference = nodeMap[i] - i;
shift = difference;
tpm.setNodeValue(this.treeModel, this.treeModel.getExternalNode(nodeMap[i]-1), -1.0);
System.out.println("Setting external node: " + (nodeMap[i]-1));
externalMissing++;
}
}
System.out.println("External node with trait set to -1.0 = " + externalMissing);
index++;*/
}
}
/**
* The newly added taxa still need to be provided with trait values if there are any.
* @param traitModels List of the trait models for which trait values need to be imputed / interpolated.
*/
public void interpolateTraitValues(ArrayList<TreeParameterModel> traitModels) {
System.out.println();
for (TreeParameterModel tpm : traitModels) {
int numberOfInterpolations = 0;
for (int i = 0; i < treeModel.getNodeCount(); i++) {
if (tpm.getNodeValue(treeModel, treeModel.getNode(i)) == -1.0) {
System.out.println("Current trait = -1.0 for node: " + treeModel.getNode(i));
numberOfInterpolations++;
double newValue = -1.0;
//get trait value from sibling
NodeRef parent = treeModel.getParent(treeModel.getNode(i));
for (int j = 0; j < treeModel.getChildCount(parent); j++) {
NodeRef child = treeModel.getChild(parent, j);
if (tpm.getNodeValue(treeModel, child) != -1.0) {
tpm.setNodeValue(treeModel, treeModel.getNode(i), tpm.getNodeValue(treeModel, child) + 1.0);
System.out.println("Checking sibling trait.");
System.out.println("Setting node trait for node " + treeModel.getNode(i) + " to " + tpm.getNodeValue(treeModel, treeModel.getNode(i)));
break;
}
}
//if not successful, get trait from its parent
if (tpm.getNodeValue(treeModel, treeModel.getNode(i)) == -1.0) {
NodeRef currentNode = treeModel.getNode(i);
//TODO Check for situations where no proper trait can be found
while (currentNode != treeModel.getRoot() && tpm.getNodeValue(treeModel, currentNode) == -1.0) {
currentNode = treeModel.getParent(currentNode);
}
tpm.setNodeValue(treeModel, treeModel.getNode(i), tpm.getNodeValue(treeModel, currentNode) + 1.0);
System.out.println("Checking parent trait.");
System.out.println("Setting node trait for node " + treeModel.getNode(i) + " to " + tpm.getNodeValue(treeModel, currentNode));
}
//adjust the other trait values after a trait has been imputed
for (int j = 0; j < treeModel.getNodeCount(); j++) {
if (treeModel.getNode(j) != treeModel.getNode(i)) {
if (tpm.getNodeValue(treeModel, treeModel.getNode(j)) >= tpm.getNodeValue(treeModel, treeModel.getNode(i))) {
System.out.print("Updating trait from " + tpm.getNodeValue(treeModel, treeModel.getNode(j)));
tpm.setNodeValue(treeModel, treeModel.getNode(j), tpm.getNodeValue(treeModel, treeModel.getNode(j)) + 1.0);
System.out.println(" to " + tpm.getNodeValue(treeModel, treeModel.getNode(j)));
}
}
}
}
/*if (tpm.getNodeValue(treeModel, treeModel.getNode(i)) == -1.0) {
for (int j = 0; j < treeModel.getNodeCount(); j++) {
if (treeModel.getNode(j) != treeModel.getNode(i)) {
if (tpm.getNodeValue(treeModel, treeModel.getNode(j)) >= tpm.getNodeValue(treeModel, treeModel.getNode(i))) {
System.out.print("Updating trait from " + tpm.getNodeValue(treeModel, treeModel.getNode(j)));
tpm.setNodeValue(treeModel, treeModel.getNode(j), tpm.getNodeValue(treeModel, treeModel.getNode(j)) + 1.0);
System.out.println(" to " + tpm.getNodeValue(treeModel, treeModel.getNode(j)));
}
}
}
}*/
}
System.out.println("Number of interpolations: " + numberOfInterpolations);
}
System.out.println("Done.\n");
}
/**
* Add the remaining taxa, which can be identified through the TreeDataLikelihood XML elements.
*/
public ArrayList<NodeRef> incorporateAdditionalTaxa(CheckPointUpdaterApp.UpdateChoice choice, BranchRates rateModel) {
ArrayList<NodeRef> newTaxaNodes = new ArrayList<NodeRef>();
for (String str : newTaxaNames) {
for (int i = 0; i < treeModel.getExternalNodeCount(); i++) {
if (treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId().equals(str)) {
newTaxaNodes.add(treeModel.getExternalNode(i));
}
}
}
ArrayList<Taxon> currentTaxa = new ArrayList<Taxon>();
for (int i = 0; i < treeModel.getExternalNodeCount(); i++) {
boolean taxonFound = false;
for (String str : newTaxaNames) {
if (str.equals((treeModel.getNodeTaxon(treeModel.getExternalNode(i))).getId())) {
taxonFound = true;
}
}
if (!taxonFound) {
System.out.println("Adding " + treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId());
currentTaxa.add(treeModel.getNodeTaxon(treeModel.getExternalNode(i)));
}
}
//check the Tree(Data)Likelihoods in the connected set of likelihoods
//focus on TreeDataLikelihood, which has getTree() to get the tree for each likelihood
//also get the DataLikelihoodDelegate from TreeDataLikelihood
ArrayList<TreeDataLikelihood> likelihoods = new ArrayList<TreeDataLikelihood>();
ArrayList<Tree> trees = new ArrayList<Tree>();
ArrayList<DataLikelihoodDelegate> delegates = new ArrayList<DataLikelihoodDelegate>();
for (Likelihood likelihood : Likelihood.CONNECTED_LIKELIHOOD_SET) {
if (likelihood instanceof TreeDataLikelihood) {
likelihoods.add((TreeDataLikelihood)likelihood);
trees.add(((TreeDataLikelihood) likelihood).getTree());
delegates.add(((TreeDataLikelihood) likelihood).getDataLikelihoodDelegate());
}
}
//suggested to go through TreeDataLikelihoodParser and give it an extra option to create a HashMap
//keyed by the tree; am currently not overly fond of this approach
ArrayList<PatternList> patternLists = new ArrayList<PatternList>();
for (DataLikelihoodDelegate del : delegates) {
if (del instanceof BeagleDataLikelihoodDelegate) {
patternLists.add(((BeagleDataLikelihoodDelegate) del).getPatternList());
} else if (del instanceof MultiPartitionDataLikelihoodDelegate) {
MultiPartitionDataLikelihoodDelegate mpdld = (MultiPartitionDataLikelihoodDelegate)del;
List<PatternList> list = mpdld.getPatternLists();
for (PatternList pList : list) {
patternLists.add(pList);
}
}
}
if (patternLists.size() == 0) {
throw new RuntimeException("No patterns detected. Please make sure the XML file is BEAST 1.9 compatible.");
}
//aggregate all patterns to create distance matrix
//TODO What about different trees for different partitions?
Patterns patterns = new Patterns(patternLists.get(0));
if (patternLists.size() > 1) {
for (int i = 1; i < patternLists.size(); i++) {
patterns.addPatterns(patternLists.get(i));
}
}
//set the patterns for the distance matrix computations
choice.setPatterns(patterns);
//add new taxa one at a time
for (NodeRef newTaxon : newTaxaNodes) {
treeModel.setNodeHeight(newTaxon, treeModel.getNodeTaxon(newTaxon).getHeight());
System.out.println("\nadding Taxon: " + newTaxon + " (height = " + treeModel.getNodeHeight(newTaxon) + ")");
//check if this taxon has a more recent sampling date than all other nodes in the current TreeModel
double offset = checkCurrentTreeNodes(newTaxon, treeModel.getRoot());
System.out.println("Sampling date offset when adding " + newTaxon + " = " + offset);
//if so, update all nodes current in the tree (i.e. recursively from the root)
//AND set its current node height to 0.0
if (offset < 0.0) {
System.out.println("Updating all node heights with offset " + Math.abs(offset));
updateAllTreeNodes(Math.abs(offset), treeModel.getRoot());
treeModel.setNodeHeight(newTaxon, 0.0);
} else if (offset == 0.0) {
treeModel.setNodeHeight(newTaxon, 0.0);
}
//get the closest Taxon to the Taxon that needs to be added
//take into account which taxa can currently be chosen
Taxon closest = choice.getClosestTaxon(treeModel.getNodeTaxon(newTaxon), currentTaxa);
System.out.println("\nclosest Taxon: " + closest + " with original height: " + closest.getHeight());
//get the distance between these two taxa
double distance = choice.getDistance(treeModel.getNodeTaxon(newTaxon), closest);
System.out.println("at distance: " + distance);
//find the NodeRef for the closest Taxon (do not rely on node numbering)
NodeRef closestRef = null;
//careful with node numbering and subtract number of new taxa
for (int i = 0; i < treeModel.getExternalNodeCount(); i++) {
if (treeModel.getNodeTaxon(treeModel.getExternalNode(i)) == closest) {
closestRef = treeModel.getExternalNode(i);
}
}
System.out.println(closestRef + " with height " + treeModel.getNodeHeight(closestRef));
//System.out.println("trying to set node height: " + closestRef + " from " + treeModel.getNodeHeight(closestRef) + " to " + closest.getHeight());
//treeModel.setNodeHeight(closestRef, closest.getHeight());
double timeForDistance = distance/rateModel.getBranchRate(treeModel, closestRef);
System.out.println("timeForDistance = " + timeForDistance);
//get parent node of branch that will be split
NodeRef parent = treeModel.getParent(closestRef);
//determine height of new node
//double insertHeight = Math.abs(treeModel.getNodeHeight(parent) + treeModel.getNodeHeight(closestRef))/2.0;
//TODO This insertion criterion needs a major update
double insertHeight;
if (treeModel.getNodeHeight(closestRef) < treeModel.getNodeHeight(newTaxon)) {
insertHeight = treeModel.getNodeHeight(closestRef) + (timeForDistance + Math.abs(treeModel.getNodeHeight(closestRef) - treeModel.getNodeHeight(newTaxon)))/2.0;
System.out.println("treeModel.getNodeHeight(closestRef) < treeModel.getNodeHeight(newTaxon): " + insertHeight);
} else {
insertHeight = treeModel.getNodeHeight(closestRef) + (timeForDistance - Math.abs(treeModel.getNodeHeight(closestRef) - treeModel.getNodeHeight(newTaxon)))/2.0;
System.out.println("treeModel.getNodeHeight(closestRef) >= treeModel.getNodeHeight(newTaxon): " + insertHeight);
}
if (insertHeight > treeModel.getNodeHeight(parent) || insertHeight < treeModel.getNodeHeight(closestRef)) {
insertHeight = treeModel.getNodeHeight(parent) - EPSILON*(treeModel.getNodeHeight(parent) - treeModel.getNodeHeight(closestRef));
}
System.out.println("insert at height: " + insertHeight);
//pass on all the necessary variables to a method that adds the new taxon to the tree
addTaxonAlongBranch(newTaxon, parent, closestRef, insertHeight);
//option to print tree after each taxon addition
System.out.println("\nTree after adding taxon " + newTaxon + ":\n" + treeModel.toString());
//add newly added Taxon to list of current taxa
currentTaxa.add(treeModel.getNodeTaxon(newTaxon));
}
//System.out.println(treeModel.toString());
return newTaxaNodes;
}
/**
* Add a given offset to all node height (both internal and external) of a tree
* @param offset The offset to add to the node height
* @param currentRoot Root of the tree
*/
private void updateAllTreeNodes(double offset, NodeRef currentRoot) {
treeModel.setNodeHeight(currentRoot, treeModel.getNodeHeight(currentRoot) + offset);
for (int i = 0; i < treeModel.getChildCount(currentRoot); i++) {
updateAllTreeNodes(offset, treeModel.getChild(currentRoot, i));
}
}
/**
* Check if the taxon to be added has a more recent sampling date than all external nodes currently in the tree.
* @param newTaxon The taxon to be added
* @param currentRoot The current connected tree that does not yet contain the @newTaxon taxon
* @return Offset of the node height of the new taxon versus the current most recent sampling date
*/
private double checkCurrentTreeNodes(NodeRef newTaxon, NodeRef currentRoot) {
/*System.out.println(currentRoot);
if (treeModel.getChildCount(currentRoot) == 0) {
System.out.println("root date = " + treeModel.getNodeTaxon(currentRoot).getDate().getTimeValue());
}*/
if (treeModel.getChildCount(currentRoot) == 0) {
return treeModel.getNodeTaxon(currentRoot).getDate().getTimeValue() - treeModel.getNodeTaxon(newTaxon).getDate().getTimeValue();
} else {
double maximum = -Double.MAX_VALUE;
for (int i = 0; i < treeModel.getChildCount(currentRoot); i++) {
maximum = Math.max(checkCurrentTreeNodes(newTaxon, treeModel.getChild(currentRoot, i)), maximum);
}
return maximum;
}
}
/**
* Adds a taxon along a branch specified by its parent and child node, at a specified height
* @param newTaxon The new Taxon to be added to the tree
* @param parentNode The parent node of the branch along which the new taxon is to be added
* @param childNode The child of the branch along which the new taxon is to be added
* @param insertHeight The height of the new internal node that will be created
*/
private void addTaxonAlongBranch(NodeRef newTaxon, NodeRef parentNode, NodeRef childNode, double insertHeight) {
treeModel.beginTreeEdit();
//remove child node that correspondes to childNode argument
treeModel.removeChild(parentNode, childNode);
//add a currently unused internal node as the new child node
NodeRef internalNode = null;
for (int i = 0; i < treeModel.getInternalNodeCount(); i++) {
if (treeModel.getChildCount(treeModel.getInternalNode(i)) == 0 && treeModel.getParent(treeModel.getInternalNode(i)) == null) {
internalNode = treeModel.getInternalNode(i);
System.out.println("\ninternal node found: " + internalNode.getNumber());
break;
}
}
if (internalNode == null) {
throw new RuntimeException("No free internal node found for adding a new taxon.");
}
//add internal node as a child of the old parent node
treeModel.addChild(parentNode, internalNode);
//add the old child node as a child of the new internal node
treeModel.addChild(internalNode, childNode);
//add the new taxon as a child of the new internal node
treeModel.addChild(internalNode, newTaxon);
//still need to set the height of the new internal node
treeModel.setNodeHeight(internalNode, insertHeight);
System.out.println("\nparent node (" + parentNode + ") height: " + treeModel.getNodeHeight(parentNode));
System.out.println("child node (" + childNode + ") height: " + treeModel.getNodeHeight(childNode));
System.out.println("internal node (" + internalNode + ") height: " + treeModel.getNodeHeight(internalNode));
treeModel.endTreeEdit();
}
/**
* Check whether all the old taxa are still present in the new analysis. Also check whether the
* additional taxa have been put at the end of the taxa list.
* @param taxaNames The taxa names (and order) from the old XML, retrieved from the checkpoint file
* @return An ArrayList containing the names of the additional taxa.
*/
private boolean checkTaxaOrder(String[] taxaNames) {
int external = treeModel.getExternalNodeCount();
newTaxaNames = new ArrayList<String>();
for (int i = 0; i < external; i++) {
int j = 0;
boolean taxonFound = false;
while (!taxonFound) {
if (treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId().equals(taxaNames[j])) {
taxonFound = true;
} else {
j++;
if (j >= taxaNames.length) {
newTaxaNames.add(treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId());
taxonFound = true;
}
}
}
}
System.out.println();
for (String str : newTaxaNames) {
System.out.println("New taxon found: " + str);
}
System.out.println();
//With added taxa the order of the external nodes changes
//Aim here is to build a starting tree containing only the taxa from the previous analysis.
for (int i = 0; i < taxaNames.length; i++) {
if (!taxaNames[i].equals(treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId())) {
String error = taxaNames[i] + " vs. " + treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId();
System.out.println("Taxa listed in the wrong order; append new taxa at the end:\n" + error);
//throw new RuntimeException("Taxa listed in the wrong order; append new taxa at the end:\n" + error);
return false;
} else {
System.out.println(taxaNames[i] + " vs. " + treeModel.getNodeTaxon(treeModel.getExternalNode(i)).getId());
}
}
return true;
}
private void createNodeMap(String[] taxaNames) {
System.out.println("Creating a node mapping:");
int external = treeModel.getExternalNodeCount();
nodeMap = new int[external];
for (int i = 0; i < taxaNames.length; i++) {
for (int j = 0; j < external; j++) {
if (taxaNames[i].equals(treeModel.getNodeTaxon(treeModel.getExternalNode(j)).getId())) {
//taxon found
nodeMap[i] = j;
}
}
}
}
}