The AHA Model  Revision: 12809
Reference implementation 04 (HEDG02_04)
the_evolution Module Reference

Implementation of the genetic algorithm. More...

Functions/Subroutines

subroutine init_environment_objects ()
 Initialise the environmental objects. Most of the environmental objects, such as the environment, habitats etc. are kept static throughout the model running. There are, however, patterned and stochastic changes in the environment, such as diurnal variation of the illumination level. More...
 
integer function, public preevol_steps_adaptive (generation)
 Calculate the adaptive number of time steps for the fixed fitness preevolution stage of the genetic algorithm. More...
 
subroutine, public preevol_steps_adaptive_save_csv (csv_file_name, is_success)
 This is a diagnostic subroutine to save the number of time steps for the adaptive GA. More...
 
subroutine generations_swap ()
 Swap generation pointers between parents and offspring. More...
 
subroutine selection ()
 Select reproducing agents, the best commondata::ga_reproduce_pr portion of agents. More...
 
subroutine mate_reproduce ()
 Mate, reproduce and mutate. More...
 
subroutine, public generations_loop_ga ()
 This procedure implements the main Genetic Algorithm for evolving the agents. More...
 

Variables

character(len= *), parameter, private modname = "(THE_EVOLUTION)"
 
type(timer_cpu), public stopwatch_global
 Model-global stopwatch objects. More...
 
type(timer_cpu), public stopwatch_generation
 
type(timer_cpu), public stopwatch_op_current
 
type(timer_cpu), public single
 
type(timer_cpu), public operation
 
type(habitat), public habitat_safe
 We have an environment composed of two habitats, safe and a dangerous. More...
 
type(habitat), public habitat_dangerous
 
type(population), target, public generation_one
 Here we create instances for two populations which will then serve as parents and offspring. And then we declare pointers that will point to parents and offspring. More...
 
type(population), target, public generation_two
 
type(population), pointer, public proto_parents
 
type(population), pointer, public proto_offspring
 

Detailed Description

Implementation of the genetic algorithm.

THE_EVOLUTION module

The Genetic Algorithm is implemented here

Function/Subroutine Documentation

◆ init_environment_objects()

subroutine the_evolution::init_environment_objects
private

Initialise the environmental objects. Most of the environmental objects, such as the environment, habitats etc. are kept static throughout the model running. There are, however, patterned and stochastic changes in the environment, such as diurnal variation of the illumination level.

Build the environmental objects

Build the overall environment "universe". It can be used for the whole-environment placement of objects, e.g. random walks of an agent crossing the borders between the habitats.

Build the habitats.

Define and allocate the global array of all habitats available to the agents. See the_environment::global_habitats_available for details of this global array. This is now made using the the_environment::assemble() procedure.

Allocation of the the_environment::global_habitats_available is checked. If it turns out not allocated, a critical error is signalled in the logger and the program calls commondata::system_halt().

Save initial diagnostic data

Output the number of the habitats in the global array the_environment::global_habitats_available and their labels into the logger.

Certain data are also saved. Their names start from the init_ prefix.

  • Save initial food data (uniform distribution as built at init). Note that the distribution of the food items can change at each time step due to vertical migration of the food items and their local random Gaussian movements.

Save predators' data.

  • Save the basic data on the dynamics of illumination, food items and visibility across the life span of the agents.

Save plots

If the plotting is enabled (see commondata::is_plotting), some plots of the initialisation data are also saved.

  • Save debug scatterplots of food items distribution within in the habitats.
  • Save debug scatterplots of predators distribution in the habitats.
  • Save histograms of food item sizes.

Definition at line 62 of file m_evolut.f90.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ preevol_steps_adaptive()

integer function, public the_evolution::preevol_steps_adaptive ( integer, intent(in), optional  generation)

Calculate the adaptive number of time steps for the fixed fitness preevolution stage of the genetic algorithm.

The number of time steps in the fixed-fitness pre-evolution genetic algorithm is calculated using an adaptive algorithm. Briefly, the number of time steps (total lifespan) at the early stages of evolution (the first generations) is very short and increases as the evolution proceeds towards the maximum set by commondata::preevol_tsteps.

Note
The time steps data generated by this function for each GA generation are saved in CSV file by the_evolution::preevol_steps_adaptive_save_csv().
Parameters
[in]generationgeneration optional current generation number, if not provided, set to commondata::global_generation_number_current.
Returns
The number of lifecycle time steps at the specific generation.

Implementation notes

