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¶
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, population_growth_function: Callable = <function population_incremental>, 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 for Genetic Algorithms.
- 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).
population_growth_function (Callable) – Function that defines what the population growth is, steady, generational etc. (default = None).
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, is_deterministic: bool = False)¶
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).
is_deterministic (bool) – Sets the whole program as deterministic, which improves function calls through memoisation (default = False).
- 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)
signaturename (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, population_growth_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).
parent_selection_params (dict) – Dict of params for custom selection function (default = None).
parent_combination_params (dict) – Dict of params for custom combination function (default = None).
survivor_selection_params (dict) – Dict of params for custom survivor selection function (default = None).
population_growth_params (dict) – Dict of params for custom population growth function (defualt = 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
- 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.