package beast.evolution.operators; import beast.core.Description; import beast.core.Input; import beast.core.Input.Validate; import beast.core.Operator; import beast.core.parameter.IntegerParameter; import beast.core.parameter.Parameter; import beast.core.parameter.RealParameter; import beast.util.Randomizer; @Description("Assign one or more parameter values to a uniformly selected value in its range.") public class UniformOperator extends Operator { final public Input<Parameter<?>> parameterInput = new Input<>("parameter", "a real or integer parameter to sample individual values for", Validate.REQUIRED, Parameter.class); final public Input<Integer> howManyInput = new Input<>("howMany", "number of items to sample, default 1, must be less than the dimension of the parameter", 1); int howMany; Parameter<?> parameter; double lower, upper; int lowerIndex, upperIndex; @Override public void initAndValidate() { parameter = parameterInput.get(); if (parameter instanceof RealParameter) { lower = (Double) parameter.getLower(); upper = (Double) parameter.getUpper(); } else if (parameter instanceof IntegerParameter) { lowerIndex = (Integer) parameter.getLower(); upperIndex = (Integer) parameter.getUpper(); } else { throw new IllegalArgumentException("parameter should be a RealParameter or IntergerParameter, not " + parameter.getClass().getName()); } howMany = howManyInput.get(); if (howMany > parameter.getDimension()) { throw new IllegalArgumentException("howMany it too large: must be less than the dimension of the parameter"); } } @Override public double proposal() { for (int n = 0; n < howMany; ++n) { // do not worry about duplication, does not matter int index = Randomizer.nextInt(parameter.getDimension()); if (parameter instanceof IntegerParameter) { int newValue = Randomizer.nextInt(upperIndex - lowerIndex + 1) + lowerIndex; // from 0 to n-1, n must > 0, ((IntegerParameter) parameter).setValue(index, newValue); } else { double newValue = Randomizer.nextDouble() * (upper - lower) + lower; ((RealParameter) parameter).setValue(index, newValue); } } return 0.0; } }