/*
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);
}
}