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.exchange; |
---|
9 | import ec.*; |
---|
10 | import ec.util.*; |
---|
11 | import java.io.*; |
---|
12 | |
---|
13 | /* |
---|
14 | * InterPopulationExchange.java |
---|
15 | * |
---|
16 | * Created Sat Feb 10 13:44:11 EST 2001 |
---|
17 | * By: Liviu Panait |
---|
18 | */ |
---|
19 | |
---|
20 | /** |
---|
21 | * InterPopulationExchange is an Exchanger which implements a simple exchanger |
---|
22 | * between subpopulations. IterPopulationExchange uses an arbitrary graph topology |
---|
23 | * for migrating individuals from subpopulations. The assumption is that all |
---|
24 | * subpopulations have the same representation and same task to solve, otherwise |
---|
25 | * the exchange between subpopulations does not make much sense. |
---|
26 | |
---|
27 | * <p>InterPopulationExchange has a topology which is similar to the one used by |
---|
28 | * IslandExchange. Every few generations, a subpopulation will send some number |
---|
29 | * of individuals to other subpopulations. Since all subpopulations evolve at |
---|
30 | * the same generational speed, this is a synchronous procedure (IslandExchange |
---|
31 | * instead is asynchronous by default, though you can change it to synchronous). |
---|
32 | |
---|
33 | * <p>Individuals are sent from a subpopulation prior to breeding. They are stored |
---|
34 | * in a waiting area until after all subpopulations have bred; thereafter they are |
---|
35 | * added into the new subpopulation. This means that the subpopulation order doesn't |
---|
36 | * matter. Also note that it means that some individuals will be created during breeding, |
---|
37 | * and immediately killed to make way for the migrants. A little wasteful, we know, |
---|
38 | * but it's simpler that way. |
---|
39 | |
---|
40 | <p><b>Parameters</b><br> |
---|
41 | <table> |
---|
42 | <tr><td valign=top><tt><i>base</i>.chatty</tt><br> |
---|
43 | <font size=-1>boolean, default = true</font></td> |
---|
44 | <td valign=top> Should we be verbose or silent about our exchanges? |
---|
45 | </td></tr> |
---|
46 | </table> |
---|
47 | <p><i>Note:</i> For each subpopulation in your population, there <b>must</b> be |
---|
48 | one exch.subpop... declaration set. |
---|
49 | <table> |
---|
50 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.select</tt><br> |
---|
51 | <font size=-1>classname, inherits and != ec.SelectionMethod</font></td> |
---|
52 | <td valign=top> The selection method used by subpopulation #n for picking |
---|
53 | migrants to emigrate to other subpopulations. If not set, uses the default parameter below. |
---|
54 | </td></tr> |
---|
55 | <tr><td valign=top><tt><i>base</i>.select</tt><br> |
---|
56 | <font size=-1>classname, inherits and != ec.SelectionMethod</font></td> |
---|
57 | <td valign=top> |
---|
58 | <i>server</i>: Default parameter: the selection method used by a given subpopulation for picking |
---|
59 | migrants to emigrate to other subpopulations. |
---|
60 | </td></tr> |
---|
61 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.select-to-die</tt><br> |
---|
62 | <font size=-1>classname, inherits and != ec.SelectionMethod (Default is random selection)</font></td> |
---|
63 | <td valign=top> The selection method used by subpopulation #n for picking |
---|
64 | individuals to be replaced by migrants. If not set, uses the default parameter below. |
---|
65 | </td></tr> |
---|
66 | <tr><td valign=top><tt><i>base</i>.select-to-die</tt><br> |
---|
67 | <font size=-1>classname, inherits and != ec.SelectionMethod (Default is random selection)</font></td> |
---|
68 | <td valign=top> |
---|
69 | <i>server</i>: Default parameter: the selection method used by a given subpopulation for picking |
---|
70 | individuals to be replaced by migrants. |
---|
71 | </td></tr> |
---|
72 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.mod</tt><br> |
---|
73 | <font size=-1>int >= 1</font></td> |
---|
74 | <td valign=top> The number of generations that subpopulation #n waits between |
---|
75 | sending emigrants. If not set, uses the default parameter below. |
---|
76 | </td></tr> |
---|
77 | <tr><td valign=top><tt><i>base</i>.mod</tt><br> |
---|
78 | <font size=-1>int >= 1</font></td> |
---|
79 | <td valign=top> |
---|
80 | <i>server</i>: Default parameter: the number of generations that a given subpopulation waits between |
---|
81 | sending emigrants. |
---|
82 | </td></tr> |
---|
83 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.start</tt><br> |
---|
84 | <font size=-1>int >= 0</font></td> |
---|
85 | <td valign=top> The generation when subpopulation #n begins sending emigrants. If not set, uses the default parameter below. |
---|
86 | </td></tr> |
---|
87 | <tr><td valign=top><tt><i>base</i>.start</tt><br> |
---|
88 | <font size=-1>int >= 0</font></td> |
---|
89 | <td valign=top> |
---|
90 | <i>server</i>: Default parameter: the generation when a given subpopulation begins sending emigrants. |
---|
91 | </td></tr> |
---|
92 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.size</tt><br> |
---|
93 | <font size=-1>int >= 0</font></td> |
---|
94 | <td valign=top> The number of emigrants sent at one time by generation #n. If not set, uses the default parameter below. |
---|
95 | </td></tr> |
---|
96 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.num-dest</tt><br> |
---|
97 | <font size=-1>int >= 0</font></td> |
---|
98 | <td valign=top> The number of destination subpopulations for this subpopulation. |
---|
99 | </td></tr> |
---|
100 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.dest.<i>m</i></tt><br> |
---|
101 | <font size=-1>int >= 0</font></td> |
---|
102 | <td valign=top> Subpopulation #n's destination #m is this subpopulation. |
---|
103 | </td></tr> |
---|
104 | </table> |
---|
105 | |
---|
106 | <p><b>Parameter bases</b><br> |
---|
107 | <table> |
---|
108 | <tr><td valign=top><tt><i>base</i>.subpop.<i>n</i>.select</tt><br> |
---|
109 | <td>selection method for subpopulation #n's migrants</td></tr> |
---|
110 | </table> |
---|
111 | |
---|
112 | * |
---|
113 | * @author Liviu Panait & Sean Luke |
---|
114 | * @version 2.0 |
---|
115 | */ |
---|
116 | |
---|
117 | |
---|
118 | public class InterPopulationExchange extends Exchanger |
---|
119 | { |
---|
120 | // static inner classes don't need SerialVersionUIDs |
---|
121 | static class IPEInformation implements Serializable |
---|
122 | { |
---|
123 | // the selection method |
---|
124 | SelectionMethod immigrantsSelectionMethod; |
---|
125 | |
---|
126 | // the selection method |
---|
127 | SelectionMethod indsToDieSelectionMethod; |
---|
128 | |
---|
129 | // the number of destination subpopulations |
---|
130 | int numDest; |
---|
131 | |
---|
132 | // the subpopulations where individuals need to be sent |
---|
133 | int[] destinations; |
---|
134 | |
---|
135 | // the modulo |
---|
136 | int modulo; |
---|
137 | |
---|
138 | // the start (offset) |
---|
139 | int offset; |
---|
140 | |
---|
141 | // the size |
---|
142 | int size; |
---|
143 | } |
---|
144 | |
---|
145 | |
---|
146 | /** The subpopulation delimiter */ |
---|
147 | public static final String P_SUBPOP = "subpop"; |
---|
148 | |
---|
149 | /** The parameter for the modulo (how many generations should pass between consecutive sendings of individuals */ |
---|
150 | public static final String P_MODULO = "mod"; |
---|
151 | |
---|
152 | /** The number of emigrants to be sent */ |
---|
153 | public static final String P_SIZE = "size"; |
---|
154 | |
---|
155 | /** How many generations to pass at the beginning of the evolution before the first |
---|
156 | emigration from the current subpopulation */ |
---|
157 | public static final String P_OFFSET = "start"; |
---|
158 | |
---|
159 | /** The number of destinations from current island */ |
---|
160 | public static final String P_DEST_FOR_SUBPOP = "num-dest"; |
---|
161 | |
---|
162 | /** The prefix for destinations */ |
---|
163 | public static final String P_DEST = "dest"; |
---|
164 | |
---|
165 | /** The selection method for sending individuals to other islands */ |
---|
166 | public static final String P_SELECT_METHOD = "select"; |
---|
167 | |
---|
168 | /** The selection method for deciding individuals to be replaced by immigrants */ |
---|
169 | public static final String P_SELECT_TO_DIE_METHOD = "select-to-die"; |
---|
170 | |
---|
171 | /** Whether or not we're chatty */ |
---|
172 | public static final String P_CHATTY = "chatty"; |
---|
173 | |
---|
174 | /** My parameter base -- I need to keep this in order to help the server |
---|
175 | reinitialize contacts */ |
---|
176 | // SERIALIZE |
---|
177 | public Parameter base; |
---|
178 | |
---|
179 | IPEInformation[] exchangeInformation; |
---|
180 | |
---|
181 | // storage for the incoming immigrants: 2 sizes: |
---|
182 | // the subpopulation and the index of the emigrant |
---|
183 | // this is virtually the array of mailboxes |
---|
184 | Individual[][] immigrants; |
---|
185 | |
---|
186 | // the number of immigrants in the storage for each of the subpopulations |
---|
187 | int[] nImmigrants; |
---|
188 | |
---|
189 | int nrSources; |
---|
190 | |
---|
191 | public boolean chatty; |
---|
192 | |
---|
193 | // sets up the Island Exchanger |
---|
194 | public void setup( final EvolutionState state, final Parameter _base ) |
---|
195 | { |
---|
196 | base = _base; |
---|
197 | |
---|
198 | Parameter p_numsubpops = new Parameter( ec.Initializer.P_POP ).push( ec.Population.P_SIZE ); |
---|
199 | int numsubpops = state.parameters.getInt(p_numsubpops,null,1); |
---|
200 | if ( numsubpops == 0 ) |
---|
201 | { |
---|
202 | // later on, Population will complain with this fatally, so don't |
---|
203 | // exit here, just deal with it and assume that you'll soon be shut |
---|
204 | // down |
---|
205 | } |
---|
206 | |
---|
207 | // how many individuals (maximally) would each of the mailboxes have to hold |
---|
208 | int[] incoming = new int[ numsubpops ]; |
---|
209 | |
---|
210 | // allocate some of the arrays |
---|
211 | exchangeInformation = new IPEInformation[ numsubpops ]; |
---|
212 | for( int i = 0 ; i < numsubpops ; i++ ) |
---|
213 | exchangeInformation[i] = new IPEInformation(); |
---|
214 | nImmigrants = new int[ numsubpops ]; |
---|
215 | |
---|
216 | Parameter p; |
---|
217 | |
---|
218 | Parameter localBase = base.push( P_SUBPOP ); |
---|
219 | |
---|
220 | chatty = state.parameters.getBoolean(base.push(P_CHATTY), null, true); |
---|
221 | |
---|
222 | for( int i = 0 ; i < numsubpops ; i++ ) |
---|
223 | { |
---|
224 | |
---|
225 | // update the parameter for the new context |
---|
226 | p = localBase.push( "" + i ); |
---|
227 | |
---|
228 | // read the selection method |
---|
229 | exchangeInformation[i].immigrantsSelectionMethod = (SelectionMethod) |
---|
230 | state.parameters.getInstanceForParameter( p.push( P_SELECT_METHOD ), base.push(P_SELECT_METHOD), ec.SelectionMethod.class ); |
---|
231 | if( exchangeInformation[i].immigrantsSelectionMethod == null ) |
---|
232 | state.output.fatal( "Invalid parameter.", p.push( P_SELECT_METHOD ), base.push(P_SELECT_METHOD) ); |
---|
233 | exchangeInformation[i].immigrantsSelectionMethod.setup( state, p.push(P_SELECT_METHOD) ); |
---|
234 | |
---|
235 | // read the selection method |
---|
236 | if( state.parameters.exists( p.push( P_SELECT_TO_DIE_METHOD ), base.push(P_SELECT_TO_DIE_METHOD ) ) ) |
---|
237 | exchangeInformation[i].indsToDieSelectionMethod = (SelectionMethod) |
---|
238 | state.parameters.getInstanceForParameter( p.push( P_SELECT_TO_DIE_METHOD ), base.push( P_SELECT_TO_DIE_METHOD ), ec.SelectionMethod.class ); |
---|
239 | else // use RandomSelection |
---|
240 | exchangeInformation[i].indsToDieSelectionMethod = new ec.select.RandomSelection(); |
---|
241 | exchangeInformation[i].indsToDieSelectionMethod.setup( state, p.push(P_SELECT_TO_DIE_METHOD)); |
---|
242 | |
---|
243 | // get the modulo |
---|
244 | exchangeInformation[i].modulo = state.parameters.getInt( p.push( P_MODULO ), base.push(P_MODULO ), 1 ); |
---|
245 | if( exchangeInformation[i].modulo == 0 ) |
---|
246 | state.output.fatal( "Parameter not found, or it has an incorrect value.", p.push( P_MODULO ), base.push( P_MODULO ) ); |
---|
247 | |
---|
248 | // get the offset |
---|
249 | exchangeInformation[i].offset = state.parameters.getInt( p.push( P_OFFSET ), base.push( P_OFFSET ), 0 ); |
---|
250 | if( exchangeInformation[i].offset == -1 ) |
---|
251 | state.output.fatal( "Parameter not found, or it has an incorrect value.", p.push( P_OFFSET ), base.push( P_OFFSET ) ); |
---|
252 | |
---|
253 | // get the size |
---|
254 | exchangeInformation[i].size = state.parameters.getInt( p.push( P_SIZE ), base.push( P_SIZE ), 1 ); |
---|
255 | if( exchangeInformation[i].size == 0 ) |
---|
256 | state.output.fatal( "Parameter not found, or it has an incorrect value.", p.push( P_SIZE ), base.push( P_SIZE ) ); |
---|
257 | |
---|
258 | // get the number of destinations |
---|
259 | exchangeInformation[i].numDest = state.parameters.getInt( p.push( P_DEST_FOR_SUBPOP ), null, 0 ); |
---|
260 | if( exchangeInformation[i].numDest == -1 ) |
---|
261 | state.output.fatal( "Parameter not found, or it has an incorrect value.", p.push( P_DEST_FOR_SUBPOP ) ); |
---|
262 | |
---|
263 | exchangeInformation[i].destinations = new int[ exchangeInformation[i].numDest ]; |
---|
264 | // read the destinations |
---|
265 | for( int j = 0 ; j < exchangeInformation[i].numDest ; j++ ) |
---|
266 | { |
---|
267 | exchangeInformation[i].destinations[j] = |
---|
268 | state.parameters.getInt( p.push( P_DEST ).push( "" + j ), null, 0 ); |
---|
269 | if( exchangeInformation[i].destinations[j] == -1 || |
---|
270 | exchangeInformation[i].destinations[j] >= numsubpops ) |
---|
271 | state.output.fatal( "Parameter not found, or it has an incorrect value.", p.push( P_DEST ).push( "" + j ) ); |
---|
272 | // update the maximum number of incoming individuals for the destination island |
---|
273 | incoming[ exchangeInformation[i].destinations[j] ] += exchangeInformation[i].size; |
---|
274 | } |
---|
275 | |
---|
276 | } |
---|
277 | |
---|
278 | // calculate the maximum number of incoming individuals to be stored in the mailbox |
---|
279 | int max = -1; |
---|
280 | |
---|
281 | for( int i = 0 ; i < incoming.length ; i++ ) |
---|
282 | if( max == - 1 || max < incoming[i] ) |
---|
283 | max = incoming[i]; |
---|
284 | |
---|
285 | // set up the mailboxes |
---|
286 | immigrants = new Individual[ numsubpops ][ max ]; |
---|
287 | |
---|
288 | } |
---|
289 | |
---|
290 | |
---|
291 | /** |
---|
292 | Initializes contacts with other processes, if that's what you're doing. |
---|
293 | Called at the beginning of an evolutionary run, before a population is set up. |
---|
294 | It doesn't do anything, as this exchanger works on only 1 computer. |
---|
295 | */ |
---|
296 | public void initializeContacts(EvolutionState state) |
---|
297 | { |
---|
298 | } |
---|
299 | |
---|
300 | /** |
---|
301 | Initializes contacts with other processes, if that's what you're doing. Called after restarting from a checkpoint. |
---|
302 | It doesn't do anything, as this exchanger works on only 1 computer. |
---|
303 | */ |
---|
304 | public void reinitializeContacts(EvolutionState state) |
---|
305 | { |
---|
306 | } |
---|
307 | |
---|
308 | |
---|
309 | |
---|
310 | public Population preBreedingExchangePopulation(EvolutionState state) |
---|
311 | { |
---|
312 | // exchange individuals between subpopulations |
---|
313 | // BUT ONLY if the modulo and offset are appropriate for this |
---|
314 | // generation (state.generation) |
---|
315 | // I am responsible for returning a population. This could |
---|
316 | // be a new population that I created fresh, or I could modify |
---|
317 | // the existing population and return that. |
---|
318 | |
---|
319 | // for each of the islands that sends individuals |
---|
320 | for( int i = 0 ; i < exchangeInformation.length ; i++ ) |
---|
321 | { |
---|
322 | |
---|
323 | // else, check whether the emigrants need to be sent |
---|
324 | if( ( state.generation >= exchangeInformation[i].offset ) && |
---|
325 | ( ( exchangeInformation[i].modulo == 0 ) || |
---|
326 | ( ( ( state.generation - exchangeInformation[i].offset ) % exchangeInformation[i].modulo ) == 0 ) ) ) |
---|
327 | { |
---|
328 | |
---|
329 | // send the individuals!!!! |
---|
330 | |
---|
331 | // for each of the islands where we have to send individuals |
---|
332 | for( int x = 0 ; x < exchangeInformation[i].numDest ; x++ ) |
---|
333 | { |
---|
334 | |
---|
335 | if (chatty) state.output.message( "Sending the emigrants from subpopulation " + |
---|
336 | i + " to subpopulation " + |
---|
337 | exchangeInformation[i].destinations[x] ); |
---|
338 | |
---|
339 | // select "size" individuals and send then to the destination as emigrants |
---|
340 | exchangeInformation[i].immigrantsSelectionMethod.prepareToProduce( state, i, 0 ); |
---|
341 | for( int y = 0 ; y < exchangeInformation[i].size ; y++ ) // send all necesary individuals |
---|
342 | { |
---|
343 | // get the index of the immigrant |
---|
344 | int index = exchangeInformation[i].immigrantsSelectionMethod.produce( i, state, 0 ); |
---|
345 | // copy the individual to the mailbox of the destination subpopulation |
---|
346 | immigrants[ exchangeInformation[i].destinations[x] ] |
---|
347 | [ nImmigrants[ exchangeInformation[i].destinations[x] ] ] = |
---|
348 | state.population.subpops[ i ].individuals[ index ]; |
---|
349 | // increment the counter with the number of individuals in the mailbox |
---|
350 | nImmigrants[ exchangeInformation[i].destinations[x] ]++; |
---|
351 | } |
---|
352 | exchangeInformation[i].immigrantsSelectionMethod.finishProducing( state, i, 0 ); // end the selection step |
---|
353 | } |
---|
354 | } |
---|
355 | } |
---|
356 | |
---|
357 | return state.population; |
---|
358 | |
---|
359 | } |
---|
360 | |
---|
361 | |
---|
362 | public Population postBreedingExchangePopulation(EvolutionState state) |
---|
363 | { |
---|
364 | // receiving individuals from other islands |
---|
365 | // same situation here of course. |
---|
366 | |
---|
367 | for( int x = 0 ; x < nImmigrants.length ; x++ ) |
---|
368 | { |
---|
369 | |
---|
370 | if( nImmigrants[x] > 0 && chatty ) |
---|
371 | { |
---|
372 | state.output.message( "Immigrating " + nImmigrants[x] + |
---|
373 | " individuals from mailbox for subpopulation " + x ); |
---|
374 | } |
---|
375 | |
---|
376 | int len = state.population.subpops[x].individuals.length; |
---|
377 | // double check that we won't go into an infinite loop! |
---|
378 | if ( nImmigrants[x] >= state.population.subpops[x].individuals.length ) |
---|
379 | state.output.fatal("Number of immigrants ("+nImmigrants[x] + |
---|
380 | ") is larger than subpopulation #" + x + "'s size (" + |
---|
381 | len +"). This would cause an infinite loop in the selection-to-die procedure."); |
---|
382 | |
---|
383 | boolean[] selected = new boolean[ len ]; |
---|
384 | int[] indeces = new int[ nImmigrants[x] ]; |
---|
385 | for( int i = 0 ; i < selected.length ; i++ ) |
---|
386 | selected[i] = false; |
---|
387 | exchangeInformation[x].indsToDieSelectionMethod.prepareToProduce( state, x, 0 ); |
---|
388 | for( int i = 0 ; i < nImmigrants[x] ; i++ ) |
---|
389 | { |
---|
390 | do { |
---|
391 | indeces[i] = exchangeInformation[x].indsToDieSelectionMethod.produce( x, state, 0 ); |
---|
392 | } while( selected[indeces[i]] ); |
---|
393 | selected[indeces[i]] = true; |
---|
394 | } |
---|
395 | exchangeInformation[x].indsToDieSelectionMethod.finishProducing( state, x, 0 ); |
---|
396 | |
---|
397 | for( int y = 0 ; y < nImmigrants[x] ; y++ ) |
---|
398 | { |
---|
399 | |
---|
400 | // read the individual |
---|
401 | state.population.subpops[x].individuals[ indeces[y] ] = immigrants[x][y]; |
---|
402 | |
---|
403 | // reset the evaluated flag (the individuals are not evaluated in the current island */ |
---|
404 | state.population.subpops[x].individuals[ indeces[y] ].evaluated = false; |
---|
405 | |
---|
406 | } |
---|
407 | |
---|
408 | // reset the number of immigrants in the mailbox for the current subpopulation |
---|
409 | // this doesn't need another synchronization, because the thread is already synchronized |
---|
410 | nImmigrants[x] = 0; |
---|
411 | } |
---|
412 | |
---|
413 | return state.population; |
---|
414 | |
---|
415 | } |
---|
416 | |
---|
417 | /** Called after preBreedingExchangePopulation(...) to evaluate whether or not |
---|
418 | the exchanger wishes the run to shut down (with ec.EvolutionState.R_FAILURE). |
---|
419 | This would happen for two reasons. First, another process might have found |
---|
420 | an ideal individual and the global run is now over. Second, some network |
---|
421 | or operating system error may have occurred and the system needs to be shut |
---|
422 | down gracefully. |
---|
423 | This function does not return a String as soon as it wants to exit (another island found |
---|
424 | the perfect individual, or couldn't connect to the server). Instead, it sets a flag, called |
---|
425 | message, to remember next time to exit. This is due to a need for a graceful |
---|
426 | shutdown, where checkpoints are working properly and save all needed information. */ |
---|
427 | public String runComplete(EvolutionState state) |
---|
428 | { |
---|
429 | return null; |
---|
430 | } |
---|
431 | |
---|
432 | /** Closes contacts with other processes, if that's what you're doing. Called at the end of an evolutionary run. result is either ec.EvolutionState.R_SUCCESS or ec.EvolutionState.R_FAILURE, indicating whether or not an ideal individual was found. */ |
---|
433 | public void closeContacts(EvolutionState state, int result) |
---|
434 | { |
---|
435 | } |
---|
436 | |
---|
437 | } |
---|