1 | /*
|
---|
2 | Copyright 2006 by Sean Luke
|
---|
3 | Licensed under the Academic Free License version 3.0
|
---|
4 | See the file "LICENSE" for more information
|
---|
5 | */
|
---|
6 |
|
---|
7 |
|
---|
8 | package ec;
|
---|
9 | import ec.util.*;
|
---|
10 | import java.io.File;
|
---|
11 | import java.io.PrintWriter;
|
---|
12 |
|
---|
13 | /*
|
---|
14 | * Evolve.java
|
---|
15 | *
|
---|
16 | * Created: Wed Aug 11 17:49:01 1999
|
---|
17 | * By: Sean Luke
|
---|
18 | */
|
---|
19 |
|
---|
20 | /**
|
---|
21 | * Evolve is the main entry class for an evolutionary computation run.
|
---|
22 | *
|
---|
23 | * <p> An EC run is done with one of two argument formats:
|
---|
24 | *
|
---|
25 | * <p><tt>java ec.Evolve -file </tt><i>parameter_file [</i><tt>-p </tt><i>parameter=value]*</i>
|
---|
26 | *
|
---|
27 | * <p>This starts a new evolutionary run, using the parameter file <i>parameter_file</i>.
|
---|
28 | * The user can provide optional overriding parameters on the command-line with the <tt>-p</tt> option.
|
---|
29 | *
|
---|
30 | * <p><tt>java ec.Evolve -checkpoint </tt><i>checkpoint_file</i>
|
---|
31 | *
|
---|
32 | * <p>This starts up an evolutionary run from a previous checkpoint file.
|
---|
33 | *
|
---|
34 | * <p>The basic Evolve class has a main() loop with a simple job iteration facility.
|
---|
35 | * If you'd like to run the evolutionary system four times, each with a different random
|
---|
36 | * seed, you might do:
|
---|
37 | *
|
---|
38 | * <p><tt>java ec.Evolve -file </tt><i>parameter_file</i> <tt>-p jobs=4</tt>
|
---|
39 | *
|
---|
40 | * <p>Here, Evolve will run the first time with the random seed equal to whatever's specified
|
---|
41 | * in your file, then job#2 will be run with the seed + 1, job#3 with the seed + 2,
|
---|
42 | * and job#4 with the seed + 3. If you have multiple seeds, ECJ will try to make sure they're
|
---|
43 | * all different even across jobs by adding the job number * numberOfSeeds to each of them.
|
---|
44 | * This means that if you're doing multiple jobs with multiple seeds, you should probably set
|
---|
45 | * seed.0 to x, seed.1 to x+1, seed.2 to x+2, etc. for best results. It also works if seed.0
|
---|
46 | * is x, seed.1 is y (a number much bigger than x), seed.2 is z (a number much bigger than y) etc.
|
---|
47 | *
|
---|
48 | * If you set seed.0=time etc. for multiple jobs, the values of each seed will be set to the
|
---|
49 | * current time that the job starts plus the job number * numberOfSeeds. As current time always
|
---|
50 | * goes up, this shouldn't be an issue. However it's theoretically possible that if you checkpoint and restart
|
---|
51 | * on another system with a clock set back in time, you could get the same seed in a later job.
|
---|
52 | *
|
---|
53 | * <p><b>main() has been designed to be modified.</b> The comments for the Evolve.java file contain
|
---|
54 | * a lot discussion of how ECJ's main() bootstraps the EvolutionState object and runs it, plus a much
|
---|
55 | * simpler example of main() and explanations for how main() works.
|
---|
56 | *
|
---|
57 |
|
---|
58 | <p><b>Parameters</b><br>
|
---|
59 | <table>
|
---|
60 |
|
---|
61 | <tr><td valign=top><tt>jobs</tt></br>
|
---|
62 | <font size=-1> int >= 1 (default)</font></td>
|
---|
63 | <td valign=top>(The number of jobs to iterate. The current job number (0...jobs-1) will be added to each seed UNLESS the seed is loaded from the system time. The job number also gets added as a prefix (if the number of jobs is more than 1)).</td></tr>
|
---|
64 |
|
---|
65 | <tr><td valign=top><tt>nostore</tt><br>
|
---|
66 | <font size=-1> bool = <tt>true</tt> or <tt>false</tt> (default)</font></td>
|
---|
67 | <td valign=top>(should the ec.util.Output facility <i>not</i> store announcements in memory?)</td></tr>
|
---|
68 |
|
---|
69 | <tr><td valign=top><tt>flush</tt><br>
|
---|
70 | <font size=-1> bool = <tt>true</tt> or <tt>false</tt> (default)</font></td>
|
---|
71 | <td valign=top>(should I flush all output as soon as it's printed (useful for debugging when an exception occurs))</td></tr>
|
---|
72 |
|
---|
73 | <tr><td valign=top><tt>evalthreads</tt><br>
|
---|
74 | <font size=-1>int >= 1</font></td>
|
---|
75 | <td valign=top>(the number of threads to spawn for evaluation)</td></tr>
|
---|
76 |
|
---|
77 | <tr><td valign=top><tt>breedthreads</tt><br>
|
---|
78 | <font size=-1>int >= 1</font></td>
|
---|
79 | <td valign=top>(the number of threads to spawn for breeding)</td></tr>
|
---|
80 |
|
---|
81 | <tr><td valign=top><tt>seed.</tt><i>n</i><br>
|
---|
82 | <font size=-1>int != 0, or string = <tt>time</tt></font></td>
|
---|
83 | <td valign=top>(the seed for random number generator #<i>n</i>. <i>n</i> should range from 0 to Max(evalthreads,breedthreads)-1. If value is <tt>time</tt>, then the seed is based on the system clock plus <i>n</i>.)</td></tr>
|
---|
84 |
|
---|
85 | <tr><td valign=top><tt>state</tt><br>
|
---|
86 | <font size=-1>classname, inherits and != ec.EvolutionState</font></td>
|
---|
87 | <td valign=top>(the EvolutionState object class)</td></tr>
|
---|
88 |
|
---|
89 | <tr><td valign=top><tt>print-accessed-params</tt><br>
|
---|
90 | <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</td>
|
---|
91 | <td valign=top>(at the end of a run, do we print out a list of all the parameters requested during the run?)</td></tr>
|
---|
92 |
|
---|
93 | <tr><td valign=top><tt>print-used-params</tt><br>
|
---|
94 | <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</td>
|
---|
95 | <td valign=top>(at the end of a run, do we print out a list of all the parameters actually <i>used</i> during the run?)</td></tr>
|
---|
96 |
|
---|
97 | <tr><td valign=top><tt>print-unaccessed-params</tt><br>
|
---|
98 | <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</td>
|
---|
99 | <td valign=top>(at the end of a run, do we print out a list of all the parameters NOT requested during the run?)</td></tr>
|
---|
100 |
|
---|
101 | <tr><td valign=top><tt>print-unused-params</tt><br>
|
---|
102 | <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</td>
|
---|
103 | <td valign=top>(at the end of a run, do we print out a list of all the parameters NOT actually used during the run?)</td></tr>
|
---|
104 |
|
---|
105 | <tr><td valign=top><tt>print-all-params</tt><br>
|
---|
106 | <font size=-1>bool = <tt>true</tt> or <tt>false</tt> (default)</td>
|
---|
107 | <td valign=top>(at the end of a run, do we print out a list of all the parameters stored in the parameter database?)</td></tr>
|
---|
108 |
|
---|
109 | </table>
|
---|
110 | *
|
---|
111 | *
|
---|
112 | * @author Sean Luke
|
---|
113 | * @version 1.0
|
---|
114 | */
|
---|
115 |
|
---|
116 | public class Evolve
|
---|
117 | {
|
---|
118 | public final static String P_PRINTACCESSEDPARAMETERS = "print-accessed-params";
|
---|
119 | public final static String P_PRINTUSEDPARAMETERS = "print-used-params";
|
---|
120 | public final static String P_PRINTALLPARAMETERS = "print-all-params";
|
---|
121 | public final static String P_PRINTUNUSEDPARAMETERS = "print-unused-params";
|
---|
122 | public final static String P_PRINTUNACCESSEDPARAMETERS = "print-unaccessed-params";
|
---|
123 |
|
---|
124 | /** The argument indicating that we're starting up from a checkpoint file. */
|
---|
125 | public static final String A_CHECKPOINT = "-checkpoint";
|
---|
126 |
|
---|
127 | /** The argument indicating that we're starting fresh from a new parameter file. */
|
---|
128 | public static final String A_FILE = "-file";
|
---|
129 |
|
---|
130 | /** evalthreads parameter */
|
---|
131 | public static final String P_EVALTHREADS = "evalthreads";
|
---|
132 |
|
---|
133 | /** breedthreads parameter */
|
---|
134 | public static final String P_BREEDTHREADS = "breedthreads";
|
---|
135 |
|
---|
136 | /** seed parameter */
|
---|
137 | public static final String P_SEED = "seed";
|
---|
138 |
|
---|
139 | /** 'time' seed parameter value */
|
---|
140 | public static final String V_SEED_TIME = "time";
|
---|
141 |
|
---|
142 | /** state parameter */
|
---|
143 | public static final String P_STATE = "state";
|
---|
144 |
|
---|
145 | /** 'auto' thread parameter value */
|
---|
146 | public static final String V_THREADS_AUTO = "auto";
|
---|
147 |
|
---|
148 |
|
---|
149 | /** Restores an EvolutionState from checkpoint if "-checkpoint FILENAME" is in the command-line arguments. */
|
---|
150 | public static EvolutionState possiblyRestoreFromCheckpoint(String[] args)
|
---|
151 | {
|
---|
152 | for(int x=0;x<args.length-1;x++)
|
---|
153 | if (args[x].equals(A_CHECKPOINT))
|
---|
154 | {
|
---|
155 | System.err.println("Restoring from Checkpoint " + args[x+1]);
|
---|
156 | try
|
---|
157 | {
|
---|
158 | return Checkpoint.restoreFromCheckpoint(args[x+1]);
|
---|
159 | }
|
---|
160 | catch(Exception e)
|
---|
161 | {
|
---|
162 | Output.initialError("An exception was generated upon starting up from a checkpoint.\nHere it is:\n" + e);
|
---|
163 | }
|
---|
164 | }
|
---|
165 | return null; // should never happen
|
---|
166 | }
|
---|
167 |
|
---|
168 | /** Loads a ParameterDatabase from checkpoint if "-params" is in the command-line arguments. */
|
---|
169 | public static ParameterDatabase loadParameterDatabase(String[] args)
|
---|
170 | {
|
---|
171 | ParameterDatabase parameters = null;
|
---|
172 | for(int x=0;x<args.length-1;x++)
|
---|
173 | if (args[x].equals(A_FILE))
|
---|
174 | try
|
---|
175 | {
|
---|
176 | parameters = new ParameterDatabase(
|
---|
177 | new File(new File(args[x+1]).getAbsolutePath()),
|
---|
178 | args);
|
---|
179 | break;
|
---|
180 | }
|
---|
181 | catch(Exception e)
|
---|
182 | {
|
---|
183 | Output.initialError(
|
---|
184 | "An exception was generated upon reading the parameter file \"" +
|
---|
185 | args[x+1] + "\".\nHere it is:\n" + e);
|
---|
186 | }
|
---|
187 | if (parameters==null)
|
---|
188 | Output.initialError("No parameter file was specified." );
|
---|
189 | return parameters;
|
---|
190 | }
|
---|
191 |
|
---|
192 |
|
---|
193 | /** Loads the number of threads. */
|
---|
194 | public static int determineThreads(Output output, ParameterDatabase parameters, Parameter threadParameter)
|
---|
195 | {
|
---|
196 | int thread = 1;
|
---|
197 | String tmp_s = parameters.getString(threadParameter,null);
|
---|
198 | if (tmp_s==null) // uh oh
|
---|
199 | {
|
---|
200 | output.fatal("Threads number must exist.",threadParameter,null);
|
---|
201 | }
|
---|
202 | else if (V_THREADS_AUTO.equalsIgnoreCase(tmp_s))
|
---|
203 | {
|
---|
204 | Runtime runtime = Runtime.getRuntime();
|
---|
205 | try { return ((Integer)runtime.getClass().getMethod("availableProcessors", (Class[])null).
|
---|
206 | invoke(runtime,(Object[])null)).intValue(); }
|
---|
207 | catch (Exception e)
|
---|
208 | {
|
---|
209 | output.fatal("Whoa! This Java version is too old to have the Runtime.availableProcessors() method available.\n" +
|
---|
210 | "This means you can't use 'auto' as a threads option.",threadParameter,null);
|
---|
211 | }
|
---|
212 | }
|
---|
213 | else
|
---|
214 | {
|
---|
215 | try
|
---|
216 | {
|
---|
217 | thread = parameters.getInt(threadParameter,null);
|
---|
218 | }
|
---|
219 | catch (NumberFormatException e)
|
---|
220 | {
|
---|
221 | output.fatal("Invalid, non-integer threads value ("+thread+")",threadParameter,null);
|
---|
222 | }
|
---|
223 | }
|
---|
224 | return thread;
|
---|
225 | }
|
---|
226 |
|
---|
227 | /** Primes the generator. Mersenne Twister seeds its first 624 numbers using a basic
|
---|
228 | linear congruential generator; thereafter it uses the MersenneTwister algorithm to
|
---|
229 | build new seeds. Those first 624 numbers are generally just fine, but to be extra
|
---|
230 | safe, you can prime the generator by calling nextInt() on it some (N>1) * 624 times.
|
---|
231 | This method does exactly that, presently with N=2. */
|
---|
232 | public static MersenneTwisterFast primeGenerator(MersenneTwisterFast generator)
|
---|
233 | {
|
---|
234 | for(int i = 0; i < 624 * 2; i++)
|
---|
235 | generator.nextInt();
|
---|
236 | return generator;
|
---|
237 | }
|
---|
238 |
|
---|
239 | /** Loads a random generator seed. First, the seed is loaded from the seedParameter. If the parameter
|
---|
240 | is V_SEED_TIME, the seed is set to the currentTime value. Then the seed is incremented by the offset.
|
---|
241 | This method is broken out of initialize(...) primarily to share code with ec.eval.MasterProblem.*/
|
---|
242 | public static int determineSeed(Output output, ParameterDatabase parameters, Parameter seedParameter, long currentTime, int offset, boolean auto)
|
---|
243 | {
|
---|
244 | int seed = 1; // have to initialize to make the compiler happy
|
---|
245 | String tmp_s = parameters.getString(seedParameter,null);
|
---|
246 | if (tmp_s==null && !auto) // uh oh
|
---|
247 | {
|
---|
248 | output.fatal("Seed must exist.",seedParameter,null);
|
---|
249 | }
|
---|
250 | else if (V_SEED_TIME.equalsIgnoreCase(tmp_s) || (tmp_s == null && auto))
|
---|
251 | {
|
---|
252 | if (tmp_s == null && auto)
|
---|
253 | output.warnOnce("Using automatic determination number of threads, but not all seeds are defined.\nThe rest will be defined using the wall clock time.");
|
---|
254 | seed = (int)currentTime; // using low-order bits so it's probably okay
|
---|
255 | if (seed==0)
|
---|
256 | output.fatal("Whoa! This Java version is returning 0 for System.currentTimeMillis(), which ain't right. This means you can't use '"+V_SEED_TIME+"' as a seed ",seedParameter,null);
|
---|
257 | }
|
---|
258 | else
|
---|
259 | {
|
---|
260 | try
|
---|
261 | {
|
---|
262 | seed = parameters.getInt(seedParameter,null);
|
---|
263 | }
|
---|
264 | catch (NumberFormatException e)
|
---|
265 | {
|
---|
266 | output.fatal("Invalid, non-integer seed value ("+seed+")",seedParameter,null);
|
---|
267 | }
|
---|
268 | }
|
---|
269 | return seed + offset;
|
---|
270 | }
|
---|
271 |
|
---|
272 |
|
---|
273 | /** Initializes an evolutionary run given the parameters and a random seed adjustment (added to each random seed).
|
---|
274 | The adjustment offers a convenient way to change the seeds of the random number generators each time you
|
---|
275 | do a new evolutionary run. You are of course welcome to replace the random number generators after initialize(...)
|
---|
276 | but before startFresh(...) */
|
---|
277 |
|
---|
278 | public static EvolutionState initialize(ParameterDatabase parameters, int randomSeedOffset)
|
---|
279 | {
|
---|
280 | Output output;
|
---|
281 | // 1. create the output
|
---|
282 |
|
---|
283 | output = new Output(true);
|
---|
284 |
|
---|
285 | // stdout is always log #0. stderr is always log #1.
|
---|
286 | // stderr accepts announcements, and both are fully verbose
|
---|
287 | // by default.
|
---|
288 | output.addLog(ec.util.Log.D_STDOUT,false);
|
---|
289 | output.addLog(ec.util.Log.D_STDERR,true);
|
---|
290 |
|
---|
291 | // now continue intialization
|
---|
292 | return initialize(parameters, randomSeedOffset, output);
|
---|
293 | }
|
---|
294 |
|
---|
295 |
|
---|
296 | /** Initializes an evolutionary run given the parameters and a random seed adjustment (added to each random seed),
|
---|
297 | with the Output pre-constructed.
|
---|
298 | The adjustment offers a convenient way to change the seeds of the random number generators each time you
|
---|
299 | do a new evolutionary run. You are of course welcome to replace the random number generators after initialize(...)
|
---|
300 | but before startFresh(...) */
|
---|
301 |
|
---|
302 | public static EvolutionState initialize(ParameterDatabase parameters, int randomSeedOffset, Output output)
|
---|
303 | {
|
---|
304 | EvolutionState state=null;
|
---|
305 | MersenneTwisterFast[] random;
|
---|
306 | int[] seeds;
|
---|
307 | int breedthreads = 1;
|
---|
308 | int evalthreads = 1;
|
---|
309 | boolean store;
|
---|
310 | int x;
|
---|
311 |
|
---|
312 | // output was already created for us.
|
---|
313 | output.systemMessage(Version.message());
|
---|
314 |
|
---|
315 | // 2. set up thread values
|
---|
316 |
|
---|
317 | breedthreads = Evolve.determineThreads(output, parameters, new Parameter(P_BREEDTHREADS));
|
---|
318 | evalthreads = Evolve.determineThreads(output, parameters, new Parameter(P_EVALTHREADS));
|
---|
319 | boolean auto = (V_THREADS_AUTO.equalsIgnoreCase(parameters.getString(new Parameter(P_BREEDTHREADS),null)) ||
|
---|
320 | V_THREADS_AUTO.equalsIgnoreCase(parameters.getString(new Parameter(P_EVALTHREADS),null))); // at least one thread is automatic. Seeds may need to be dynamic.
|
---|
321 |
|
---|
322 | // 3. create the Mersenne Twister random number generators,
|
---|
323 | // one per thread
|
---|
324 |
|
---|
325 | random = new MersenneTwisterFast[breedthreads > evalthreads ?
|
---|
326 | breedthreads : evalthreads];
|
---|
327 | seeds = new int[random.length];
|
---|
328 |
|
---|
329 | String seedMessage = "Seed: ";
|
---|
330 | int time = (int)(System.currentTimeMillis());
|
---|
331 | for (x=0;x<random.length;x++)
|
---|
332 | {
|
---|
333 | seeds[x] = determineSeed(output,parameters,new Parameter(P_SEED).push(""+x),
|
---|
334 | time+x,random.length * randomSeedOffset, auto);
|
---|
335 | for (int y=0;y<x;y++)
|
---|
336 | if (seeds[x]==seeds[y])
|
---|
337 | output.fatal(P_SEED+"."+x+" ("+seeds[x]+") and "+P_SEED+"."+y+" ("+seeds[y]+") ought not be the same seed.",null,null);
|
---|
338 | random[x] = Evolve.primeGenerator(new MersenneTwisterFast(seeds[x])); // we prime the generator to be more sure of randomness.
|
---|
339 | seedMessage = seedMessage + seeds[x] + " ";
|
---|
340 | }
|
---|
341 |
|
---|
342 | // 4. Start up the evolution
|
---|
343 |
|
---|
344 | // what evolution state to use?
|
---|
345 | state = (EvolutionState)
|
---|
346 | parameters.getInstanceForParameter(new Parameter(P_STATE),null,
|
---|
347 | EvolutionState.class);
|
---|
348 | state.parameters = parameters;
|
---|
349 | state.random = random;
|
---|
350 | state.output = output;
|
---|
351 | state.evalthreads = evalthreads;
|
---|
352 | state.breedthreads = breedthreads;
|
---|
353 | state.randomSeedOffset = randomSeedOffset;
|
---|
354 |
|
---|
355 | output.systemMessage("Threads: breed/" + breedthreads + " eval/" + evalthreads);
|
---|
356 | output.systemMessage(seedMessage);
|
---|
357 |
|
---|
358 | return state;
|
---|
359 | }
|
---|
360 |
|
---|
361 |
|
---|
362 | /** Begins a fresh evolutionary run with a given state. The state should have been
|
---|
363 | provided by initialize(...). The jobPrefix is added to the front of output and
|
---|
364 | checkpoint filenames. If it's null, nothing is added to the front. */
|
---|
365 |
|
---|
366 | public static void cleanup(EvolutionState state)
|
---|
367 | {
|
---|
368 | // flush the output
|
---|
369 | state.output.flush();
|
---|
370 |
|
---|
371 | // Possibly print out the run parameters
|
---|
372 | PrintWriter pw = new PrintWriter(System.err);
|
---|
373 |
|
---|
374 | // before we print out access information, we need to still "get" these
|
---|
375 | // parameters, so that they show up as accessed and gotten.
|
---|
376 | state.parameters.getBoolean(new Parameter(P_PRINTUSEDPARAMETERS),null,false);
|
---|
377 | state.parameters.getBoolean(new Parameter(P_PRINTACCESSEDPARAMETERS),null,false);
|
---|
378 | state.parameters.getBoolean(new Parameter(P_PRINTUNUSEDPARAMETERS),null,false);
|
---|
379 | state.parameters.getBoolean(new Parameter(P_PRINTUNACCESSEDPARAMETERS),null,false);
|
---|
380 | state.parameters.getBoolean(new Parameter(P_PRINTALLPARAMETERS),null,false);
|
---|
381 |
|
---|
382 | //...okay, here we go...
|
---|
383 |
|
---|
384 | if (state.parameters.getBoolean(new Parameter(P_PRINTUSEDPARAMETERS),null,false))
|
---|
385 | {
|
---|
386 | pw.println("\n\nUsed Parameters\n===============\n");
|
---|
387 | state.parameters.listGotten(pw);
|
---|
388 | }
|
---|
389 |
|
---|
390 | if (state.parameters.getBoolean(new Parameter(P_PRINTACCESSEDPARAMETERS),null,false))
|
---|
391 | {
|
---|
392 | pw.println("\n\nAccessed Parameters\n===================\n");
|
---|
393 | state.parameters.listAccessed(pw);
|
---|
394 | }
|
---|
395 |
|
---|
396 | if (state.parameters.getBoolean(new Parameter(P_PRINTUNUSEDPARAMETERS),null,false))
|
---|
397 | {
|
---|
398 | pw.println("\n\nUnused Parameters\n"+
|
---|
399 | "================= (Ignore parent.x references) \n");
|
---|
400 | state.parameters.listNotGotten(pw);
|
---|
401 | }
|
---|
402 |
|
---|
403 | if (state.parameters.getBoolean(new Parameter(P_PRINTUNACCESSEDPARAMETERS),null,false))
|
---|
404 | {
|
---|
405 | pw.println("\n\nUnaccessed Parameters\n"+
|
---|
406 | "===================== (Ignore parent.x references) \n");
|
---|
407 | state.parameters.listNotAccessed(pw);
|
---|
408 | }
|
---|
409 |
|
---|
410 | if (state.parameters.getBoolean(new Parameter(P_PRINTALLPARAMETERS),null,false))
|
---|
411 | {
|
---|
412 | pw.println("\n\nAll Parameters\n==============\n");
|
---|
413 | // list only the parameters visible. Shadowed parameters not shown
|
---|
414 | state.parameters.list(pw,false);
|
---|
415 | }
|
---|
416 |
|
---|
417 | pw.flush();
|
---|
418 |
|
---|
419 | System.err.flush();
|
---|
420 | System.out.flush();
|
---|
421 |
|
---|
422 | // finish by closing down Output. This is because gzipped and other buffered
|
---|
423 | // streams just don't shut write themselves out, and finalize isn't called
|
---|
424 | // on them because Java's being obnoxious. Pretty stupid.
|
---|
425 | state.output.close();
|
---|
426 | }
|
---|
427 |
|
---|
428 |
|
---|
429 |
|
---|
430 |
|
---|
431 | /*
|
---|
432 |
|
---|
433 | * MAIN
|
---|
434 | *
|
---|
435 | * Evolve has... evolved from previous Evolves. The goal behind these changes is:
|
---|
436 | * 1. To provide a simple jobs facility
|
---|
437 | * 2. To make it easy for you to make your own main(), including more
|
---|
438 | * sophisticated jobs facilities.
|
---|
439 | *
|
---|
440 | * Before we get into the specifics of this file, let's first look at the main
|
---|
441 | * evolution loop in EvolutionState.java. The general code is:
|
---|
442 | * 1. If I was loaded from a checkpoint, call the hook startFromCheckpoint()
|
---|
443 | * 2. If I'm instead starting from scratch, call the hook startFresh()
|
---|
444 | * 3. Loop:
|
---|
445 | * 4. result = evolve()
|
---|
446 | * 5. If result != EvolutionState.R_NOTDONE, break from loop
|
---|
447 | * 6. Call the hook finish(result)
|
---|
448 | *
|
---|
449 | * That's all there's to it. Various EvolutionState classes need to implement
|
---|
450 | * the startFromCheckpoint, startFresh, evolve, and finish methods. This basic
|
---|
451 | * evolution loop is encapsulated in a convenience method called EvolutionState.run(...).
|
---|
452 | *
|
---|
453 | * Evolve.java is little more than code to fire up the right EvolutionState class,
|
---|
454 | * call run(...), and then shut down. The complexity mostly comes from bringing
|
---|
455 | * up the class (loading it from checkpoint or from scratch) and in shutting down.
|
---|
456 | * Here's the general mechanism:
|
---|
457 | *
|
---|
458 | * - To load from checkpoint, we must find the checkpoint filename and call
|
---|
459 | * Checkpoint.restoreFromCheckpoint(filename) to generate the EvolutionState
|
---|
460 | * instance. Evolve.java provides a convenience function for this called
|
---|
461 | * possiblyRestoreFromCheckpoint(...), which returns null if there *isn't*
|
---|
462 | * a checkpoint file to load from. Else it returns the unfrozen EvolutionState.
|
---|
463 | *
|
---|
464 | * - To instead set up from scratch, you have to do a bunch of stuff to set up the state.
|
---|
465 | * First, you need to load a parameter database. Evolve.java has a convenience function
|
---|
466 | * for that called loadParameterDatabase(...). Second, you must do a series
|
---|
467 | * of items: (1) generate an Output object (2) identify the number of threads
|
---|
468 | * (3) create the MersenneTwisterFast random number generators (4) instantiate
|
---|
469 | * the EvolutionState subclass instance (5) plug these items, plus the random
|
---|
470 | * seed offset and the parameter database, into the instance. These five
|
---|
471 | * steps are done for you in a convenience function called initialize(...).
|
---|
472 | *
|
---|
473 | * - Now the state is ready to go. Call run(...) on your EvolutionState
|
---|
474 | * (or do the evolution loop described above manually if you wish)
|
---|
475 | *
|
---|
476 | * - Finally, to shut down, you need to (1) flush the Output (2) print out
|
---|
477 | * the used, accessed, unused, unaccessed, and all parameters if the user
|
---|
478 | * requested a printout at the end [rarely] (3) flush System.err and System.out
|
---|
479 | * for good measure, and (4) close Output -- which closes its streams except
|
---|
480 | * for System.err and System.out. There is a convenience function for this as
|
---|
481 | * well. It's called cleanup(...).
|
---|
482 | *
|
---|
483 | * - Last, you shut down with System.exit(0) -- very important because it quits
|
---|
484 | * any remaining threads the user might have had running and forgot about.
|
---|
485 | *
|
---|
486 | * So there you have it. Several convenience functions in Evolve...
|
---|
487 | * Evolve.possiblyRestoreFromCheckpoint
|
---|
488 | * Evolve.loadParameterDatabase
|
---|
489 | * Evolve.initialize
|
---|
490 | * EvolutionState.run
|
---|
491 | * Evolve.cleanup
|
---|
492 | * ... result in a very simple basic main() function:
|
---|
493 | *
|
---|
494 | *
|
---|
495 | * public static void main(String[] args)
|
---|
496 | * {
|
---|
497 | * EvolutionState state = possiblyRestoreFromCheckpoint(args);
|
---|
498 | * if (state!=null) // loaded from checkpoint
|
---|
499 | * state.run(EvolutionState.C_STARTED_FROM_CHECKPOINT);
|
---|
500 | * else
|
---|
501 | * {
|
---|
502 | * state = initialize(loadParameterDatabase(args), 0);
|
---|
503 | * state.run(EvolutionState.C_STARTED_FRESH);
|
---|
504 | * }
|
---|
505 | * cleanup(state);
|
---|
506 | * System.exit(0);
|
---|
507 | * }
|
---|
508 | *
|
---|
509 | *
|
---|
510 | * Piece of cake!
|
---|
511 | *
|
---|
512 | * The more extravagant main(...) you see below just has a few extra gizmos for
|
---|
513 | * doing basic job iteration. EvolutionState has two convenience slots for
|
---|
514 | * doing job iteration:
|
---|
515 | *
|
---|
516 | * job (an Object[] use this as you like)
|
---|
517 | * runtimeArguments (a String[] put args in here)
|
---|
518 | *
|
---|
519 | * The reason these are slots in EvolutionState is so you can store this information
|
---|
520 | * across checkpoints and continue where you had started job-number-wise when the
|
---|
521 | * user starts up from a checkpoint again.
|
---|
522 | *
|
---|
523 | * You'll probably want the EvolutionState to output its stat files etc. using unique
|
---|
524 | * prefixes to differentiate between jobs (0.stat, 1.stat, or whatever you like -- it
|
---|
525 | * doesn't have to be numbers), and you'll also probably want checkpoint files to be
|
---|
526 | * similarly prefixed. So you'll probably want to do:
|
---|
527 | *
|
---|
528 | * state.output.setFilePrefix(jobPrefix);
|
---|
529 | * state.checkpointPrefix = jobPrefix + state.checkpointPrefix;
|
---|
530 | *
|
---|
531 | * The extravagant main below is basically doing this. We're using state.job to stash
|
---|
532 | * away a single iterated job number, stored as an Integer in state.job[0], and then
|
---|
533 | * iterating that way, making sure we stash the job number and runtime arguments each time
|
---|
534 | * so we can recover them when loading from checkpoint. We use the "jobs" parameter
|
---|
535 | * to determine how many jobs to run. If this number is 1, we don't even bother to set
|
---|
536 | * the file prefixes, so ECJ generates files just like it used to.
|
---|
537 | *
|
---|
538 | * It's important to note that this main was created with the assumption that you might
|
---|
539 | * modify it for your own purposes. Do you want a nested loop, perhaps to do all combinations
|
---|
540 | * of two parameters or something? Rewrite it to use two array slots in the job array.
|
---|
541 | * Want to store more information on a per-job basis? Feel free to use the job array any
|
---|
542 | * way you like -- it's ONLY used by this main() loop.
|
---|
543 | *
|
---|
544 | */
|
---|
545 |
|
---|
546 |
|
---|
547 |
|
---|
548 |
|
---|
549 |
|
---|
550 |
|
---|
551 | /** Top-level evolutionary loop. */
|
---|
552 |
|
---|
553 | public static void main(String[] args)
|
---|
554 | {
|
---|
555 | EvolutionState state;
|
---|
556 | ParameterDatabase parameters;
|
---|
557 |
|
---|
558 | // if we're loading from checkpoint, let's finish out the most recent job
|
---|
559 | state = possiblyRestoreFromCheckpoint(args);
|
---|
560 | int currentJob = 0; // the next job number (0 by default)
|
---|
561 |
|
---|
562 | // this simple job iterator just uses the 'jobs' parameter, iterating from 0 to 'jobs' - 1
|
---|
563 | // inclusive. The current job number is stored in state.jobs[0], so we'll begin there if
|
---|
564 | // we had loaded from checkpoint.
|
---|
565 |
|
---|
566 | if (state != null) // loaded from checkpoint
|
---|
567 | {
|
---|
568 | // extract the next job number from state.job[0] (where in this example we'll stash it)
|
---|
569 | try
|
---|
570 | {
|
---|
571 | if (state.runtimeArguments == null)
|
---|
572 | Output.initialError("Checkpoint completed from job started by foreign program (probably GUI). Exiting...");
|
---|
573 | args = state.runtimeArguments; // restore runtime arguments from checkpoint
|
---|
574 | currentJob = ((Integer)(state.job[0])).intValue() + 1; // extract next job number
|
---|
575 | }
|
---|
576 | catch (Exception e)
|
---|
577 | {
|
---|
578 | Output.initialError("EvolutionState's jobs variable is not set up properly. Exiting...");
|
---|
579 | }
|
---|
580 |
|
---|
581 | state.run(EvolutionState.C_STARTED_FROM_CHECKPOINT);
|
---|
582 | cleanup(state);
|
---|
583 | }
|
---|
584 |
|
---|
585 | // A this point we've finished out any previously-checkpointed job. If there was
|
---|
586 | // one such job, we've updated the current job number (currentJob) to the next number.
|
---|
587 | // Otherwise currentJob is 0.
|
---|
588 |
|
---|
589 | // Now we're going to load the parameter database to see if there are any more jobs.
|
---|
590 | // We could have done this using the previous parameter database, but it's no big deal.
|
---|
591 | parameters = loadParameterDatabase(args);
|
---|
592 | if (currentJob == 0) // no current job number yet
|
---|
593 | currentJob = parameters.getIntWithDefault(new Parameter("current-job"), null, 0);
|
---|
594 | if (currentJob < 0)
|
---|
595 | Output.initialError("The 'current-job' parameter must be >= 0 (or not exist, which defaults to 0)");
|
---|
596 |
|
---|
597 | int numJobs = parameters.getIntWithDefault(new Parameter("jobs"), null, 1);
|
---|
598 | if (numJobs < 1)
|
---|
599 | Output.initialError("The 'jobs' parameter must be >= 1 (or not exist, which defaults to 1)");
|
---|
600 |
|
---|
601 |
|
---|
602 | // Now we know how many jobs remain. Let's loop for that many jobs. Each time we'll
|
---|
603 | // load the parameter database scratch (except the first time where we reuse the one we
|
---|
604 | // just loaded a second ago). The reason we reload from scratch each time is that the
|
---|
605 | // experimenter is free to scribble all over the parameter database and it'd be nice to
|
---|
606 | // have everything fresh and clean. It doesn't take long to load the database anyway,
|
---|
607 | // it's usually small.
|
---|
608 | for(int job = currentJob ; job < numJobs; job++)
|
---|
609 | {
|
---|
610 | try
|
---|
611 | {
|
---|
612 | // load the parameter database (reusing the very first if it exists)
|
---|
613 | if (parameters == null)
|
---|
614 | parameters = loadParameterDatabase(args);
|
---|
615 |
|
---|
616 | // Initialize the EvolutionState, then set its job variables
|
---|
617 | state = initialize(parameters, job); // pass in job# as the seed increment
|
---|
618 | state.output.systemMessage("Job: " + job);
|
---|
619 | state.job = new Object[1]; // make the job argument storage
|
---|
620 | state.job[0] = new Integer(job); // stick the current job in our job storage
|
---|
621 | state.runtimeArguments = args; // stick the runtime arguments in our storage
|
---|
622 | if (numJobs > 1) // only if iterating (so we can be backwards-compatible),
|
---|
623 | {
|
---|
624 | String jobFilePrefix = "job." + job + ".";
|
---|
625 | state.output.setFilePrefix(jobFilePrefix); // add a prefix for checkpoint/output files
|
---|
626 | state.checkpointPrefix = jobFilePrefix + state.checkpointPrefix; // also set up checkpoint prefix
|
---|
627 | }
|
---|
628 |
|
---|
629 | // Here you can set up the EvolutionState's parameters further before it's setup(...).
|
---|
630 | // This includes replacing the random number generators, changing values in state.parameters,
|
---|
631 | // changing instance variables (except for job and runtimeArguments, please), etc.
|
---|
632 |
|
---|
633 |
|
---|
634 |
|
---|
635 |
|
---|
636 |
|
---|
637 | // now we let it go
|
---|
638 | state.run(EvolutionState.C_STARTED_FRESH);
|
---|
639 | cleanup(state); // flush and close various streams, print out parameters if necessary
|
---|
640 | parameters = null; // so we load a fresh database next time around
|
---|
641 | }
|
---|
642 | catch (Throwable e) // such as an out of memory error caused by this job
|
---|
643 | {
|
---|
644 | e.printStackTrace();
|
---|
645 | state = null;
|
---|
646 | System.gc(); // take a shot!
|
---|
647 | }
|
---|
648 | }
|
---|
649 |
|
---|
650 | System.exit(0);
|
---|
651 | }
|
---|
652 | }
|
---|