Using the Toolbox¶
The Toolbox class is the main component of computational evolution,
which is a container for all the necessary tools to build genetic algorithms and solve
evolutionary problems with. Using a toolbox enables the separation of algorithms from the
operators, which makes it easy to hot swap the operators during evolution in a plug-and-play
manner as needed.
The two most important methods of a toolbox are register() and unregister(), which are
used to add or remove tools from the toolbox. The register() method takes at least two
arguments: an alias and a function that is going to be associated with the alias. All
subsequent arguments, if there are any, will be implicitly passed into the associated
function when the registered method is called.
Attention
Alias names must be valid Python identifiers. Registering the same alias again overwrites the previous callable.
from deap_er import Toolbox
def add_func(a, b, c):
return a + b + c
toolbox = Toolbox()
toolbox.register("first_alias", add_func, a=1, b=2, c=3) # args are passed implicitly
toolbox.register("other_alias", add_func) # args are passed explicitly
result = toolbox.first_alias() # result == 6
result = toolbox.other_alias(1, 2, 3) # result == 6
The preceding code registers two aliases in the toolbox: first_alias and other_alias,
which point to the same function add_func. In the case of toolbox.first_alias(), the
arguments were already passed during registration, so the method needs to be called without
arguments. In the case of toolbox.other_alias(1, 2, 3), the arguments must be passed
explicitly, because the method was registered without arguments. The difference between
the two is that the arguments of first_alias are static, while the arguments of
other_alias are dynamic.
Tool Registration¶
A toolbox is only as useful as the tools registered into it. The tools module contains a number of useful tools that can be registered into a toolbox. While all tools in the module can be registered into a toolbox, some of them such as algorithms or statistics, are equally useful independently. More on tools in the Operators and Algorithms chapter.
Aliases are attached at runtime, so type checkers do not see
toolbox.mate until you register it. A new toolbox already has
clone (copy.deepcopy) and map (map). For list or
array.array individuals, register tools.clone_individual
instead — it copies the genes and the fitness without a full
deepcopy. Register evaluate_batch when a generation should be
scored in one call; the builtin loops use it in place of
map + evaluate. See
Operators and Algorithms and
Multiprocessing.
from deap_er import Toolbox, tools
toolbox = Toolbox()
toolbox.register("clone", tools.clone_individual)
toolbox.register("mate", tools.cx_two_point)
toolbox.register("mutate", tools.mut_flip_bit, mut_prob=0.2)
toolbox.register("select", tools.sel_tournament, contestants=3)
toolbox.register("evaluate", tools.bm_sphere)
pop, log = tools.ea_simple(**args)
Tool Decoration¶
Tool decoration is a powerful feature that allows the precise control of parameters during the evolution process. For example, in the case of constrained domains, a tool decorator can be used on mutation and crossover operators to prevent the individuals from growing out-of-bounds.
The following example defines a decorator that checks if the solution values of offsprings are out-of-bounds and clamps them to the predefined limit values if this is the case. Whenever either of the decorated tools is called, bounds will be checked on the resulting offsprings.
Note
This decorator works for both crossover and mutation operators, because the return type of these operators must be a tuple.
def clamp(min, max):
def wrapper(func):
def wrapped(*args, **kwargs):
offsprings: tuple = func(*args, **kwargs)
for child in offsprings:
for i in range(len(child)):
if child[i] > max:
child[i] = max
elif child[i] < min:
child[i] = min
return offsprings
return wrapped
return wrapper
toolbox.register("mate", tools.cx_blend, alpha=0.2)
toolbox.register("mutate", tools.mut_gaussian,
mu=0, sigma=2, mut_prob=0.2
)
toolbox.decorate("mate", clamp(MIN, MAX))
toolbox.decorate("mutate", clamp(MIN, MAX))
For Gaussian mutation alone, tools.mut_gaussian_bounded clamps each
mutated gene into [low, up] inside the operator. Blend has a boxed
form too (cx_blend_bounded). The decorator remains the generic wrap
for any other tool.