package dr.evomodel.epidemiology.casetocase; import dr.evolution.tree.NodeRef; import dr.evomodel.tree.TreeModel; import dr.inference.operators.SimpleMCMCOperator; import dr.math.MathUtils; import dr.xml.*; /** * Created with IntelliJ IDEA. * User: Matthew Hall * Date: 13/07/2012 * Time: 14:46 * To change this template use File | Settings | File Templates. */ public class NodePaintingSwitchOperator extends SimpleMCMCOperator{ public static final String NODE_PAINTING_SWITCH_OPERATOR = "nodePaintingSwitchOperator"; private CaseToCaseTransmissionLikelihood c2cLikelihood; public NodePaintingSwitchOperator(CaseToCaseTransmissionLikelihood c2cLikelihood, double weight){ this.c2cLikelihood = c2cLikelihood; setWeight(weight); } public String getOperatorName(){ return NODE_PAINTING_SWITCH_OPERATOR; } /* Switch the painting of a randomly selected internal node from the painting of one of its children to the * painting of the other, and adjust the rest of the tree to ensure the result is still a painting.*/ public double doOperation(){ TreeModel tree = c2cLikelihood.getTree(); int internalNodeCount = tree.getInternalNodeCount(); int nodeToSwitch = MathUtils.nextInt(internalNodeCount); NodeRef node = tree.getInternalNode(nodeToSwitch); AbstractCase currentFarm = c2cLikelihood.getBranchMap()[node.getNumber()]; for(int i=0; i<tree.getChildCount(node); i++){ if(c2cLikelihood.getBranchMap()[tree.getChild(node,i).getNumber()]!=currentFarm){ adjustTree(tree, node, c2cLikelihood.getBranchMap()); flagForRecalculation(tree, node, c2cLikelihood.getRecalculationArray()); } } c2cLikelihood.makeDirty(); return 1; } /*Look at the children of the current node, pick the one that is not labelled the same, change the node's * label to that one, then go up the tree and replace any others higher up with the same label*/ private static void adjustTree(TreeModel tree, NodeRef node, AbstractCase[] map){ AbstractCase originalFarm = map[node.getNumber()]; if(tree.isExternal(node)){ throw new RuntimeException("Node is external"); } AbstractCase[] childFarms = new AbstractCase[2]; for(int i=0; i<tree.getChildCount(node); i++){ childFarms[i] = map[tree.getChild(node, i).getNumber()]; } if(childFarms[0]==childFarms[1]){ throw new RuntimeException("Node children are the same"); } if(originalFarm!=childFarms[0]&&originalFarm!=childFarms[1]){ throw new RuntimeException("You've ended up with a node neither of whose children are have the same " + "label as itself. Help!"); } AbstractCase newFarm; for(int i=0; i<2; i++){ if(childFarms[i]!=originalFarm){ newFarm=childFarms[i]; map[node.getNumber()]=newFarm; if(!tree.isRoot(node)){ changeParentNodes(tree, node, originalFarm, newFarm, map); } } } } private static void changeParentNodes(TreeModel tree, NodeRef node, AbstractCase originalFarm, AbstractCase newFarm, AbstractCase[] map){ NodeRef parent = tree.getParent(node); if(map[parent.getNumber()]==originalFarm){ map[parent.getNumber()]=newFarm; if(!tree.isRoot(parent)){ changeParentNodes(tree, parent, originalFarm, newFarm, map); } } } private static void flagForRecalculation(TreeModel tree, NodeRef node, boolean[] flags){ for(int i=0; i<tree.getChildCount(node); i++){ flags[tree.getChild(node,i).getNumber()]=true; } NodeRef currentNode=node; while(!tree.isRoot(currentNode)){ flags[currentNode.getNumber()]=true; currentNode = tree.getParent(currentNode); } flags[currentNode.getNumber()]=true; } public String getPerformanceSuggestion(){ return "Not implemented"; } /* Parser */ public static XMLObjectParser PARSER = new AbstractXMLObjectParser(){ public String getParserName(){ return NODE_PAINTING_SWITCH_OPERATOR; } public Object parseXMLObject(XMLObject xo) throws XMLParseException { CaseToCaseTransmissionLikelihood ftLikelihood = (CaseToCaseTransmissionLikelihood) xo.getChild(CaseToCaseTransmissionLikelihood.class); final double weight = xo.getDoubleAttribute("weight"); return new NodePaintingSwitchOperator(ftLikelihood, weight); } public String getParserDescription(){ return "This operator switches the painting of a random internal node from the painting of one of its" + "children to the painting of the other"; } public Class getReturnType() { return NodePaintingSwitchOperator.class; } public XMLSyntaxRule[] getSyntaxRules() { return rules; } private final XMLSyntaxRule[] rules = { AttributeRule.newDoubleRule("weight"), new ElementRule(CaseToCaseTransmissionLikelihood.class), }; }; }