/*
* RateVarianceScaleOperator.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.operators;
import dr.evolution.tree.NodeRef;
import dr.evomodel.tree.TreeModel;
import dr.evomodelxml.operators.RateVarianceScaleOperatorParser;
import dr.inference.model.Bounds;
import dr.inference.model.Parameter;
import dr.inference.operators.AbstractCoercableOperator;
import dr.inference.operators.CoercionMode;
import dr.inference.operators.OperatorUtils;
import dr.math.MathUtils;
import java.util.ArrayList;
import java.util.List;
/**
* A special operator for scaling the variance of the autocorrelated clock model
* and subsequnetly the rates in a tree.
*
* @author Michael Defoin Platel
*/
public class RateVarianceScaleOperator extends AbstractCoercableOperator {
private TreeModel tree;
private Parameter variance;
public RateVarianceScaleOperator(TreeModel tree, Parameter variance, double scale, CoercionMode mode) {
super(mode);
this.scaleFactor = scale;
this.tree = tree;
this.variance = variance;
}
/**
* scale the rates of a subtree and return the hastings ratio.
*/
public final double doOperation() {
final double scale = (scaleFactor + (MathUtils.nextDouble() * ((1.0 / scaleFactor) - scaleFactor)));
//Scale the variance
double oldValue = variance.getParameterValue(0);
double newValue = scale * oldValue;
double logq = -Math.log(scale);
final Bounds<Double> bounds = variance.getBounds();
if (newValue < bounds.getLowerLimit(0) || newValue > bounds.getUpperLimit(0)) {
// throw new OperatorFailedException("proposed value outside boundaries");
return Double.NEGATIVE_INFINITY;
}
variance.setParameterValue(0, newValue);
//Scale the rates of the tree accordingly
NodeRef root = tree.getRoot();
final int index = root.getNumber();
List<NodeRef> listNode = new ArrayList<NodeRef>();
getSubtree(listNode, tree.getNode(index));
final double rateScale = Math.sqrt(scale);
for (NodeRef node : listNode) {
oldValue = tree.getNodeRate(node);
newValue = oldValue * rateScale;
tree.setNodeRate(node, newValue);
}
// According to the hastings ratio in the scale Operator
logq += (listNode.size() - 2) * Math.log(rateScale);
return logq;
}
void getSubtree(List<NodeRef> listNode, NodeRef parent) {
listNode.add(parent);
int nbChildren = tree.getChildCount(parent);
for (int c = 0; c < nbChildren; c++) {
getSubtree(listNode, tree.getChild(parent, c));
}
}
/**
* This method should be overridden by operators that need to do something just before the return of doOperation.
*
* @param newValue the proposed parameter value
* @param oldValue the old parameter value
*/
void cleanupOperation(double newValue, double oldValue) {
// DO NOTHING
}
//MCMCOperator INTERFACE
public final String getOperatorName() {
return "rateVarianceScale";
}
public double getCoercableParameter() {
return Math.log(1.0 / scaleFactor - 1.0);
}
public void setCoercableParameter(double value) {
scaleFactor = 1.0 / (Math.exp(value) + 1.0);
}
public double getRawParameter() {
return scaleFactor;
}
public double getScaleFactor() {
return scaleFactor;
}
public double getTargetAcceptanceProbability() {
return 0.234;
}
public double getMinimumAcceptanceLevel() {
return 0.1;
}
public double getMaximumAcceptanceLevel() {
return 0.4;
}
public double getMinimumGoodAcceptanceLevel() {
return 0.20;
}
public double getMaximumGoodAcceptanceLevel() {
return 0.30;
}
public final String getPerformanceSuggestion() {
double prob = Utils.getAcceptanceProbability(this);
double targetProb = getTargetAcceptanceProbability();
dr.util.NumberFormatter formatter = new dr.util.NumberFormatter(5);
double sf = OperatorUtils.optimizeScaleFactor(scaleFactor, prob, targetProb);
if (prob < getMinimumGoodAcceptanceLevel()) {
return "Try setting scaleFactor to about " + formatter.format(sf);
} else if (prob > getMaximumGoodAcceptanceLevel()) {
return "Try setting scaleFactor to about " + formatter.format(sf);
} else return "";
}
public String toString() {
return RateVarianceScaleOperatorParser.SCALE_OPERATOR + "(" + " [" + scaleFactor + ", " + (1.0 / scaleFactor) + "]";
}
//PRIVATE STUFF
private double scaleFactor = 0.5;
}