[6152] | 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 | }
|
---|