{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Make sure you run this at the begining**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"import math\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Append template path to sys path\n",
"sys.path.append(os.getcwd() + \"/template\") "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from utils.load_data import load_data\n",
"from utils.load_data import log\n",
"from utils.visualize_tsp import plotTSP\n",
"from utils.tsp import TSP\n",
"from utils.tsp import TSP_Bench\n",
"from utils.tsp import TSP_Bench_ALL"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Workshop Starts Here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Get familiar with your dataset"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are problems at different levels. **3 simple, 2 medium, 1 hard**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"for root, _, files in os.walk('./template/data'):\n",
" if(files):\n",
" for f in files:\n",
" print(str(root) + \"/\" + f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ulysses16 = np.array(load_data(\"./template/data/simple/ulysses16.tsp\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"ulysses16[:]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"plt.scatter(ulysses16[:, 0], ulysses16[:, 1])\n",
"for i in range(0, 16):\n",
" plt.annotate(i, (ulysses16[i, 0], ulysses16[i, 1]+0.5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naive Solution: In Order"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"simple_sequence = list(range(0, 16))\n",
"print(simple_sequence)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plotTSP([simple_sequence], ulysses16, num_iters=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naive Solution: Random Permutation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"random_permutation = np.random.permutation(16).tolist()\n",
"print(random_permutation)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plotTSP([random_permutation], ulysses16, num_iters=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Best Solution"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"best_ulysses16 = [0, 13, 12, 11, 6, 5, 14, 4, 10, 8, 9, 15, 2, 1, 3, 7]\n",
"plotTSP([best_ulysses16], ulysses16, num_iters=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculate Fitness (Sum of all Distances)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def dist(node_0, node_1, coords):\n",
" \"\"\"\n",
" Euclidean distance between two nodes.\n",
" \"\"\"\n",
" coord_0, coord_1 = coords[node_0], coords[node_1]\n",
" return math.sqrt((coord_0[0] - coord_1[0]) ** 2 + (coord_0[1] - coord_1[1]) ** 2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"Coordinate of City 0:\", ulysses16[0])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"Coordinate of City 1:\", ulysses16[1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"print(\"Distance Between\", dist(0, 1, ulysses16))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def fitness(solution, coords):\n",
" N = len(coords)\n",
" cur_fit = 0\n",
" for i in range(len(solution)):\n",
" cur_fit += dist(solution[i % N], solution[(i + 1) % N], coords)\n",
" return cur_fit"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print (\"Order Fitness:\\t\", fitness(simple_sequence, ulysses16))\n",
"print (\"Random Fitness:\\t\", fitness(random_permutation, ulysses16))\n",
"print (\"Best Fitness:\\t\", fitness(best_ulysses16, ulysses16))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naive Random Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"import numpy as np\n",
"\n",
"class MyRandomModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
" random_solutions = []\n",
" for i in range(0, max_it):\n",
" solution = np.random.permutation(self.N).tolist()\n",
" random_solutions.append(solution)\n",
" self.fitness_list.append(self.fitness(solution))\n",
"\n",
" self.best_solution = random_solutions[self.fitness_list.index(min(self.fitness_list))]\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyRandomModel, max_it=100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Minimum Spanning Tree (Depth First)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MyDFSModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
"\n",
" MST_solutions = []\n",
" # Depth First: Set one city as starting point, iterate to the end, then select next city as starting point.\n",
" for i in range(0, self.N):\n",
" solution = []\n",
" solution.append(i)\n",
" unvisited_list = list(range(0, self.N))\n",
" cur_city = i\n",
"# print(\"[starting]\", i)\n",
" for steps in range(self.N - 1):\n",
"# print(unvisited_list)\n",
" unvisited_list.remove(cur_city)\n",
" closest_neighbour = -1\n",
" shortest_distance = math.inf\n",
" for j in unvisited_list:\n",
" if(self.dist(cur_city, j) < shortest_distance):\n",
" closest_neighbour = j\n",
" shortest_distance = self.dist(cur_city, j)\n",
" solution.append(closest_neighbour)\n",
" cur_city = closest_neighbour\n",
" MST_solutions.append(solution)\n",
" self.fitness_list.append(self.fitness(solution))\n",
"\n",
" self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ]\n",
"\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyDFSModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Minimum Spanning Tree (Breadth First)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MyBFSModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
"\n",
" UCS_solutions = []\n",
" \n",
" for i in range(0, self.N):\n",
" solution = [i]\n",
" UCS_solutions.append(solution)\n",
" \n",
" # Breadth First: Set each city as starting point, then go to next city simultaneously\n",
" for step in range(0, self.N - 1):\n",
" # print(\"[step]\", step)\n",
" unvisited_list = list(range(0, self.N))\n",
" # For each search path\n",
" for i in range(0, self.N):\n",
" cur_city = UCS_solutions[i][-1]\n",
" unvisited_list = list( set(range(0, self.N)) - set(UCS_solutions[i]) )\n",
" # print(unvisited_list)\n",
" closest_neighbour = -1\n",
" shortest_distance = math.inf\n",
" for j in unvisited_list:\n",
" if(self.dist(cur_city, j) < shortest_distance):\n",
" closest_neighbour = j\n",
" shortest_distance = self.dist(cur_city, j)\n",
" UCS_solutions[i].append(closest_neighbour)\n",
"\n",
" for i in range(0, self.N):\n",
" self.fitness_list.append(self.fitness(UCS_solutions[i]))\n",
" \n",
" self.best_solution = UCS_solutions[ self.fitness_list.index(min(self.fitness_list)) ]\n",
" self.fitness_list.append(self.fitness(self.best_solution))\n",
"\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyBFSModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dynamic Programming (DFS)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Costs a lot of memory"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MyDPDModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" def getMST(self, node):\n",
" MST = []\n",
" distances = []\n",
" for i in range(0, self.N):\n",
" if i != node:\n",
" MST.append(i)\n",
" distances.append(self.dist(node, i))\n",
" return [x for _,x in sorted(zip(distances, MST))]\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
"\n",
" MST_solutions = []\n",
"\n",
" # Depth First: Set one city as starting point, iterate to the end, then select next city as starting point.\n",
" MSTs = []\n",
" for i in range(0, self.N):\n",
" MSTs.append([-1] * self.N)\n",
" for i in range(0, self.N):\n",
" solution = []\n",
" solution.append(i)\n",
" unvisited_list = list(range(0, self.N))\n",
" cur_city = i\n",
" # print(\"[starting]\", i)\n",
" for steps in range(self.N - 1):\n",
" # print(unvisited_list)\n",
" unvisited_list.remove(cur_city)\n",
" if MSTs[cur_city][0] == -1:\n",
" MST = self.getMST(cur_city)\n",
" MSTs[cur_city] = MST\n",
" \n",
" for j in MSTs[cur_city]:\n",
" if(j in unvisited_list):\n",
" solution.append(j)\n",
" cur_city = j\n",
" break\n",
" # print(solution)\n",
" MST_solutions.append(solution)\n",
" self.fitness_list.append(self.fitness(solution))\n",
"\n",
" self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ]\n",
"\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyDPDModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dynamic Programming (BFS)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MyDPBModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" def getMST(self, node):\n",
" MST = []\n",
" distances = []\n",
" for i in range(0, self.N):\n",
" if i != node:\n",
" MST.append(i)\n",
" distances.append(self.dist(node, i))\n",
" return [x for _,x in sorted(zip(distances, MST))]\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
"\n",
" MST_solutions = []\n",
" \n",
" for i in range(0, self.N):\n",
" solution = [i]\n",
" MST_solutions.append(solution)\n",
" \n",
" MSTs = []\n",
" for i in range(0, self.N):\n",
" MSTs.append([-1] * self.N)\n",
"\n",
" # Breadth First: Set each city as starting point, then go to next city simultaneously\n",
" for step in range(0, self.N - 1):\n",
" # print(\"[step]\", step)\n",
" unvisited_list = list(range(0, self.N))\n",
" # For each search path\n",
" for i in range(0, self.N):\n",
" cur_city = MST_solutions[i][-1]\n",
" unvisited_list = list( set(range(0, self.N)) - set(MST_solutions[i]) )\n",
"\n",
" if MSTs[cur_city][0] == -1:\n",
" MST = self.getMST(cur_city)\n",
" MSTs[cur_city] = MST\n",
"\n",
" for j in MSTs[cur_city]:\n",
" if(j in unvisited_list):\n",
" MST_solutions[i].append(j)\n",
" break\n",
"\n",
" for i in range(0, self.N):\n",
" self.fitness_list.append(self.fitness(MST_solutions[i]))\n",
" \n",
" self.best_solution = MST_solutions[ self.fitness_list.index(min(self.fitness_list)) ]\n",
" self.fitness_list.append(self.fitness(self.best_solution))\n",
"\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyDPBModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Your Smart Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MyModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def init(self, nodes):\n",
" \"\"\"\n",
" Put your initialization here.\n",
" \"\"\"\n",
" super().init(nodes)\n",
"\n",
" self.log(\"Nothing to initialize in your model now\")\n",
"\n",
" def fit(self, max_it):\n",
" \"\"\"\n",
" Put your iteration process here.\n",
" \"\"\"\n",
" self.best_solution = np.random.permutation(self.N).tolist()\n",
" self.fitness_list.append(self.fitness(self.best_solution))\n",
"\n",
" return self.best_solution, self.fitness_list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test your Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_problem = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench(tsp_file, MyModel)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test All Dataset"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"for root, _, files in os.walk('./template/data'):\n",
" if(files):\n",
" for f in files:\n",
" print(str(root) + \"/\" + f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_path = './template/data'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_results(best_solutions, times, title):\n",
" fig = plt.figure()\n",
" nodes = [len(s) for s in best_solutions]\n",
" data = np.array([[node, time] for node, time in sorted(zip(nodes, times))])\n",
" plt.plot(data[:, 0], data[:, 1], 'o-')\n",
" fig.suptitle(title, fontsize=20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"print(\"Random Search\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyRandomModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Random Model\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"print(\"Depth First Search\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDFSModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Depth First Search\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"print(\"Breadth First Search\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyBFSModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Breadth First Search\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"print(\"Dynamic Programming (Depth First)\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDPDModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Dynamic Programming (Depth First)\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"print(\"Dynamic Progrmaming (Breadth First)\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyDPBModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Dynamic Programming (Breadth First)\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conclusions (Random, BFS, DFS, DP)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Simple\n",
"# ulysses16: 77 (BFS), 84 (DFS)\n",
"# att48: 39236 (BFS), 40763 (DFS)\n",
"# st70: 761 (BFS), 901 (DFS)\n",
"\n",
"# Medium\n",
"# a280: 3088 (BFS), 3558 (DFS)\n",
"# pcb442: 58952 (BFS), 61984 (DFS)\n",
"\n",
"# Hard\n",
"# dsj1000: time-out (DP-BFS) 23,552,227 (DP-DFS)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"
1. All different models get the same results every time (except random).
\n", "2. All different models have an exponential time complexity (except random).
\n", "3. Depth First Seach is a little faster than Breadth First Search, but Breadth First get better results.
\n", "4. Only dynamic programming solves the problem with 1000 cities (Fast).
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# In the next workshop \n", "# Will try to solve the problem with 1000 cities faster, and get better results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3" } }, "nbformat": 4, "nbformat_minor": 4 }