First Genetic Algorithm
What is a Genetic Algorithm
Evolutionary programming is a family of global optimization techniques that are biologically inspired. It works similarly to the natural process in evolutionary biology. Trial and error is a large component along with stochasticity and survival of the fittest. The type of evolutionary computation that we will focus on is genetic algorithms which became popular by the American Professor of Psychology, Electrical Engineering, and Computer Science, John Holland. Genetic algorithms work using natural selection of different possible solutions competing with one another. It incorporates biological concepts such as selection, crossover, and mutation. Candidate solutions can mate with each other and create offspring who can then compete with other solutions in their generation.
Components of a Genetic Algorithm
- Gene
- Chromosome
- Population
- Fitness Function
- Selection
- Crossover
- Mutation
Programming Genetic Algorithms in Python
It’s typical to use object oriented programming when developing Genetic Algorithms in code. In our simple example, let’s create a class called Agent
. In the language of GA, these are called the chromosome. This chromosome will simply be a string in our program but I’d recommend always thinking in terms of GA and the termology used in this domain. If the text string represents a chromosome, then the characters that make up this string are its genes. Below is an example of a simple genetic algorithm that will be given the task of finding the string: ericpena
. The algorithm is broken up into distrinct methods for clarity.
from fuzzywuzzy import fuzz
import random
import string
class Agent:
def __init__(self, length):
# Initialize a new agent
self.string = ''.join(random.choice(string.ascii_letters) for _ in range(length))
self.fitness = -1
def __str__(self):
return 'String: ' + str(self.string) + ' Fitness: ' + str(self.fitness)
Initialize Population
in_str = None
in_str_len = None
population = 20
generations = 2500
def init_agents(population, length):
return [Agent(length) for _ in range(population)]
Define Fitness Function
def fitness(agents):
for agent in agents:
agent.fitness = fuzz.ratio(agent.string, in_str)
return agents
Define Selection Criteria
def selection(agents):
agents = sorted(agents, key=lambda agent: agent.fitness, reverse=True)
print('\n'.join(map(str, agents)))
agents = agents[:int(0.2 * len(agents))]
return agents
Define Crossover Mechanism
def crossover(agents):
offspring = []
for _ in range(int((population - len(agents)) / 2)):
parent1 = random.choice(agents)
parent2 = random.choice(agents)
child1 = Agent(in_str_len)
child2 = Agent(in_str_len)
split = random.randint(0, in_str_len)
child1.string = parent1.string[0:split] + parent2.string[split:in_str_len]
child2.string = parent2.string[0:split] + parent1.string[split:in_str_len]
offspring.append(child1)
offspring.append(child2)
agents.extend(offspring)
return agents
Define GA Mutation
def mutation(agents):
for agent in agents:
for idx, param in enumerate(agent.string):
if random.uniform(0.0, 1.0) <= 0.1:
agent.string = agent.string[0:idx] + \
random.choice(string.ascii_letters) + \
agent.string[idx + 1:in_str_len]
return agents
Running GA
def ga():
agents = init_agents(population, in_str_len)
for generation in range(generations):
print('Generation: ' + str(generation))
agents = fitness(agents)
agents = selection(agents)
agents = crossover(agents)
agents = mutation(agents)
if any(agent.fitness >= 90 for agent in agents):
print('Threshold met!')
exit(0)
if __name__ == '__main__':
in_str = 'ericpena'
in_str_len = len(in_str)
ga()
Results
- Generation 0 — String: CKvOGQJL Fitness: 0
- Generation 3 — String: iJdvcmeX Fitness: 38
- Generation 11 — String: iJOqcmea Fitness: 50
- Generation 85 — String: evXicpea Fitness: 75
- Generation 299 — String: esricena Fitness: 88
As can be seen, the longer we allow this Genetic Algorithm to run, the closer the solution gets to the objective string: ericpena