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
        for i in np.arange(p)[mask]:
            offspring[i] += np.random.normal(0, xstd[i])
        if debug:
            print ("Mutated", sum(mask), "positions", offspring)

        # 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 [ ]: