This example presents how to explore a NetLogo model step by step with an Evolutionary/Genetic Algorithm (EA/GA) in OpenMOLE.
evaporation-rate
and diffusion-rate
which minimises the eating time of each food source.
To build our fitness function, we modify the NetLogo Ants source code to store for each food source the first ticks
indicating that this food source is empty.
to compute-fitness
if ((sum [food] of patches with [food-source-number = 1] = 0) and (final-ticks-food1 = 0)) [
set final-ticks-food1 ticks ]
if ((sum [food] of patches with [food-source-number = 2] = 0) and (final-ticks-food2 = 0)) [
set final-ticks-food2 ticks ]
if ((sum [food] of patches with [food-source-number = 3] = 0) and (final-ticks-food3 = 0)) [
set final-ticks-food3 ticks ]
end
At the end of each simulation we return the values for the three objectives (or criteria) :
// Define the input variables
val gPopulation = Val[Double]
val gDiffusionRate = Val[Double]
val gEvaporationRate = Val[Double]
val seed = Val[Int]
// Define the output variables
val food1 = Val[Double]
val food2 = Val[Double]
val food3 = Val[Double]
// Define the NetlogoTask
val cmds = Seq("run-to-grid")
val ants =
NetLogo5Task(workDirectory / "Ants.nlogo", cmds, seed = seed) set (
// Map the OpenMOLE variables to NetLogo variables
netLogoInputs += (gPopulation, "gpopulation"),
netLogoInputs += (gDiffusionRate, "gdiffusion-rate"),
netLogoInputs += (gEvaporationRate, "gevaporation-rate"),
netLogoOutputs += ("final-ticks-food1", food1),
netLogoOutputs += ("final-ticks-food2", food2),
netLogoOutputs += ("final-ticks-food3", food3),
// Define default values for inputs of the model
seed := 42,
gPopulation := 125.0,
gDiffusionRate := 50.0,
gEvaporationRate := 50
)
// Define the hooks to collect the results
val displayHook = ToStringHook(food1, food2, food3)
// Start a workflow with 1 task
val model_execution = (ants hook displayHook) start
The result of this execution should look like:
{food1=746.0, food2=1000.0, food3=2109.0}
/tmp/ants
.
Notice how the evaluation
parameter of the SteadyStateEvolution
method, is the netlogo task i.e. running the model, which indeed provides an evaluation of the genome
(parameter settings) efficiency regarding the objectives
.
// Define the population size: 100.
// Define the inputs and their respective variation bounds.
// Define the objectives to minimize.
// Tell OpenMOLE that this model is stochastic and that it should generate a seed for each execution
// Define the fitness evaluation
// Define the parallelism level
// Terminate after 10000 evaluations
val evolution =
SteadyStateEvolution(
// Define the population (10) and the number of generations (100).
// Define the inputs and their respective variation bounds.
// Define the objectives to minimize.
algorithm =
NSGA2(
mu = 100,
genome = Seq(gDiffusionRate in (0.0, 99.0), gEvaporationRate in (0.0, 99.0)),
objectives = Seq(food1, food2, food3),
stochastic = Stochastic(seed = seed)
),
evaluation = ants,
parallelism = 10,
termination = 1000
)
// Define a hook to save the Pareto frontier
val savePopulationHook = SavePopulationHook(evolution, workDirectory / "results")
// Plug everything together to create the workflow
(evolution hook savePopulationHook)
val evolution =
SteadyStateEvolution(
algorithm =
NSGA2(
mu = 100,
genome = Seq(gDiffusionRate in (0.0, 99.0), gEvaporationRate in (0.0, 99.0)),
objectives = Seq(food1, food2, food3),
stochastic = Stochastic(seed = seed)
),
evaluation = ants,
termination = 1 hour
)
// Define the island model with 1,000 concurrent islands. Each island gets 50 individuals sampled from the global
// population. The algorithm stops after 100,000 islands evaluations.
val island =
IslandEvolution(
evolution,
parallelism = 1000,
termination = 100000
)
val savePopulationHook = SavePopulationHook(island, workDirectory / "results")
// Define the execution environment
val env = EGIEnvironment("biomed", openMOLEMemory = 1200 megabytes, cpuTime = 4 hours)
// Define the workflow
(island on env hook savePopulationHook)