Symbolic Regression
Basic Version
1from deap_er import creator
2from deap_er import tools
3from deap_er import base
4from deap_er import gp
5import operator
6import random
7import numpy
8import math
9
10
11random.seed(1234) # disables randomization
12
13
14def safe_div(left, right):
15 try:
16 return left / right
17 except ZeroDivisionError:
18 return 1
19
20
21def evaluate(individual, toolbox, points):
22 func = toolbox.compile(expr=individual)
23 sq_errors = ((func(x) - x**4 - x**3 - x**2 - x)**2 for x in points)
24 result = math.fsum(sq_errors) / len(points)
25 return result, # The comma is essential here.
26
27
28def setup():
29 pset = gp.PrimitiveSet("MAIN", 1)
30 pset.add_primitive(operator.add, 2)
31 pset.add_primitive(operator.sub, 2)
32 pset.add_primitive(operator.mul, 2)
33 pset.add_primitive(safe_div, 2)
34 pset.add_primitive(operator.neg, 1)
35 pset.add_primitive(math.cos, 1)
36 pset.add_primitive(math.sin, 1)
37 pset.add_ephemeral_constant("rand101", lambda: random.randint(-1, 1))
38 pset.rename_arguments(ARG0='x')
39
40 creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
41 creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
42
43 toolbox = base.Toolbox()
44 toolbox.register("expr", gp.gen_half_and_half, prim_set=pset, min_depth=1, max_depth=2)
45 toolbox.register("individual", tools.init_iterate, creator.Individual, toolbox.expr)
46 toolbox.register("population", tools.init_repeat, list, toolbox.individual)
47 toolbox.register("compile", gp.compile_tree, prim_set=pset)
48 toolbox.register("mate", gp.cx_one_point)
49 toolbox.register("expr_mut", gp.gen_full, min_depth=0, max_depth=2)
50 toolbox.register("mutate", gp.mut_uniform, expr=toolbox.expr_mut, prim_set=pset)
51 toolbox.register("select", tools.sel_tournament, contestants=3)
52 toolbox.register("evaluate", evaluate, toolbox=toolbox, points=[x / 10. for x in range(-10, 10)])
53 toolbox.decorate("mate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
54 toolbox.decorate("mutate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
55
56 stats_size = tools.Statistics(len)
57 stats_fit = tools.Statistics(lambda ind: ind.fitness.values)
58 mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)
59 mstats.register("avg", numpy.mean)
60 mstats.register("std", numpy.std)
61 mstats.register("min", numpy.min)
62 mstats.register("max", numpy.max)
63
64 return toolbox, mstats
65
66
67def print_results(best_ind):
68 if not best_ind.fitness.values < (1.0e-3,):
69 raise RuntimeError('Evolution failed to converge.')
70 print(f'\nEvolution converged correctly.')
71
72
73def main():
74 toolbox, mstats = setup()
75 pop = toolbox.population(size=300)
76 hof = tools.HallOfFame(1)
77 args = dict(
78 toolbox=toolbox,
79 population=pop,
80 generations=40,
81 cx_prob=0.5,
82 mut_prob=0.1,
83 hof=hof,
84 stats=mstats,
85 verbose=True # prints stats
86 )
87 tools.ea_simple(**args)
88 print_results(hof[0])
89
90
91if __name__ == "__main__":
92 main()
Using Numpy
1from deap_er import creator
2from deap_er import tools
3from deap_er import base
4from deap_er import gp
5import random
6import numpy
7
8
9random.seed(1234) # disables randomization
10
11
12def safe_div(left, right):
13 with numpy.errstate(divide='ignore', invalid='ignore'):
14 x = numpy.divide(left, right)
15 if isinstance(x, numpy.ndarray):
16 x[numpy.isinf(x)] = 1
17 x[numpy.isnan(x)] = 1
18 elif numpy.isinf(x) or numpy.isnan(x):
19 x = 1
20 return x
21
22
23def evaluate(individual, toolbox, samples, values):
24 func = toolbox.compile(expr=individual)
25 diff = numpy.sum((func(samples) - values)**2)
26 return diff,
27
28
29def setup():
30 samples = numpy.linspace(-1, 1, 10000)
31 values = samples ** 4 + samples ** 3 + samples ** 2 + samples
32
33 pset = gp.PrimitiveSet("MAIN", 1)
34 pset.add_primitive(numpy.add, 2, name="vadd")
35 pset.add_primitive(numpy.subtract, 2, name="vsub")
36 pset.add_primitive(numpy.multiply, 2, name="vmul")
37 pset.add_primitive(safe_div, 2)
38 pset.add_primitive(numpy.negative, 1, name="vneg")
39 pset.add_primitive(numpy.cos, 1, name="vcos")
40 pset.add_primitive(numpy.sin, 1, name="vsin")
41 pset.add_ephemeral_constant("rand101", lambda: random.randint(-1, 1))
42 pset.rename_arguments(ARG0='x')
43
44 creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
45 creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
46
47 toolbox = base.Toolbox()
48 toolbox.register("expr", gp.gen_half_and_half, prim_set=pset, min_depth=1, max_depth=2)
49 toolbox.register("individual", tools.init_iterate, creator.Individual, toolbox.expr)
50 toolbox.register("population", tools.init_repeat, list, toolbox.individual)
51 toolbox.register("compile", gp.compile_tree, prim_set=pset)
52 toolbox.register("evaluate", evaluate, toolbox=toolbox, samples=samples, values=values)
53 toolbox.register("select", tools.sel_tournament, contestants=3)
54 toolbox.register("mate", gp.cx_one_point)
55 toolbox.register("expr_mut", gp.gen_full, min_depth=0, max_depth=2)
56 toolbox.register("mutate", gp.mut_uniform, expr=toolbox.expr_mut, prim_set=pset)
57
58 stats = tools.Statistics(lambda ind: ind.fitness.values)
59 stats.register("avg", numpy.mean)
60 stats.register("std", numpy.std)
61 stats.register("min", numpy.min)
62 stats.register("max", numpy.max)
63
64 return toolbox, stats
65
66
67def print_results(best_ind):
68 if not best_ind.fitness.values < (1.0e-3,):
69 raise RuntimeError('Evolution failed to converge.')
70 print(f'\nEvolution converged correctly.')
71
72
73def main():
74 toolbox, stats = setup()
75 pop = toolbox.population(size=300)
76 hof = tools.HallOfFame(1)
77 args = dict(
78 toolbox=toolbox,
79 population=pop,
80 generations=40,
81 cx_prob=0.5,
82 mut_prob=0.1,
83 hof=hof,
84 stats=stats,
85 verbose=True # prints stats
86 )
87 tools.ea_simple(**args)
88 print_results(hof[0])
89
90
91if __name__ == "__main__":
92 main()
Using ADF-s
1from deap_er import creator
2from deap_er import tools
3from deap_er import base
4from deap_er import gp
5import operator
6import random
7import numpy
8import math
9
10
11random.seed(1234) # disables randomization
12
13CX_PROB = 0.5
14MUT_PROB = 0.2
15GENS = 40
16
17
18def safe_div(left, right):
19 try:
20 return left / right
21 except ZeroDivisionError:
22 return 1
23
24
25def evaluate(individual, toolbox, points):
26 func = toolbox.compile(expr=individual)
27 sq_errors = ((func(x) - x**4 - x**3 - x**2 - x)**2 for x in points)
28 result = math.fsum(sq_errors) / len(points)
29 return result, # The comma is essential here.
30
31
32def add_primitives(prim_set):
33 prim_set.add_primitive(operator.add, 2)
34 prim_set.add_primitive(operator.sub, 2)
35 prim_set.add_primitive(operator.mul, 2)
36 prim_set.add_primitive(safe_div, 2)
37 prim_set.add_primitive(operator.neg, 1)
38 prim_set.add_primitive(math.cos, 1)
39 prim_set.add_primitive(math.sin, 1)
40
41
42def setup():
43 adf_set_2 = gp.PrimitiveSet("ADF2", 2)
44 add_primitives(adf_set_2)
45
46 adf_set_1 = gp.PrimitiveSet("ADF1", 2)
47 add_primitives(adf_set_1)
48 adf_set_1.add_adf(adf_set_2)
49
50 adf_set_0 = gp.PrimitiveSet("ADF0", 2)
51 add_primitives(adf_set_0)
52 adf_set_0.add_adf(adf_set_1)
53 adf_set_0.add_adf(adf_set_2)
54
55 pset = gp.PrimitiveSet("MAIN", 1)
56 add_primitives(pset)
57 pset.add_ephemeral_constant("rand101", lambda: random.randint(-1, 1))
58 pset.add_adf(adf_set_0)
59 pset.add_adf(adf_set_1)
60 pset.add_adf(adf_set_2)
61 pset.rename_arguments(ARG0='x')
62
63 prim_sets = (pset, adf_set_0, adf_set_1, adf_set_2)
64
65 creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
66 creator.create("Tree", gp.PrimitiveTree)
67 creator.create("Individual", list, fitness=creator.FitnessMin)
68
69 toolbox = base.Toolbox()
70 toolbox.register('adf_expr_0', gp.gen_full, prim_set=adf_set_0, min_depth=1, max_depth=2)
71 toolbox.register('adf_expr_1', gp.gen_full, prim_set=adf_set_1, min_depth=1, max_depth=2)
72 toolbox.register('adf_expr_2', gp.gen_full, prim_set=adf_set_2, min_depth=1, max_depth=2)
73 toolbox.register('main_expr', gp.gen_half_and_half, prim_set=pset, min_depth=1, max_depth=2)
74
75 toolbox.register('ADF0', tools.init_iterate, creator.Tree, toolbox.adf_expr_0)
76 toolbox.register('ADF1', tools.init_iterate, creator.Tree, toolbox.adf_expr_1)
77 toolbox.register('ADF2', tools.init_iterate, creator.Tree, toolbox.adf_expr_2)
78 toolbox.register('MAIN', tools.init_iterate, creator.Tree, toolbox.main_expr)
79
80 func_cycle = [toolbox.MAIN, toolbox.ADF0, toolbox.ADF1, toolbox.ADF2]
81
82 toolbox.register('individual', tools.init_cycle, creator.Individual, func_cycle)
83 toolbox.register('population', tools.init_repeat, list, toolbox.individual)
84 toolbox.register('compile', gp.compile_adf_tree, prim_sets=prim_sets)
85 toolbox.register('mate', gp.cx_one_point)
86 toolbox.register('expr', gp.gen_full, min_depth=1, max_depth=2)
87 toolbox.register('mutate', gp.mut_uniform, expr=toolbox.expr)
88 toolbox.register('select', tools.sel_tournament, contestants=3)
89 toolbox.register('evaluate', evaluate, toolbox=toolbox, points=[x / 10. for x in range(-10, 10)])
90 toolbox.decorate("mate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
91 toolbox.decorate("mutate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
92
93 stats = tools.Statistics(lambda ind: ind.fitness.values)
94 stats.register("avg", numpy.mean)
95 stats.register("std", numpy.std)
96 stats.register("min", numpy.min)
97 stats.register("max", numpy.max)
98
99 logbook = tools.Logbook()
100 logbook.header = "gen", "evals", "std", "min", "avg", "max"
101
102 return toolbox, stats, logbook, prim_sets
103
104
105def print_results(best_ind):
106 if not best_ind.fitness.values < (0.5,):
107 raise RuntimeError('Evolution failed to converge.')
108 print(f'\nEvolution converged correctly.')
109
110
111def main():
112 toolbox, stats, logbook, psets = setup()
113 hof = tools.HallOfFame(1)
114
115 def log_stats(ngen=0):
116 hof.update(pop)
117 record = stats.compile(pop)
118 logbook.record(gen=ngen, evals=len(pop), **record)
119 print(logbook.stream)
120
121 pop = toolbox.population(size=100)
122 for ind in pop:
123 ind.fitness.values = toolbox.evaluate(ind)
124
125 log_stats()
126
127 for generations in range(1, GENS):
128 offspring = toolbox.select(pop, len(pop))
129 offspring = [toolbox.clone(ind) for ind in offspring]
130
131 for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
132 for tree1, tree2 in zip(ind1, ind2):
133 if random.random() < CX_PROB:
134 toolbox.mate(tree1, tree2)
135 del ind1.fitness.values
136 del ind2.fitness.values
137
138 for ind in offspring:
139 for tree, pset in zip(ind, psets):
140 if random.random() < MUT_PROB:
141 toolbox.mutate(individual=tree, prim_set=pset)
142 del ind.fitness.values
143
144 invalids = [ind for ind in offspring if not ind.fitness.is_valid()]
145 for ind in invalids:
146 ind.fitness.values = toolbox.evaluate(ind)
147 pop = offspring
148
149 log_stats(generations)
150
151 print_results(hof[0])
152
153
154if __name__ == "__main__":
155 main()
Using HARM-GP
1from deap_er import creator
2from deap_er import tools
3from deap_er import base
4from deap_er import gp
5import operator
6import random
7import numpy
8import math
9
10
11random.seed(1234) # disables randomization
12
13
14def safe_div(left, right):
15 try:
16 return left / right
17 except ZeroDivisionError:
18 return 1
19
20
21def evaluate(individual, points, toolbox):
22 func = toolbox.compile(expr=individual)
23 sq_errors = ((func(x) - x**4 - x**3 - x**2 - x)**2 for x in points)
24 result = math.fsum(sq_errors) / len(points)
25 return result, # The comma is essential here.
26
27
28def setup():
29 pset = gp.PrimitiveSet("MAIN", 1)
30 pset.add_primitive(operator.add, 2)
31 pset.add_primitive(operator.sub, 2)
32 pset.add_primitive(operator.mul, 2)
33 pset.add_primitive(safe_div, 2)
34 pset.add_primitive(operator.neg, 1)
35 pset.add_primitive(math.cos, 1)
36 pset.add_primitive(math.sin, 1)
37 pset.add_ephemeral_constant("rand101", lambda: random.randint(-1, 1))
38 pset.rename_arguments(ARG0='x')
39
40 creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
41 creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
42
43 toolbox = base.Toolbox()
44 toolbox.register("expr", gp.gen_half_and_half, prim_set=pset, min_depth=1, max_depth=2)
45 toolbox.register("individual", tools.init_iterate, creator.Individual, toolbox.expr)
46 toolbox.register("population", tools.init_repeat, list, toolbox.individual)
47 toolbox.register("compile", gp.compile_tree, prim_set=pset)
48 toolbox.register("mate", gp.cx_one_point)
49 toolbox.register("expr_mut", gp.gen_full, min_depth=0, max_depth=2)
50 toolbox.register("mutate", gp.mut_uniform, expr=toolbox.expr_mut, prim_set=pset)
51 toolbox.register("select", tools.sel_tournament, contestants=3)
52 toolbox.register("evaluate", evaluate, points=[x / 10. for x in range(-10, 10)], toolbox=toolbox)
53 toolbox.decorate("mate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
54 toolbox.decorate("mutate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
55
56 stats_size = tools.Statistics(len)
57 stats_fit = tools.Statistics(lambda ind: ind.fitness.values)
58 mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)
59 mstats.register("avg", numpy.mean)
60 mstats.register("std", numpy.std)
61 mstats.register("min", numpy.min)
62 mstats.register("max", numpy.max)
63
64 return toolbox, mstats
65
66
67def print_results(best_ind):
68 if not best_ind.fitness.values < (1.0e-3,):
69 raise RuntimeError('Evolution failed to converge.')
70 print(f'\nEvolution converged correctly.')
71
72
73def main():
74 toolbox, mstats = setup()
75 pop = toolbox.population(size=300)
76 hof = tools.HallOfFame(1)
77 args = dict(
78 toolbox=toolbox,
79 population=pop,
80 generations=40,
81 cx_prob=0.5,
82 mut_prob=0.1,
83 hof=hof,
84 stats=mstats,
85 verbose=True # prints stats
86 )
87 gp.harm(**args)
88 print_results(hof[0])
89
90
91if __name__ == "__main__":
92 main()
Using Epsilon Lexicase
1from deap_er import creator
2from deap_er import tools
3from deap_er import base
4from deap_er import gp
5import operator
6import random
7import numpy
8import math
9
10
11random.seed(1234) # disables randomization
12
13
14def safe_div(left, right):
15 try:
16 return left / right
17 except ZeroDivisionError:
18 return 1
19
20
21def evaluate(individual, toolbox, points):
22 func = toolbox.compile(expr=individual)
23 sq_errors = ((func(x) - x**4 - x**3 - x**2 - x)**2 for x in points)
24 result = math.fsum(sq_errors) / len(points)
25 return result, # The comma is essential here.
26
27
28def setup():
29 pset = gp.PrimitiveSet("MAIN", 1)
30 pset.add_primitive(operator.add, 2)
31 pset.add_primitive(operator.sub, 2)
32 pset.add_primitive(operator.mul, 2)
33 pset.add_primitive(safe_div, 2)
34 pset.add_primitive(operator.neg, 1)
35 pset.add_primitive(math.cos, 1)
36 pset.add_primitive(math.sin, 1)
37 pset.add_ephemeral_constant("rand101", lambda: random.randint(-1, 1))
38 pset.rename_arguments(ARG0='x')
39
40 creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
41 creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
42
43 toolbox = base.Toolbox()
44 toolbox.register("expr", gp.gen_half_and_half, prim_set=pset, min_depth=1, max_depth=2)
45 toolbox.register("individual", tools.init_iterate, creator.Individual, toolbox.expr)
46 toolbox.register("population", tools.init_repeat, list, toolbox.individual)
47 toolbox.register("compile", gp.compile_tree, prim_set=pset)
48 toolbox.register("mate", gp.cx_one_point)
49 toolbox.register("expr_mut", gp.gen_full, min_depth=0, max_depth=2)
50 toolbox.register("mutate", gp.mut_uniform, expr=toolbox.expr_mut, prim_set=pset)
51 toolbox.register("select", tools.sel_epsilon_lexicase)
52 toolbox.register("evaluate", evaluate, toolbox=toolbox, points=[x / 10. for x in range(-10, 10)])
53 toolbox.decorate("mate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
54 toolbox.decorate("mutate", gp.static_limit(limiter=operator.attrgetter("height"), max_value=17))
55
56 stats_size = tools.Statistics(len)
57 stats_fit = tools.Statistics(lambda ind: ind.fitness.values)
58 mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)
59 mstats.register("avg", numpy.mean)
60 mstats.register("std", numpy.std)
61 mstats.register("min", numpy.min)
62 mstats.register("max", numpy.max)
63
64 return toolbox, mstats
65
66
67def print_results(best_ind):
68 if not best_ind.fitness.values < (1.0e-3,):
69 raise RuntimeError('Evolution failed to converge.')
70 print(f'\nEvolution converged correctly.')
71
72
73def main():
74 toolbox, mstats = setup()
75 pop = toolbox.population(size=300)
76 hof = tools.HallOfFame(1)
77 args = dict(
78 toolbox=toolbox,
79 population=pop,
80 generations=40,
81 cx_prob=0.5,
82 mut_prob=0.1,
83 hof=hof,
84 stats=mstats,
85 verbose=True # prints stats
86 )
87 tools.ea_simple(**args)
88 print_results(hof[0])
89
90
91if __name__ == "__main__":
92 main()