import math
import random
import numpy as np
import matplotlib.pyplot as plot
'''
Parameters.
'''
individuals_count = 30
childs_count = 200
ackley_n = 30
ackley_x_min = -40
ackley_x_max = 40
gaussian_deviation_min = 0.0001
gaussian_deviation_max = 12
learning_rate = 1/math.sqrt(2*math.sqrt(individuals_count))
learning_rate_l = 1/math.sqrt(2*individuals_count)
max_iteration_count = 500
'''
Metrics.
'''
solution = []
sample_rate = 10
sampled_axis = []
sampled_bests = []
sampled_averages = []
sampled_deviations = []
'''
Ackley function implementation.
'''
def ackley(v):
const_n = len(v)
const_c1 = 20.0
const_c2 = 0.2
const_c3 = 2*math.pi
a = sum([math.pow(x, 2) for x in v])
b = (1/const_n)*a
c = (-const_c2)*math.sqrt(b)
d = (-const_c1)*math.exp(c)
e = sum([math.cos(const_c3*x) for x in v])
f = (1/const_n)*e
g = math.exp(f)
h = d - g + const_c1 + math.exp(1)
return h
'''
Assumes the fitness is equal to the ackley function evaluation.
'''
def fitness(individual):
return ackley(individual[:ackley_n])
'''
Generates a random individual. The individuals are represented as lists of
floats, composed by: the first "ackley_n" numbers are the solution vector,
the following "ackley_n" values are the mutation step size in each direction
and then a single number representing the individual fitness.
'''
def generateIndividual():
min = gaussian_deviation_min
max = gaussian_deviation_max
x_vals = [random.uniform(ackley_x_min, ackley_x_max) for _ in range(ackley_n)]
sigma_vals = [random.uniform(min, max) for _ in range(ackley_n)]
individual = x_vals + sigma_vals + [0]
individual[-1] = fitness(individual)
return individual
'''
Generate the initial population.
'''
population = []
for i in range(individuals_count):
population.append(generateIndividual())
'''
Include the initial population in the metrics calculation.
'''
solution = population[0]
fitnesses = [p[-1] for p in population]
best = population[0][-1]
mean = np.mean(fitnesses)
std_deviation = np.std(fitnesses)
sampled_axis.append(0)
sampled_bests.append(best)
sampled_averages.append(mean)
sampled_deviations.append(std_deviation)
'''
Main algorithm loop.
'''
for iteration in range(max_iteration_count):
'''
Print the current iteration and the best fitness found.
'''
print("{:7.0f}".format(iteration+1) + " " + str(solution[-1]))
'''
Parents selection & Recombination. We adopt a global recombination strategy
along with a discrete recombination for the object part and an intermediate
recombination for the strategy parameters. So, basically, each family can
have multiple parents generating a child whose alleles [x1...xi] are
selected randomically from their parents and the strategy parameters are
averaged.
'''
childs = []
for i in range(childs_count):
count = random.randint(2, (individuals_count/2))
family = [random.choice(population) for _ in range(count)]
x_vals = [random.choice(family)[k] for k in range(ackley_n)]
sigma_vals = [(sum(a[k+ackley_n] for a in family)/count) for k in range(ackley_n)]
child = x_vals + sigma_vals + [0]
child[-1] = fitness(child)
childs.append(child)
'''
Mutate childs (Uncorrelated Mutation With N Step Sizes, Eiben Pag. 60).
'''
for child in childs:
const_g = random.gauss(0, 1)
for i in range(ackley_n):
g = random.gauss(0, 1)
a = learning_rate_l*const_g
b = learning_rate*g
sigma = child[i+ackley_n]
sigma_l = sigma*math.exp(a+b)
if sigma_l < gaussian_deviation_min:
sigma_l = gaussian_deviation_min
child[i+ackley_n] = sigma_l
for i in range(ackley_n):
g = random.gauss(0, 1)
x = child[i]+(child[i+ackley_n]*g)
if ackley_x_min <= x and x <= ackley_x_max:
child[i] = x
child[-1] = fitness(child)
'''
Survivor selection.
'''
childs.sort(key=lambda x: x[-1], reverse=False)
population = childs[:individuals_count]
'''
Update metrics (retain best solution found).
'''
if solution[-1] > population[0][-1]:
solution = population[0]
'''
Sampling, populate data sources to generate graphs later.
'''
if (iteration % sample_rate == 0):
fitnesses = [p[-1] for p in population]
best = population[0][-1]
mean = np.mean(fitnesses)
std_deviation = np.std(fitnesses)
sampled_axis.append(iteration+1)
sampled_bests.append(best)
sampled_averages.append(mean)
sampled_deviations.append(std_deviation)
'''
Present results.
'''
print("")
print("Best solution (" + str(solution[-1]) + "):")
print("")
'''
Generate graphs for the best individual, fitnesses mean and fitnesses standard
deviation over the iterations.
'''
plot.figure()
plot.plot(sampled_axis, sampled_bests)
plot.xlabel("Iter")
plot.ylabel("Best Value")
plot.savefig("results/algorithm2-best.png", bbox_inches="tight")
plot.figure()
plot.plot(sampled_axis, sampled_averages)
plot.xlabel("Iteration")
plot.ylabel("Fitness Averages")
plot.savefig("results/algorithm2-averages.png", bbox_inches="tight")
plot.figure()
plot.plot(sampled_axis, sampled_deviations)
plot.xlabel("Iteration")
plot.ylabel("Fitness Standard Deviations")
plot.savefig("results/algorithm2-deviations.png", bbox_inches="tight")