The number of time steps in this fixed fitness pre-evol adaptive GA algorithm is calculated based on a linear interpolation from a grid defined by the two arrays:

  • STEPS_ABSCISSA – grid abscissa, from the first generation to the total number of generations commondata::generations.

STEPS_ORDINATE – grid ordinate, ranging from the number of time steps in one diel cycle to the total number of time steps in the fixed fitness pre-evolution stage commondata::preevol_tsteps.

However, for debugging purposes, evolution time steps can be set to a specific fixed value. This value is set by commondata::preevol_tsteps_force_debug integer parameter and for this fixed value to be forced, commondata::preevol_tsteps_force_debug_enabled must be TRUE.

Then, the total (adaptive) number of time steps is determined by the integer lower limit (floor) of the linear interpolation DDPINTERPOL() procedure, with further limitation that its result value must be within the range of [t,T], where t is the length of a single diel cycle, T is the number of time steps in the pre-evolution stage.

Note
Plotting commands:
  • htintrpl.exe [1 50 75 101] [0 0.8 0.95 1] [1] [nonlinear]

Definition at line 211 of file m_evolut.f90.

Here is the caller graph for this function:

◆ preevol_steps_adaptive_save_csv()

subroutine, public the_evolution::preevol_steps_adaptive_save_csv ( character(len=*), intent(in)  csv_file_name,
logical, intent(out), optional  is_success 
)

This is a diagnostic subroutine to save the number of time steps for the adaptive GA.

Parameters
[in]csv_file_namecsv_file_name the name of the CSV file to save the arrays.
[out]is_successis_success Flag showing that data save was successful (if TRUE).

Definition at line 289 of file m_evolut.f90.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generations_swap()

subroutine the_evolution::generations_swap
private

Swap generation pointers between parents and offspring.

Definition at line 318 of file m_evolut.f90.

Here is the caller graph for this function:

◆ selection()

subroutine the_evolution::selection
private

Select reproducing agents, the best commondata::ga_reproduce_pr portion of agents.

The best (sorted) parents are copied to the offspring population object. Note that the number of such reproducing parents is determined by the the_population::population::ga_reproduce_max() method.

Old fixed proportion implementation:

proto_offspring(:ga_reproduce_n) = proto_parents(:ga_reproduce_n)

The best parents (elite group) are then re-initialised from the genome for the next generation using the_individual::individual_agent::init() method.

Definition at line 333 of file m_evolut.f90.

Here is the caller graph for this function:

◆ mate_reproduce()

subroutine the_evolution::mate_reproduce
private

Mate, reproduce and mutate.

Calculate adaptive mutation rate

Loop through all the non-elite population members. These individuals are created from the genomes of the elite group. The non-elite individuals are from commondata::ga_reproduce_n+1 to commondata::popsize.

  • If chromosomes are not allocated, this means it is a new individual. We have to initialise it – now as random. The same is true for all individuals that the_genome::individual::genome::is_dead().
  • Two agents are randomly chosen from the population. They become the mother and the father of new proto_offspring agents. The mother and the father exchange their genetic material using the the_genome::individual_genome::recombine_random() method. Note that the mother must be the_genome::individual_genome::is_female() and the father, the_genome::individual_genome::is_male().
  • Once the genome of the offspring is created from recombination data, the offspring are subjected to random mutation using the the_genome::individual_genome::mutate() backend.
  • After this, the whole agent is initialised using he constructor the_genome::individual_agent::init(), but without random initialisation of the genome, the latter is based on the recombination data from the parents.

Finally, loop through the elite group and introduce random mutations there too with the_genome::individual_genome::mutate().

Note
This is disabled (elitism).

Definition at line 364 of file m_evolut.f90.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generations_loop_ga()

subroutine, public the_evolution::generations_loop_ga

This procedure implements the main Genetic Algorithm for evolving the agents.

Objects for the GA

energy_sd_gen1_birth_mort – standard deviationof the birth energy reserves, for forced selective birth mortality. See the_population::population::mortality_birth().

Objects for generation-wise statistics file

The definitions below are for the objects that are used to write generation-wise statistics in the generation_stats_record_write() sub-procedure.

  • csv_file_generstats: CSV_IO File handle object for generation-wise statistics. See CSV_IO module for details.
    Note
    Note that file_io::file_handle object is not used here because it breaks Intel Fortran compiler: as the file unit somehow gets lost in generation_stats_record_write(). GNU gfortran has no issues here.

