Natural Selection

Evolutionary Algorithm tools in Python

A Python package for creating easy EA experiments, containing easy to use functions and classes for setting up and running Genetic Algorithms and Genetic Programs. Natural Selection is built on minimal dependencies, only requiring numpy for random functions.

Starting

Installation

Using pip:

pip install natural-selection

Usage

Import the tools:

from natural_selection import Island
from natural_selection.genetic_algorithms import Gene, Chromosome, Individual
from natural_selection.genetic_algorithms.utils.random_functions import random_int, random_gaussian

Then simply create a GA experiment:

from natural_selection import Island
from natural_selection.genetic_algorithms import Gene, Chromosome, Individual
from natural_selection.genetic_algorithms.utils.random_functions import random_int, random_gaussian

# Create a gene
g_1 = Gene(name="test_int", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
g_2 = Gene(name="test_real", value=0.5, gene_max=1.0, gene_min=0.1, randomise_function=random_gaussian)

# Add a list of genes to a genome
gen = Chromosome([g_1, g_2])

# Next, create an individual to carry these genes and evaluate them
fitness_function = lambda island, individual, x, y: individual.chromosome[0].value * x + individual.chromosome[0].value * y
adam = Individual(fitness_function, name="Adam", chromosome=gen)

# Now we can create an island for running the evolutionary process
# Notice the fitness function parameters are given here.
params = dict()
params['x'] = 0.5
params['y'] = 0.2
isolated_island = Island(function_params=params)

# Using a single individual, we can create a new population
isolated_island.initialise(adam, population_size=5)

# And finally, we let the randomness of life do its thing: optimise
best_individual = isolated_island.evolve(n_generations=5)

# After running for a few generations, we have an individual with the highest fitness
fitness = best_individual.fitness
genes = best_individual.chromosome

for gene in genes:
  print(gene.name, gene.value)

Islands

Islands are the main engines that drive the evolutionary process. They can be customised with different selection, crossover, and mutation operators, giving the experimenter more flexibility when creating experiments.

from natural_selection.genetic_algorithms import Gene
from natural_selection.genetic_algorithms import Chromosome
from natural_selection.genetic_algorithms import Individual
from natural_selection import Island
from natural_selection.genetic_algorithms.utils.random_functions import random_int, random_gaussian

# Create a gene
g_1 = Gene(name="test_int", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
g_2 = Gene(name="test_real", value=0.5, gene_max=1.0, gene_min=0.1, randomise_function=random_gaussian)

# Create a chromosome

chromosome = Chromosome([g_1, g_2])

# Next, create an individual to carry these genes and evaluate them
fitness_function = lambda island, individual, x, y: individual.chromosome[0].value * x + individual.chromosome[1].value * y
adam = Individual(fitness_function=fitness_function, name="Adam", chromosome=chromosome)

# Now we can create an island for running the evolutionary process
# Notice the fitness function parameters are given here.
params = dict()
params['x'] = 0.5
params['y'] = 0.2
isolated_island = Island(function_params=params)

# Using a single individual, we can create a new population
isolated_island.initialise(adam, population_size=5)

# And finally, we let the randomness of life do its thing: optimise
best_individual = isolated_island.evolve(n_generations=5)

# After running for a few generations, we have an individual with the highest fitness
fitness = best_individual.fitness
genes = best_individual.chromosome

for gene in genes:
  print(gene.name, gene.value)

Islands are customisable in how they operate on population members.

Island class

class natural_selection.__init__.Island(function_params: Optional[dict] = None, maximise_function: bool = True, initialisation_function: Callable = <function initialise_population_random>, parent_selection: Callable = <function selection_elites_top_n>, parent_combination: Callable = <function selection_parents_two>, crossover_function: Callable = <function crossover_two_uniform>, mutation_function: Callable = <function mutation_randomize>, crossover_prob_function: Callable = <function crossover_prob_function_classic>, mutation_prob_function: Callable = <function mutation_prob_function_classic>, survivor_selection_function: Callable = <function selection_survivors_all>, alien_spawn_function: Callable = <function alien_spawn_default>, clone_function: Callable = <function clone_classic>, random_seed: Optional[int] = None, name: Optional[str] = None, verbose: bool = True, logging_function: Optional[Callable] = None, save_checkpoint_function: Callable = <function default_save_checkpoint_function>, filepath: Optional[str] = None, save_checkpoint_level: int = 0, core_count: Union[int, float] = 1, population_evaluation_function: Callable = <function evaluate_individuals_sequentially>, allow_twins: bool = False)

A simple Island to perform a Genetic Algorithm. By default the selection, mutation, crossover, and probability functions default to the classic functions.

Parameters
  • function_params (dict) – The parameters for the fitness function (default = None).

  • maximise_function (bool) – If True, the fitness value is maximised, False will minimise the function fitness (default = True).

  • parent_selection (Callable) – Function for selecting individuals for crossover (default = None).

  • initialisation_function (Callable) – A function for randomly creating new individuals from the given adam.

  • parent_combination (Callable) – Function for combining parents for crossover (default = None).

  • crossover_function (Callable) – Function for crossover (default = None).

  • mutation_function (Callable) – Function for mutation (default = None).

  • crossover_prob_function (Callable) – Random probability function for crossover (default = None).

  • mutation_prob_function (Callable) – Random probability function for mutation (default = None).

  • clone_function (Callable) – Function for cloning (default = None).

  • survivor_selection_function (Callable) – Function for selecting survivors (default = None).

  • alien_spawn_function (Callable) – Function for spawning new aliens during each generation (default = None).

  • random_seed (int) – Random seed for random and Numpy generators, set to None for no seed (default = None).

  • name (str) – General name for island, useful when working with multiple islands (default = None).

  • verbose (bool) – Print all information (default = None).

  • logging_function (Callable) – Function for custom message logging, such as server logging (default = None).

  • save_checkpoint_function (Callable) – Function for custom checkpoint saving (default = None).

  • filepath (str) – If a filepath is specified, the pickled island is loaded from it, skipping the rest of initialisation (default = None).

  • save_checkpoint_level (int) – Level of checkpoint saving 0 = none, 1 = per generation, 2 = per evaluation (default = 0).

  • core_count (int, float) – Number of cores to split evaluation on, for all cores, set -1, use float for fractional (default = 1).

  • population_evaluation_function (Callable) – Function taking a list of individuals and calling the fitness functions (default = None).

  • allow_twins (bool) – Only add new offspring to the population if they have a unique chromosome (default = False).

unique_genome

List of unique chromosomes.

Type

list

generation_info

List of dicts detailing info for every generation.

Type

list

population

The full population of members.

Type

list

parents

All parents selected during the run.

Type

list

elites

All elites selected during the run.

Type

list

mutants

All mutants created during the run.

Type

list

children

All children created during the run.

Type

list

generation_count

The current generation number.

Type

int

checkpoints_dir

Directory name of where all checkpoints are saved.

Type

str

lineage

A graph of lineage for the whole population.

Type

dict

add_new_property(key: str, value: Any)

Method to add new properties (attributes).

Parameters
  • key (str) – Name of property.

  • value (Any) – Anything.

create_chromosome(genes: Optional[list] = None, gene_verify_func: Optional[Callable] = None, chromosome_properties: Optional[dict] = None)

Wrapping function to create a new Chromosome. Useful when writing new initialisation functions. See Chromosome class.

Parameters
  • genes (list) – list of initialised Gene objects.

  • gene_verify_func (Callable) – A function to verify gene compatibility func(gene,loc,chromosome) (default = None).

  • chromosome_properties (dict) – For custom functions, extra params may be given (default = None).

Returns

A new Chromosome.

Return type

chromosome

create_gene(name: str, value: Any, randomise_function: Callable, gene_max: Optional[Any] = None, gene_min: Optional[Any] = None, mu: Optional[Any] = None, sig: Optional[Any] = None, step_lower_bound: Optional[Any] = None, step_upper_bound: Optional[Any] = None, choices: Optional[Iterable] = None, gene_properties: Optional[dict] = None)

Wrapping function to create a new Gene. Useful when writing new initialisation functions. See Gene class.

Parameters
  • name (str) – Gene name. The gene name also acts as a compatibility reference.

  • value (Any) – The value, could be any type.

  • randomise_function (Callable) – A function to randomise the gene, taking the min and max as input with signature func(self).

  • gene_max (Any, numeric type) – Max value for random number generator (default = None).

  • gene_min (Any, numeric type) – Min value for random number generator (default = None).

  • mu (Any, numeric type) – Mean value of distribution to sample from (default = None).

  • sig (Any, numeric type) – Std. Dev. value of distribution to sample from (default = None).

  • step_lower_bound (Any, numeric type) – For uniform stepping functions, defines lower bound of range (default = None).

  • step_upper_bound (Any, numeric type) – For uniform stepping functions, defines upper bound of range (default = None).

  • choices (Iterable) – List of choices, categorical or not, to randomly choose from (default = None).

  • gene_properties (dict) – For custom random functions, extra params may be given (default = None).

Returns

A new Gene object.

Return type

gene

create_genetic_program(fitness_function: Optional[Callable] = None, node_tree: Optional[natural_selection.genetic_programs.Node] = None, operators: Optional[List[Union[Type, natural_selection.genetic_programs.node_operators.Operator]]] = None, terminals: Optional[List[Union[str, int, float]]] = None, max_depth: int = 3, min_depth: int = 1, growth_mode: str = 'grow', terminal_prob: float = 0.5, tree_generator: Callable = <function random_generate>, name: Optional[str] = None, species_type: Optional[str] = None, add_to_population: bool = False, program_properties: Optional[dict] = None)

Wrapping function to create a new GeneticProgram. Useful when writing new initialisation functions. See GeneticProgram class.

Parameters
  • fitness_function (Callable) – Function with func(Node, island, **params) signature (default = None).

  • node_tree (Node) – A starting node tree (default = None).

  • operators (list) – List of all operators that nodes can be constructed from (default = None).

  • terminals (list) – List of all terminals that can be included in the node tree, can be numeric or strings for variables (default = None).

  • max_depth (int) – Maximum depth that node tree can grow (default = 3).

  • min_depth (int) – Minimum depth that node tree must be (default = 1).

  • growth_mode (str) – Type of tree growth method to use, “full” or “grow” (default = “grow”).

  • terminal_prob (float) – Probability of a generated node is a terminal (default = 0.5).

  • tree_generator (Callable) – Function with to create the tree.

  • name (str) – Name for keeping track of lineage (default = None).

  • species_type (str) – A unique string to identify the species type, for preventing cross polluting (default = None).

  • add_to_population (bool) – Add this new program to the population (default = False).

  • program_properties (dict) – For fitness functions, extra params may be given (default = None).

Returns

Newly created GeneticProgram.

Return type

program

create_individual(fitness_function: Callable, name: Optional[str] = None, chromosome: Optional[natural_selection.genetic_algorithms.Chromosome] = None, species_type: Optional[str] = None, add_to_population: bool = False, individual_properties: Optional[dict] = None)

Wrapping function to create a new Individual. Useful when writing new initialisation functions. See Individual class.

Parameters
  • fitness_function (Callable) – Function with func(Chromosome, island, **params) signature

  • name (str) – Name for keeping track of lineage (default = None).

  • chromosome (Chromosome) – A Chromosome object, initialised (default = None).

  • species_type (str) – A unique string to identify the species type, for preventing cross polluting (default = None).

  • add_to_population (bool) – Add this new individual to the population (default = False).

  • individual_properties (dict) – For fitness functions, extra params may be given (default = None).

Returns

A new Individual.

Return type

individual

create_node(label: Optional[str] = None, arity: int = 1, operator: Optional[natural_selection.genetic_programs.node_operators.Operator] = None, is_terminal=False, terminal_value=None, children: Optional[List] = None)

Wrapping function to create a new node. Useful when writing new initialisation functions. See Node class.

Parameters
  • label (str) – Optionally set the label, only used for variable terminals (default = None).

  • arity (int) – Optionally set the function arity, the norm being 2 for functions (default = 1).

  • operator (Operator) – If the node is a function, set the operator (default = None).

  • is_terminal (bool) – Explicitly define if the node is a terminal (default = None).

  • terminal_value (Any) – Only set if the node is terminal and a constant value (default = None).

  • children (list) – Add a list of child nodes, list length must match arity (default = None).

Returns

A new Node object.

Return type

node

evolve(starting_generation: int = 0, n_generations: int = 5, crossover_probability: float = 0.5, mutation_probability: float = 0.25, crossover_params: Optional[dict] = None, mutation_params: Optional[dict] = None, parent_selection_params: Optional[dict] = None, parent_combination_params: Optional[dict] = None, survivor_selection_params: Optional[dict] = None, alien_spawn_params: Optional[dict] = None, criterion_function: Optional[Callable] = None, criterion_params: Optional[dict] = None, pre_generation_check_function: Optional[Callable] = None, post_generation_check_function: Optional[Callable] = None, post_evolution_function: Optional[Callable] = None)natural_selection.genetic_algorithms.Individual

Starts the evolutionary run.

Parameters
  • starting_generation (int) – Starting generation (default = 0).

  • n_generations (int) – Number of generations to run (default = 5).

  • crossover_probability (float) – Initial crossover probability (default = 0.5).

  • mutation_probability (float) – Initial mutation probability (default = 0.25).

  • crossover_params (dict) – Dict of params for custom crossover function (default = None).

  • mutation_params (dict) – Dict of params for custom mutation function (default = None).

  • selection_params (dict) – Dict of params for custom selection function (default = None).

  • alien_spawn_params (dict) – Dict of params for alien spawn function (default = None).

  • criterion_function (Callable) – A function to evaluate if the desired criterion has been met (default = None).

  • criterion_params (dict) – Function parameters for criterion (default = None).

  • pre_generation_check_function (Callable) – A function to perform some custom pre-action at the start of every generation, with signature func(generation, island) (default = None).

  • post_generation_check_function (Callable) – A function to perform some custom post-action at the end of every generation, with signature func(generation, island) (default = None).

  • post_evolution_function (Callable) – A function to perform some custom post-action after full evolution cycle, with signature func(island) (default = None).

Returns

The fittest Individual found.

Return type

Individual

get(key: str, default=None)

Gets the value of a property or returns default if it doesn’t exist.

Parameters
  • key (str) – Property name.

  • default – Value to return if the property is not found (default = None).

Returns

The property of the individual.

Return type

any

get_properties()

Gets a dict of the custom properties that were added at initialisation or the add_new_property method.

Returns

All custom properties.

Return type

dict

import_migrants(migrants: list, reset_fitness: bool = False, species_check: bool = True, allow_twins: bool = True)

Imports a list of individuals, with option to re-evaluate them. Skips the individual if the genetic code is already in the population.

Parameters
  • migrants (list) – List of Individuals.

  • reset_fitness (bool) – Reset the fitness of new members and evaluate them (default = False).

  • species_check (bool) – Safely check that imported members are compatible with population (default = True).

  • allow_twins (bool) – Only add migrants to the population if they have a unique chromosome (default = True).

initialise(adam: Union[natural_selection.genetic_algorithms.Individual, natural_selection.genetic_programs.GeneticProgram], population_size: int = 8, initialisation_params: Optional[dict] = None, evaluate_population: bool = True)

Starts the population by taking an initial individual as template and creating new ones from it. Island species_type is set to adam’s species type.

Parameters
  • adam (Individual) – Individual to clone from.

  • population_size (int) – Size of population.

  • initialisation_params (dict) – Custom params for custom initialisation functions (default = None).

  • evaluate_population (bool) – Evaluate the newly created population (default = True).

load_island(filepath: str)

Loads a pickled self from the given file path.

Parameters

filepath (str) – File path to pickled island.

save_island(filepath: str)

Dumps a pickled self to the given file path.

Parameters

filepath (str) – File path to pickled island.

write_lineage_json(filename: str)

Dumps the lineage safely to JSON file.

Parameters

filename (str) – Output file.

write_report(filename: str, output_json: bool = False)

Write the generational history to CSV (or JSON) file.

Parameters
  • filename (str) – Output file.

  • output_json (bool) – Write as JSON instead of CSV (default = False).

Utils

See Island Helper functions for helper tools.

Changes and history

See Changelog for version history.

Version 0.2.27 (2022-09-02):

  • A population_evaluation_function can now be added to define custom fitness function calls to individuals.

  • This makes the way open for multi server fitness evaluation.

  • Added natural_selection.utils.

  • Moved clone_classic to natural_selection.utils.

  • Moved get_random_string to natural_selection.utils.

  • Moved default_save_checkpoint_function to natural_selection.utils.

  • Moved post_evolution_function_save_all to natural_selection.utils.

  • Added evaluate_individual_multiproc_wrapper to natural_selection.utils.

  • Added evaluate_individuals_sequentially to natural_selection.utils.

Indices and tables