/* Copyright 2006 by Sean Luke Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package ec; import java.util.*; import java.io.*; import ec.util.*; /* * Subpopulation.java * * Created: Tue Aug 10 20:34:14 1999 * By: Sean Luke */ /** * Subpopulation is a group which is basically an array of Individuals. * There is always one or more Subpopulations in the Population. Each * Subpopulation has a Species, which governs the formation of the Individuals * in that Subpopulation. Subpopulations also contain a Fitness prototype * which is cloned to form Fitness objects for individuals in the subpopulation. * *

An initial subpopulation is populated with new random individuals * using the populate(...) method. This method typically populates * by filling the array with individuals created using the Subpopulations' * species' emptyClone() method, though you might override this to create * them with other means, by loading from text files for example. * *

In a multithreaded area of a run, Subpopulations should be considered * immutable. That is, once they are created, they should not be modified, * nor anything they contain. This protocol helps ensure read-safety under * multithreading race conditions. *

Parameters
base.size
int >= 1
(total number of individuals in the subpopulation)
base.species
classname, inherits and != ec.Species
(the class of the subpopulations' Species)
base.fitness
classname, inherits and != ec.Fitness
(the class for the prototypical Fitness for individuals in this subpopulation)
base.file
String
(pathname of file from which the population is to be loaded. If not defined, or empty, then the population will be initialized at random in the standard manner)
base.duplicate-retries
int >= 0
(during initialization, when we produce an individual which already exists in the subpopulation, the number of times we try to replace it with something unique. Ignored if we're loading from a file.)

Default Base
ec.subpop

Parameter bases
base.species species (the subpopulations' species)
* @author Sean Luke * @version 1.0 */ public class Subpopulation implements Group { /** A new subpopulation should be loaded from this file if it is non-null; otherwise they should be created at random. */ public File loadInds; /** The species for individuals in this subpopulation. */ public Species species; /** The subpopulation's individuals. */ public Individual[] individuals; /** Do we allow duplicates? */ public int numDuplicateRetries; public static final String P_SUBPOPULATION = "subpop"; public static final String P_FILE = "file"; public static final String P_SUBPOPSIZE = "size"; // parameter for number of subpops or pops public static final String P_SPECIES = "species"; public static final String P_RETRIES = "duplicate-retries"; public static final String NUM_INDIVIDUALS_PREAMBLE = "Number of Individuals: "; public static final String INDIVIDUAL_INDEX_PREAMBLE = "Individual Number: "; public Parameter defaultBase() { return ECDefaults.base().push(P_SUBPOPULATION); } /** Returns an instance of Subpopulation just like it had been before it was populated with individuals. You may need to override this if you override Subpopulation. IMPORTANT NOTE: if the size of the array in Subpopulation has been changed, then the clone will take on the new array size. This helps some evolution strategies. @see Group#emptyClone() */ public Group emptyClone() { try { Subpopulation p = (Subpopulation)clone(); p.species = species; // don't throw it away...maybe this is a bad idea... p.individuals = new Individual[individuals.length]; // empty return p; } catch (CloneNotSupportedException e) { throw new InternalError(); } // never happens } public void setup(final EvolutionState state, final Parameter base) { Parameter def = defaultBase(); int size; // do we load from a file? loadInds = state.parameters.getFile( base.push(P_FILE),null); // what species do we use? species = (Species) state.parameters.getInstanceForParameter( base.push(P_SPECIES),def.push(P_SPECIES), Species.class); species.setup(state,base.push(P_SPECIES)); // how big should our subpopulation be? size = state.parameters.getInt( base.push(P_SUBPOPSIZE),def.push(P_SUBPOPSIZE),1); if (size<=0) state.output.fatal( "Subpopulation size must be an integer >= 1.\n", base.push(P_SUBPOPSIZE),def.push(P_SUBPOPSIZE)); // How often do we retry if we find a duplicate? numDuplicateRetries = state.parameters.getInt( base.push(P_RETRIES),def.push(P_RETRIES),0); if (numDuplicateRetries < 0) state.output.fatal( "The number of retries for duplicates must be an integer >= 0.\n", base.push(P_RETRIES),def.push(P_RETRIES)); individuals = new Individual[size]; } public void populate(EvolutionState state, int thread) { // should we load individuals from a file? -- duplicates are permitted if (loadInds!=null) { try { readSubpopulation(state, new LineNumberReader(new FileReader(loadInds))); } catch (IOException e) { state.output.fatal("An IOException occurred when trying to read from the file " + loadInds + ". The IOException was: \n" + e); } } else { Hashtable h = null; if (numDuplicateRetries >= 1) h = new Hashtable(individuals.length / 2); // seems reasonable for(int x=0;x= 1) { // check for duplicates Object o = h.get(individuals[x]); if (o == null) // found nothing, we're safe // hash it and go { h.put(individuals[x],individuals[x]); break; } } } // oh well, we tried to cut down the duplicates } } } /** Prints an entire subpopulation in a form readable by humans. @deprecated Verbosity no longer has meaning */ public final void printSubpopulationForHumans(final EvolutionState state, final int log, final int verbosity) { printSubpopulationForHumans(state, log); } /** Prints an entire subpopulation in a form readable by humans but also parseable by the computer using readSubpopulation(EvolutionState, LineNumberReader). @deprecated Verbosity no longer has meaning */ public final void printSubpopulation(final EvolutionState state, final int log, final int verbosity) { printSubpopulation(state, log); } /** Prints an entire subpopulation in a form readable by humans, with a verbosity of Output.V_NO_GENERAL. */ public void printSubpopulationForHumans(final EvolutionState state, final int log) { state.output.println(NUM_INDIVIDUALS_PREAMBLE + individuals.length, log); for(int i = 0 ; i < individuals.length; i++) { state.output.println(INDIVIDUAL_INDEX_PREAMBLE + Code.encode(i), log); individuals[i].printIndividualForHumans(state, log); } } /** Prints an entire subpopulation in a form readable by humans but also parseable by the computer using readSubpopulation(EvolutionState, LineNumberReader) with a verbosity of Output.V_NO_GENERAL. */ public void printSubpopulation(final EvolutionState state, final int log) { state.output.println(NUM_INDIVIDUALS_PREAMBLE + Code.encode(individuals.length), log); for(int i = 0 ; i < individuals.length; i++) { state.output.println(INDIVIDUAL_INDEX_PREAMBLE + Code.encode(i), log); individuals[i].printIndividual(state, log); } } /** Prints an entire subpopulation in a form readable by humans but also parseable by the computer using readSubpopulation(EvolutionState, LineNumberReader). */ public void printSubpopulation(final EvolutionState state, final PrintWriter writer) { writer.println(NUM_INDIVIDUALS_PREAMBLE + Code.encode(individuals.length)); for(int i = 0 ; i < individuals.length; i++) { writer.println(INDIVIDUAL_INDEX_PREAMBLE + Code.encode(i)); individuals[i].printIndividual(state, writer); } } /** Reads a subpopulation from the format generated by printSubpopulation(....). If the number of individuals is not identical, the individuals array will be deleted and replaced with a new array, and a warning will be generated as individuals will have to be created using newIndividual(...) rather than readIndividual(...). */ public void readSubpopulation(final EvolutionState state, final LineNumberReader reader) throws IOException { // read in number of individuals and check to see if this appears to be a valid subpopulation int numIndividuals = Code.readIntegerWithPreamble(NUM_INDIVIDUALS_PREAMBLE, state, reader); // read in individuals if (numIndividuals != individuals.length) { state.output.warnOnce("On reading subpopulation from text stream, the subpopulation size didn't match.\n" + "Had to resize and use newIndividual() instead of readIndividual()."); individuals = new Individual[numIndividuals]; for(int i = 0 ; i < individuals.length; i++) { int j = Code.readIntegerWithPreamble(INDIVIDUAL_INDEX_PREAMBLE, state, reader); // sanity check if (j!=i) state.output.warnOnce("On reading subpopulation from text stream, some individual indexes in the subpopulation did not match."); individuals[i] = species.newIndividual(state, reader); } } else for(int i = 0 ; i < individuals.length; i++) { int j = Code.readIntegerWithPreamble(INDIVIDUAL_INDEX_PREAMBLE, state, reader); // sanity check if (j!=i) state.output.warnOnce("On reading subpopulation from text stream, some individual indexes in the subpopulation did not match."); if (individuals[i] != null) individuals[i].readIndividual(state, reader); else { state.output.warnOnce("On reading subpopulation from text stream, some of the preexisting subpopulation's slots were null.\n" + "Had to use newIndividual() instead of readIndividual(). If you're starting an evolutionary run by reading an\n" + "existing population from a file, this is expected -- ignore this message."); individuals[i] = species.newIndividual(state, reader); } } } /** Writes a subpopulation in binary form, in a format readable by readSubpopulation(EvolutionState, DataInput). */ public void writeSubpopulation(final EvolutionState state, final DataOutput dataOutput) throws IOException { dataOutput.writeInt(individuals.length); for(int i = 0 ; i < individuals.length; i++) individuals[i].writeIndividual(state, dataOutput); } /** Reads a subpopulation in binary form, from the format generated by writeSubpopulation(...). If the number of individuals is not identical, the individuals array will be deleted and replaced with a new array, and a warning will be generated as individuals will have to be created using newIndividual(...) rather than readIndividual(...) */ public void readSubpopulation(final EvolutionState state, final DataInput dataInput) throws IOException { int numIndividuals = dataInput.readInt(); if (numIndividuals != individuals.length) { state.output.warnOnce("On reading subpopulation from binary stream, the subpopulation size was incorrect.\n" + "Had to resize and use newIndividual() instead of readIndividual()."); individuals = new Individual[numIndividuals]; for(int i = 0 ; i < individuals.length; i++) individuals[i] = species.newIndividual(state, dataInput); } else for(int i = 0 ; i < individuals.length; i++) individuals[i].readIndividual(state, dataInput); } }