package bsearch.space;
import org.nlogo.util.MersenneTwisterFast;
public strictfp class DoubleDiscreteSpec extends ParameterSpec {
private double dMin;
private double dStep;
private double dMax;
public DoubleDiscreteSpec(String name, double min, double step, double max) {
super(name);
dMin = min;
dStep = step;
// change dMax to be the actual largest value allowed, given the step size
dMax = min + step * StrictMath.floor((max-min)/step);
}
private double enforceValidRange(double d)
{
if (d < dMin)
d = dMin;
else if (d > dMax)
d = dMax;
return dMin + StrictMath.round((d - dMin) / dStep) * dStep;
}
@Override
public Double generateRandomValue(MersenneTwisterFast rng) {
return rng.nextInt(1 + (int)StrictMath.floor((dMax - dMin)/dStep)) * dStep + dMin;
}
/**
* @param obj - Double - parameter value to be mutated
* @param mutStrength - controls the magnitude of the Gaussian mutation.
* (not to be confused with the mutation-rate, which controls the likelihood of mutation).
For example, mutStrength=0.1 corresponds to a stdDev of 10% of the parameter's range
This means that 68% of the time, the mutation will fall within +/- 10%
and 95% of the time, the mutation will fall within +/- 20%.
* @param rng - random number generator
* @returns the result of adding Gaussian noise to the given parameter value,
* clamped to be one of the possible discrete double values in this parameter's range.
*/
@Override
public Double mutate(Object obj, double mutStrength, MersenneTwisterFast rng) {
double mutStdDev = (dMax - dMin) * mutStrength;
return enforceValidRange((Double)obj + mutStdDev * rng.nextGaussian());
}
@Override
public int choiceCount()
{
return 1 + (int)StrictMath.floor((dMax - dMin)/dStep) ;
}
@Override
public String toString()
{
return "[ \"" + name + "\" [ " + dMin + " " + dStep + " " + dMax + " ]]";
}
@Override
public Object getValueFromChoice( long choice , long maxNumChoices)
{
if (choice < choiceCount())
{
return new Double(dMin + dStep * choice);
}
else
{
// If the choice is greater than the choices, we assume this is because with bitstring representations,
// the maxNumChoices is the next power of 2 above choiceCount().
// In this case, we try to spread out these "extra" choice settings across the whole range, since
// this seems more fair than choosing all low settings.
// (Of course, some numbers are still more likely to be chosen than others, but that's unavoidable.)
long wrappedChoice = (long) StrictMath.round((double) (choice % choiceCount()) / (maxNumChoices - choiceCount()) * choiceCount());
return new Double(dMin + dStep * wrappedChoice);
}
}
@Override
public long getChoiceIndexFromValue(Object val, long maxNumChoices) {
if (!(val instanceof Number))
{
System.out.println(val);
throw new IllegalStateException("Type mismatch: can't represent a non-number using this double-valued parameter specification.");
}
double dVal = ((Number) val).doubleValue();
return (long) StrictMath.round((dVal - dMin) / dStep);
}
}