/*
* BitMoveOperator.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.inference.model.Parameter;
import dr.math.MathUtils;
import java.util.ArrayList;
import java.util.List;
/**
* A generic operator that moves k 1 bits to k zero locations.
*
* @author Alexei Drummond
* @version $Id$
*/
public class BitMoveOperator extends SimpleMCMCOperator {
public BitMoveOperator(Parameter bitsParameter, Parameter valuesParameter, int numBitsToMove, double weight) {
this.bitsParameter = bitsParameter;
this.valuesParameter = valuesParameter;
if (valuesParameter != null && bitsParameter.getDimension() != valuesParameter.getDimension()) {
throw new IllegalArgumentException("bits parameter must be same length as values parameter");
}
this.numBitsToMove = numBitsToMove;
setWeight(weight);
}
/**
* Pick a random k ones in the vector and move them to a random k zero positions.
*/
public final double doOperation() {
final int dim = bitsParameter.getDimension();
List<Integer> ones = new ArrayList<Integer>();
List<Integer> zeros = new ArrayList<Integer>();
for (int i = 0; i < dim; i++) {
if (bitsParameter.getParameterValue(i) == 1.0) {
ones.add(i);
} else {
zeros.add(i);
}
}
if (ones.size() >= numBitsToMove && zeros.size() >= numBitsToMove) {
for (int i = 0; i < numBitsToMove; i++) {
int myOne = ones.remove(MathUtils.nextInt(ones.size()));
int myZero = zeros.remove(MathUtils.nextInt(zeros.size()));
bitsParameter.setParameterValue(myOne, 0.0);
bitsParameter.setParameterValue(myZero, 1.0);
if (valuesParameter != null) {
double value1 = valuesParameter.getParameterValue(myOne);
double value2 = valuesParameter.getParameterValue(myZero);
valuesParameter.setParameterValue(myOne, value2);
valuesParameter.setParameterValue(myZero, value1);
}
}
} else throw new RuntimeException("Not enough bits to move!");
return 0.0;
}
// Interface MCMCOperator
public final String getOperatorName() {
StringBuilder builder = new StringBuilder();
builder.append("bitMove(");
builder.append(bitsParameter.getParameterName());
if (valuesParameter != null) {
builder.append(", ").append(valuesParameter.getParameterName());
}
builder.append(", ").append(numBitsToMove).append(")");
return builder.toString();
}
public final String getPerformanceSuggestion() {
return "no performance suggestion";
}
public String toString() {
return getOperatorName();
}
// Private instance variables
private Parameter bitsParameter = null;
private Parameter valuesParameter = null;
private int numBitsToMove = 1;
}