/*
* MulTreeNodeSlide.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.evomodel.alloppnet.operators;
import dr.evolution.tree.NodeRef;
import dr.evolution.tree.Tree;
import dr.evomodel.alloppnet.speciation.MulSpeciesTreeModel;
import dr.evomodel.alloppnet.speciation.MulSpeciesBindings;
import dr.evomodelxml.operators.TreeNodeSlideParser;
import dr.inference.model.Parameter;
import dr.inference.operators.SimpleMCMCOperator;
import dr.math.MathUtils;
import jebl.util.FixedBitSet;
import java.util.Arrays;
/**
* An operator on a multiply labelled species tree based on the ideas of Mau et all. Very similar to
* JH's TreeNodeSlide.
*
* <a href="http://citeseer.ist.psu.edu/rd/27056960%2C5592%2C1%2C0.25%2CDownload/http://citeseer.ist.psu.edu/cache/papers/cs/5768/ftp:zSzzSzftp.stat.wisc.eduzSzpubzSznewtonzSzlastfinal.pdf/mau98bayesian.pdf">
* Bayesian Phylogenetic Inference via Markov Chain Monte Carlo Methods</a>
*
* @author Joseph Heled, Graham Jones
* Date: 21/12/2011
*/
public class MulTreeNodeSlide extends SimpleMCMCOperator {
private final MulSpeciesTreeModel multree;
private final MulSpeciesBindings species;
private final int[] preOrderIndexBefore;
private final int[] preOrderIndexAfter;
public MulTreeNodeSlide(MulSpeciesTreeModel tree, MulSpeciesBindings species /*, boolean outgroupOnly*/, double weight) {
this.multree = tree;
this.species = species;
// this.outgroupOnly = outgroupOnly;
preOrderIndexBefore = new int[tree.getNodeCount()];
Arrays.fill(preOrderIndexBefore, -1);
preOrderIndexAfter= new int[tree.getNodeCount()];
Arrays.fill(preOrderIndexAfter, -1);
setWeight(weight);
}
public String getPerformanceSuggestion() {
return "none";
}
public String getOperatorName() {
return TreeNodeSlideParser.TREE_NODE_REHEIGHT + "(" + multree.getId() + "," + species.getId() + ")";
}
public double doOperation() {
operateOneNode(0.0);
return 0;
}
public void operateOneNode(final double factor) {
// #print "operate: tree", ut.treerep(t)
// if( verbose) System.out.println(" Mau at start: " + tree.getSimpleTree());
final int count = multree.getExternalNodeCount();
assert count == species.nSpSeqs();
NodeRef[] order = new NodeRef[2 * count - 1];
boolean[] swapped = new boolean[count-1];
mauCanonical(multree, order, swapped);
// internal node to change
// count-1 - number of internal nodes
int which = MathUtils.nextInt(count - 1);
FixedBitSet left = new FixedBitSet(count);
FixedBitSet right = new FixedBitSet(count);
for(int k = 0; k < 2*which+1; k += 2) {
left.set(multree.speciesIndex(order[k]));
}
for(int k = 2*(which+1); k < 2*count; k += 2) {
right.set(multree.speciesIndex(order[k]));
}
double newHeight;
if( factor > 0 ) {
newHeight = multree.getNodeHeight(order[2*which+1]) * factor;
} else {
final double limit = species.speciationUpperBound(left, right);
newHeight = MathUtils.nextDouble() * limit;
}
multree.beginTreeEdit();
multree.setPreorderIndices(preOrderIndexBefore);
final NodeRef node = order[2 * which + 1];
multree.setNodeHeight(node, newHeight);
mauReconstruct(multree, order, swapped);
// restore pre-order of pops -
{
multree.setPreorderIndices(preOrderIndexAfter);
double[] splitPopValues = null;
for(int k = 0; k < preOrderIndexBefore.length; ++k) {
final int b = preOrderIndexBefore[k];
if( b >= 0 ) {
final int a = preOrderIndexAfter[k];
if( a != b ) {
//if( verbose) System.out.println("pops: " + a + " <- " + b);
final Parameter p1 = multree.sppSplitPopulations;
if( splitPopValues == null ) {
splitPopValues = p1.getParameterValues();
}
if( multree.constPopulation() ) {
p1.setParameterValue(count + a, splitPopValues[count + b]);
} else {
for(int i = 0; i < 2; ++i) {
p1.setParameterValue(count + 2*a + i, splitPopValues[count + 2*b + i]);
}
}
}
}
}
}
multree.endTreeEdit();
}
/**
* Obtain an ordering of tree tips from randomly swaping the children order in internal nodes.
*
* @param tree tree to create order from
* @param order Nodes in their random order (only odd indices are filled)
* @param wasSwapped true if internal node was swapped
*/
static private void mauCanonical(Tree tree, NodeRef[] order, boolean[] wasSwapped) {
mauCanonicalSub(tree, tree.getRoot(), 0, order, wasSwapped);
}
static private int mauCanonicalSub(Tree tree, NodeRef node, int loc, NodeRef[] order, boolean[] wasSwaped) {
if( tree.isExternal(node) ) {
order[loc] = node; assert (loc & 0x1) == 0;
return loc + 1;
}
final boolean swap = MathUtils.nextBoolean();
//wasSwaped[(loc-1)/2] = swap;
int l = mauCanonicalSub(tree, tree.getChild(node, swap ? 1 : 0), loc, order, wasSwaped);
order[l] = node; assert (l & 0x1) == 1;
wasSwaped[(l-1)/2] = swap;
l = mauCanonicalSub(tree, tree.getChild(node, swap ? 0 : 1), l+1, order, wasSwaped);
return l;
}
static private void mauReconstruct(MulSpeciesTreeModel tree, NodeRef[] order, boolean[] swapped) {
final NodeRef root = mauReconstructSub(tree, 0, swapped.length, order, swapped);
if( tree.getRoot() != root ) {
tree.setRoot(root);
}
}
static private NodeRef mauReconstructSub(MulSpeciesTreeModel tree, int from, int to, NodeRef[] order, boolean[] wasSwaped) {
if( from == to ) {
return order[2*from];
}
int rootIndex = -1;
{
double h = -1;
for(int i = from; i < to; ++i) {
final double v = tree.getNodeHeight(order[2 * i + 1]);
if( h < v ) {
h = v;
rootIndex = i;
}
}
}
final NodeRef root = order[2 * rootIndex + 1];
final NodeRef lchild = tree.getChild(root, 0);
final NodeRef rchild = tree.getChild(root, 1);
NodeRef lTargetChild = mauReconstructSub(tree, from, rootIndex, order, wasSwaped);
NodeRef rTargetChild = mauReconstructSub(tree, rootIndex+1, to, order, wasSwaped);
if( wasSwaped[rootIndex] ) {
NodeRef z = lTargetChild;
lTargetChild = rTargetChild;
rTargetChild = z;
}
if( lchild != lTargetChild ) {
tree.replaceChild(root, lchild, lTargetChild);
}
if( rchild != rTargetChild ) {
tree.replaceChild(root, rchild, rTargetChild);
}
return root;
}
}