26 KiB
26 KiB
None
<html>
<head>
</head>
</html>
Make sure you run this at the begining
In [ ]:
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 [ ]:
from utils.load_data import load_data
from utils.visualize_tsp import plotTSP
from tsp import TSP_Bench_ONE
from tsp import TSP_Bench_PATH
from tsp import TSP_Bench_ALL
Workshop Starts Here¶
Get familiar with your dataset¶
There are problems at different levels. 3 simple, 2 medium, 1 difficult.
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 [ ]:
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 [ ]:
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
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
In [ ]:
tsp_file = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyRandomModel)
In [ ]:
plt.plot(fitness_list, 'o-')
Minimum Spanning Tree (Depth First)¶
In [ ]:
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
In [ ]:
tsp_file = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyDFSModel)
In [ ]:
plt.plot(fitness_list, 'o-')
Minimum Spanning Tree (Breadth First)¶
In [ ]:
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
In [ ]:
tsp_file = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyBFSModel)
In [ ]:
plt.plot(fitness_list, 'o-')
Dynamic Programming (DFS)¶
Costs a lot of memory
In [ ]:
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
In [ ]:
tsp_file = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyDPDModel)
In [ ]:
plt.plot(fitness_list, 'o-')
Dynamic Programming (BFS)¶
In [ ]:
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
In [ ]:
tsp_file = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyDPBModel)
In [ ]:
plt.plot(fitness_list, 'o-')
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, nodes):
"""
Put your initialization here.
"""
super().init(nodes)
self.log("Nothing to initialize in your model now")
def fit(self, max_it=1000):
"""
Put your iteration process here.
"""
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
Test your Model¶
In [ ]:
tsp_problem = './template/data/simple/ulysses16.tsp'
In [ ]:
best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyModel)
Test All Dataset¶
In [ ]:
tsp_path = './template'
for root, _, files in os.walk(tsp_path + '/data'):
if(files):
for f in files:
print(str(root) + "/" + f)
In [ ]:
def plot_results(best_solutions, times, title):
fig = plt.figure()
nodes = [len(s) for s in best_solutions]
data = np.array([[node, time] for node, time in sorted(zip(nodes, times))])
plt.plot(data[:, 0], data[:, 1], 'o-')
fig.suptitle(title, fontsize=20)
In [ ]:
print("Random Search")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyRandomModel)
In [ ]:
plot_results(best_solutions, times, "Random Model")
In [ ]:
print("Depth First Search")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDFSModel)
In [ ]:
plot_results(best_solutions, times, "Depth First Search")
In [ ]:
print("Breadth First Search")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyBFSModel)
In [ ]:
plot_results(best_solutions, times, "Breadth First Search")
In [ ]:
print("Dynamic Programming (Depth First)")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDPDModel)
In [ ]:
plot_results(best_solutions, times, "Dynamic Programming (Depth First)")
In [ ]:
print("Dynamic Progrmaming (Breadth First)")
best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDPBModel)
In [ ]:
plot_results(best_solutions, times, "Dynamic Programming (Breadth First)")
Conclusions (Random, BFS, DFS, DP)¶
In [ ]:
# Simple
# ulysses16: 77 (BFS), 84 (DFS)
# att48: 39236 (BFS), 40763 (DFS)
# st70: 761 (BFS), 901 (DFS)
# Medium
# a280: 3088 (BFS), 3558 (DFS)
# pcb442: 58952 (BFS), 61984 (DFS)
# Difficult
# dsj1000: time-out (DP-BFS) 23,552,227 (DP-DFS)
1. All different models get the same results every time (except random).
2. All different models have an exponential time complexity (except random).
3. Depth First Seach is a little faster than Breadth First Search, but Breadth First get better results.
4. Only dynamic programming solves the problem with 1000 cities (Fast).
In [ ]:
# In the next workshop
# Will try to solve the problem with 1000 cities faster, and get better results
In [ ]: