moving most used functions to specitic python files

This commit is contained in:
Louis 2023-06-17 18:09:21 +02:00
parent b21ecce51e
commit 0da622371f
9 changed files with 182 additions and 93 deletions

View File

@ -2,7 +2,7 @@ from sklearn.cluster import KMeans
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import random, time import random, time
from clustering import split_tour_across_clusters from libs.clustering import split_tour_across_clusters
def generate_cities(nb, max_coords=1000): def generate_cities(nb, max_coords=1000):
return [random.sample(range(max_coords), 2) for _ in range(nb)] return [random.sample(range(max_coords), 2) for _ in range(nb)]

View File

@ -2,7 +2,7 @@ from sklearn.cluster import KMeans
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import random, time, math import random, time, math
from clustering import split_tour_across_clusters from libs.clustering import split_tour_across_clusters
random.seed(1) random.seed(1)
@ -66,7 +66,7 @@ def simulated_annealing(cities, color='b', temperature=100000, cooling_rate=0.99
plt.ioff() plt.ioff()
return best_solution return best_solution
nb_ville = 100 nb_ville = 200
max_coords = 1000 max_coords = 1000
nb_truck = 4 nb_truck = 4
temperature = 10000 temperature = 10000

View File

@ -2,7 +2,7 @@ from sklearn.cluster import KMeans
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import random, time, math import random, time, math
from clustering import split_tour_across_clusters from libs.clustering import split_tour_across_clusters
def generate_cities(nb, max_coords=1000): def generate_cities(nb, max_coords=1000):
return [random.sample(range(max_coords), 2) for _ in range(nb)] return [random.sample(range(max_coords), 2) for _ in range(nb)]

View File

@ -2,9 +2,9 @@ from sklearn.cluster import KMeans
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
import random, time, math import random, time, math
from clustering import split_tour_across_clusters from libs.clustering import split_tour_across_clusters
random.seed(2) random.seed(3)
def generate_cities(nb, max_coords=1000): def generate_cities(nb, max_coords=1000):
return [random.sample(range(max_coords), 2) for _ in range(nb)] return [random.sample(range(max_coords), 2) for _ in range(nb)]
@ -60,9 +60,9 @@ class AntColony:
return [self.cities[i] for i in best_ant] return [self.cities[i] for i in best_ant]
nb_ville = 50 nb_ville = 50
max_coords = 10 max_coords = 1000
nb_truck = 1 nb_truck = 2
max_time = 1 max_time = 3
nb_ants = 10 nb_ants = 10
max_time_per_cluster = max_time / nb_truck max_time_per_cluster = max_time / nb_truck
@ -123,6 +123,13 @@ for i, cluster_indices in enumerate(clusters.values()):
print("Total distance for cluster", i, ": ", total_distance(best_route)) print("Total distance for cluster", i, ": ", total_distance(best_route))
# calculate total distance for all clusters
full_total_distance = 0
for route, color in best_routes:
full_total_distance += total_distance(route)
print("Total distance for all clusters: ", full_total_distance)
for i, (route, color) in enumerate(best_routes): for i, (route, color) in enumerate(best_routes):
x = [city[0] for city in route] x = [city[0] for city in route]
y = [city[1] for city in route] y = [city[1] for city in route]

View File

@ -1,81 +0,0 @@
from sklearn.cluster import KMeans
import numpy as np
def split_tour_across_clusters(cities, nb_truck):
if nb_truck == 1:
return {0: list(range(len(cities)))}
# clustering initial
kmeans = KMeans(n_clusters=nb_truck, random_state=0).fit(cities)
clusters = {i:[] for i in range(nb_truck)}
# assignation des indices des villes aux clusters
for i, label in enumerate(kmeans.labels_):
clusters[label].append(i)
max_iterations = len(cities)
iteration = 0
while True:
iteration += 1
if iteration > max_iterations:
print("Le nombre maximum d'itérations a été atteint. La boucle a été interrompue.")
break
# calcul des tailles de clusters
cluster_sizes = {i:len(clusters[i]) for i in range(nb_truck)}
# identification du cluster le plus grand et du plus petit
max_cluster = max(cluster_sizes, key=cluster_sizes.get)
min_cluster = min(cluster_sizes, key=cluster_sizes.get)
# s'il n'y a pas de grande disparité, on arrête la boucle
if cluster_sizes[max_cluster] - cluster_sizes[min_cluster] <= 1:
break
# calcul du centre de chaque cluster
cluster_centers = {i:np.mean([cities[index] for index in clusters[i]], axis=0) for i in range(nb_truck)}
# calcul des distances entre le centre du cluster le plus grand et les autres
distances = {i:np.linalg.norm(cluster_centers[max_cluster]-cluster_centers[i]) for i in range(nb_truck)}
del distances[max_cluster] # on supprime la distance vers lui-même
if nb_truck >= 3:
# on identifie les 2 clusters les plus proches
closest_clusters = sorted(distances, key=distances.get)[:2]
# parmi les deux clusters les plus proches, on choisit le plus petit
if cluster_sizes[closest_clusters[0]] <= cluster_sizes[closest_clusters[1]]:
target_cluster = closest_clusters[0]
else:
target_cluster = closest_clusters[1]
else:
closest_clusters = sorted(distances, key=distances.get)[:1]
target_cluster = closest_clusters[0]
# si le transfert va créer une plus grande disparité, on arrête la boucle
if cluster_sizes[target_cluster] >= cluster_sizes[max_cluster]:
break
# calcul des distances entre le centre du cluster cible et les villes du cluster le plus grand
distances_to_target = {index:np.linalg.norm(cluster_centers[target_cluster]-cities[index])
for index in clusters[max_cluster]}
# on identifie la ville la plus proche du centre du cluster cible
closest_city_index = min(distances_to_target, key=distances_to_target.get)
# on transfère la ville du cluster le plus grand au cluster cible
clusters[target_cluster].append(closest_city_index)
clusters[max_cluster].remove(closest_city_index)
# Ajout du point de départ et d'arrivée pour chaque cluster
depot_index = 0
for cluster in clusters.values():
if cluster[0] != depot_index:
cluster.insert(0, depot_index)
if cluster[-1] != depot_index:
cluster.append(depot_index)
return clusters

View File

@ -1,3 +1 @@
[[6734.0, 1453.0], [2233.0, 10.0], [5530.0, 1424.0], [401.0, 841.0], [3082.0, 1644.0], [7608.0, 4458.0], [7573.0, 3716.0], [7265.0, 1268.0], [6898.0, 1885.0], [1112.0, 2049.0], [5468.0, 2606.0], [5989.0, 2873.0], [4706.0, 2674.0], [4612.0, 2035.0], [6347.0, 2683.0], [6107.0, 669.0], [7611.0, 5184.0], [7462.0, 3590.0], [['6734', '1453'], ['2233', '10'], ['5530', '1424'], ['401', '841'], ['3082', '1644'], ['7608', '4458'], ['7573', '3716'], ['7265', '1268'], ['6898', '1885'], ['1112', '2049'], ['5468', '2606'], ['5989', '2873'], ['4706', '2674'], ['4612', '2035'], ['6347', '2683'], ['6107', '669'], ['7611', '5184'], ['7462', '3590'], ['7732', '4723'], ['5900', '3561'], ['4483', '3369'], ['6101', '1110'], ['5199', '2182'], ['1633', '2809'], ['4307', '2322'], ['675', '1006'], ['7555', '4819'], ['7541', '3981'], ['3177', '756'], ['7352', '4506'], ['7545', '2801'], ['3245', '3305'], ['6426', '3173'], ['4608', '1198'], ['23', '2216'], ['7248', '3779'], ['7762', '4595'], ['7392', '2244'], ['3484', '2829'], ['6271', '2135'], ['4985', '140'], ['1916', '1569'], ['7280', '4899'], ['7509', '3239'], ['10', '2676'], ['6807', '2993'], ['5185', '3258'], ['3023', '1942']]
[7732.0, 4723.0], [5900.0, 3561.0], [4483.0, 3369.0], [6101.0, 1110.0], [5199.0, 2182.0], [1633.0, 2809.0], [4307.0, 2322.0], [675.0, 1006.0], [7555.0, 4819.0], [7541.0, 3981.0], [3177.0, 756.0], [7352.0, 4506.0], [7545.0, 2801.0], [3245.0, 3305.0], [6426.0, 3173.0], [4608.0, 1198.0], [23.0, 2216.0], [7248.0, 3779.0],
[7762.0, 4595.0], [7392.0, 2244.0], [3484.0, 2829.0], [6271.0, 2135.0], [4985.0, 140.0], [1916.0, 1569.0], [7280.0, 4899.0], [7509.0, 3239.0], [10.0, 2676.0], [6807.0, 2993.0], [5185.0, 3258.0], [3023.0, 1942.0]]

51
tests/libs/aco.py Normal file
View File

@ -0,0 +1,51 @@
import math, random, time, numpy as np
def distance(city1, city2):
return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) + 1e-10
def total_distance(cities):
return sum([distance(cities[i - 1], cities[i]) for i in range(len(cities))])
class AntColony:
def __init__(self, cities, n_ants, alpha=1, beta=2, evaporation=0.5, intensification=2, max_time=0.1):
self.cities = cities
self.n = len(cities)
self.n_ants = n_ants
self.alpha = alpha
self.beta = beta
self.evaporation = evaporation
self.intensification = intensification
self.max_time = max_time
self.pheromones = [[1 / self.n for _ in range(self.n)] for __ in range(self.n)]
def choose_next_city(self, ant):
unvisited_cities = [i for i in range(self.n) if i not in ant]
probabilities = [self.pheromones[ant[-1]][i] ** self.alpha * ((1 / distance(self.cities[ant[-1]], self.cities[i])) ** self.beta) for i in unvisited_cities]
total = sum(probabilities)
if total == 0:
probabilities = [1 / len(unvisited_cities) for _ in unvisited_cities]
else:
probabilities = [p / total for p in probabilities]
return np.random.choice(unvisited_cities, p=probabilities)
def update_pheromones(self, ant):
pheromones_delta = self.intensification / total_distance([self.cities[i] for i in ant])
for i in range(len(ant) - 1):
self.pheromones[ant[i]][ant[i+1]] += pheromones_delta
def run(self):
best_ant = []
best_distance = float('inf')
start_time = time.time()
while time.time() - start_time < self.max_time:
ants = [[random.randint(0, self.n - 1)] for _ in range(self.n_ants)]
for ant in ants:
for _ in range(self.n - 1):
ant.append(self.choose_next_city(ant))
ant_distance = total_distance([self.cities[i] for i in ant])
if ant_distance < best_distance:
best_distance = ant_distance
best_ant = ant.copy()
self.update_pheromones(ant)
self.pheromones = [[(1 - self.evaporation) * p for p in row] for row in self.pheromones]
return [self.cities[i] for i in best_ant]

