Skip to content

Simple Genetic Algorithm problem

In this first problem, we will apply the GA of Holland without using almost any libraries, just raw python.

We will try to achieve a target individual known from the start.

Libraries

In this problem we will use just the random library

import random

GA Parameters

Every GA problem is based on some mandatory parameters such as Number of genes, Population size, Mutation rate and Crossover rate

POPULATION_SIZE = 10
NB_GENES = 10
MUTATION_RATE = 0.2
CROSSING_RATE = 0.7

For this problem we need additional parameters for the selection algorithm and our Target chromosome

TARGET_CHROMOSOME = [1, 1, 0, 1, 0, 0, 1, 1, 1, 0]
TOURNAMENT_SELECTION_SIZE = 4

Chromosome

We need to model our Chromosome (individual) by a class, that contain the genes and the fitness

class Chromosome:
    '''Chromosome Init'''
    def __init__(self):
        self.genes = []
        self.fitness = 0
        i = 0
        while i < NB_GENES:
            self.genes.append(random.randint(0,1))
            i += 1

    '''Chromosome Get Value'''
    def get_genes(self):
        return self.genes

    '''Chromosome Fitness'''
    def get_fitness(self):
        # this init is very important
        self.fitness = 0
        for i in range(NB_GENES):
            if(self.genes[i] == TARGET_CHROMOSOME[i]):
                self.fitness += 1
        return self.fitness

    def __str__(self):
        return self.genes.__str__()

Population

After definig our chromosome, we need to define our Population class.

class Population:
    '''Population Init'''
    def __init__(self, size):
        self.chromosomes = []
        i = 0
        while i < size :
            self.chromosomes.append( Chromosome() )
            i += 1
        self.chromosomes.sort(key=lambda x: x.get_fitness(), reverse=True)

    '''Get All Population Chromosomes'''
    def get_chromosomes(self):
        return self.chromosomes

    def print_population(self, gen_number):
        print("\n-----------------------Generation Summary---------------------------")
        print("--------------------------------------------------------------------")
        print("Generation #", gen_number, "| Fittest chromosome fitness:", self.get_chromosomes()[0].get_fitness())
        print("Target chromosome", TARGET_CHROMOSOME)
        print("--------------------------------------------------------------------")
        i = 0
        for x in self.get_chromosomes():
            print("Chromosome #",i," :",x,"| Fitness", x.get_fitness())
            i += 1

Genetic Algorithm Core

the kernel of the genetic algorithm consist of selecting the good individual, making the crossing and mutate them in order to produce a better generation. All of that goes in our next class called GeneticAlgorithm, contains only static methods (optional), because we don't need an object of GeneticAlgorithm Type.

Selection

In this Algorithm example, we will use the tournament selection.

@staticmethod
def select_tournament(pop):
    tournament_pop = Population(0)
    i = 0
    while i < TOURNAMENT_SELECTION_SIZE :
        tournament_pop.get_chromosomes().append(pop.get_chromosomes()[random.randrange(0,POPULATION_SIZE)])
        i += 1
    tournament_pop.get_chromosomes().sort(key=lambda x: x.get_fitness(), reverse=True)
    return tournament_pop.get_chromosomes()[0]

We can define and use another type of selection , which is the Wheel Selection.

@staticmethod
def select_Wheel(pop):
    partialSum = 0
    sumFitness = 0
    for chromosome in pop.get_chromosomes():
        sumFitness += chromosome.get_fitness()

    randomShot = random.random() * sumFitness

    i = -1
    while partialSum < randomShot and i < POPULATION_SIZE-1 :
        i += 1
        partialSum += pop.get_chromosomes()[i].get_fitness()

    return pop.get_chromosomes()[i]

CrossOver

In this Algorithm example, we will use the One Point crossover.

@staticmethod
def crossover_chromosomes(parent1, parent2):
    if random.random() < CROSSING_RATE: 
        child1 = Chromosome()
        child2 = Chromosome()

        '''One Point Cross Over'''
        index = random.randrange(1, NB_GENES)
        child1.genes = parent1.get_genes()[:index] + parent2.get_genes()[index:]
        child2.genes = parent2.get_genes()[:index] + parent1.get_genes()[index:]

        print("\nMaking a cross")
        print("Parent1: ",parent1.get_genes())
        print("Parent2: ",parent2.get_genes())
        print("Child1 : ", child1.get_genes())
        print("Child1 : ", child2.get_genes())

        return child1, child2
    else:
        return parent1, parent2

Mutation

In this Algorithm example, we will use the One Bit Flip.

@staticmethod
def mutate_chromosome(chromosome):
    if random.random() < MUTATION_RATE:
        print("\nMaking a mutation")
        print("From: ",chromosome.get_genes())

        random_bit_position = random.randrange(0,NB_GENES)
        if chromosome.get_genes()[random_bit_position] == 0:
            chromosome.get_genes()[random_bit_position] = 1
        else:
            chromosome.get_genes()[random_bit_position] = 0

        print("To:   ",chromosome.get_genes())

Evolve the population

In this method we will evolve our generation by using the selection, crossover and the mutation.

'''Population evolution Cross Over --> Mutation'''
@staticmethod
def evolve(pop):
    new_pop = Population(0)
    #'''Keep The Fittests Chromosomes'''
    #for i in range(NUMBER_OF_ELITE_CHROMOSOMES):
    #    new_pop.get_chromosomes().append(pop.get_chromosomes()[i])

    print("\nCrossover and Mutation Trace:")
    while new_pop.get_chromosomes().__len__() < POPULATION_SIZE:
        parent1 = GeneticAlgorithm.select_tournament(pop)
        parent2 = GeneticAlgorithm.select_tournament(pop)


        child1, child2 = GeneticAlgorithm.crossover_chromosomes(parent1, parent2)


        GeneticAlgorithm.mutate_chromosome(child1)
        GeneticAlgorithm.mutate_chromosome(child2)


        new_pop.get_chromosomes().append(child1)

        # make sure to not depass the population size if we keep the elite
        if len(new_pop.get_chromosomes()) < POPULATION_SIZE:
            new_pop.get_chromosomes().append(child2)

    new_pop.get_chromosomes().sort(key=lambda x: x.get_fitness(), reverse=True)   
    return new_pop

Main program

After definig all our classes and methods, we will run our main program

generation_number = 0
MAX_FITNESS = TARGET_CHROMOSOME.__len__()
population = Population(POPULATION_SIZE)
population.print_population(generation_number)

while population.get_chromosomes()[0].get_fitness() < MAX_FITNESS :
    generation_number += 1
    population = GeneticAlgorithm.evolve(population)
    population.print_population(generation_number)