1 | /* |
---|
2 | Copyright 2006 by Sean Luke and George Mason University |
---|
3 | Licensed under the Academic Free License version 3.0 |
---|
4 | See the file "LICENSE" for more information |
---|
5 | */ |
---|
6 | |
---|
7 | |
---|
8 | package ec.vector; |
---|
9 | |
---|
10 | import ec.util.*; |
---|
11 | import java.io.*; |
---|
12 | import ec.*; |
---|
13 | |
---|
14 | /* |
---|
15 | * VectorSpecies.java |
---|
16 | * |
---|
17 | * Created: Thu Mar 22 17:44:00 2001 |
---|
18 | * By: Liviu Panait |
---|
19 | */ |
---|
20 | |
---|
21 | /** |
---|
22 | * VectorSpecies is a species which can create VectorIndividuals. Different |
---|
23 | * VectorSpecies are used for different kinds of VectorIndividuals: a plain |
---|
24 | * VectorSpecies is probably only applicable for BitVectorIndividuals. |
---|
25 | * |
---|
26 | * <p>VectorSpecies supports the following recombination methods:</p> |
---|
27 | * <ul> |
---|
28 | * <li><b>One-point crossover</b>.</li> |
---|
29 | * <li><b>Two-point crossover</b>.</li> |
---|
30 | * <li><b>Uniform crossover</b> - inaccurately called "any-point".</li> |
---|
31 | * <li><b>Line recombination</b> - children are random points on a line between |
---|
32 | * the two parents.</li> |
---|
33 | * <li><b>Intermediate recombination</b> - the value of each component of the |
---|
34 | * vector is between the values of that component of the parent vectors. |
---|
35 | * </li> |
---|
36 | * </ul> |
---|
37 | * <p>Note that for LongVectorIndividuals, there are certain values that will |
---|
38 | * never be created by line and intermediate recombination, because the |
---|
39 | * recombination is calculated using doubles and then rounded to the nearest |
---|
40 | * long. For large enough values (but still smaller than the maximum long), the |
---|
41 | * difference between one double and the next is greater than one.</p> |
---|
42 | * |
---|
43 | * <p>VectorSpecies has three wasy to determine the initial size of the individual:</p> |
---|
44 | * <ul> |
---|
45 | * <li><b>A fixed size</b>.</li> |
---|
46 | * <li><b>Geometric distribution</b>.</li> |
---|
47 | * <li><b>Uniform distribution</b></li> |
---|
48 | * </ul> |
---|
49 | * |
---|
50 | * <p>If the algorithm used is the geometric distribution, the VectorSpecies starts at a |
---|
51 | * minimum size and continues flipping a coin with a certain "resize probability", |
---|
52 | * increasing the size each time, until the coin comes up tails (fails). The chunk size |
---|
53 | * must be 1 in this case. |
---|
54 | * |
---|
55 | * <p> If the algorithm used is the uniform distribution, the VectorSpecies picks a random |
---|
56 | * size between a provided minimum and maximum size, inclusive. The chunk size |
---|
57 | * must be 1 in this case. |
---|
58 | * |
---|
59 | * <p>If the size is fixed, then you can also provide a "chunk size" which constrains the |
---|
60 | * locations in which crossover can be performed (only along chunk boundaries). The genome |
---|
61 | * size must be a multiple of the chunk size in this case. |
---|
62 | * |
---|
63 | * <p>VectorSpecies also contains a number of parameters guiding how the individual |
---|
64 | * crosses over and mutates. |
---|
65 | |
---|
66 | <p><b>Parameters</b><br> |
---|
67 | <table> |
---|
68 | <tr><td valign=top><i>base</i>.<tt>genome-size</tt><br> |
---|
69 | <font size=-1>int >= 1 or one of: geometric, uniform</font></td> |
---|
70 | <td valign=top>(size of the genome, or if 'geometric' or 'uniform', the algorithm used to size the initial genome)</td></tr> |
---|
71 | |
---|
72 | <tr><td valign=top><i>base</i>.<tt>chunk-size</tt><br> |
---|
73 | <font size=-1>1 <= int <= genome-size (default=1)</font></td> |
---|
74 | <td valign=top>(the chunk size for crossover (crossover will only occur on chunk boundaries))</td></tr> |
---|
75 | |
---|
76 | <tr><td valign=top><i>base</i>.<tt>geometric-prob</tt><br> |
---|
77 | <font size=-1>0.0 <= float < 1.0</font></td> |
---|
78 | <td valign=top>(the coin-flip probability for increasing the initial size using the geometric distribution)</td></tr> |
---|
79 | |
---|
80 | <tr><td valign=top><i>base</i>.<tt>min-initial-size</tt><br> |
---|
81 | <font size=-1>int >= 0</font></td> |
---|
82 | <td valign=top>(the minimum initial size of the genome)</td></tr> |
---|
83 | |
---|
84 | <tr><td valign=top><i>base</i>.<tt>max-initial-size</tt><br> |
---|
85 | <font size=-1>int >= min-initial-size</font></td> |
---|
86 | <td valign=top>(the maximum initial size of the genome)</td></tr> |
---|
87 | |
---|
88 | <tr><td valign=top><i>base</i>.<tt>crossover-type</tt><br> |
---|
89 | <font size=-1>string, one of: one, two, any</font></td> |
---|
90 | <td valign=top>(default crossover type (one-point, two-point, any-point (uniform), line, or intermediate)</td></tr> |
---|
91 | |
---|
92 | <tr><td valign=top><i>base</i>.<tt>crossover-prob</tt><br> |
---|
93 | <font size=-1>0.0 >= float >= 1.0 </font></td> |
---|
94 | <td valign=top>(probability that a gene will get crossed over during any-point crossover)</td></tr> |
---|
95 | |
---|
96 | <tr><td valign=top><i>base</i>.<tt>mutation-prob</tt><br> |
---|
97 | <font size=-1>0.0 <= float <= 1.0 </font></td> |
---|
98 | <td valign=top>(probability that a gene will get mutated over default mutation)</td></tr> |
---|
99 | |
---|
100 | <tr><td valign=top><i>base</i>.<tt>line-extension</tt><br> |
---|
101 | <font size=-1>float >= 0.0 </font></td> |
---|
102 | <td valign=top>(for line and intermediate recombination, how far along the line or outside of the hypercube children can be. If this value is zero, all children must be within the hypercube.) |
---|
103 | |
---|
104 | </table> |
---|
105 | |
---|
106 | <p><b>Default Base</b><br> |
---|
107 | vector.species |
---|
108 | |
---|
109 | * @author Sean Luke and Liviu Panait |
---|
110 | * @version 1.0 |
---|
111 | */ |
---|
112 | |
---|
113 | public class VectorSpecies extends Species |
---|
114 | { |
---|
115 | public static final String P_VECTORSPECIES = "species"; |
---|
116 | |
---|
117 | public final static String P_CROSSOVERTYPE = "crossover-type"; |
---|
118 | public final static String P_CHUNKSIZE = "chunk-size"; |
---|
119 | public final static String V_ONE_POINT = "one"; |
---|
120 | public final static String V_TWO_POINT = "two"; |
---|
121 | public final static String V_ANY_POINT = "any"; |
---|
122 | public final static String V_LINE_RECOMB = "line"; |
---|
123 | public final static String V_INTERMED_RECOMB = "intermediate"; |
---|
124 | public final static String V_SIMULATED_BINARY = "sbx"; |
---|
125 | public final static String P_CROSSOVER_DISTRIBUTION_INDEX = "crossover-distribution-index"; |
---|
126 | public final static String P_MUTATIONPROB = "mutation-prob"; |
---|
127 | public final static String P_CROSSOVERPROB = "crossover-prob"; |
---|
128 | public final static String P_GENOMESIZE = "genome-size"; |
---|
129 | public final static String P_LINEDISTANCE = "line-extension"; |
---|
130 | public final static String V_GEOMETRIC = "geometric"; |
---|
131 | public final static String P_GEOMETRIC_PROBABILITY = "geometric-prob"; |
---|
132 | public final static String V_UNIFORM = "uniform"; |
---|
133 | public final static String P_UNIFORM_MIN = "min-initial-size"; |
---|
134 | public final static String P_UNIFORM_MAX = "max-initial-size"; |
---|
135 | |
---|
136 | public final static int C_ONE_POINT = 0; |
---|
137 | public final static int C_TWO_POINT = 1; |
---|
138 | public final static int C_ANY_POINT = 128; |
---|
139 | public final static int C_LINE_RECOMB = 256; |
---|
140 | public final static int C_INTERMED_RECOMB = 512; |
---|
141 | public final static int C_SIMULATED_BINARY = 1024; |
---|
142 | public final static int C_NONE = 0; |
---|
143 | public final static int C_GEOMETRIC = 1; |
---|
144 | public final static int C_UNIFORM = 2; |
---|
145 | |
---|
146 | /** Probability that a gene will mutate */ |
---|
147 | public float mutationProbability; |
---|
148 | /** Probability that a gene will cross over -- ONLY used in V_ANY_POINT crossover */ |
---|
149 | public float crossoverProbability; |
---|
150 | /** What kind of crossover do we have? */ |
---|
151 | public int crossoverType; |
---|
152 | /** How big of a genome should we create on initialization? */ |
---|
153 | public int genomeSize; |
---|
154 | /** What should the SBX distribution index be? */ |
---|
155 | public int crossoverDistributionIndex; |
---|
156 | /** How should we reset the genome? */ |
---|
157 | public int genomeResizeAlgorithm; |
---|
158 | /** What's the smallest legal genome? */ |
---|
159 | public int minInitialSize; |
---|
160 | /** What's the largest legal genome? */ |
---|
161 | public int maxInitialSize; |
---|
162 | /** With what probability would our genome be at least 1 larger than it is now during initialization? */ |
---|
163 | public float genomeIncreaseProbability; |
---|
164 | /** How big of chunks should we define for crossover? */ |
---|
165 | public int chunksize; |
---|
166 | /** How far along the long a child can be located for line or intermediate recombination */ |
---|
167 | public double lineDistance; |
---|
168 | /** Was the initial size determined dynamically? */ |
---|
169 | public boolean dynamicInitialSize = false; |
---|
170 | |
---|
171 | // we use warned here because it's quite a bit faster than calling warnOnce |
---|
172 | protected boolean warned = false; |
---|
173 | EvolutionState state; |
---|
174 | protected void warnAboutGene(int gene) |
---|
175 | { |
---|
176 | state.output.warnOnce("Attempt to access maxGene or minGene from IntegerVectorSpecies beyond initial genomeSize.\n" + |
---|
177 | "From now on, maxGene(a) = maxGene(maxGeneIndex) for a >= maxGeneIndex. Likewise for minGene(...)"); |
---|
178 | warned = true; |
---|
179 | } |
---|
180 | |
---|
181 | public Parameter defaultBase() |
---|
182 | { |
---|
183 | return VectorDefaults.base().push(P_VECTORSPECIES); |
---|
184 | } |
---|
185 | |
---|
186 | public void setup(final EvolutionState state, final Parameter base) |
---|
187 | { |
---|
188 | // setup constraints FIRST so the individuals can see them when they're |
---|
189 | // set up. |
---|
190 | |
---|
191 | Parameter def = defaultBase(); |
---|
192 | |
---|
193 | this.state = state; |
---|
194 | |
---|
195 | String genomeSizeForm = state.parameters.getString(base.push(P_GENOMESIZE),def.push(P_GENOMESIZE)); |
---|
196 | if (genomeSizeForm == null) // clearly an error |
---|
197 | { |
---|
198 | state.output.fatal("No genome size specified.", base.push(P_GENOMESIZE),def.push(P_GENOMESIZE)); |
---|
199 | } |
---|
200 | else if (genomeSizeForm.equals(V_GEOMETRIC)) |
---|
201 | { |
---|
202 | dynamicInitialSize = true; |
---|
203 | genomeSize = 1; |
---|
204 | genomeResizeAlgorithm = C_GEOMETRIC; |
---|
205 | chunksize = state.parameters.getIntWithDefault(base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE),1); |
---|
206 | if (chunksize != 1) |
---|
207 | state.output.fatal("To use Geometric size initialization, VectorSpecies must have a chunksize of 1", |
---|
208 | base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE)); |
---|
209 | minInitialSize = state.parameters.getInt(base.push(P_UNIFORM_MIN),def.push(P_UNIFORM_MIN), 0); |
---|
210 | if (minInitialSize < 0) |
---|
211 | { |
---|
212 | state.output.warning("Gemoetric size initialization used, but no minimum initial size provided. Assuming minimum is 0."); |
---|
213 | minInitialSize = 0; |
---|
214 | } |
---|
215 | genomeIncreaseProbability = state.parameters.getFloatWithMax(base.push(P_GEOMETRIC_PROBABILITY),def.push(P_GEOMETRIC_PROBABILITY),0.0, 1.0); |
---|
216 | if (genomeIncreaseProbability < 0.0 || genomeIncreaseProbability >= 1.0) // note >= |
---|
217 | state.output.fatal("To use Gemoetric size initialization, the genome increase probability must be >= 0.0 and < 1.0", |
---|
218 | base.push(P_GEOMETRIC_PROBABILITY),def.push(P_GEOMETRIC_PROBABILITY)); |
---|
219 | } |
---|
220 | else if (genomeSizeForm.equals(V_UNIFORM)) |
---|
221 | { |
---|
222 | dynamicInitialSize = true; |
---|
223 | genomeSize = 1; |
---|
224 | genomeResizeAlgorithm = C_UNIFORM; |
---|
225 | chunksize = state.parameters.getIntWithDefault(base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE),1); |
---|
226 | if (chunksize != 1) |
---|
227 | state.output.fatal("To use Uniform size initialization, VectorSpecies must have a chunksize of 1", |
---|
228 | base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE)); |
---|
229 | minInitialSize = state.parameters.getInt(base.push(P_UNIFORM_MIN),def.push(P_UNIFORM_MIN),0); |
---|
230 | if (minInitialSize < 0) |
---|
231 | state.output.fatal("To use Uniform size initialization, you must set a minimum initial size >= 0", |
---|
232 | base.push(P_UNIFORM_MIN),def.push(P_UNIFORM_MIN)); |
---|
233 | maxInitialSize = state.parameters.getInt(base.push(P_UNIFORM_MAX),def.push(P_UNIFORM_MAX),0); |
---|
234 | if (maxInitialSize < 0) |
---|
235 | state.output.fatal("To use Uniform size initialization, you must set a maximum initial size >= 0", |
---|
236 | base.push(P_UNIFORM_MAX),def.push(P_UNIFORM_MAX)); |
---|
237 | if (maxInitialSize < minInitialSize) |
---|
238 | state.output.fatal("To use Uniform size initialization, you must set a maximum initial size >= the minimum initial size", |
---|
239 | base.push(P_UNIFORM_MAX),def.push(P_UNIFORM_MAX)); |
---|
240 | } |
---|
241 | else // it's a number |
---|
242 | { |
---|
243 | genomeSize = state.parameters.getInt(base.push(P_GENOMESIZE),def.push(P_GENOMESIZE),1); |
---|
244 | if (genomeSize==0) |
---|
245 | state.output.error("VectorSpecies must have a genome size > 0", |
---|
246 | base.push(P_GENOMESIZE),def.push(P_GENOMESIZE)); |
---|
247 | |
---|
248 | genomeResizeAlgorithm = C_NONE; |
---|
249 | |
---|
250 | chunksize = state.parameters.getIntWithDefault(base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE),1); |
---|
251 | if (chunksize <= 0 || chunksize > genomeSize) |
---|
252 | state.output.fatal("VectorSpecies must have a chunksize which is > 0 and < genomeSize", |
---|
253 | base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE)); |
---|
254 | if (genomeSize % chunksize != 0) |
---|
255 | state.output.fatal("VectorSpecies must have a genomeSize which is a multiple of chunksize", |
---|
256 | base.push(P_CHUNKSIZE),def.push(P_CHUNKSIZE)); |
---|
257 | } |
---|
258 | |
---|
259 | mutationProbability = state.parameters.getFloatWithMax( |
---|
260 | base.push(P_MUTATIONPROB),def.push(P_MUTATIONPROB),0.0,1.0); |
---|
261 | if (mutationProbability==-1.0) |
---|
262 | state.output.error("VectorSpecies must have a mutation probability between 0.0 and 1.0 inclusive", |
---|
263 | base.push(P_MUTATIONPROB),def.push(P_MUTATIONPROB)); |
---|
264 | |
---|
265 | String ctype = state.parameters.getStringWithDefault(base.push(P_CROSSOVERTYPE), def.push(P_CROSSOVERTYPE), null); |
---|
266 | crossoverType = C_ONE_POINT; |
---|
267 | if (ctype==null) |
---|
268 | state.output.warning("No crossover type given for VectorSpecies, assuming one-point crossover", |
---|
269 | base.push(P_CROSSOVERTYPE),def.push(P_CROSSOVERTYPE)); |
---|
270 | else if (ctype.equalsIgnoreCase(V_ONE_POINT)) |
---|
271 | crossoverType=C_ONE_POINT; // redundant |
---|
272 | else if (ctype.equalsIgnoreCase(V_TWO_POINT)) |
---|
273 | crossoverType=C_TWO_POINT; |
---|
274 | else if (ctype.equalsIgnoreCase(V_ANY_POINT)) |
---|
275 | crossoverType=C_ANY_POINT; |
---|
276 | else if (ctype.equalsIgnoreCase(V_LINE_RECOMB)) |
---|
277 | crossoverType=C_LINE_RECOMB; |
---|
278 | else if (ctype.equalsIgnoreCase(V_INTERMED_RECOMB)) |
---|
279 | crossoverType=C_INTERMED_RECOMB; |
---|
280 | else if (ctype.equalsIgnoreCase(V_SIMULATED_BINARY)) |
---|
281 | crossoverType=C_SIMULATED_BINARY; |
---|
282 | else state.output.error("VectorSpecies given a bad crossover type: " + ctype, |
---|
283 | base.push(P_CROSSOVERTYPE),def.push(P_CROSSOVERTYPE)); |
---|
284 | |
---|
285 | if (crossoverType==C_LINE_RECOMB || crossoverType==C_INTERMED_RECOMB) |
---|
286 | { |
---|
287 | if (!(this instanceof IntegerVectorSpecies) && !(this instanceof FloatVectorSpecies)) |
---|
288 | state.output.error("Line and intermediate recombinations are only supported by IntegerVectorSpecies and FloatVectorSpecies", base.push(P_CROSSOVERTYPE), def.push(P_CROSSOVERTYPE)); |
---|
289 | lineDistance = state.parameters.getDouble( |
---|
290 | base.push(P_LINEDISTANCE), def.push(P_LINEDISTANCE), 0.0); |
---|
291 | if (lineDistance==-1.0) |
---|
292 | state.output.error("If it's going to use line or intermediate recombination, VectorSpecies needs a line extension >= 0.0 (0.25 is common)", base.push(P_LINEDISTANCE), def.push(P_LINEDISTANCE)); |
---|
293 | } |
---|
294 | else lineDistance = 0.0; |
---|
295 | |
---|
296 | if (crossoverType==C_ANY_POINT) |
---|
297 | { |
---|
298 | crossoverProbability = state.parameters.getFloatWithMax( |
---|
299 | base.push(P_CROSSOVERPROB),def.push(P_CROSSOVERPROB),0.0,0.5); |
---|
300 | if (crossoverProbability==-1.0) |
---|
301 | state.output.error("If it's going to use any-point crossover, VectorSpecies must have a crossover probability between 0.0 and 0.5 inclusive", |
---|
302 | base.push(P_CROSSOVERPROB),def.push(P_CROSSOVERPROB)); |
---|
303 | } |
---|
304 | else crossoverProbability = 0.0f; |
---|
305 | |
---|
306 | if (crossoverType==C_SIMULATED_BINARY) |
---|
307 | { |
---|
308 | if (!(this instanceof FloatVectorSpecies)) |
---|
309 | state.output.error("Simulated binary crossover (SBX) is only supported by FloatVectorSpecies", base.push(P_CROSSOVERTYPE), def.push(P_CROSSOVERTYPE)); |
---|
310 | crossoverDistributionIndex = state.parameters.getInt(base.push(P_CROSSOVER_DISTRIBUTION_INDEX), def.push(P_CROSSOVER_DISTRIBUTION_INDEX), 0); |
---|
311 | if (crossoverDistributionIndex < 0) |
---|
312 | state.output.fatal("If FloatVectorSpecies is going to use simulated binary crossover (SBX), the distribution index must be defined and >= 0.", |
---|
313 | base.push(P_CROSSOVER_DISTRIBUTION_INDEX), def.push(P_CROSSOVER_DISTRIBUTION_INDEX)); |
---|
314 | } |
---|
315 | else crossoverProbability = 0.0f; |
---|
316 | |
---|
317 | state.output.exitIfErrors(); |
---|
318 | |
---|
319 | if (crossoverType != C_ANY_POINT && state.parameters.exists(base.push(P_CROSSOVERPROB),def.push(P_CROSSOVERPROB))) |
---|
320 | state.output.warning("The 'crossover-prob' parameter may only be used with any-point crossover. It states the probability that a particular gene will be crossed over. If you were looking for the probability of crossover happening at *all*, look at the 'likelihood' parameter.", |
---|
321 | base.push(P_CROSSOVERPROB),def.push(P_CROSSOVERPROB)); |
---|
322 | |
---|
323 | // NOW call super.setup(...), which will in turn set up the prototypical individual |
---|
324 | super.setup(state,base); |
---|
325 | } |
---|
326 | |
---|
327 | public Individual newIndividual(final EvolutionState state, int thread) |
---|
328 | |
---|
329 | { |
---|
330 | VectorIndividual newind = (VectorIndividual)(super.newIndividual(state, thread)); |
---|
331 | |
---|
332 | if (genomeResizeAlgorithm == C_NONE) |
---|
333 | newind.reset( state, thread ); |
---|
334 | else if (genomeResizeAlgorithm == C_UNIFORM) |
---|
335 | { |
---|
336 | int size = state.random[thread].nextInt(maxInitialSize - minInitialSize + 1) + minInitialSize; |
---|
337 | newind.reset(state, thread, size); |
---|
338 | } |
---|
339 | else if (genomeResizeAlgorithm == C_GEOMETRIC) |
---|
340 | { |
---|
341 | int size = minInitialSize; |
---|
342 | while(state.random[thread].nextBoolean(genomeIncreaseProbability)) size++; |
---|
343 | newind.reset(state, thread, size); |
---|
344 | } |
---|
345 | |
---|
346 | return newind; |
---|
347 | } |
---|
348 | } |
---|
349 | |
---|
350 | |
---|