Example for evolutionary regression with genome reordering

Example demonstrating the effect of genome reordering.

References

Goldman B.W., Punch W.F. (2014): Analysis of Cartesian Genetic Programming’s Evolutionary Mechanisms DOI: 10.1109/TEVC.2014.2324539

# The docopt str is added explicitly to ensure compatibility with
# sphinx-gallery.
docopt_str = """
   Usage:
     example_reorder.py [--max-generations=<N>]

   Options:
     -h --help
     --max-generations=<N>  Maximum number of generations [default: 300]
"""

import matplotlib.pyplot as plt
import numpy as np
import scipy.constants
from docopt import docopt

import cgp

args = docopt(docopt_str)

We first define a target function.

def f_target(x):
    return x ** 2 + 1.0

Then we define the objective function for the evolution. It uses the mean-squared error between the output of the expression represented by a given individual and the target function evaluated on a set of random points.

def objective(individual):
    if not individual.fitness_is_None():
        return individual

    n_function_evaluations = 10000

    np.random.seed(12345)

    f = individual.to_numpy()
    x = np.random.uniform(-4, 4, n_function_evaluations)
    y = f(x)
    loss = np.mean((f_target(x) - y) ** 2)
    individual.fitness = -loss

    return individual

Next, we set up the evolutionary search. We first define the parameters for the genome of individuals, and two evolutionary algorithms without (default) and with genome reordering.

seed = 1234

ea_params_with_reorder = {"reorder_genome": True}

evolve_params = {"max_generations": int(args["--max-generations"]), "termination_fitness": 0.0}

We create two populations that will be evolved

pop = cgp.Population()
pop_with_reorder = cgp.Population()

and an instance of the (mu + lambda) evolutionary algorithm

ea_with_reorder = cgp.ea.MuPlusLambda(**ea_params_with_reorder)

We define two callbacks for recording of fitness over generations

history = {}
history["fitness_champion"] = []


def recording_callback(pop):
    history["fitness_champion"].append(pop.champion.fitness)


history_with_reorder = {}
history_with_reorder["fitness_champion"] = []


def recording_callback_with_reorder(pop):
    history_with_reorder["fitness_champion"].append(pop.champion.fitness)

and finally perform the evolution of the two populations

pop = cgp.evolve(
    objective, pop, **evolve_params, print_progress=True, callback=recording_callback,
)

pop_with_reorder = cgp.evolve(
    objective,
    pop_with_reorder,
    ea_with_reorder,
    **evolve_params,
    print_progress=True,
    callback=recording_callback_with_reorder,
)

Out:

[2/300] max fitness: -47.50872532342548
[3/300] max fitness: -47.50872532342548
[4/300] max fitness: -47.50872532342548
[5/300] max fitness: -41.615623791520306
[6/300] max fitness: -41.615623791520306
[7/300] max fitness: -41.615623791520306
[8/300] max fitness: -41.615623791520306
[9/300] max fitness: -41.615623791520306
[10/300] max fitness: -41.615623791520306
[11/300] max fitness: -41.615623791520306
[12/300] max fitness: -41.615623791520306
[13/300] max fitness: -41.615623791520306
[14/300] max fitness: -41.615623791520306
[15/300] max fitness: -41.615623791520306
[16/300] max fitness: -41.615623791520306
[17/300] max fitness: -41.615623791520306
[18/300] max fitness: -41.615623791520306
[19/300] max fitness: -41.615623791520306
[20/300] max fitness: -41.615623791520306
[21/300] max fitness: -41.615623791520306
[22/300] max fitness: -41.615623791520306
[23/300] max fitness: -41.615623791520306
[24/300] max fitness: -33.86588233032766
[25/300] max fitness: -33.86588233032766
[26/300] max fitness: -33.86588233032766
[27/300] max fitness: -33.86588233032766
[28/300] max fitness: -33.86588233032766
[29/300] max fitness: -33.86588233032766
[30/300] max fitness: -33.86588233032766
[31/300] max fitness: -33.86588233032766
[32/300] max fitness: -33.86588233032766
[33/300] max fitness: -33.86588233032766
[34/300] max fitness: -33.86588233032766
[35/300] max fitness: -33.86588233032766
[36/300] max fitness: -33.86588233032766
[37/300] max fitness: -33.86588233032766
[38/300] max fitness: -33.86588233032766
[39/300] max fitness: -33.86588233032766
[40/300] max fitness: -33.86588233032766
[41/300] max fitness: -33.86588233032766
[42/300] max fitness: -33.86588233032766
[43/300] max fitness: -33.86588233032766
[44/300] max fitness: -33.86588233032766
[45/300] max fitness: -33.86588233032766
[46/300] max fitness: -1.0
[47/300] max fitness: -1.0
[48/300] max fitness: -1.0
[49/300] max fitness: 0.0

[2/300] max fitness: -47.50872532342548
[3/300] max fitness: -47.50872532342548
[4/300] max fitness: -33.86588233032766
[5/300] max fitness: -33.86588233032766
[6/300] max fitness: -33.86588233032766
[7/300] max fitness: -33.86588233032766
[8/300] max fitness: -33.86588233032766
[9/300] max fitness: -33.86588233032766
[10/300] max fitness: -33.86588233032766
[11/300] max fitness: -33.86588233032766
[12/300] max fitness: -1.0
[13/300] max fitness: -1.0
[14/300] max fitness: -1.0
[15/300] max fitness: -1.0
[16/300] max fitness: -1.0
[17/300] max fitness: -1.0
[18/300] max fitness: -1.0
[19/300] max fitness: -1.0
[20/300] max fitness: -1.0
[21/300] max fitness: -1.0
[22/300] max fitness: 0.0

After finishing the evolution, we plot the evolution of the fittest individual with and without genome reordering

width = 9.0
fig = plt.figure(1, figsize=[width, width / scipy.constants.golden])

plt.plot(history["fitness_champion"], label="Champion")
plt.plot(history_with_reorder["fitness_champion"], label="Champion with reorder")

plt.xlabel("Generation")
plt.ylabel("Fitness")
plt.legend(["Champion", "Champion with reorder"])

plt.yscale("symlog")
plt.ylim(-1.0e2, 0.1)
plt.axhline(0.0, color="0.7")

fig.savefig("example_reorder.pdf", dpi=300)
example reorder

Total running time of the script: ( 0 minutes 3.199 seconds)

Gallery generated by Sphinx-Gallery