Minimal example for evolutionary regressionΒΆ

Example demonstrating the use of Cartesian genetic programming for a simple regression task.

# The docopt str is added explicitly to ensure compatibility with
# sphinx-gallery.
docopt_str = """
   Usage:
     example_minimal.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 = 1000

    np.random.seed(1234)

    f = individual.to_func()
    loss = 0
    for x in np.random.uniform(-4, 4, n_function_evaluations):
        # the callable returned from `to_func` accepts and returns
        # lists; accordingly we need to pack the argument and unpack
        # the return value
        y = f(x)
        loss += (f_target(x) - y) ** 2

    individual.fitness = -loss / n_function_evaluations

    return individual

Next, we set up the evolutionary search. We first define the parameters for the population, the genome of individuals, and the evolutionary algorithm.

population_params = {"n_parents": 1, "seed": 8188211}

genome_params = {
    "n_inputs": 1,
    "n_outputs": 1,
    "n_columns": 12,
    "n_rows": 1,
    "levels_back": 5,
    "primitives": (cgp.Add, cgp.Sub, cgp.Mul, cgp.ConstantFloat),
}

ea_params = {"n_offsprings": 4, "mutation_rate": 0.03, "n_processes": 2}

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

We create a population that will be evolved

pop = cgp.Population(**population_params, genome_params=genome_params)

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

ea = cgp.ea.MuPlusLambda(**ea_params)

We define a callback for recording of fitness over generations

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


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

and finally perform the evolution

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

Out:

[2/300] max fitness: -91.5955167981362
[3/300] max fitness: -67.0965931324104
[4/300] max fitness: -67.0965931324104
[5/300] max fitness: -67.0965931324104
[6/300] max fitness: -54.97072455431481
[7/300] max fitness: -54.97072455431481
[8/300] max fitness: -54.97072455431481
[9/300] max fitness: -54.97072455431481
[10/300] max fitness: -54.97072455431481
[11/300] max fitness: -54.97072455431481
[12/300] max fitness: -54.97072455431481
[13/300] max fitness: -54.97072455431481
[14/300] max fitness: -54.97072455431481
[15/300] max fitness: -54.97072455431481
[16/300] max fitness: -54.97072455431481
[17/300] max fitness: -54.97072455431481
[18/300] max fitness: -54.97072455431481
[19/300] max fitness: -54.97072455431481
[20/300] max fitness: -54.97072455431481
[21/300] max fitness: -54.97072455431481
[22/300] max fitness: -54.97072455431481
[23/300] max fitness: -54.97072455431481
[24/300] max fitness: -54.97072455431481
[25/300] max fitness: -54.97072455431481
[26/300] max fitness: -54.97072455431481
[27/300] max fitness: -54.97072455431481
[28/300] max fitness: -54.97072455431481
[29/300] max fitness: -54.97072455431481
[30/300] max fitness: -54.97072455431481
[31/300] max fitness: -54.97072455431481
[32/300] max fitness: -54.97072455431481
[33/300] max fitness: -54.97072455431481
[34/300] max fitness: -54.97072455431481
[35/300] max fitness: -54.97072455431481
[36/300] max fitness: -54.97072455431481
[37/300] max fitness: -54.97072455431481
[38/300] max fitness: -54.97072455431481
[39/300] max fitness: -54.97072455431481
[40/300] max fitness: -54.97072455431481
[41/300] max fitness: -54.97072455431481
[42/300] max fitness: -54.97072455431481
[43/300] max fitness: -54.97072455431481
[44/300] max fitness: -54.97072455431481
[45/300] max fitness: -54.97072455431481
[46/300] max fitness: -54.97072455431481
[47/300] max fitness: -54.97072455431481
[48/300] max fitness: -54.97072455431481
[49/300] max fitness: -54.97072455431481
[50/300] max fitness: -54.97072455431481
[51/300] max fitness: -54.97072455431481
[52/300] max fitness: -54.97072455431481
[53/300] max fitness: -54.97072455431481
[54/300] max fitness: -54.97072455431481
[55/300] max fitness: -54.97072455431481
[56/300] max fitness: -54.97072455431481
[57/300] max fitness: -54.97072455431481
[58/300] max fitness: -54.97072455431481
[59/300] max fitness: -54.97072455431481
[60/300] max fitness: -54.97072455431481
[61/300] max fitness: -54.97072455431481
[62/300] max fitness: -54.97072455431481
[63/300] max fitness: -54.97072455431481
[64/300] max fitness: -54.97072455431481
[65/300] max fitness: -54.97072455431481
[66/300] max fitness: -54.97072455431481
[67/300] max fitness: -54.97072455431481
[68/300] max fitness: -54.97072455431481
[69/300] max fitness: -54.97072455431481
[70/300] max fitness: -54.97072455431481
[71/300] max fitness: -54.97072455431481
[72/300] max fitness: -54.97072455431481
[73/300] max fitness: -54.97072455431481
[74/300] max fitness: -54.97072455431481
[75/300] max fitness: -54.97072455431481
[76/300] max fitness: -54.97072455431481
[77/300] max fitness: -54.97072455431481
[78/300] max fitness: -54.97072455431481
[79/300] max fitness: -54.97072455431481
[80/300] max fitness: -54.97072455431481
[81/300] max fitness: -54.97072455431481
[82/300] max fitness: -54.97072455431481
[83/300] max fitness: -54.97072455431481
[84/300] max fitness: -54.97072455431481
[85/300] max fitness: -54.97072455431481
[86/300] max fitness: -54.97072455431481
[87/300] max fitness: -44.844855976219186
[88/300] max fitness: -1.0
[89/300] max fitness: -1.0
[90/300] max fitness: -1.0
[91/300] max fitness: -1.0
[92/300] max fitness: -1.0
[93/300] max fitness: -1.0
[94/300] max fitness: -1.0
[95/300] max fitness: -1.0
[96/300] max fitness: -1.0
[97/300] max fitness: -1.0
[98/300] max fitness: -1.0
[99/300] max fitness: -1.0
[100/300] max fitness: -1.0
[101/300] max fitness: -1.0
[102/300] max fitness: -1.0
[103/300] max fitness: -1.0
[104/300] max fitness: -1.0
[105/300] max fitness: -1.0
[106/300] max fitness: -1.0
[107/300] max fitness: -1.0
[108/300] max fitness: -1.0
[109/300] max fitness: -1.0
[110/300] max fitness: -1.0
[111/300] max fitness: -1.0
[112/300] max fitness: -1.0
[113/300] max fitness: -1.0
[114/300] max fitness: -1.0
[115/300] max fitness: -1.0
[116/300] max fitness: -1.0
[117/300] max fitness: -1.0
[118/300] max fitness: -1.0
[119/300] max fitness: -1.0
[120/300] max fitness: -1.0
[121/300] max fitness: -1.0
[122/300] max fitness: -1.0
[123/300] max fitness: -1.0
[124/300] max fitness: -1.0
[125/300] max fitness: -1.0
[126/300] max fitness: -1.0
[127/300] max fitness: -1.0
[128/300] max fitness: -1.0
[129/300] max fitness: -1.0
[130/300] max fitness: -1.0
[131/300] max fitness: -1.0
[132/300] max fitness: -1.0
[133/300] max fitness: -1.0
[134/300] max fitness: -1.0
[135/300] max fitness: -1.0
[136/300] max fitness: -1.0
[137/300] max fitness: -1.0
[138/300] max fitness: -1.0
[139/300] max fitness: -1.0
[140/300] max fitness: -1.0
[141/300] max fitness: -1.0
[142/300] max fitness: -1.0
[143/300] max fitness: -1.0
[144/300] max fitness: -1.0
[145/300] max fitness: -1.0
[146/300] max fitness: -1.0
[147/300] max fitness: -1.0
[148/300] max fitness: -1.0
[149/300] max fitness: -1.0
[150/300] max fitness: -1.0
[151/300] max fitness: -1.0
[152/300] max fitness: -1.0
[153/300] max fitness: -1.0
[154/300] max fitness: -1.0
[155/300] max fitness: -1.0
[156/300] max fitness: -1.0
[157/300] max fitness: -1.0
[158/300] max fitness: -1.0
[159/300] max fitness: -1.0
[160/300] max fitness: -1.0
[161/300] max fitness: -1.0
[162/300] max fitness: -1.0
[163/300] max fitness: -1.0
[164/300] max fitness: -1.0
[165/300] max fitness: -1.0
[166/300] max fitness: -1.0
[167/300] max fitness: -1.0
[168/300] max fitness: -1.0
[169/300] max fitness: -1.0
[170/300] max fitness: -1.0
[171/300] max fitness: -1.0
[172/300] max fitness: -1.0
[173/300] max fitness: -1.0
[174/300] max fitness: -1.0
[175/300] max fitness: -1.0
[176/300] max fitness: -1.0
[177/300] max fitness: -1.0
[178/300] max fitness: -1.0
[179/300] max fitness: -1.0
[180/300] max fitness: -1.0
[181/300] max fitness: -1.0
[182/300] max fitness: -1.0
[183/300] max fitness: -1.0
[184/300] max fitness: -1.0
[185/300] max fitness: -1.0
[186/300] max fitness: -1.0
[187/300] max fitness: -1.0
[188/300] max fitness: -1.0
[189/300] max fitness: -1.0
[190/300] max fitness: -1.0
[191/300] max fitness: -1.0
[192/300] max fitness: -1.0
[193/300] max fitness: -1.0
[194/300] max fitness: -1.0
[195/300] max fitness: -1.0
[196/300] max fitness: -1.0
[197/300] max fitness: -1.0
[198/300] max fitness: -1.0
[199/300] max fitness: -1.0
[200/300] max fitness: -1.0
[201/300] max fitness: -1.0
[202/300] max fitness: -1.0
[203/300] max fitness: -1.0
[204/300] max fitness: -1.0
[205/300] max fitness: -1.0
[206/300] max fitness: -1.0
[207/300] max fitness: -1.0
[208/300] max fitness: -1.0
[209/300] max fitness: -1.0
[210/300] max fitness: -1.0
[211/300] max fitness: -1.0
[212/300] max fitness: -1.0
[213/300] max fitness: -1.0
[214/300] max fitness: -1.0
[215/300] max fitness: -1.0
[216/300] max fitness: -1.0
[217/300] max fitness: -1.0
[218/300] max fitness: -1.0
[219/300] max fitness: -1.0
[220/300] max fitness: -1.0
[221/300] max fitness: -1.0
[222/300] max fitness: -1.0
[223/300] max fitness: -1.0
[224/300] max fitness: -1.0
[225/300] max fitness: -1.0
[226/300] max fitness: -1.0
[227/300] max fitness: -1.0
[228/300] max fitness: -1.0
[229/300] max fitness: -1.0
[230/300] max fitness: -1.0
[231/300] max fitness: -1.0
[232/300] max fitness: -1.0
[233/300] max fitness: -1.0
[234/300] max fitness: 0.0

After finishing the evolution, we plot the result and log the final evolved expression.

width = 9.0
fig, axes = plt.subplots(1, 2, figsize=(width, width / scipy.constants.golden))

ax_fitness, ax_function = axes[0], axes[1]
ax_fitness.set_xlabel("Generation")
ax_fitness.set_ylabel("Fitness")

ax_fitness.plot(history["fitness_champion"], label="Champion")

ax_fitness.set_yscale("symlog")
ax_fitness.set_ylim(-1.0e2, 0.1)
ax_fitness.axhline(0.0, color="0.7")

f = pop.champion.to_func()
x = np.linspace(-5.0, 5.0, 20)
y = [f(x_i) for x_i in x]
y_target = [f_target(x_i) for x_i in x]

ax_function.plot(x, y_target, lw=2, alpha=0.5, label="Target")
ax_function.plot(x, y, "x", label="Champion")
ax_function.legend()
ax_function.set_ylabel(r"$f(x)$")
ax_function.set_xlabel(r"$x$")

fig.savefig("example_minimal.pdf", dpi=300)
example minimal

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

Gallery generated by Sphinx-Gallery