To create a genetic simulator in python we will be utilizing the numpy and matplotlib libraries. The following is a general outline of the steps we will need to follow:
Here is a sample implementation of a genetic algorithm for optimizing a function that takes two inputs(x,y) and returns a value:
import numpy as np
import matplotlib.pyplot as plt
def f(x,y):
"""
The function to optimize
"""
return np.sin(x) + np.cos(y)
def initialize_population(pop_size, gene_length):
"""
Creates a population of individuals
"""
return np.random.randint(2, size=(pop_size,gene_length))
def select_parents(population, fitness):
"""
Select the best-fit individuals from the population
"""
sorted_fitness = sorted(range(len(fitness)), key=lambda x: fitness[x], reverse=True)
parents = np.empty_like(population)
for i in range(len(parents)):
parents[i] = population[sorted_fitness[i]]
return parents[:len(population)//2,:], parents[len(population)//2:,:]
def crossover(parents):
"""
Creates new individuals by combining genes from the best-fit individuals
"""
new_pop = np.empty_like(parents)
for i in range(parents.shape[0]):
cross_point = np.random.randint(parents.shape[1])
new_pop[i,:cross_point] = parents[i, :cross_point]
new_pop[i,cross_point:] = parents[(i+1)%parents.shape[0],cross_point:]
return new_pop
def mutate(population):
"""
Randomly mutate some genes in the population to introduce variation
"""
for i in range(population.shape[0]):
mutation_point = np.random.randint(population.shape[1])
population[i,mutation_point] = 1 - population[i,mutation_point]
return population
# Parameters
pop_size = 50
gene_length = 20
num_generations = 500
mutation_prob = 0.05
# Initialization
population = initialize_population(pop_size, gene_length)
fitness = np.empty(pop_size)
# Let's evolve!
for generation in range(num_generations):
# Evaluate Fitness
for i in range(pop_size):
genes = population[i,:]
x = (np.sum(2**np.arange(gene_length)*genes[:gene_length//2])/sum(2**np.arange(gene_length//2)))
y = (np.sum(2**np.arange(gene_length)*genes[gene_length//2:])/sum(2**np.arange(gene_length//2)))
fitness[i] = f(x,y)
# Visualize the best result
index = np.argmax(fitness)
best_genes = population[index,:]
x = (np.sum(2**np.arange(gene_length)*best_genes[:gene_length//2])/sum(2**np.arange(gene_length//2)))
y = (np.sum(2**np.arange(gene_length)*best_genes[gene_length//2:])/sum(2**np.arange(gene_length//2)))
if generation % 50 == 0:
print(f'{generation} Best Genes: {best_genes} Best Fitness: {fitness[index]}')
print(f'Result: f({round(x,4)}, {round(y,4)}) = {round(fitness[index],4)}')
# Selection
parents_a, parents_b = select_parents(population, fitness)
# Crossover
population[:pop_size//2,:] = crossover(parents_a)
population[pop_size//2:,:] = crossover(parents_b)
# Mutation
for i in range(pop_size):
if np.random.rand() < mutation_prob:
population[i,:] = mutate(population[i,:])
# Plot the final result (Optional)
x_vals = np.linspace(-1*np.pi, np.pi, 50)
y_vals = np.linspace(-1*np.pi, np.pi, 50)
z_vals = np.zeros((len(x_vals), len(y_vals)))
for i in range(len(x_vals)):
for j in range(len(y_vals)):
z_vals[i,j] = f(x_vals[i], y_vals[j])
X, Y = np.meshgrid(x_vals, y_vals)
fig = plt.figure(figsize=(6,5))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, z_vals, cmap='viridis')
ax.scatter(x, y, fitness[index], s=100, color='red')
plt.title(f'Result: f({round(x,4)}, {round(y,4)}) = {round(fitness[index],4)}')
plt.show()
This code utilizes the numpy library to handle numpy array operations efficiently and the matplotlib library to visualize the results. By running this code we can find maximum value of the function by optimizing the parameters x and y.