From 3a4ab956995f2fff2bb8888bd1e7b45a7e417869 Mon Sep 17 00:00:00 2001 From: Wu Han Date: Fri, 8 Jan 2021 16:27:39 +0000 Subject: [PATCH] [new] Different test time for different problems --- template/main.py | 5 +- template/model/my_model.py | 15 +- template/model/my_model_ACO.py | 138 ++++++++++++++ template/model/my_model_AS.py | 76 ++++++++ template/model/my_model_BFS.py | 49 +++++ template/model/my_model_DFS.py | 44 +++++ template/model/my_model_DPB.py | 63 +++++++ template/model/my_model_DPD.py | 59 ++++++ template/model/my_model_GA.py | 171 ++++++++++++++++++ template/model/my_model_HillClimb.py | 75 ++++++++ template/model/my_model_Random.py | 27 +++ .../model/{anneal_model.py => my_model_SA.py} | 21 ++- template/model/my_model_UCS.py | 71 ++++++++ template/{utils => }/tsp.py | 97 ++++++---- 14 files changed, 853 insertions(+), 58 deletions(-) create mode 100644 template/model/my_model_ACO.py create mode 100644 template/model/my_model_AS.py create mode 100644 template/model/my_model_BFS.py create mode 100644 template/model/my_model_DFS.py create mode 100644 template/model/my_model_DPB.py create mode 100644 template/model/my_model_DPD.py create mode 100644 template/model/my_model_GA.py create mode 100644 template/model/my_model_HillClimb.py create mode 100644 template/model/my_model_Random.py rename template/model/{anneal_model.py => my_model_SA.py} (89%) create mode 100644 template/model/my_model_UCS.py rename template/{utils => }/tsp.py (72%) diff --git a/template/main.py b/template/main.py index ecd6f46..1f73678 100644 --- a/template/main.py +++ b/template/main.py @@ -2,9 +2,8 @@ import os import signal import json -from utils.tsp import TSP -from utils.tsp import TSP_Bench_ALL +from tsp import TSP_Bench_ALL from model.my_model import MyModel if __name__ == "__main__": - TSP_Bench_ALL("./data", MyModel) + TSP_Bench_ALL('./', MyModel) diff --git a/template/model/my_model.py b/template/model/my_model.py index c44aead..a0fcc62 100644 --- a/template/model/my_model.py +++ b/template/model/my_model.py @@ -1,7 +1,6 @@ import math import random from model.base_model import Model -import numpy as np class MyModel(Model): def __init__(self): @@ -13,15 +12,13 @@ class MyModel(Model): """ super().init(nodes) - def fit(self, max_it): + self.log("Nothing to initialize in your model now") + + def fit(self, max_it=1000): """ Put your iteration process here. """ - random_solutions = [] - for i in range(0, max_it): - solution = np.random.permutation(self.N).tolist() - random_solutions.append(solution) - self.fitness_list.append(self.fitness(solution)) + self.best_solution = np.random.permutation(self.N).tolist() + self.fitness_list.append(self.fitness(self.best_solution)) - self.best_solution = random_solutions[self.fitness_list.index(min(self.fitness_list))] - return self.best_solution, self.fitness_list \ No newline at end of file + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_ACO.py b/template/model/my_model_ACO.py new file mode 100644 index 0000000..c9239ec --- /dev/null +++ b/template/model/my_model_ACO.py @@ -0,0 +1,138 @@ +import math +import random +from model.base_model import Model + +class MyACOModel(Model): + def __init__(self): + super().__init__() + + class Edge: + def __init__(self, a, b, weight, initial_pheromone): + self.a = a + self.b = b + self.weight = weight + self.pheromone = initial_pheromone + + class Ant: + def __init__(self, alpha, beta, num_nodes, edges): + self.alpha = alpha + self.beta = beta + self.num_nodes = num_nodes + self.edges = edges + self.tour = None + self.distance = 0.0 + + def _select_node(self): + roulette_wheel = 0.0 + unvisited_nodes = [node for node in range(self.num_nodes) if node not in self.tour] + heuristic_total = 0.0 + for unvisited_node in unvisited_nodes: + heuristic_total += self.edges[self.tour[-1]][unvisited_node].weight + for unvisited_node in unvisited_nodes: + roulette_wheel += pow(self.edges[self.tour[-1]][unvisited_node].pheromone, self.alpha) * \ + pow((heuristic_total / self.edges[self.tour[-1]][unvisited_node].weight), self.beta) + random_value = random.uniform(0.0, roulette_wheel) + wheel_position = 0.0 + for unvisited_node in unvisited_nodes: + wheel_position += pow(self.edges[self.tour[-1]][unvisited_node].pheromone, self.alpha) * \ + pow((heuristic_total / self.edges[self.tour[-1]][unvisited_node].weight), self.beta) + if wheel_position >= random_value: + return unvisited_node + + def find_tour(self): + self.tour = [random.randint(0, self.num_nodes - 1)] + while len(self.tour) < self.num_nodes: + self.tour.append(self._select_node()) + return self.tour + + def get_distance(self): + self.distance = 0.0 + for i in range(self.num_nodes): + self.distance += self.edges[self.tour[i]][self.tour[(i + 1) % self.num_nodes]].weight + return self.distance + + def init(self, nodes): + super().init(nodes) + + # Set hypter-parameters + mode=['ACS'] + colony_size=10 + elitist_weight=1.0 + min_scaling_factor=0.001 + alpha=1.0 + beta=3.0 + rho=0.1 + pheromone_deposit_weight=1.0 + initial_pheromone=1.0 + labels = None + + self.mode = str(mode[0]) + self.colony_size = colony_size + self.elitist_weight = elitist_weight + self.min_scaling_factor = min_scaling_factor + self.rho = rho + self.pheromone_deposit_weight = pheromone_deposit_weight + self.num_nodes = len(nodes) + self.nodes = nodes + + if labels is not None: + self.labels = labels + else: + self.labels = range(1, self.num_nodes + 1) + self.edges = [[None] * self.num_nodes for _ in range(self.num_nodes)] + for i in range(self.num_nodes): + for j in range(i + 1, self.num_nodes): + self.edges[i][j] = self.edges[j][i] = self.Edge(i, j, math.sqrt( + pow(self.nodes[i][0] - self.nodes[j][0], 2.0) + pow(self.nodes[i][1] - self.nodes[j][1], 2.0)), + initial_pheromone) + self.ants = [self.Ant(alpha, beta, self.num_nodes, self.edges) for _ in range(self.colony_size)] + self.global_best_tour = None + self.global_best_distance = float("inf") + + def _add_pheromone(self, tour, distance, weight=1.0): + pheromone_to_add = self.pheromone_deposit_weight / distance + for i in range(self.num_nodes): + self.edges[tour[i]][tour[(i + 1) % self.num_nodes]].pheromone += weight * pheromone_to_add + + def _acs(self, max_it): + for step in range(0, max_it): + # print('[step]', step) + for ant in self.ants: + self._add_pheromone(ant.find_tour(), ant.get_distance()) + if ant.distance < self.global_best_distance: + self.global_best_tour = ant.tour + self.global_best_distance = ant.distance + self.fitness_list.append(ant.distance) + for i in range(self.num_nodes): + for j in range(i + 1, self.num_nodes): + self.edges[i][j].pheromone *= (1.0 - self.rho) + + def _elitist(self, max_it): + for step in range(0, max_it): + # print('[step]', step) + for ant in self.ants: + self._add_pheromone(ant.find_tour(), ant.get_distance()) + if ant.distance < self.global_best_distance: + self.global_best_tour = ant.tour + self.global_best_distance = ant.distance + self.fitness_list.append(ant.distance) + self._add_pheromone(self.global_best_tour, self.global_best_distance, weight=self.elitist_weight) + for i in range(self.num_nodes): + for j in range(i + 1, self.num_nodes): + self.edges[i][j].pheromone *= (1.0 - self.rho) + + def fit(self, max_it=1000): + """ + Execute simulated annealing algorithm. + """ + # Initialize with the greedy solution. + if self.mode == 'ACS': + self._acs(max_it) + elif self.mode == 'Elitist': + self._elitist(max_it) + else: + print("Un supported") + # print('Sequence : <- {0} ->'.format(' - '.join(str(self.labels[i]) for i in self.global_best_tour))) + # print('Total distance travelled to complete the tour : {0}\n'.format(round(self.global_best_distance, 2))) + + return self.global_best_tour, self.fitness_list diff --git a/template/model/my_model_AS.py b/template/model/my_model_AS.py new file mode 100644 index 0000000..b49f38a --- /dev/null +++ b/template/model/my_model_AS.py @@ -0,0 +1,76 @@ +import math +import random +from model.base_model import Model + +class MyASModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def getMST(self, node): + MST = [] + distances = [] + for i in range(0, self.N): + if i != node: + MST.append(i) + distances.append(self.dist(node, i)) + return [x for _,x in sorted(zip(distances, MST))] + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + MST_solutions = [] + + for i in range(0, self.N): + solution = [i] + MST_solutions.append(solution) + + # Breadth First: Set each city as starting point, then go to next city simultaneously + for step in range(0, self.N - 1): + # print("[step]", step) + unvisited_list = list(range(0, self.N)) + # For each search path + for i in range(0, self.N): + cur_city = MST_solutions[i][-1] + unvisited_list = list( set(range(0, self.N)) - set(MST_solutions[i]) ) + closest_neighbour = -1 + min_f = math.inf + + for j in unvisited_list: + g = self.dist(cur_city, j) + sub_unvisited_list = unvisited_list.copy() + sub_unvisited_list.remove(j) + + sub_cur_city = self.getMST(j)[0] + h = 0 + while len(sub_unvisited_list) > 0: + if(len(unvisited_list) == 2): + break + else: + for k in self.getMST(sub_cur_city): + if k in sub_unvisited_list: + h = h + self.dist(sub_cur_city, k) + sub_cur_city = k + sub_unvisited_list.remove(k) + break + # Get f(x) = g(x) + h(x) + + f = g + h + if(f < min_f): + closest_neighbour = j + min_f = f + MST_solutions[i].append(closest_neighbour) + + for i in range(0, self.N): + self.fitness_list.append(self.fitness(MST_solutions[i])) + + self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ] + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_BFS.py b/template/model/my_model_BFS.py new file mode 100644 index 0000000..ab76321 --- /dev/null +++ b/template/model/my_model_BFS.py @@ -0,0 +1,49 @@ +import math +import random +from model.base_model import Model + +class MyBFSModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + UCS_solutions = [] + + for i in range(0, self.N): + solution = [i] + UCS_solutions.append(solution) + + # Breadth First: Set each city as starting point, then go to next city simultaneously + for step in range(0, self.N - 1): + # print("[step]", step) + unvisited_list = list(range(0, self.N)) + # For each search path + for i in range(0, self.N): + cur_city = UCS_solutions[i][-1] + unvisited_list = list( set(range(0, self.N)) - set(UCS_solutions[i]) ) + # print(unvisited_list) + closest_neighbour = -1 + shortest_distance = math.inf + for j in unvisited_list: + if(self.dist(cur_city, j) < shortest_distance): + closest_neighbour = j + shortest_distance = self.dist(cur_city, j) + UCS_solutions[i].append(closest_neighbour) + + for i in range(0, self.N): + self.fitness_list.append(self.fitness(UCS_solutions[i])) + + self.best_solution = UCS_solutions[ self.fitness_list.index(min(self.fitness_list)) ] + self.fitness_list.append(self.fitness(self.best_solution)) + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_DFS.py b/template/model/my_model_DFS.py new file mode 100644 index 0000000..781f2c3 --- /dev/null +++ b/template/model/my_model_DFS.py @@ -0,0 +1,44 @@ +import math +import random +from model.base_model import Model + +class MyDFSModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + MST_solutions = [] + # Depth First: Set one city as starting point, iterate to the end, then select next city as starting point. + for i in range(0, self.N): + solution = [] + solution.append(i) + unvisited_list = list(range(0, self.N)) + cur_city = i + # print("[starting]", i) + for steps in range(self.N - 1): + # print(unvisited_list) + unvisited_list.remove(cur_city) + closest_neighbour = -1 + shortest_distance = math.inf + for j in unvisited_list: + if(self.dist(cur_city, j) < shortest_distance): + closest_neighbour = j + shortest_distance = self.dist(cur_city, j) + solution.append(closest_neighbour) + cur_city = closest_neighbour + MST_solutions.append(solution) + self.fitness_list.append(self.fitness(solution)) + + self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ] + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_DPB.py b/template/model/my_model_DPB.py new file mode 100644 index 0000000..66aa462 --- /dev/null +++ b/template/model/my_model_DPB.py @@ -0,0 +1,63 @@ +import math +import random +from model.base_model import Model + +class MyDPBModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def getMST(self, node): + MST = [] + distances = [] + for i in range(0, self.N): + if i != node: + MST.append(i) + distances.append(self.dist(node, i)) + return [x for _,x in sorted(zip(distances, MST))] + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + MST_solutions = [] + + for i in range(0, self.N): + solution = [i] + MST_solutions.append(solution) + + MSTs = [] + for i in range(0, self.N): + MSTs.append([-1] * self.N) + + # Breadth First: Set each city as starting point, then go to next city simultaneously + for step in range(0, self.N - 1): + # print("[step]", step) + unvisited_list = list(range(0, self.N)) + # For each search path + for i in range(0, self.N): + cur_city = MST_solutions[i][-1] + unvisited_list = list( set(range(0, self.N)) - set(MST_solutions[i]) ) + + if MSTs[cur_city][0] == -1: + MST = self.getMST(cur_city) + MSTs[cur_city] = MST + + for j in MSTs[cur_city]: + if(j in unvisited_list): + MST_solutions[i].append(j) + break + + for i in range(0, self.N): + self.fitness_list.append(self.fitness(MST_solutions[i])) + + self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ] + self.fitness_list.append(self.fitness(self.best_solution)) + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_DPD.py b/template/model/my_model_DPD.py new file mode 100644 index 0000000..da378b8 --- /dev/null +++ b/template/model/my_model_DPD.py @@ -0,0 +1,59 @@ +import math +import random +from model.base_model import Model + +class MyDPDModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def getMST(self, node): + MST = [] + distances = [] + for i in range(0, self.N): + if i != node: + MST.append(i) + distances.append(self.dist(node, i)) + return [x for _,x in sorted(zip(distances, MST))] + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + MST_solutions = [] + + # Depth First: Set one city as starting point, iterate to the end, then select next city as starting point. + MSTs = [] + for i in range(0, self.N): + MSTs.append([-1] * self.N) + for i in range(0, self.N): + solution = [] + solution.append(i) + unvisited_list = list(range(0, self.N)) + cur_city = i + # print("[starting]", i) + for steps in range(self.N - 1): + # print(unvisited_list) + unvisited_list.remove(cur_city) + if MSTs[cur_city][0] == -1: + MST = self.getMST(cur_city) + MSTs[cur_city] = MST + + for j in MSTs[cur_city]: + if(j in unvisited_list): + solution.append(j) + cur_city = j + break + # print(solution) + MST_solutions.append(solution) + self.fitness_list.append(self.fitness(solution)) + + self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ] + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_GA.py b/template/model/my_model_GA.py new file mode 100644 index 0000000..83221c5 --- /dev/null +++ b/template/model/my_model_GA.py @@ -0,0 +1,171 @@ +import math +import random +from model.base_model import Model +from random import randint, sample + +class Gene: # City + def __init__(self, name, lat, lng): + self.name = name + self.lat = lat + self.lng = lng + + def get_distance_to(self, dest): + return math.sqrt( (self.lng - dest.lng) ** 2 + (self.lat - dest.lat) ** 2 ) + +class Individual: # Route: possible solution to TSP + def __init__(self, genes): + assert(len(genes) > 3) + self.genes = genes + self.__reset_params() + + def swap(self, gene_1, gene_2): + self.genes[0] + a, b = self.genes.index(gene_1), self.genes.index(gene_2) + self.genes[b], self.genes[a] = self.genes[a], self.genes[b] + self.__reset_params() + + def add(self, gene): + self.genes.append(gene) + self.__reset_params() + + @property + def fitness(self): + if self.__fitness == 0: + self.__fitness = 1 / self.travel_cost # Normalize travel cost + return self.__fitness + + @property + def travel_cost(self): # Get total travelling cost + if self.__travel_cost == 0: + for i in range(len(self.genes)): + origin = self.genes[i] + if i == len(self.genes) - 1: + dest = self.genes[0] + else: + dest = self.genes[i+1] + + self.__travel_cost += origin.get_distance_to(dest) + + return self.__travel_cost + + def __reset_params(self): + self.__travel_cost = 0 + self.__fitness = 0 + +class Population: # Population of individuals + def __init__(self, individuals): + self.individuals = individuals + + @staticmethod + def gen_individuals(sz, genes): + individuals = [] + for _ in range(sz): + individuals.append(Individual(sample(genes, len(genes)))) + return Population(individuals) + + def add(self, route): + self.individuals.append(route) + + def rmv(self, route): + self.individuals.remove(route) + + def get_fittest(self): + fittest = self.individuals[0] + for route in self.individuals: + if route.fitness > fittest.fitness: + fittest = route + + return fittest + +class MyGAModel(Model): + def __init__(self): + super().__init__() + self.iteration = 0 + + def init(self, nodes): + super().init(nodes) + + def evolve(self, pop, tourn_size, mut_rate): + new_generation = Population([]) + pop_size = len(pop.individuals) + elitism_num = pop_size // 4 + + # Elitism + for _ in range(elitism_num): + fittest = pop.get_fittest() + new_generation.add(fittest) + pop.rmv(fittest) + + # Crossover + for _ in range(elitism_num, pop_size): + parent_1 = self.selection(new_generation, tourn_size) + parent_2 = self.selection(new_generation, tourn_size) + child = self.crossover(parent_1, parent_2) + new_generation.add(child) + + # Mutation + for i in range(elitism_num, pop_size): + self.mutate(new_generation.individuals[i], mut_rate) + + return new_generation + + def crossover(self, parent_1, parent_2): + def fill_with_parent1_genes(child, parent, genes_n): + start_at = randint(0, len(parent.genes)-genes_n-1) + finish_at = start_at + genes_n + for i in range(start_at, finish_at): + child.genes[i] = parent_1.genes[i] + + def fill_with_parent2_genes(child, parent): + j = 0 + for i in range(0, len(parent.genes)): + if child.genes[i] == None: + while parent.genes[j] in child.genes: + j += 1 + child.genes[i] = parent.genes[j] + j += 1 + + genes_n = len(parent_1.genes) + child = Individual([None for _ in range(genes_n)]) + fill_with_parent1_genes(child, parent_1, genes_n // 2) + fill_with_parent2_genes(child, parent_2) + + return child + + def mutate(self, individual, rate): + for _ in range(len(individual.genes)): + if random.random() < rate: + sel_genes = sample(individual.genes, 2) + individual.swap(sel_genes[0], sel_genes[1]) + + def selection(self, population, competitors_n): + return Population(sample(population.individuals, competitors_n)).get_fittest() + + def fit(self, max_it=100): + """ + Execute simulated annealing algorithm. + """ + pop_size = 1000 + mut_rate = 0.9 + tourn_size = 100 + + genes = [Gene(num, city[0], city[1]) for num, city in enumerate(self.coords)] + self.genes = genes + + population = Population.gen_individuals(pop_size, genes) + + + for it in range(0, max_it): + mut_rate = mut_rate * 0.95 + if mut_rate < 0.05: + mut_rate = 0.05 + population = self.evolve(population, tourn_size, mut_rate) + cost = population.get_fittest().travel_cost + + it += 1 + self.fitness_list.append(cost) + # print("[step] ", it, " [mut] ", mut_rate, " [best] ", self.fitness_list[self.fitness_list.index(min(self.fitness_list))]) + + self.best_solution = [gene.name for gene in population.get_fittest().genes] + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_HillClimb.py b/template/model/my_model_HillClimb.py new file mode 100644 index 0000000..93ac2d0 --- /dev/null +++ b/template/model/my_model_HillClimb.py @@ -0,0 +1,75 @@ +import math +import random +import numpy as np +from model.base_model import Model + +class MyHillClimbModel(Model): + + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def random_tour(self): + return np.random.permutation(self.N).tolist() + + def all_pairs(self, size, shuffle=random.shuffle): + r1 = list(range(size)) + r2 = list(range(size)) + if shuffle: + shuffle(r1) + shuffle(r2) + for i in r1: + for j in r2: + yield (i,j) + + def move_operator(self, tour): + '''generator to return all possible variations + where the section between two cities are swapped''' + for i,j in self.all_pairs(len(tour)): + if i != j: + copy=tour[:] + if i < j: + copy[i:j+1]=reversed(tour[i:j+1]) + else: + copy[i+1:]=reversed(tour[:j]) + copy[:j]=reversed(tour[i+1:]) + if copy != tour: # no point returning the same tour + yield copy + + def fit(self, max_it=100): + """ + Put your iteration process here. + """ + + self.best_solution = self.random_tour() + best_score = -self.fitness(self.best_solution) + + num_evaluations = 0 + + while num_evaluations < max_it: + # examine moves around our current position + move_made = False + for next_solution in self.move_operator(self.best_solution): + if num_evaluations >= max_it: + print("Max iteration reached:", max_it) + break + + # see if this move is better than the current + next_score = -self.fitness(next_solution) + num_evaluations += 1 + if next_score > best_score: + self.best_solution = next_solution + self.fitness_list.append(self.fitness(self.best_solution)) + best_score=next_score + move_made=True + break # depth first search + + if not move_made: + break # we couldn't find a better move (must be at a local maximum) + + return self.best_solution, self.fitness_list diff --git a/template/model/my_model_Random.py b/template/model/my_model_Random.py new file mode 100644 index 0000000..8c170ac --- /dev/null +++ b/template/model/my_model_Random.py @@ -0,0 +1,27 @@ +import math +import random +from model.base_model import Model +import numpy as np + +class MyRandomModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + random_solutions = [] + for i in range(0, max_it): + solution = np.random.permutation(self.N).tolist() + random_solutions.append(solution) + self.fitness_list.append(self.fitness(solution)) + + self.best_solution = random_solutions[self.fitness_list.index(min(self.fitness_list))] + return self.best_solution, self.fitness_list diff --git a/template/model/anneal_model.py b/template/model/my_model_SA.py similarity index 89% rename from template/model/anneal_model.py rename to template/model/my_model_SA.py index bcb1a9d..0443691 100644 --- a/template/model/anneal_model.py +++ b/template/model/my_model_SA.py @@ -2,21 +2,24 @@ import math import random from model.base_model import Model -class SimAnneal(Model): - def __init__(self, T=-1, alpha=-1, stopping_T=-1): +class MySAModel(Model): + def __init__(self): super().__init__() self.iteration = 0 - self.T = T + def init(self, nodes): + super().init(nodes) + + # Set hyper-parameters + T = -1 + stopping_temperature = -1 + alpha = 0.99 + + self.T = math.sqrt(self.N) if T == -1 else T self.alpha = 0.995 if alpha == -1 else alpha - self.stopping_temperature = 1e-8 if stopping_T == -1 else stopping_T + self.stopping_temperature = 1e-8 if stopping_temperature == -1 else stopping_temperature - def init(self, coords): - super().init(coords) - - if (self.T == -1): - self.T = math.sqrt(self.N) self.T_save = self.T # save inital T to reset if batch annealing is used def initial_solution(self): diff --git a/template/model/my_model_UCS.py b/template/model/my_model_UCS.py new file mode 100644 index 0000000..b5a103d --- /dev/null +++ b/template/model/my_model_UCS.py @@ -0,0 +1,71 @@ +import math +import random +from model.base_model import Model + +class MyUCSModel(Model): + def __init__(self): + super().__init__() + + def init(self, nodes): + """ + Put your initialization here. + """ + super().init(nodes) + + def getMST(self, node): + MST = [] + distances = [] + for i in range(0, self.N): + if i != node: + MST.append(i) + distances.append(self.dist(node, i)) + return [x for _,x in sorted(zip(distances, MST))] + + def fit(self, max_it=1000): + """ + Put your iteration process here. + """ + + UCS_solutions = [] + UCS_losses = [] + + for i in range(0, self.N): + solution = [i] + UCS_solutions.append(solution) + UCS_losses.append(math.inf) + + MSTs = [] + for i in range(0, self.N): + MSTs.append([-1] * self.N) + + # Breadth First: Set each city as starting point, then go to next city simultaneously + min_loss = math.inf + while(len(UCS_solutions[ UCS_losses.index(min(UCS_losses)) ]) != self.N): + unvisited_list = list(range(0, self.N)) + min_loss = min(UCS_losses) + # For each search path + for i in range(0, self.N): + if UCS_losses[i] == min_loss: + cur_city = UCS_solutions[i][-1] + unvisited_list = list( set(range(0, self.N)) - set(UCS_solutions[i]) ) + if MSTs[cur_city][0] == -1: + MST = self.getMST(cur_city) + MSTs[cur_city] = MST + + for j in MSTs[cur_city]: + if(j in unvisited_list): + UCS_solutions[i].append(j) + + N = len(UCS_solutions[i]) + cur_fit = 0 + for k in range(len(UCS_solutions[i])): + coord_0, coord_1 = self.coords[UCS_solutions[i][k % N]], self.coords[UCS_solutions[i][(k + 1) % N]] + cur_fit += math.sqrt((coord_0[0] - coord_1[0]) ** 2 + (coord_0[1] - coord_1[1]) ** 2) + UCS_losses[i] = cur_fit + # if(UCS_losses[i] < min_loss): + # min_loss = UCS_losses[i] + break + self.best_solution = UCS_solutions[ UCS_losses.index(min(UCS_losses)) ] + self.fitness_list.append(self.fitness(self.best_solution)) + + return self.best_solution, self.fitness_list diff --git a/template/utils/tsp.py b/template/tsp.py similarity index 72% rename from template/utils/tsp.py rename to template/tsp.py index 25f810d..87ecd32 100644 --- a/template/utils/tsp.py +++ b/template/tsp.py @@ -26,40 +26,7 @@ def fitness(solution, coords): cur_fit += dist(solution[i % N], solution[(i + 1) % N], coords) return cur_fit -def TSP_Bench(tsp_file, model, *args, max_it=1000, timeout=60): - - start = timeit.default_timer() - - # Model Running - best_solution, fitness_list = TSP(tsp_file, model, *args, max_it=max_it,timeout=timeout) - # Model End - - stop = timeit.default_timer() - print('[*] Running for: {time:.2f} seconds'.format(time=(stop - start))) - - print() - return best_solution, fitness_list, (stop - start) - -def TSP_Bench_ALL(tsp_file_path, model, *args, max_it=1000, timeout=60): - best_solutions = [] - fitness_lists = [] - times = [] - for root, _, files in os.walk(tsp_file_path): - if(files): - for f in files: - # Get input file name - tsp_file = str(root) + '/' + str(f) - log(tsp_file) - - # Run TSP - best_solution, fitness_list, time = TSP_Bench(tsp_file, model, *args, max_it=max_it,timeout=timeout) - best_solutions.append(best_solution) - fitness_lists.append(fitness_lists) - times.append(time) - - return best_solutions, fitness_lists, times - -def TSP(tsp_file, model, *args, max_it=1000, timeout=60): +def TSP(tsp_file, model, timeout=60): best_solution = [] fitness_list = [] @@ -76,8 +43,8 @@ def TSP(tsp_file, model, *args, max_it=1000, timeout=60): # Try your algorithm try: - model.init(nodes, *args) - best_solution, fitness_list = model.fit(max_it) + model.init(nodes) + best_solution, fitness_list = model.fit() except Exception as exc: if(str(exc) == "Timeout"): log("Timeout -3") @@ -144,4 +111,60 @@ def TSP(tsp_file, model, *args, max_it=1000, timeout=60): with open('output/' + os.path.splitext(os.path.basename(tsp_file))[0] + '.json', 'w') as outfile: json.dump(data, outfile) - return best_solution, fitness_list \ No newline at end of file + return best_solution, fitness_list + +def TSP_Bench_ONE(tsp_file, model, timeout=60): + + start = timeit.default_timer() + + # Model Running + best_solution, fitness_list = TSP(tsp_file, model, timeout=timeout) + # Model End + + stop = timeit.default_timer() + print('[*] Running for: {time:.2f} seconds'.format(time=(stop - start))) + + print() + return best_solution, fitness_list, (stop - start) + +def TSP_Bench_PATH(tsp_file_path, model, timeout=60): + best_solutions = [] + fitness_lists = [] + times = [] + for root, _, files in os.walk(tsp_file_path): + if(files): + for f in files: + # Get input file name + tsp_file = os.path.join(root, str(f)) + log(tsp_file) + + # Run TSP + best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, model, timeout=timeout) + best_solutions.append(best_solution) + fitness_lists.append(fitness_lists) + times.append(time) + + return best_solutions, fitness_lists, times + +def TSP_Bench_ALL(root, model): + best_solutions_all = [] + fitness_lists_all = [] + times_all = [] + + best_solutions_simple, fitness_lists_simple, times_simple = TSP_Bench_PATH(os.path.join(root, "data/simple/"), model, timeout=60) + best_solutions_medium, fitness_lists_medium, times_medium = TSP_Bench_PATH(os.path.join(root, "data/medium/"), model, timeout=180) + best_solutions_hard, fitness_lists_hard, times_hard = TSP_Bench_PATH(os.path.join(root, "data/hard/"), model, timeout=300) + + best_solutions_all.append(best_solutions_simple) + best_solutions_all.append(best_solutions_medium) + best_solutions_all.append(best_solutions_hard) + + fitness_lists_all.append(fitness_lists_simple) + fitness_lists_all.append(fitness_lists_medium) + fitness_lists_all.append(fitness_lists_hard) + + times_all.append(times_simple) + times_all.append(times_medium) + times_all.append(times_hard) + + return [item for sublist in best_solutions_all for item in sublist], [item for sublist in fitness_lists_all for item in sublist], [item for sublist in times_all for item in sublist]