file_stats_gener_record: Record for the generation-wise statistics file.

  • FILE_STATS_GENER_COLS: an array of column names for the generation-wise statistics file.
  • FILE_STATS_RECORD_LEN: The maximum length of the CSV record assuming the maximum length of a single field is commondata::label_length; the number of fields is equal to the size of the columns array FILE_STATS_GENER_COLS.

Parameters for the GA stopping rule

Parameters determining the stopping rule for the fixed fitness genetic algorithm. These are based on the values obtained in the first generation. If in any succeeding generation, they fall below the first generation values, evolution is considered unsuccessful and the main GA loop stops. The number of alive agents at the first random generation.

  • Evolution should stop with unsuccessful status if the number of alive agents falls below this value.

The number of agents that have increased their body mass at the first random generation.

  • Evolution should stop with unsuccessful status if the number of alive agents falls below this value.

Preliminary steps

Global_Generation_Number_Current is the global generation number. It is first initialised to 1.

commondata::Global_Rescale_Maximum_Motivation is the global maximum motivation value, it is fixed at the start of the simulation to an arbitrary high value but is automatically updated from the maximum motivation value across all agents after each time step.

The stopping rule parameters based on the first generation values are initialised to some values allowing the first generation to occur safely, i.e. with sufficiently large number of randomly created pre-optimal agents.

  • If the number of alive agents is smaller than this minimum number, GA stops: some parameters must be tweaked.

The number of agents growing is set to a large negative value commondata::unknown,so initial zero is always larger, so evolution is allowed to start.

Initialise the environment

All environmental objects are initialised with init_environment_objects().

Initialise base agent population objects

New populations of agents are now built and initialised: (a) generation_one, (b) generation_two These population objects serve as targets for two pointer objects: (a) proto_parents, (b) proto_offspring.

  • Initialise the whole generation_one of the agents, commondata::popsize is the size of the population.
  • Also initialise the generation_two, that will then take parents' values.

Calculate initial fitness of the agents in the generation_one for the pre-evolution phase. At this stage fitness is equal to the maximum value (note that fitness is actually a reverse of fitness) and is not very interesting.

Place all the agents that have been initialised to random spatial positions in the safe habitat (habitat_safe), they have just the uniformly distributed spatial positions at start.

Note
Note that the initial vertical position and distribution of the agents depends on these parameters:

See the_population::individ_posit_in_environ_uniform() for details.

Transfer pointers: parents and offspring populations

Allocate the first proto_parents and proto_offspring population objects, they are pointers to generation_one and generation_two target objects.

Calculate statistical parameters of the initial generation for selective birth mortality. See the_population::population::mortality_birth().

These values are then logged.

Save diagnostics data

Save initialisation data in the debug mode.

  • Saving histograms of agents' body length.
  • Saving histograms of agents' body mass.
  • Saving histograms of agents' energy.
  • Saving histograms of agents' smr.

SAVE_DATA_INIT block: The random initialisation individual data for the whole parent population are saved to csv files:

  • Individual agent's data, file init_agents_;
  • Initial genome data, file init_genome_.
  • The number of time steps in the adaptive GA

The generation wise statistics file generations_ is opened for writing ...

... and the first row of column names FILE_STATS_GENER_COLS is written.

Note
Note that the main body of the statistical data is processed in the sub-procedure generation_stats_record_write().

The average distance between the food items is reported to the log. The average distance between the food items is good to know, e.g. to compare it with the agent's random walk step size.

Pre-evolution stage

Pre-evolution stage involves the Genetic Algorithm that is based on selection of agents based on an explicit global fitness. It aims to produce a population of agents that can stably sustain for the whole commondata::lifespan

GENERATIONS_PREEVOL: The main loop of (pre-) evolution

At this stage the main loop of generations evolving is started. The conditions for continuing the main evolution loop are as follows:

Check stop file

The commondata::stop_file file is checked upon each generation. If this stop file exists, the simulation cycle is terminated at this stage and the program then exits from the generation loop.

Diagnostics

Stopwatch object for calculating time since generation start is initialised.

Initially, place all the agents in the proto_parents population randomly uniformly in the safe habitat (habitat_safe). However, note that the initial vertical position and distribution of the agents depends on these parameters:

See the_population::individ_posit_in_environ_uniform() method for details.

Initialise the global generation-wise counter of the number of agents that die as a consequence of predation the_population::global_ind_n_eaten_by_predators, as opposed to starvation.

If it is not the first generation, replenish all food items (i.e. for all habitats), they are restored to the "available" (non-eaten) state. Two methods can be used here:

The global habitat array the_environment::global_habitats_available is then updated by the_environment::assemble() procedure.

If it is the first generation, it does not make sense doing this as the environment has been already fully initialised in the init_environment_objects() procedure.

lifecycle_preevol for proto_parents

Start the loop of the life cycle of all agents of the proto_parents. It includes commondata::preevol_tsteps time steps. (Note that commondata::preevol_tsteps is less than commondata::lifespan). This is implemented in the lifecycle_preevol() procedure.

Calculate the number of agents alive and agents growing. These values are used later, including as a criterion of GA deterioration.

Report these values in the logger.

After the agents went through their life cycle, their fitness is processed.

  • Fitness of all proto_parents agents is recalculated following their performance in the full lifecycle.

The agents proto_parents are sorted by fitness.

If this is the first generation, determine the GA deterioration stopping parameters, evolution "failure"

These Generation one parameters are also reported to the logger.

  • SAVE_DATA_INDS_GENERATION block: The individual statistical data for the whole proto_parents population are saved using the the_population::population class bound save_ methods:
  • Individual agent's data, file agents_
  • The genome data, file genome_
  • Memory stacks data, file memory_.
  • Movement history data, file movements_.
  • Behaviour history data, file behaviours_.
  • SAVE_DATA_FOOD_POST: The food resources data for all the habitats are saved using the the_environment::food_resource::save_csv() method.
  • Generation-wise statistics are calculated and saved in the CSV file. This is implemented in the generation_stats_record_write() subprocedure.
  • Check if the unsuccessful evolution criterion is met. If yes, terminate the GA.
    • The number of agents that are alive exceeds that in the first generation: there must be improvement (at least in debug).
    • The number of agents that have grown exceeds that in the first generation.
  • If this is the first generation, terminate GA if the number of agents alive < 1/100 of the commpndata::popsize or if no agents are growing.

Selection

Select reproducing minority: the_evolution::selection()

Exchange of the genetic material

A minority of the best parents produces the proto_offspring population object: the_evolution::mate_reproduce().

Reset individual IDs of proto_offspring

Finalise the generation cycle: swap pointers

Swap populations: proto_offspring are now proto_parents: the_evolution::generations_swap().

End of the generations loop

Finally, the global generation counter commondata::global_generation_number_current is incremented by one.

After all generations were done, the CSV file csv_file_generstats that saves generation-wise statistics is closed.

System terminates

Finally, the concluding procedure commondata::system_halt() is called for the normal termination of the model.

Definition at line 445 of file m_evolut.f90.

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ modname

character (len=*), parameter, private the_evolution::modname = "(THE_EVOLUTION)"
private

Definition at line 35 of file m_evolut.f90.

◆ stopwatch_global

type(timer_cpu), public the_evolution::stopwatch_global

Model-global stopwatch objects.

Note
Use the keyword TIMER: (LTAG_TIMER) for logging, e.g. call LOG_MSG( LTAG_TIMER // stopwatch_op_currentshow() )

Definition at line 40 of file m_evolut.f90.

◆ stopwatch_generation

type(timer_cpu), public the_evolution::stopwatch_generation

Definition at line 40 of file m_evolut.f90.

◆ stopwatch_op_current

type(timer_cpu), public the_evolution::stopwatch_op_current

Definition at line 40 of file m_evolut.f90.

◆ single

type(timer_cpu), public the_evolution::single

Definition at line 40 of file m_evolut.f90.

◆ operation

type(timer_cpu), public the_evolution::operation

Definition at line 40 of file m_evolut.f90.

◆ habitat_safe

type(habitat), public the_evolution::habitat_safe

We have an environment composed of two habitats, safe and a dangerous.

Definition at line 45 of file m_evolut.f90.

◆ habitat_dangerous

type(habitat), public the_evolution::habitat_dangerous

Definition at line 45 of file m_evolut.f90.

◆ generation_one

type(population), target, public the_evolution::generation_one

Here we create instances for two populations which will then serve as parents and offspring. And then we declare pointers that will point to parents and offspring.

Definition at line 50 of file m_evolut.f90.

◆ generation_two

type(population), target, public the_evolution::generation_two

Definition at line 51 of file m_evolut.f90.

◆ proto_parents

type(population), pointer, public the_evolution::proto_parents

Definition at line 52 of file m_evolut.f90.

◆ proto_offspring

type(population), pointer, public the_evolution::proto_offspring

Definition at line 53 of file m_evolut.f90.