64 KiB
64 KiB
None
<html>
<head>
</head>
</html>
Make sure you run this at the begining
In [2]:
import os
import sys
import math
import numpy as np
import matplotlib.pyplot as plt
# Append template path to sys path
sys.path.append(os.getcwd() + "/template")
In [3]:
from utils.load_data import load_data
from utils.load_data import log
from utils.visualize_tsp import plotTSP
from utils.tsp import TSP
Workshop Starts Here¶
Get familiar with your dataset¶
There are problems at different levels. 3 simple, 2 medium, 1 hard.
In [ ]:
for root, _, files in os.walk('./template/data'):
if(files):
for f in files:
print(str(root) + "/" + f)
In [ ]:
ulysses16 = np.array(load_data("./template/data/simple/ulysses16.tsp"))
In [ ]:
ulysses16[:]
In [ ]:
plt.scatter(ulysses16[:, 0], ulysses16[:, 1])
for i in range(0, 16):
plt.annotate(i, (ulysses16[i, 0], ulysses16[i, 1]+0.5))
Naive Solution: In Order¶
In [ ]:
simple_sequence = list(range(0, 16))
print(simple_sequence)
In [ ]:
plotTSP([simple_sequence], ulysses16, num_iters=1)
Naive Solution: Random Permutation¶
In [ ]:
random_permutation = np.random.permutation(16).tolist()
print(random_permutation)
In [ ]:
plotTSP([random_permutation], ulysses16, num_iters=1)
Best Solution¶
In [ ]:
best_ulysses16 = [0, 13, 12, 11, 6, 5, 14, 4, 10, 8, 9, 15, 2, 1, 3, 7]
plotTSP([best_ulysses16], ulysses16, num_iters=1)
Calculate Fitness (Sum of all Distances)¶
In [6]:
def dist(node_0, node_1, coords):
"""
Euclidean distance between two nodes.
"""
coord_0, coord_1 = coords[node_0], coords[node_1]
return math.sqrt((coord_0[0] - coord_1[0]) ** 2 + (coord_0[1] - coord_1[1]) ** 2)
In [ ]:
print("Coordinate of City 0:", ulysses16[0])
In [ ]:
print("Coordinate of City 1:", ulysses16[1])
In [ ]:
print("Distance Between", dist(0, 1, ulysses16))
In [7]:
def fitness(solution, coords):
N = len(coords)
cur_fit = 0
for i in range(len(solution)):
cur_fit += dist(solution[i % N], solution[(i + 1) % N], coords)
return cur_fit
In [ ]:
print ("Order Fitness:\t", fitness(simple_sequence, ulysses16))
print ("Random Fitness:\t", fitness(random_permutation, ulysses16))
print ("Best Fitness:\t", fitness(best_ulysses16, ulysses16))
Naive Random Model¶
In [ ]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self):
super().__init__()
def init(self, coords):
"""
Put your initialization here.
"""
super().init(coords)
self.log("Nothing to initialize in your model now")
def fit(self, max_it=1000, visualize=False):
"""
Put your iteration process here.
"""
self.log("Naive Random Solution")
self.best_solution = np.random.permutation(self.N).tolist()
self.fitness_list.append(self.fitness(self.best_solution))
return self.best_solution, self.fitness_list
In [ ]:
tsp_problem = './template/data/simple/ulysses16.tsp'
tsp_coords = np.array(load_data(tsp_problem))
import timeit
start = timeit.default_timer()
# Your Model Running
model = MyModel()
best_solution, fitness_list = TSP(tsp_problem, model)
# Your Model End
stop = timeit.default_timer()
print()
print('[*] Running for: {time:.1f} seconds'.format(time=(stop - start)))
print()
print ("Best Fitness:\t", fitness(best_solution, tsp_coords))
print ("Best Solution:\t", best_solution)
Minimum Spanning Tree (Depth First)¶
In [85]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self):
super().__init__()
def init(self, coords):
"""
Put your initialization here.
"""
super().init(coords)
self.log("Nothing to initialize in your model now")
def fit(self, max_it=1000, visualize=False):
"""
Put your iteration process here.
"""
self.log("Uniform Cost Search Solution")
UCS_solutions = []
UCS_losses = []
# 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
UCS_solutions.append(solution)
UCS_losses.append(self.fitness(solution))
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
In [86]:
tsp_problem = './template/data/medium/pcb442.tsp'
tsp_coords = np.array(load_data(tsp_problem))
import timeit
start = timeit.default_timer()
# Your Model Running
model = MyModel()
best_solution, fitness_list = TSP(tsp_problem, model)
# Your Model End
stop = timeit.default_timer()
print()
print('[*] Running for: {time:.1f} seconds'.format(time=(stop - start)))
print()
print ("Best Fitness:\t", fitness(best_solution, tsp_coords))
print ("Best Solution:\t", best_solution)
Minimum Spanning Tree (Breadth First)¶
In [87]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self):
super().__init__()
def init(self, coords):
"""
Put your initialization here.
"""
super().init(coords)
self.log("Nothing to initialize in your model now")
def fit(self, max_it=1000, visualize=False):
"""
Put your iteration process here.
"""
self.log("Uniform Cost Search Solution")
UCS_solutions = []
UCS_losses = []
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)
UCS_solutions[i]
solution = []
solution.append(i)
unvisited_list = list(range(0, self.N))
cur_city = i
# For each search path
for i in range(0, self.N):
cur_city = UCS_solutions[i][step]
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):
UCS_losses.append(self.fitness(UCS_solutions[i]))
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
In [89]:
tsp_problem = './template/data/medium/pcb442.tsp'
tsp_coords = np.array(load_data(tsp_problem))
import timeit
start = timeit.default_timer()
# Your Model Running
model = MyModel()
best_solution, fitness_list = TSP(tsp_problem, model)
# Your Model End
stop = timeit.default_timer()
print()
print('[*] Running for: {time:.1f} seconds'.format(time=(stop - start)))
print()
print ("Best Fitness:\t", fitness(best_solution, tsp_coords))
print ("Best Solution:\t", best_solution)
Dynamic Programming¶
Costs a lot of memory
In [90]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self):
super().__init__()
def init(self, coords):
"""
Put your initialization here.
"""
super().init(coords)
self.log("Nothing to initialize in your model now")
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, visualize=False):
"""
Put your iteration process here.
"""
self.log("Uniform Cost Search Solution")
UCS_solutions = []
UCS_losses = []
# 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)
UCS_solutions.append(solution)
UCS_losses.append(self.fitness(solution))
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
In [91]:
tsp_problem = './template/data/medium/pcb442.tsp'
tsp_coords = np.array(load_data(tsp_problem))
import timeit
start = timeit.default_timer()
# Your Model Running
model = MyModel()
best_solution, fitness_list = TSP(tsp_problem, model)
# Your Model End
stop = timeit.default_timer()
print()
print('[*] Running for: {time:.1f} seconds'.format(time=(stop - start)))
print()
print ("Best Fitness:\t", fitness(best_solution, tsp_coords))
print ("Best Solution:\t", best_solution)
Your Smart Model¶
In [ ]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self):
super().__init__()
def init(self, coords):
"""
Put your initialization here.
"""
super().init(coords)
self.log("Nothing to initialize in your model now")
def fit(self, max_it=1000, visualize=False):
"""
Put your iteration process here.
"""
self.log("Naive Random Solution")
self.best_solution = np.random.permutation(self.N).tolist()
self.fitness_list.append(self.fitness(self.best_solution))
return self.best_solution, self.fitness_list
Simulated Annealing¶
In [ ]:
import math
import random
from model.base_model import Model
class MyModel(Model):
def __init__(self, T=-1, alpha=-1, stopping_T=-1):
super().__init__()
self.iteration = 0
self.T = T
self.alpha = 0.995 if alpha == -1 else alpha
self.stopping_temperature = 1e-8 if stopping_T == -1 else stopping_T
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):
"""
Greedy algorithm to get an initial solution (closest-neighbour).
"""
cur_node = random.choice(self.nodes) # start from a random node
solution = [cur_node]
free_nodes = set(self.nodes)
free_nodes.remove(cur_node)
while free_nodes:
next_node = min(free_nodes, key=lambda x: self.dist(cur_node, x)) # nearest neighbour
free_nodes.remove(next_node)
solution.append(next_node)
cur_node = next_node
cur_fit = self.fitness(solution)
if cur_fit < self.best_fitness: # If best found so far, update best fitness
self.best_fitness = cur_fit
self.best_solution = solution
self.fitness_list.append(cur_fit)
return solution, cur_fit
def p_accept(self, candidate_fitness):
"""
Probability of accepting if the candidate is worse than current.
Depends on the current temperature and difference between candidate and current.
"""
return math.exp(-abs(candidate_fitness - self.cur_fitness) / self.T)
def accept(self, candidate):
"""
Accept with probability 1 if candidate is better than current.
Accept with probabilty p_accept(..) if candidate is worse.
"""
candidate_fitness = self.fitness(candidate)
if candidate_fitness < self.cur_fitness:
self.cur_fitness, self.cur_solution = candidate_fitness, candidate
if candidate_fitness < self.best_fitness:
self.best_fitness, self.best_solution = candidate_fitness, candidate
else:
if random.random() < self.p_accept(candidate_fitness):
self.cur_fitness, self.cur_solution = candidate_fitness, candidate
def fit(self, max_it=1000):
"""
Execute simulated annealing algorithm.
"""
# Initialize with the greedy solution.
self.cur_solution, self.cur_fitness = self.initial_solution()
self.log("Starting annealing.")
while self.T >= self.stopping_temperature and self.iteration < max_it:
candidate = list(self.cur_solution)
l = random.randint(1, self.N - 1)
i = random.randint(0, self.N - l)
candidate[i : (i + l)] = reversed(candidate[i : (i + l)])
self.accept(candidate)
self.T *= self.alpha
self.iteration += 1
self.fitness_list.append(self.cur_fitness)
self.log(f"Best fitness obtained: {self.best_fitness}")
improvement = 100 * (self.fitness_list[0] - self.best_fitness) / (self.fitness_list[0])
self.log(f"Improvement over greedy heuristic: {improvement : .2f}%")
return self.best_solution, self.fitness_list
Test your Model¶
In [ ]:
tsp_problem = './template/data/simple/ulysses16.tsp'
tsp_coords = np.array(load_data(tsp_problem))
In [ ]:
import timeit
start = timeit.default_timer()
# Your Model Running
model = MyModel()
best_solution, fitness_list = TSP(tsp_problem, model)
# Your Model End
stop = timeit.default_timer()
print()
print('[*] Running for: {time:.1f} seconds'.format(time=(stop - start)))
print()
print ("Best Fitness:\t", fitness(best_solution, tsp_coords))
print ("Best Solution:\t", best_solution)
Test All Dataset¶
In [83]:
for root, _, files in os.walk('./template/data'):
if(files):
for f in files:
print(str(root) + "/" + f)
In [ ]:
for root, _, files in os.walk('./template/data'):
if(files):
for f in files:
# Get input file name
tsp_file = str(root) + '/' + str(f)
log(tsp_file)
# Your Model
model = MyModel()
# Run TSP
TSP(tsp_file, model)
print()
In [ ]: