Format of Report
Format of Report
The tutorial starts by presenting the equation that we are going to implement. The
equation is shown below:
The equation has 6 inputs (x1 to x6) and 6 weights (w1 to w6) as shown and inputs
values are (x1,x2,x3,x4,x5,x6)=(4,-2,7,5,11,1). We are looking to find the
parameters (weights that maximize such equation. The idea of maximizing such
equation seems simple. The positive input is to be multiplied by the largest
possible positive number and the negative number is to be multiplied by the
smallest possible negative number. But the idea we are looking to implement is
how to make GA do that its own in order to know that it is better to use positive
weight with positive inputs and negative weights with negative inputs. Let us start
implementing GA.
At first, let us create a list of the 6 inputs and a variable to hold the number of
weights as follows:Tutorial Example
The tutorial starts by presenting the equation that we are going to implement. The
equation is shown below:
The equation has 6 inputs (x1 to x6) and 6 weights (w1 to w6) as shown and inputs
values are (x1,x2,x3,x4,x5,x6)=(4,-2,7,5,11,1). We are looking to find the
parameters (weights) that maximize such equation. The idea of maximizing such
equation seems simple. The positive input is to be multiplied by the largest
possible positive number and the negative number is to be multiplied by the
smallest possible negative number. But the idea we are looking to implement is
how to make GA do that its own in order to know that it is better to use positive
weight with positive inputs and negative weights with negative inputs. Let us start
implementing GA.
1|Page
At first, let us create a list of the 6 inputs and a variable to hold the number of
weights as follows:
equation_inputs = [4,-2,3.5,5,-11,-4.7]
num_weights = 6
The next step is to define the initial population. Based on the number of weights,
each chromosome (solution or individual) in the population will definitely have 6
genes, one gene for each weight. But the question is how many solutions per the
population? There is no fixed value for that and we can select the value that fits
well with our problem. But we could leave it generic so that it can be changed in
the code. Next, we create a variable that holds the number of solutions per
population, another to hold the size of the population, and finally, a variable that
holds the actual initial population:
import numpy
sol_per_pop = 8
2|Page
new_population = numpy.random.uniform(low=-4.0, high=4.0, size=pop_size)
After importing the numpy library, we are able to create the initial population
randomly using the numpy.random.uniform function. According to the selected
parameters, it will be of shape (8, 6). That is 8 chromosomes and each one has 6
genes, one for each weight. After running this code, the population is as follows:
Note that it is generated randomly and thus it will definitely change when get run
again.
After preparing the population, next is to follow the flowchart in figure 1. Based on
the fitness function, we are going to select the best individuals within the current
population as parents for mating. Next is to apply the GA variants (crossover and
mutation) to produce the offspring of the next generation, creating the new
population by appending both parents and offspring, and repeating such steps for
a number of iterations/generations. The next code applies these steps:
3|Page
import ga
num_generations = 5
num_parents_mating = 4
num_parents_mating)
offspring_crossover = ga.crossover(parents,
offspring_size=(pop_size[0]-parents.shape[0],
num_weights))
offspring_mutation = ga.mutation(offspring_crossover)
4|Page
# Creating the new population based on the parents and offspring.
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
The first step is to find the fitness value of each solution within the population
using the ga.cal_pop_fitness function. The implementation of such function inside
the GA module is as follows:
# The fitness function calculates the sum of products between each input and
its corresponding weight.
return fitness
The fitness function accepts both the equation inputs values (x1 to x6) in addition
to the population. The fitness value is calculated as the sum of product (SOP)
between each input and its corresponding gene (weight) according to our function.
According to the number of solutions per population, there will be a number of
SOPs. As we previously set the number of solutions to 8 in the variable named
sol_per_pop, there will be 8 SOPs as shown below:
5|Page
Note that the higher the fitness value the better the solution.
After calculating the fitness values for all solutions, next is to select the best of
them as parents in the mating pool according to the next function
ga.select_mating_pool. Such function accepts the population, the fitness values,
and the number of parents needed. It returns the parents selected. Its
implementation inside the GA module is as follows:
max_fitness_idx = max_fitness_idx[0][0]
parents[parent_num, :] = pop[max_fitness_idx, :]
6|Page
fitness[max_fitness_idx] = -99999999999
return parents
Looping through the current population, the function gets the index of the highest
fitness value because it is the best solution to be selected according to this line:
This index is used to retrieve the solution that corresponds to such fitness value
using this line:
parents[parent_num, :] = pop[max_fitness_idx, :]
To avoid selecting such solution again, its fitness value is set to a very small value
that is likely to not be selected again which is -99999999999. The parents array is
returned finally which will be as follows according to our example:
Next step is to use such selected parents for mating in order to generate the
offspring. The mating starts with the crossover operation according to the
ga.crossover function. This function accepts the parents and the offspring size. It
uses the offspring size to know the number of offspring to produce from such
parents. Such a function is implemented as follows inside the GA module:
offspring = numpy.empty(offspring_size)
# The point at which crossover takes place between two parents. Usually, it is at
the center.
crossover_point = numpy.uint8(offspring_size[1]/2)
for k in range(offspring_size[0]):
parent1_idx = k%parents.shape[0]
parent2_idx = (k+1)%parents.shape[0]
# The new offspring will have its first half of its genes taken from the first
parent.
8|Page
# The new offspring will have its second half of its genes taken from the
second parent.
return offspring
The function starts by creating an empty array based on the offspring size as in
this line:
offspring = numpy.empty(offspring_size)
Because we are using single point crossover, we need to specify the point at which
crossover takes place. The point is selected to divide the solution into two equal
halves according to this line:
crossover_point = numpy.uint8(offspring_size[1]/2)
9|Page