/*
* BackboneNodeFilter.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.evolution.tree.NodeRef;
import dr.evolution.tree.Tree;
import dr.evolution.tree.TreeNodeFilter;
import dr.evolution.tree.TreeUtils;
import dr.evolution.util.TaxonList;
import dr.inference.model.AbstractModel;
import dr.inference.model.Model;
import dr.inference.model.Variable;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Set;
/**
* @author Marc A. Suchard
* @author Andrew Rambaut
*/
public class BackboneNodeFilter extends AbstractModel implements TreeNodeFilter {
public BackboneNodeFilter(String name, Tree tree, TaxonList taxonList, boolean excludeClade, boolean includeStem) {
super(name);
this.tree = tree;
this.excludeClade = excludeClade;
this.includeStem = includeStem;
backboneSet = new HashSet<NodeRef>();
try {
tipSet = TreeUtils.getTipsBitSetForTaxa(this.tree, taxonList);
} catch (TreeUtils.MissingTaxonException e) {
e.printStackTrace();
}
if (tree instanceof TreeModel) {
addModel((TreeModel) tree);
}
backboneSetKnown = false;
}
public boolean includeNode(Tree tree, NodeRef node) {
if (!backboneSetKnown) {
computeBackBoneMap();
backboneSetKnown = true;
}
return backboneSet.contains(node);
}
public void computeBackBoneMap() {
backboneSet.clear();
recursivelyComputeBackBoneMap(tree, tree.getRoot());
}
private boolean recursivelyComputeBackBoneMap(Tree tree, NodeRef node) {
boolean onBackBone = false;
if (tree.isExternal(node)) {
if (tipSet.get(node.getNumber())) {
onBackBone = true;
}
} else {
for (int i = 0; i < tree.getChildCount(node); i++) {
NodeRef child = tree.getChild(node, i);
if (recursivelyComputeBackBoneMap(tree, child)) {
// if any of the desendents are back bone then this node is too
onBackBone = true;
}
}
}
if (onBackBone) {
recursivelyPruneStemAndClade(tree, node, includeStem, excludeClade);
return true;
}
return false;
}
private void recursivelyPruneStemAndClade(Tree tree, NodeRef node,
boolean includeStem, boolean excludeClade) {
if (!tree.isExternal(node) && !excludeClade) {
for (int i = 0; i < tree.getChildCount(node); i++) {
NodeRef child = tree.getChild(node, i);
recursivelyPruneStemAndClade(tree, child, true, true);
}
}
if (includeStem && !backboneSet.contains(node)) {
backboneSet.add(node);
}
}
@Override
protected void handleModelChangedEvent(Model model, Object object, int index) {
if (model == tree) {
backboneSetKnown = false;
}
// if (model == tree) {
// if (object instanceof TreeModel.TreeChangedEvent) {
// TreeModel.TreeChangedEvent event = (TreeModel.TreeChangedEvent) object;
// if (event.isHeightChanged()) {
// // Do nothing
// return;
// }
// if (event.isNodeParameterChanged()) {
// // Do nothing
// return;
// } // else a topology change (?)
// backboneSetKnown = false;
// return;
// }
// }
}
@Override
protected void handleVariableChangedEvent(Variable variable, int index, Variable.ChangeType type) {
}
@Override
protected void storeState() {
}
@Override
protected void restoreState() {
}
@Override
protected void acceptState() {
}
private boolean backboneSetKnown;
private final Set<NodeRef> backboneSet;
private BitSet tipSet;
private final Tree tree;
private final boolean includeStem;
private final boolean excludeClade;
}