I am trying to implement a genetic algorithm in Python and to display the result of my algorithm I wanted to create an animation using FuncAnimation from matplotlib.animation.
However, I ran into the problem that I end up with only one image. The most interesting thing is that in Google Colab I get one frame, but in Pycharm (the IDE I usually use) I get another one.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def fitness_function(x, y):
return x ** 2 + y ** 2
def initialize_population(pop_size, bounds):
return np.random.uniform(bounds[0], bounds[1], (pop_size, 2))
def select(population, fitnesses):
indices = np.argsort(fitnesses)[:len(population) // 2]
return population[indices]
def crossover(parents, offspring_size):
offsprings = []
for _ in range(offspring_size):
p1, p2 = parents[np.random.choice(len(parents), size=2, replace=False)]
alpha = np.random.rand()
child = alpha * p1 + (1 - alpha) * p2
offsprings.append(child)
return np.array(offsprings)
def mutate(population, bounds, mutation_rate=0.1):
for i in range(len(population)):
if np.random.rand() < mutation_rate:
population[i] += np.random.uniform(-1, 1, size=2)
population[i] = np.clip(population[i], bounds[0], bounds[1])
return population
def genetic_algorithm(pop_size=500, generations=500, bounds=(-10, 10)):
population = initialize_population(pop_size, bounds)
history = []
for gen in range(generations):
fitnesses = np.array([fitness_function(x, y) for x, y in population])
parents = select(population, fitnesses)
offspring_size = pop_size - len(parents)
offspring = crossover(parents, offspring_size)
population = np.vstack((parents, offspring))
population = mutate(population, bounds)
history.append(population.copy())
if gen % 50 == 0:
print(f"Generation {gen}: Best fitness = {fitnesses.min():.4f}")
return history
history = genetic_algorithm()
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
ax.set_title("Evolution of Genetic Algorithm Population")
ax.set_xlabel("X")
ax.set_ylabel("Y")
colors = np.linspace(0, 1, len(history))
scat = ax.scatter([], [], c=[], cmap='viridis', s=50, vmin=0, vmax=1)
ax.plot([0, 0], [-10, 10], color='blue', linewidth=1)
ax.plot([-10, 10], [0, 0], color='blue', linewidth=1)
def update(frame):
print(f"Frame: {frame}, History Length: {len(history)}")
data = history[frame]
scat.set_offsets(data)
scat.set_array(np.full(len(data), colors[frame]))
ax.set_title(f"Generation {frame+1}/{len(history)}")
return scat,
anim = FuncAnimation(fig, update, frames=len(history), interval=50, repeat=False)
plt.show()
print(plt.get_backend())
print(f"Number of generations: {len(history)}")
I’ve read a lot of similar questions, but none of the answers worked for me.
For example, I ran the code that is marked as the required answer in this question ( Matplotlib FuncAnimation only draws one frame ), however I still only got one frame.
If I just haven’t found a question that has an answer to mine, I apologize in advance.
Also, if you suddenly notice any mistakes in the algorithm, I would be grateful if you could point them out to me.