package bsearch.representations; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import org.nlogo.util.MersenneTwisterFast; import bsearch.space.ParameterSpec; import bsearch.space.SearchSpace; /** * A Chromosomal representation that uses Gray code for integers */ public abstract strictfp class BinaryChromosome implements Chromosome { public static final int NUM_BITS_FOR_CONTINUOUS_PARAMETERS = 16; // discretized to 65,536 choices protected final boolean[] bitstring; protected final SearchSpace searchSpace; private static int numBitsRequired(long choiceCount) { if (choiceCount > 0) { return (int) StrictMath.ceil( StrictMath.log( choiceCount ) / StrictMath.log( 2 )); } else { return NUM_BITS_FOR_CONTINUOUS_PARAMETERS; } } public BinaryChromosome( SearchSpace searchSpace , MersenneTwisterFast rng ) { super(); this.searchSpace = searchSpace; List<ParameterSpec> paramSpecs = searchSpace.getParamSpecs(); int bitstringLength = 0; for (ParameterSpec p: paramSpecs) { bitstringLength += numBitsRequired(p.choiceCount()); } bitstring = new boolean[bitstringLength]; int offset = 0; for (ParameterSpec p: paramSpecs) { int numbits = numBitsRequired(p.choiceCount()); if (numbits > 0) { binaryEncode(rng.nextLong(1L << numbits), bitstring, offset, numbits); offset += numbits; } } } public BinaryChromosome( SearchSpace searchSpace, LinkedHashMap<String,Object> paramSettings) { super(); this.searchSpace = searchSpace; List<ParameterSpec> paramSpecs = searchSpace.getParamSpecs(); if (paramSpecs.size() != paramSettings.size()) { throw new IllegalStateException("# of parameter settings does not match search space parameter specifications."); } int bitstringLength = 0; for (ParameterSpec p: paramSpecs) { bitstringLength += numBitsRequired(p.choiceCount()); } bitstring = new boolean[bitstringLength]; int offset = 0; for (int i = 0; i < paramSpecs.size(); i++) { ParameterSpec p = paramSpecs.get(i); Object value = paramSettings.get(p.getParameterName()); int numbits = numBitsRequired(p.choiceCount()); long choice = p.getChoiceIndexFromValue(value, 1L << numbits); if (numbits > 0) { binaryEncode(choice, bitstring, offset, numbits); offset += numbits; } } } protected BinaryChromosome( boolean[] bitstring, SearchSpace searchSpace ) { super() ; this.bitstring = bitstring.clone() ; this.searchSpace = searchSpace ; } public abstract void binaryEncode(long binval, boolean[] graybits, int offset, int len); public abstract long binaryDecode(boolean[] graybits, int offset, int len ); static String toBinaryString(boolean[] bitstring) { String s = ""; for (boolean b : bitstring) { s = (b?'1':'0') + s; } return s; } public String getBinaryString() { return toBinaryString(bitstring); } public LinkedHashMap<String,Object> getParamSettings() { LinkedHashMap<String,Object> paramSettings = new LinkedHashMap<String,Object>(); int offset = 0; for (ParameterSpec p: searchSpace.getParamSpecs()) { int numbits = numBitsRequired(p.choiceCount()); if (numbits > 0) { paramSettings.put(p.getParameterName(), p.getValueFromChoice(binaryDecode(bitstring, offset, numbits), 1L << numbits)); offset += numbits; } else // parameter has only one possible value, so just get it. { paramSettings.put(p.getParameterName(), p.getValueFromChoice(0,0)); } } return paramSettings; } @Override public abstract BinaryChromosome clone(); public Chromosome mutate(double mutRate, MersenneTwisterFast rng) { BinaryChromosome bc = clone(); for (int i = 0; i < bc.bitstring.length; i++) { if (rng.nextDouble() < mutRate) { bc.bitstring[i] = ! bc.bitstring[i]; // flip a bit } } return bc; } public Chromosome[] crossoverWith(Chromosome other, MersenneTwisterFast rng) { if (! (other instanceof BinaryChromosome)) { throw new IllegalStateException("Can't crossover different types of Chromosomes."); } BinaryChromosome parent0 = this; BinaryChromosome parent1 = (BinaryChromosome) other; BinaryChromosome[] children = new BinaryChromosome[2]; children[0] = parent0.clone(); children[1] = parent1.clone(); int len = parent1.bitstring.length; int splitPoint = 1 + rng.nextInt( len ) ; for (int i = 0 ; i < len; i++) { if (i >= splitPoint) { children[0].bitstring[i] = parent1.bitstring[i]; children[1].bitstring[i] = parent0.bitstring[i]; } } return children; } public SearchSpace getSearchSpace() { return searchSpace; } /** * Redefine equality for our Chromosomes. * Two chromosomes are considered equal if they have the same genotype. * */ @Override public boolean equals(Object other) { if (other instanceof BinaryChromosome) { BinaryChromosome gbc = (BinaryChromosome) other; return Arrays.equals( bitstring, gbc.bitstring); } return false; } /** * Reflects the redefined equals(Object) method, and it * should provide more efficient hashing for Chromosome keys in hashmaps. */ @Override public int hashCode() { int hash = 0; for (boolean b : bitstring) { hash *= 2; hash += b ? 1 : 0; } return hash; } }