/* Copyright 2006 by Sean Luke Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package ec; import ec.util.*; import java.io.File; import java.io.PrintWriter; /* * Evolve.java * * Created: Wed Aug 11 17:49:01 1999 * By: Sean Luke */ /** * Evolve is the main entry class for an evolutionary computation run. * *

An EC run is done with one of two argument formats: * *

java ec.Evolve -file parameter_file [-p parameter=value]* * *

This starts a new evolutionary run, using the parameter file parameter_file. * The user can provide optional overriding parameters on the command-line with the -p option. * *

java ec.Evolve -checkpoint checkpoint_file * *

This starts up an evolutionary run from a previous checkpoint file. * *

The basic Evolve class has a main() loop with a simple job iteration facility. * If you'd like to run the evolutionary system four times, each with a different random * seed, you might do: * *

java ec.Evolve -file parameter_file -p jobs=4 * *

Here, Evolve will run the first time with the random seed equal to whatever's specified * in your file, then job#2 will be run with the seed + 1, job#3 with the seed + 2, * and job#4 with the seed + 3. If you have multiple seeds, ECJ will try to make sure they're * all different even across jobs by adding the job number * numberOfSeeds to each of them. * This means that if you're doing multiple jobs with multiple seeds, you should probably set * seed.0 to x, seed.1 to x+1, seed.2 to x+2, etc. for best results. It also works if seed.0 * is x, seed.1 is y (a number much bigger than x), seed.2 is z (a number much bigger than y) etc. * * If you set seed.0=time etc. for multiple jobs, the values of each seed will be set to the * current time that the job starts plus the job number * numberOfSeeds. As current time always * goes up, this shouldn't be an issue. However it's theoretically possible that if you checkpoint and restart * on another system with a clock set back in time, you could get the same seed in a later job. * *

main() has been designed to be modified. The comments for the Evolve.java file contain * a lot discussion of how ECJ's main() bootstraps the EvolutionState object and runs it, plus a much * simpler example of main() and explanations for how main() works. *

Parameters
jobs
int >= 1 (default)
(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)).
nostore
bool = true or false (default)
(should the ec.util.Output facility not store announcements in memory?)
flush
bool = true or false (default)
(should I flush all output as soon as it's printed (useful for debugging when an exception occurs))
evalthreads
int >= 1
(the number of threads to spawn for evaluation)
breedthreads
int >= 1
(the number of threads to spawn for breeding)
seed.n
int != 0, or string = time
(the seed for random number generator #n. n should range from 0 to Max(evalthreads,breedthreads)-1. If value is time, then the seed is based on the system clock plus n.)
state
classname, inherits and != ec.EvolutionState
(the EvolutionState object class)
print-accessed-params
bool = true or false (default)
(at the end of a run, do we print out a list of all the parameters requested during the run?)
print-used-params
bool = true or false (default)
(at the end of a run, do we print out a list of all the parameters actually used during the run?)
print-unaccessed-params
bool = true or false (default)
(at the end of a run, do we print out a list of all the parameters NOT requested during the run?)
print-unused-params
bool = true or false (default)
(at the end of a run, do we print out a list of all the parameters NOT actually used during the run?)
print-all-params
bool = true or false (default)
(at the end of a run, do we print out a list of all the parameters stored in the parameter database?)
* * * @author Sean Luke * @version 1.0 */ public class Evolve { public final static String P_PRINTACCESSEDPARAMETERS = "print-accessed-params"; public final static String P_PRINTUSEDPARAMETERS = "print-used-params"; public final static String P_PRINTALLPARAMETERS = "print-all-params"; public final static String P_PRINTUNUSEDPARAMETERS = "print-unused-params"; public final static String P_PRINTUNACCESSEDPARAMETERS = "print-unaccessed-params"; /** The argument indicating that we're starting up from a checkpoint file. */ public static final String A_CHECKPOINT = "-checkpoint"; /** The argument indicating that we're starting fresh from a new parameter file. */ public static final String A_FILE = "-file"; /** evalthreads parameter */ public static final String P_EVALTHREADS = "evalthreads"; /** breedthreads parameter */ public static final String P_BREEDTHREADS = "breedthreads"; /** seed parameter */ public static final String P_SEED = "seed"; /** 'time' seed parameter value */ public static final String V_SEED_TIME = "time"; /** state parameter */ public static final String P_STATE = "state"; /** 'auto' thread parameter value */ public static final String V_THREADS_AUTO = "auto"; /** Restores an EvolutionState from checkpoint if "-checkpoint FILENAME" is in the command-line arguments. */ public static EvolutionState possiblyRestoreFromCheckpoint(String[] args) { for(int x=0;x1) * 624 times. This method does exactly that, presently with N=2. */ public static MersenneTwisterFast primeGenerator(MersenneTwisterFast generator) { for(int i = 0; i < 624 * 2; i++) generator.nextInt(); return generator; } /** Loads a random generator seed. First, the seed is loaded from the seedParameter. If the parameter is V_SEED_TIME, the seed is set to the currentTime value. Then the seed is incremented by the offset. This method is broken out of initialize(...) primarily to share code with ec.eval.MasterProblem.*/ public static int determineSeed(Output output, ParameterDatabase parameters, Parameter seedParameter, long currentTime, int offset, boolean auto) { int seed = 1; // have to initialize to make the compiler happy String tmp_s = parameters.getString(seedParameter,null); if (tmp_s==null && !auto) // uh oh { output.fatal("Seed must exist.",seedParameter,null); } else if (V_SEED_TIME.equalsIgnoreCase(tmp_s) || (tmp_s == null && auto)) { if (tmp_s == null && auto) output.warnOnce("Using automatic determination number of threads, but not all seeds are defined.\nThe rest will be defined using the wall clock time."); seed = (int)currentTime; // using low-order bits so it's probably okay if (seed==0) 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); } else { try { seed = parameters.getInt(seedParameter,null); } catch (NumberFormatException e) { output.fatal("Invalid, non-integer seed value ("+seed+")",seedParameter,null); } } return seed + offset; } /** Initializes an evolutionary run given the parameters and a random seed adjustment (added to each random seed). The adjustment offers a convenient way to change the seeds of the random number generators each time you do a new evolutionary run. You are of course welcome to replace the random number generators after initialize(...) but before startFresh(...) */ public static EvolutionState initialize(ParameterDatabase parameters, int randomSeedOffset) { Output output; // 1. create the output output = new Output(true); // stdout is always log #0. stderr is always log #1. // stderr accepts announcements, and both are fully verbose // by default. output.addLog(ec.util.Log.D_STDOUT,false); output.addLog(ec.util.Log.D_STDERR,true); // now continue intialization return initialize(parameters, randomSeedOffset, output); } /** Initializes an evolutionary run given the parameters and a random seed adjustment (added to each random seed), with the Output pre-constructed. The adjustment offers a convenient way to change the seeds of the random number generators each time you do a new evolutionary run. You are of course welcome to replace the random number generators after initialize(...) but before startFresh(...) */ public static EvolutionState initialize(ParameterDatabase parameters, int randomSeedOffset, Output output) { EvolutionState state=null; MersenneTwisterFast[] random; int[] seeds; int breedthreads = 1; int evalthreads = 1; boolean store; int x; // output was already created for us. output.systemMessage(Version.message()); // 2. set up thread values breedthreads = Evolve.determineThreads(output, parameters, new Parameter(P_BREEDTHREADS)); evalthreads = Evolve.determineThreads(output, parameters, new Parameter(P_EVALTHREADS)); boolean auto = (V_THREADS_AUTO.equalsIgnoreCase(parameters.getString(new Parameter(P_BREEDTHREADS),null)) || V_THREADS_AUTO.equalsIgnoreCase(parameters.getString(new Parameter(P_EVALTHREADS),null))); // at least one thread is automatic. Seeds may need to be dynamic. // 3. create the Mersenne Twister random number generators, // one per thread random = new MersenneTwisterFast[breedthreads > evalthreads ? breedthreads : evalthreads]; seeds = new int[random.length]; String seedMessage = "Seed: "; int time = (int)(System.currentTimeMillis()); for (x=0;x= 0 (or not exist, which defaults to 0)"); int numJobs = parameters.getIntWithDefault(new Parameter("jobs"), null, 1); if (numJobs < 1) Output.initialError("The 'jobs' parameter must be >= 1 (or not exist, which defaults to 1)"); // Now we know how many jobs remain. Let's loop for that many jobs. Each time we'll // load the parameter database scratch (except the first time where we reuse the one we // just loaded a second ago). The reason we reload from scratch each time is that the // experimenter is free to scribble all over the parameter database and it'd be nice to // have everything fresh and clean. It doesn't take long to load the database anyway, // it's usually small. for(int job = currentJob ; job < numJobs; job++) { try { // load the parameter database (reusing the very first if it exists) if (parameters == null) parameters = loadParameterDatabase(args); // Initialize the EvolutionState, then set its job variables state = initialize(parameters, job); // pass in job# as the seed increment state.output.systemMessage("Job: " + job); state.job = new Object[1]; // make the job argument storage state.job[0] = new Integer(job); // stick the current job in our job storage state.runtimeArguments = args; // stick the runtime arguments in our storage if (numJobs > 1) // only if iterating (so we can be backwards-compatible), { String jobFilePrefix = "job." + job + "."; state.output.setFilePrefix(jobFilePrefix); // add a prefix for checkpoint/output files state.checkpointPrefix = jobFilePrefix + state.checkpointPrefix; // also set up checkpoint prefix } // Here you can set up the EvolutionState's parameters further before it's setup(...). // This includes replacing the random number generators, changing values in state.parameters, // changing instance variables (except for job and runtimeArguments, please), etc. // now we let it go state.run(EvolutionState.C_STARTED_FRESH); cleanup(state); // flush and close various streams, print out parameters if necessary parameters = null; // so we load a fresh database next time around } catch (Throwable e) // such as an out of memory error caused by this job { e.printStackTrace(); state = null; System.gc(); // take a shot! } } System.exit(0); } }