In [1]:
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, clear_output
import copy

In [11]:
def GA_search(f, x0, xstd, population_size, n_generations,
prob_mutate, prob_crossover,
figure_background_f=None, initialize=True):

debug = False
p = len(x0)

# initialize population
if initialize:
population = np.zeros((population_size,p))
for i in range(p):
population[:, i] = np.random.normal(x0[i], xstd[i], population_size)
evaluations = f(population)

fig = plt.figure(figsize=(20, 10))

#Loop for generations
for generation in range(n_generations):

# Pick worst individual
randorder = np.random.permutation(range(population_size))
worst = randorder[np.argmin(evaluations[randorder])]

# Crossover
if np.random.uniform() < prob_crossover:
# Do Crossover. Pick two best
best2 = np.argsort(-evaluations)[0:2]
# Pick crossover point
point = np.random.randint(1,p)
# This always takes first part from best. Should randomly choose which part from best.
offspring = np.hstack((population[best2[0], 0:point],
population[best2[1], point:p]))
if debug:
print("Cross", best2[0], "and", best2[1], "at", point)
print(population[best2[0]], population[best2[1]], offspring)
else:
# No crossover. Just pick current best
offspring = copy.copy(population[np.argmax(evaluations), :])
if debug:
print("Best", np.argmax(evaluations), np.max(evaluations), offspring)

# Mutation. Make vector of random True False values for each component
mask = np.random.uniform(0, 1, p) < prob_mutate
offspring[i] += np.random.normal(0, xstd[i])
if debug:

# evaluate new offspring. If better than worst, replace worst.
offspringEvaluation = f(offspring.reshape((1, -1)))
if offspringEvaluation >  np.min(evaluations):
if debug:
print ("Replaced index", worst, "eval", evaluations[worst],
'with', offspringEvaluation)
evaluations[worst] = offspringEvaluation
population[worst, :] = offspring

# Graphics
if (generation + 1) % 5 == 0:
plt.clf()
plt.subplot(1, 2, 1)
plt.hist(evaluations, bins=np.linspace(0, 1.2, 10))
plt.xlabel('Fitness')
plt.ylabel('Number in Population')

plt.subplot(1, 2, 2)
if figure_background_f:
figure_background_f()
plt.plot(population[:, 0], population[:, 1], 'wo', alpha=0.8)
plt.xlim(-5, 5)
plt.ylim(-5, 5)
plt.title(f"Generation {generation + 1}")

clear_output(wait=True)
display(fig)

clear_output(wait=True)
return (population, evaluations)

In [12]:
def hills(z):
""" z has 2-d points in rows """
return np.exp(-0.1 * ((z[:, 0] - 2)**2 + (z[:, 1] - 2)**2)) + \
np.exp(-0.5 * ((z[:, 0] - 3)**2 + (z[:, 1] + 2)**2))

In [13]:
xs = np.linspace(-5, 5, 20)
ys = np.linspace(-5, 5, 20)
x, y = np.meshgrid(xs, ys)
points = np.vstack((x.flat, y.flat)).T
zs = hills(points).reshape(x.shape)

def drawContours():
plt.contourf(xs, ys, zs)
plt.colorbar()

plt.figure(figsize=(12, 10))
drawContours()

In [14]:
GA_search?

In [16]:
plt.figure(figsize=(12, 8))
res = GA_search(hills, [4, -2], [5, 5], 40, 500, 0.8, 0.5, drawContours)

<Figure size 864x576 with 0 Axes>

To which other search methods would you like to compare GA_search?

In [ ]: