/*
* UpDownOperator.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.inference.operators;
import dr.math.MathUtils;
public class UpDownOperator extends AbstractCoercableOperator {
private Scalable[] upParameter = null;
private Scalable[] downParameter = null;
private double scaleFactor;
public UpDownOperator(Scalable[] upParameter, Scalable[] downParameter,
double scale, double weight, CoercionMode mode) {
super(mode);
setWeight(weight);
this.upParameter = upParameter;
this.downParameter = downParameter;
this.scaleFactor = scale;
}
public final double getScaleFactor() {
return scaleFactor;
}
public final void setScaleFactor(double sf) {
if( (sf > 0.0) && (sf < 1.0) ) {
scaleFactor = sf;
} else {
throw new IllegalArgumentException("scale must be between 0 and 1");
}
}
/**
* change the parameter and return the hastings ratio.
*/
public final double doOperation() {
final double scale = (scaleFactor + (MathUtils.nextDouble() * ((1.0 / scaleFactor) - scaleFactor)));
int goingUp = 0, goingDown = 0;
if( upParameter != null ) {
for( Scalable up : upParameter ) {
goingUp += up.scale(scale, -1, false);
// try {
// goingUp += up.scale(scale, -1, true);
// } catch (RuntimeException re) {
// return Double.NEGATIVE_INFINITY;
// }
}
for( Scalable up : upParameter ) {
if (!up.testBounds()) {
return Double.NEGATIVE_INFINITY;
}
}
}
if( downParameter != null ) {
for( Scalable dn : downParameter ) {
goingDown += dn.scale(1.0 / scale, -1, false);
// try {
// goingDown += dn.scale(1.0 / scale, -1, true);
// } catch (RuntimeException re) {
// return Double.NEGATIVE_INFINITY;
// }
}
for( Scalable dn : downParameter ) {
if (!dn.testBounds()) {
return Double.NEGATIVE_INFINITY;
}
}
}
return (goingUp - goingDown - 2) * Math.log(scale);
}
public final String getPerformanceSuggestion() {
double prob = MCMCOperator.Utils.getAcceptanceProbability(this);
double targetProb = getTargetAcceptanceProbability();
double sf = OperatorUtils.optimizeScaleFactor(scaleFactor, prob, targetProb);
dr.util.NumberFormatter formatter = new dr.util.NumberFormatter(5);
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 final String getOperatorName() {
String name = "";
if( upParameter != null ) {
name = "up:";
for( Scalable up : upParameter ) {
name = name + up.getName() + " ";
}
}
if( downParameter != null ) {
name += "down:";
for( Scalable dn : downParameter ) {
name = name + dn.getName() + " ";
}
}
return name;
}
public double getCoercableParameter() {
return Math.log(1.0 / scaleFactor - 1.0) / Math.log(10);
}
public void setCoercableParameter(double value) {
scaleFactor = 1.0 / (Math.pow(10.0, value) + 1.0);
}
public double getRawParameter() {
return scaleFactor;
}
public double getTargetAcceptanceProbability() {
return 0.234;
}
// Since this operator invariably modifies at least 2 parameters it
// should allow lower acceptance probabilities
// as it is known that optimal acceptance levels are inversely
// proportional to the number of dimensions operated on
// AD 16/3/2004
public double getMinimumAcceptanceLevel() {
return 0.05;
}
public double getMaximumAcceptanceLevel() {
return 0.3;
}
public double getMinimumGoodAcceptanceLevel() {
return 0.10;
}
public double getMaximumGoodAcceptanceLevel() {
return 0.20;
}
}
// The old implementation for reference and until the new one is tested :)
//
//public class UpDownOperator extends AbstractCoercableOperator {
//
// public static final String UP_DOWN_OPERATOR = "upDownOperator";
// public static final String UP = "up";
// public static final String DOWN = "down";
//
// public static final String SCALE_FACTOR = "scaleFactor";
//
// public UpDownOperator(Parameter upParameter, Parameter downParameter,
// double scale, double weight, CoercionMode mode) {
//
// super(mode);
//
// this.upParameter = upParameter;
// this.downParameter = downParameter;
// this.scaleFactor = scale;
// setWeight(weight);
// }
//
// public final int getPriorType() {
// return prior.getPriorType();
// }
//
// public final double getScaleFactor() {
// return scaleFactor;
// }
//
// public final void setPriorType(int type) {
// prior.setPriorType(type);
// }
//
// public final void setScaleFactor(double sf) {
// if ((sf > 0.0) && (sf < 1.0)) scaleFactor = sf;
// else throw new IllegalArgumentException("minimum scale must be between 0 and 1");
// }
//
// /**
// * change the parameter and return the hastings ratio.
// */
// public final double doOperation() throws OperatorFailedException {
//
// final double scale = (scaleFactor + (MathUtils.nextDouble() * ((1.0 / scaleFactor) - scaleFactor)));
//
// if( upParameter != null ) {
// for(int i = 0; i < upParameter.getDimension(); i++) {
// upParameter.setParameterValue(i, upParameter.getParameterValue(i) * scale);
// }
// for(int i = 0; i < upParameter.getDimension(); i++) {
// if( upParameter.getParameterValue(i) < upParameter.getBounds().getLowerLimit(i) ||
// upParameter.getParameterValue(i) > upParameter.getBounds().getUpperLimit(i) ) {
// throw new OperatorFailedException("proposed value outside boundaries");
// }
// }
// }
//
// if( downParameter != null ) {
// for(int i = 0; i < downParameter.getDimension(); i++) {
// downParameter.setParameterValue(i, downParameter.getParameterValue(i) / scale);
// }
// for(int i = 0; i < downParameter.getDimension(); i++) {
// if( downParameter.getParameterValue(i) < downParameter.getBounds().getLowerLimit(i) ||
// downParameter.getParameterValue(i) > downParameter.getBounds().getUpperLimit(i) ) {
// throw new OperatorFailedException("proposed value outside boundaries");
// }
// }
// }
//
// final int goingUp = (upParameter == null) ? 0 : upParameter.getDimension();
// final int goingDown = (downParameter == null) ? 0 : downParameter.getDimension();
//
// final double logq = (goingUp - goingDown - 2) * Math.log(scale);
//
// return logq;
// }
//
// public final String getPerformanceSuggestion() {
//
// double prob = MCMCOperator.Utils.getAcceptanceProbability(this);
// double targetProb = getTargetAcceptanceProbability();
// double sf = OperatorUtils.optimizeScaleFactor(scaleFactor, prob, targetProb);
// dr.util.NumberFormatter formatter = new dr.util.NumberFormatter(5);
// 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 "";
// }
//
// //MCMCOperator INTERFACE
// public final String getOperatorName() {
// return (upParameter != null ? "up:" + upParameter.getParameterName() : "") +
// (downParameter != null ? " down:" + downParameter.getParameterName() : "");
// }
//
// public double getCoercableParameter() {
// return Math.log(1.0 / scaleFactor - 1.0) / Math.log(10);
// }
//
// public void setCoercableParameter(double value) {
// scaleFactor = 1.0 / (Math.pow(10.0, value) + 1.0);
// }
//
// public double getRawParameter() {
// return scaleFactor;
// }
//
// public double getTargetAcceptanceProbability() {
// return 0.234;
// }
//
// // Since this operator invariably modifies at least 2 parameters it
// // should allow lower acceptance probabilities
// // as it is known that optimal acceptance levels are inversely
// // proportional to the number of dimensions operated on
// // AD 16/3/2004
// public double getMinimumAcceptanceLevel() {
// return 0.05;
// }
//
// public double getMaximumAcceptanceLevel() {
// return 0.3;
// }
//
// public double getMinimumGoodAcceptanceLevel() {
// return 0.10;
// }
//
// public double getMaximumGoodAcceptanceLevel() {
// return 0.20;
// }
//
// public static dr.xml.XMLObjectParser PARSER = new dr.xml.AbstractXMLObjectParser() {
//
// public String getParserName() {
// return UP_DOWN_OPERATOR;
// }
//
// public Object parseXMLObject(XMLObject xo) throws XMLParseException {
//
// final double scaleFactor = xo.getDoubleAttribute(SCALE_FACTOR);
//
// final double weight = xo.getDoubleAttribute(WEIGHT);
// CoercionMode mode = CoercionMode.parseMode(xo);
//
// Parameter param1 = (Parameter) xo.getElementFirstChild(UP);
// Parameter param2 = (Parameter) xo.getElementFirstChild(DOWN);
//
// return new UpDownOperator(param1, param2, scaleFactor, weight, mode);
// }
//
// public String getParserDescription() {
// return "This element represents an operator that scales two parameters in different directions. " +
// "Each operation involves selecting a scale uniformly at random between scaleFactor and 1/scaleFactor. " +
// "The up parameter is multipled by this scale and the down parameter is divided by this scale.";
// }
//
// public Class getReturnType() {
// return UpDownOperator.class;
// }
//
// public XMLSyntaxRule[] getSyntaxRules() {
// return rules;
// }
//
// private final XMLSyntaxRule[] rules = {
// AttributeRule.newDoubleRule(SCALE_FACTOR),
// AttributeRule.newDoubleRule(WEIGHT),
// AttributeRule.newBooleanRule(AUTO_OPTIMIZE, true),
// new ElementRule(UP,
// new XMLSyntaxRule[]{new ElementRule(Parameter.class)}),
// new ElementRule(DOWN,
// new XMLSyntaxRule[]{new ElementRule(Parameter.class)})
// };
// };
//
// //PRIVATE STUFF
//
// private Parameter upParameter = null;
// private Parameter downParameter = null;
// private final ContinuousVariablePrior prior = new ContinuousVariablePrior();
// private double scaleFactor = 0.5;
//}