Implement simulated annealing

This commit is contained in:
coolneng 2021-06-22 09:58:34 +02:00
parent 89045a04c8
commit baf6e40e15
Signed by: coolneng
GPG Key ID: 9893DA236405AF57
1 changed files with 111 additions and 0 deletions

111
src/simulated_annealing.py Normal file
View File

@ -0,0 +1,111 @@
from numpy.random import choice, randint, random
from pandas import DataFrame
from itertools import combinations
from math import exp, log
def get_row_distance(source, destination, data):
row = data.query(
"""(source == @source and destination == @destination) or \
(source == @destination and destination == @source)"""
)
return row["distance"].values[0]
def compute_distance(element, solution, data):
accumulator = 0
distinct_elements = solution.query(f"point != {element}")
for _, item in distinct_elements.iterrows():
accumulator += get_row_distance(
source=element, destination=item.point, data=data
)
return accumulator
def generate_first_solution(n, m, data):
solution = DataFrame(columns=["point", "distance", "fitness"])
solution["point"] = choice(n, size=m, replace=False)
solution["distance"] = solution["point"].apply(
func=compute_distance, solution=solution, data=data
)
solution = evaluate_solution(solution, data)
return solution
def evaluate_solution(solution, data):
fitness = 0
comb = combinations(solution.index, r=2)
for index in list(comb):
elements = solution.loc[index, :]
fitness += get_row_distance(
source=elements["point"].head(n=1).values[0],
destination=elements["point"].tail(n=1).values[0],
data=data,
)
solution["fitness"] = fitness
return solution
def element_in_dataframe(solution, element):
duplicates = solution.query(f"point == {element}")
return not duplicates.empty
def generate_neighbour(previous, n, data):
solution = previous.copy()
random_index = randint(len(solution.point.values))
random_element = randint(n)
while element_in_dataframe(solution=solution, element=random_element):
random_element = randint(n)
solution["point"].loc[random_index] = random_element
solution["distance"].loc[random_index] = compute_distance(
element=solution["point"].loc[random_index], solution=solution, data=data
)
solution = evaluate_solution(solution, data)
return solution
def get_temperatures(initial_fitness, mu=0.3, phi=0.3):
T0 = (mu * initial_fitness) / -(log(phi))
Tf = 1e-3
return T0, Tf
def get_metaparameters(n, max_iterations):
max_neighbours = 10 * n
max_successes = 0.1 * max_neighbours
M = max_iterations / max_neighbours
return max_neighbours, max_successes, M
def beta(T0, Tf, M):
return (T0 - Tf) / (M * T0 * Tf)
def cool_down(T, T0, Tf, M):
return T / (1 + beta(T0, Tf, M) * T)
def simulated_annealing(n, m, data, max_iterations=10000):
initial_solution = generate_first_solution(n, m, data)
T0, Tf = get_temperatures(initial_fitness=initial_solution.fitness.values[0])
max_neighbours, max_successes, M = get_metaparameters(n, max_iterations)
current_solution = initial_solution
best_solution = initial_solution
T = T0
while T > T0:
num_successes = 0
for _ in range(max_neighbours):
neighbour = generate_neighbour(current_solution, n, data)
delta = neighbour.fitness.values[0] - current_solution.fitness.values[0]
if delta > 0 or random() < exp((-delta) / T):
current_sol = neighbour
num_successes += 1
if current_solution.fitness.values[0] > best_solution.fitness.values[0]:
best_solution = current_sol
if num_successes >= max_successes:
break
if num_successes == 0:
break
T = cool_down(T, T0, Tf, M)
return best_solution