MH-P2/src/genetic_algorithm.py

139 lines
4.8 KiB
Python
Raw Normal View History

2021-05-24 18:17:40 +02:00
from numpy import sum, append, arange, delete, where
from numpy.random import randint, choice
from pandas import DataFrame
2021-04-29 12:33:46 +02:00
2021-05-24 18:17:40 +02:00
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"])
solution["point"] = choice(n, size=m, replace=False)
solution["distance"] = solution["point"].apply(
func=compute_distance, solution=solution, data=data
)
2021-05-10 19:25:06 +02:00
return solution
2021-05-17 20:42:17 +02:00
def evaluate_element(element, data):
fitness = []
2021-05-24 18:17:40 +02:00
genotype = element.point.values
distances = data.query(f"source in @genotype and destination in @genotype")
for item in genotype[:-1]:
2021-05-17 20:42:17 +02:00
element_df = distances.query(f"source == {item} or destination == {item}")
max_distance = element_df["distance"].astype(float).max()
fitness = append(arr=fitness, values=max_distance)
distances = distances.query(f"source != {item} and destination != {item}")
return sum(fitness)
2021-04-29 12:33:46 +02:00
2021-05-24 18:17:40 +02:00
def select_random_genes(matching_genes, parents, m):
cutoff = randint(m)
distinct_genes = delete(arange(m), matching_genes)
first_parent_genes = parents[0].point.iloc[distinct_genes[cutoff:]]
second_parent_genes = parents[1].point.iloc[distinct_genes[:cutoff]]
return first_parent_genes, second_parent_genes
def repair_offspring(offspring, parents, m):
while len(offspring) != m:
if len(offspring) > m:
best_index = offspring["distance"].astype(float).idxmax()
offspring.drop(index=best_index, inplace=True)
elif len(offspring) < m:
random_parent = parents[randint(len(parents))]
best_index = random_parent["distance"].astype(float).idxmax()
best_point = random_parent["point"].loc[best_index]
offspring = offspring.append(
{"point": best_point, "distance": 0}, ignore_index=True
)
return offspring
def get_matching_genes(parents):
first_parent = parents[0].point
second_parent = parents[1].point
return where(first_parent == second_parent)
def uniform_crossover(parents, m):
offspring = DataFrame(columns=["point", "distance"])
matching_genes = get_matching_genes(parents)
offspring["point"] = parents[0].point.iloc[matching_genes]
first_genes, second_genes = select_random_genes(matching_genes, parents, m)
offspring["point"] = offspring["point"].append(first_genes)
offspring["point"] = offspring["point"].append(second_genes)
offspring["distance"] = 0
viable_offspring = repair_offspring(offspring, parents, m)
return viable_offspring
def position_crossover(parents, n):
genotypes = [parents[0].point.values, parents[1].point.values]
def crossover(mode, parents, m):
if mode == "uniform":
return uniform_crossover(parents, m)
return position_crossover(parents, m)
2021-04-29 12:33:46 +02:00
def element_in_dataframe(solution, element):
2021-05-24 18:17:40 +02:00
duplicates = solution.query(f"point == {element}")
2021-04-29 12:33:46 +02:00
return not duplicates.empty
2021-05-24 18:17:40 +02:00
def replace_worst_element(previous, n, data):
2021-04-29 12:33:46 +02:00
solution = previous.copy()
worst_index = solution["distance"].astype(float).idxmin()
2021-05-24 18:17:40 +02:00
random_element = randint(n)
2021-04-29 12:33:46 +02:00
while element_in_dataframe(solution=solution, element=random_element):
2021-05-24 18:17:40 +02:00
random_element = randint(n)
solution["point"].loc[worst_index] = random_element
solution["distance"].loc[worst_index] = compute_distance(
element=solution["point"].loc[worst_index], solution=solution, data=data
)
return solution
2021-04-29 12:33:46 +02:00
2021-05-24 18:17:40 +02:00
def get_random_solution(previous, n, data):
solution = replace_worst_element(previous, n, data)
while solution["distance"].sum() <= previous["distance"].sum():
solution = replace_worst_element(previous=solution, n=n, data=data)
2021-04-29 12:33:46 +02:00
return solution
2021-05-24 18:17:40 +02:00
def explore_neighbourhood(element, n, data, max_iterations=100000):
2021-04-29 12:33:46 +02:00
neighbourhood = []
neighbourhood.append(element)
for _ in range(max_iterations):
previous_solution = neighbourhood[-1]
2021-05-24 18:17:40 +02:00
neighbour = get_random_solution(previous=previous_solution, n=n, data=data)
2021-04-29 12:33:46 +02:00
neighbourhood.append(neighbour)
return neighbour
2021-05-10 19:25:06 +02:00
def genetic_algorithm(n, m, data):
2021-05-24 18:17:40 +02:00
first_solution = generate_first_solution(n, m, data)
2021-04-29 12:33:46 +02:00
best_solution = explore_neighbourhood(
2021-05-24 18:17:40 +02:00
element=first_solution, n=n, data=data, max_iterations=100
2021-04-29 12:33:46 +02:00
)
return best_solution