1.1. Core GA classes¶
Table of Contents
Simple classes for encapsulating Genetic Algorithms.
1.1.1. Genes¶
The most atomic part of a GA experiment is the gene. These easily encapsulate the direct encoding of genes, along with the genome. These are built for direct encoding approaches.
from natural_selection.genetic_algorithms import Gene
Genes encapsulate at core the key-value of a problem being optimised. They contain further attributes to set constraints
and facilitate randomisation. Genes can further be initialised with custom gene_properties
used by custom randomise_function
.
Under natural_selection.genetic_algorithms.utils.random_functions
a few useful gene randomisation functions can be found for easy use.
See Gene Random functions for more.
from natural_selection.genetic_algorithms import Gene
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)
1.1.1.1. Custom random functions¶
See Gene Random functions for standard random functions. To implement custom random functions, the following signature is required:
def my_random_int(gene):
"""
Random integer from range.
Args:
gene (Gene): A gene with a set `MIN` and `MAX`.
Returns:
int: Random number.
"""
return random.randint(low=gene.MIN, high=gene.MAX)
The only passed value is the instance of the gene, so all properties may be accessed. The function can then be used:
# Example of using custom random function with custom properties
g_1 = Gene(name="test_int", value=3, randomise_function=my_random_int, gene_properties={'MIN' : 1, 'MAX' : 5})
1.1.1.2. Gene class¶
- class natural_selection.genetic_algorithms.__init__.Gene(name: str, value: Any, randomise_function: Callable, gene_max: Optional[Any] = None, gene_min: Optional[Any] = None, mu: Any = 0, sig: Any = 1, step_lower_bound: Any = - 1.0, step_upper_bound: Any = 1.0, choices: Optional[Iterable] = None, gene_properties: Optional[dict] = None)¶
A simple class to encapsulate a simple gene.
- 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 gene (self) 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 = 0).
sig (Any, numeric type) – Std. Dev. value of distribution to sample from (default = 1).
step_lower_bound (Any, numeric type) – For uniform stepping functions, defines lower bound of range (default = -1.0).
step_upper_bound (Any, numeric type) – For uniform stepping functions, defines upper bound of range (default = 1.0).
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).
- add_new_property(key: str, value: Any)¶
Method to add new properties (attributes).
- Parameters
key (str) – Name of property.
value (Any) – Anything.
- 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
- randomise()¶
Sets a random value gene with randomised value.
1.1.2. Chromosomes¶
Chromosomes encapsulate ordered lists of genes, with some possibilities to do gene verification on updating.
from natural_selection.genetic_algorithms import Gene
from natural_selection.genetic_algorithms import Chromosome
from natural_selection.genetic_algorithms.utils.random_functions import random_int
# Create a gene
g_1 = Gene(name="test_int_1", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
g_2 = Gene(name="test_int_2", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
# Create a chromosome
chromosome = Chromosome([g_1, g_2])
Chromosomes will be used in individuals in the next step.
1.1.2.1. Gene verification¶
Custom gene verification functions can be added to preform logical checks when changing genes, through crossover.
def verify_gene_type(gene, loc, chromosome):
"""
A simple example verification to ensure that the swapped gene is the same type.
"""
if chromosome[loc].name != gene.name
return False
return True
g_1 = Gene(name="test_int_1", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
chromosome = Chromosome(genes=[g_1], gene_verify_func=verify_gene_type)
The signature takes the current inserted gene, the index of insertion, and the chromosome instance as input.
If False
is returned, the process raises a General GA Error Class exception.
1.1.2.2. Chromosome class¶
- class natural_selection.genetic_algorithms.__init__.Chromosome(genes: Optional[list] = None, gene_verify_func: Optional[Callable] = None, chromosome_properties: Optional[dict] = None)¶
A class that encapsulates an ordered sequence of Gene objects.
Note
gene_verify_func should take the gene, index of gene, and chromosome (self) as parameters.
- 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).
- add_new_property(key: str, value: Any)¶
Method to add new properties (attributes).
- Parameters
key (str) – Name of property.
value (Any) – Anything.
- append(gene: natural_selection.genetic_algorithms.__init__.Gene)¶
Simple appending of Gene type objects.
- Parameters
gene (Gene) – Gene
- 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
- randomise_all_genes()¶
Randomises all genes in chromosome.
- randomise_gene(index: int)¶
Randomise a gene at index.
- Parameters
index (int) – Index of gene.
- to_dict() → collections.OrderedDict¶
Helper function to convert chromosome into a key-value Python dictionary, assuming genes have unique names!
- Returns
Ordered dictionary of genes.
- Return type
OrderedDict
- to_list() → list¶
Helper function to convert chromosome into a Python list of the gene values.
- Returns
Values of the genes in order, as a list.
- Return type
list
1.1.3. Individuals¶
An individual fully encapsulates a problem solution, having an initialised chromosome with initialised genes, a fitness value and a evaluation (fitness) function.
from natural_selection.genetic_algorithms import Gene
from natural_selection.genetic_algorithms import Chromosome
from natural_selection.genetic_algorithms import Individual
from natural_selection.genetic_algorithms.utils.random_functions import random_int
# Create a gene
g_1 = Gene(name="test_int_1", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
g_2 = Gene(name="test_int_2", value=3, gene_max=10, gene_min=1, randomise_function=random_int)
# 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[0].value * y
adam = Individual(fitness_function=fitness_function, name="Adam", chromosome=chromosome)
1.1.3.1. The fitness function¶
The fitness function is what calculates the fitness of an individual, the function being optimised. The genes (or chromosome) are the parameters to the function being optimised. Every individual is assigned the same fitness function, although it is technically possible to give individuals different functions.
Fitness functions are defined by you and they need to have the following signature:
def my_fitness_function(individual, island, ...):
# function logic
# all attributes of the individual can be accessed here
return fitness
Both individual
and island
are required function parameters.
The function parameters are defined in the signature:
def my_fitness_function(individual, island, x, c, d):
# some random example of a fitness function
print(individual.name, individual.fitness)
fitness = (x * individual.chromosome[0].value) * (c * individual.chromosome[1].value) + (d * individual.chromosome[2].value)
return fitness
1.1.3.2. Individual class¶
- class natural_selection.genetic_algorithms.__init__.Individual(fitness_function: Optional[Callable] = None, name: Optional[str] = None, chromosome: Optional[natural_selection.genetic_algorithms.__init__.Chromosome] = None, species_type: Optional[str] = None, filepath: Optional[str] = None, individual_properties: Optional[dict] = None)¶
A class that encapsulates a single individual, with genetic code and a fitness evaluation function.
- Parameters
fitness_function (Callable) – Function with
func(Chromosome, island, **params)
signature (default = None).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).
filepath (str) – Skip init and load from a pickled file.
individual_properties (dict) – For fitness functions, extra params may be given (default = None).
- fitness¶
The fitness score after evaluation.
- Type
Numeric
- age¶
How many generations was the individual alive.
- Type
int
- genetic_code¶
String representation of Chromosome.
- Type
str
- history¶
List of dicts of every evaluation.
- Type
list
- parents¶
List of strings of parent names.
- Type
list
- add_gene(gene: natural_selection.genetic_algorithms.__init__.Gene)¶
Appends a gene to the chromosome.
- Parameters
gene (Gene) – Gene to add.
- add_new_property(key: str, value: Any)¶
Method to add new properties (attributes).
- Parameters
key (str) – Name of property.
value (Any) – Anything.
- birthday(add: int = 1)¶
Add to the age. This is for keeping track of how many generations an individual has “lived” through.
- Parameters
add (int) – Amount to age.
- evaluate(params: Optional[dict] = None, island=None) → Any¶
Run the fitness function with the given params.
- Parameters
params (dict) – Named dict of eval params (default = None).
island (Island) – Pass the Island for advanced fitness functions based on Island properties and populations (default = None).
- Returns
Fitness value.
- Return type
numeric
- 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() → dict¶
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
- load(filepath: str)¶
Load an individual from a pickle file.
- Parameters
filepath (str) – File path to load from.
- register_parent_names(parents: list, reset_parent_name_list: bool = True)¶
In keeping lineage of family lines, the names of parents are kept track of.
- Parameters
parents (list) – A list of Individuals of the parents.
- reset_fitness(fitness: Optional[Any] = None, reset_genetic_code: bool = True)¶
Reset (or set) the fitness of the individual.
- Parameters
fitness (Any) – New fitness value (default = None).
reset_genetic_code (bool) – Whether to reset the genetic code. (default = True)
- reset_name(name: Optional[str] = None)¶
A function to reset the name of an individual, helping to keep linage of families.
- Parameters
name (str) – Name (default = None).
- save(filepath: str)¶
Save an individual to a pickle file.
- Parameters
filepath (str) – File path to write to.
- unique_genetic_code(force_update: bool = False) → str¶
Gets the unique genetic code, generating if it is undefined.
- Parameters
force_update (bool) – Force update of genetic_code property (default = False).
- Returns
String name of Chromosome.
- Return type
str
1.1.3.3. Selection functions¶
There are generally three different selection operations that GA employ:
Elite selection
Parent selection
Survivor selection
Islands are initialised by default with classic selection function, but other functions can be used, in addition to writing custom selection functions.
By default the selection_elites_top_n
function is used in islands. This can be swapped out for other Parent selection
functions:
from natural_selection.genetic_algorithms.operators.selection import selection_elites_tournament
isolated_island = Island(function_params=params, elite_selection=selection_elites_tournament)
All selection functions take individuals
(the population, or list of individuals) and island
as parameters by
default, but may take different or custom inputs. With the above example, selection_elites_tournament
takes extra parameters.
To specify the selection function parameters, a dictionary of values can be passed in the evolve
method.
from natural_selection.genetic_algorithms.operators.selection import selection_elites_tournament
esp = {'n' : 4, 'tournament_size' : 5}
isolated_island.evolve(elite_selection_params=esp)
To read up about different elite selection functions and their parameters, see Parent selection.