Holland
Holland is a genetic algorithm library for Python. The package is application-agnostic and can be used on any problem for which there is a way to encode solutions and evalute the success of a potential solution. The package is named for John H. Holland, a pioneer in genetic algorithms research.
The genetic algorithm is a search algorithm for finding solutions to problems. It is a metaheuristic, meaning that it is a heuristic for finding heuristics for solving other problems. In order for the genetic algorithm to be used on a problem, two things are required: a way to encode solutions to the problem and a method for evaluating the "fitness" of a possible solution.
With these two components the general process for the genetic algorithm is:
- Generate initial (random) solutions
- Evaluate each solution's fitness
- Generate new solutions by performing crossover and mutation on existing solutions according to their fitness
- Return to step 2
For such a simple algorithm, solutions found from employing the genetic algorithm can be quite complex and highly performant.
Holland can be installed using pip with pip install holland.
The repository Holland-Gym contains examples of evolved solutions to some environments from Open AI's Gym.
Hello World
Here is a "Hello World!" example:
from holland import Evolver
from holland.library import get_uniform_crossover_function
from holland.utils import bound_value
import random
# Define a fitness function
def fitness_function(genome):
message = genome["message"]
target = "Hello World!"
score = 0
for i in range(len(message)):
score += abs(ord(target[i]) - ord(message[i]))
return score
def mutation_function(value):
mutated_value = ord(value) * random.random() * 2
return chr(
bound_value(
mutated_value,
minimum=32, maximum=126, to_int=True
)
)
# Define genome parameters for individuals
genome_params = {
"message": {
"type": "[str]",
"size": len("Hello World!"),
"initial_distribution": lambda: chr(random.randint(32, 126)),
"crossover_function": get_uniform_crossover_function(),
"mutation_function": mutation_function,
"mutation_rate": 0.15
}
}
# Define how to select individuals for reproduction
selection_strategy = {"pool": {"top": 10}}
# Run Evolution
evolver = Evolver(
fitness_function,
genome_params,
selection_strategy,
should_maximize_fitness=False
)
final_population = evolver.evolve(
stop_conditions={"target_fitness": 0}
) With a sample run:
Generation: 0; Top Score: 201: N~flx.JGcu-*
Generation: 1; Top Score: 98: Xljlw);mj]f
Generation: 2; Top Score: 64: =c}kk SmsYf
Generation: 3; Top Score: 37: Kcjlk$Vms]f
Generation: 4; Top Score: 24: Cdjkn Smshf
Generation: 5; Top Score: 16: Idjln Vmshf
Generation: 6; Top Score: 14: Idjln Voshf
Generation: 7; Top Score: 11: Hdjln Vmslf
Generation: 8; Top Score: 9: Hdjln Voslf
Generation: 9; Top Score: 8: Hdjln Vosle
Generation: 10; Top Score: 7: Hdmln Vosle
Generation: 11; Top Score: 6: Hdlln Vosle
Generation: 12; Top Score: 5: Hdllo Vosle
Generation: 13; Top Score: 4: Hdllo Vosle!
Generation: 14; Top Score: 3: Hello Vosle!
Generation: 15; Top Score: 2: Hello Wosle!
Generation: 16; Top Score: 2: Hello Wosle!
Generation: 17; Top Score: 1: Hello Worle!
Generation: 18; Top Score: 1: Hello Worle!
Generation: 19; Top Score: 1: Hello Worle!
Generation: 20; Top Score: 0: Hello World!
Best genome:
{
'message': ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!']
}