81
tests/libs/clustering.py Normal file
View File

@ -0,0 +1,81 @@
from sklearn.cluster import KMeans
import numpy as np
def split_tour_across_clusters(cities, nb_truck):
if nb_truck == 1:
return {0: list(range(len(cities)))}
# clustering initial
kmeans = KMeans(n_clusters=nb_truck, random_state=0).fit(cities)
clusters = {i:[] for i in range(nb_truck)}
# assignation des indices des villes aux clusters
for i, label in enumerate(kmeans.labels_):
clusters[label].append(i)
max_iterations = len(cities)
iteration = 0
while True:
iteration += 1
if iteration > max_iterations:
print("Le nombre maximum d'itérations a été atteint. La boucle a été interrompue.")
break
# calcul des tailles de clusters
cluster_sizes = {i:len(clusters[i]) for i in range(nb_truck)}
# identification du cluster le plus grand et du plus petit
max_cluster = max(cluster_sizes, key=cluster_sizes.get)
min_cluster = min(cluster_sizes, key=cluster_sizes.get)
# s'il n'y a pas de grande disparité, on arrête la boucle
if cluster_sizes[max_cluster] - cluster_sizes[min_cluster] <= 1:
break
# calcul du centre de chaque cluster
cluster_centers = {i:np.mean([cities[index] for index in clusters[i]], axis=0) for i in range(nb_truck)}
# calcul des distances entre le centre du cluster le plus grand et les autres
distances = {i:np.linalg.norm(cluster_centers[max_cluster]-cluster_centers[i]) for i in range(nb_truck)}
del distances[max_cluster] # on supprime la distance vers lui-même
if nb_truck >= 3:
# on identifie les 2 clusters les plus proches
closest_clusters = sorted(distances, key=distances.get)[:2]
# parmi les deux clusters les plus proches, on choisit le plus petit
if cluster_sizes[closest_clusters[0]] <= cluster_sizes[closest_clusters[1]]:
target_cluster = closest_clusters[0]
else:
target_cluster = closest_clusters[1]
else:
closest_clusters = sorted(distances, key=distances.get)[:1]
target_cluster = closest_clusters[0]
# si le transfert va créer une plus grande disparité, on arrête la boucle
if cluster_sizes[target_cluster] >= cluster_sizes[max_cluster]:
break
# calcul des distances entre le centre du cluster cible et les villes du cluster le plus grand
distances_to_target = {index:np.linalg.norm(cluster_centers[target_cluster]-cities[index])
for index in clusters[max_cluster]}
# on identifie la ville la plus proche du centre du cluster cible
closest_city_index = min(distances_to_target, key=distances_to_target.get)
# on transfère la ville du cluster le plus grand au cluster cible
clusters[target_cluster].append(closest_city_index)
clusters[max_cluster].remove(closest_city_index)
# Ajout du point de départ et d'arrivée pour chaque cluster
depot_index = 0
for cluster in clusters.values():
if cluster[0] != depot_index:
cluster.insert(0, depot_index)
if cluster[-1] != depot_index:
cluster.append(depot_index)
return clusters

View File

@ -0,0 +1,33 @@
import math, random
def distance(city1, city2):
return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
def total_distance(cities):
return sum([distance(cities[i - 1], cities[i]) for i in range(len(cities))])
def simulated_annealing(cities, temperature=10000, cooling_rate=0.9999, temperature_ok=0.001, cluster_index=0):
interration = 0
current_solution = cities.copy()
best_solution = cities.copy()
while temperature > temperature_ok:
new_solution = current_solution.copy()
# Swap two cities in the route
i = random.randint(0, len(new_solution) - 1)
j = random.randint(0, len(new_solution) - 1)
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
# Calculate the acceptance probability
current_energy = total_distance(current_solution)
new_energy = total_distance(new_solution)
delta = new_energy - current_energy
if delta < 0 or random.random() < math.exp(-delta / temperature):
current_solution = new_solution
if total_distance(current_solution) < total_distance(best_solution):
best_solution = current_solution
# Cool down
temperature *= cooling_rate
interration += 1
# Print every 1000 iterations
if interration % 1000 == 0:
print("Cluster", cluster_index, ": iteration", interration, "with current total distance", total_distance(current_solution))
return best_solution