Populations and individuals

At the core of evolutionary search is the concept of a population that consists of multiple individuals with associated fitness values. In genetic programming each of these individuals contains one or multiple genomes which are compiled to computational graphs. The fitness of each individual is typically assessed by how good these computational graphs solve a given problem.

Population

A population (see cgp.Population()) comprises a fixed number of parent individuals which produce offspring in every generation of the evolutionary algorithm. At each generation, some parents, chosen by a particular selection technique, produce offspring by cloning and randomly mutating the clones.

Individuals

There are two types of individuals:

These classes are thin wrappers around the Genome (see cgp.Genome()) and the CartesianGraph (see cgp.CartesianGraph()) classes.

Any custom properties of individuals that are set within the objective function are copied to their offspring. This, for example, allows recording of variables used during fitness evaluation:

def objective(individual):
    individual.fitness = 1.0
    individual.my_custom_attribute = 123
    return individual

history = {}
history["my_custom_attribute"] = []
def recording_callback(pop):
    history["my_custom_attribute"].append(pop.champion.my_custom_attribute)

Genome

A Genome instance represents a particular genotype, i.e., a specific realization of a 2-dimensional Cartesian graph. It provides methods for randomizing and mutating the genotype. It also stores numerical values of parameters in operator nodes that can be tuned via local search (see Searching numerical values for parameters via local search).

Cartesian Graph

The CartesianGraph class compiles genotypes to phenotypes, i.e., computational graphs. Several compilation targets are available:

  • to_func creates a Python callable

  • to_nympy creates a Python callable that accepts a NumPy array as input and returns a NumPy array

  • to_torch creates a PyTorch module equipped with a forward method that accepts a PyTorch tensor as input and returns a PyTorch tensor

  • to_sympy returns SymPy expressions representing the computational graph