{ "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": [ "\"TSP\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"solutions\"" ] }, { "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": {}, "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": true }, "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": [ "## Ant Colony Optimization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://github.com/rochakgupta/aco-tsp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math\n", "import random\n", "from model.base_model import Model\n", "\n", "class MyACOModel(Model):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " class Edge:\n", " def __init__(self, a, b, weight, initial_pheromone):\n", " self.a = a\n", " self.b = b\n", " self.weight = weight\n", " self.pheromone = initial_pheromone\n", "\n", " class Ant:\n", " def __init__(self, alpha, beta, num_nodes, edges):\n", " self.alpha = alpha\n", " self.beta = beta\n", " self.num_nodes = num_nodes\n", " self.edges = edges\n", " self.tour = None\n", " self.distance = 0.0\n", "\n", " def _select_node(self):\n", " roulette_wheel = 0.0\n", " unvisited_nodes = [node for node in range(self.num_nodes) if node not in self.tour]\n", " heuristic_total = 0.0\n", " for unvisited_node in unvisited_nodes:\n", " heuristic_total += self.edges[self.tour[-1]][unvisited_node].weight\n", " for unvisited_node in unvisited_nodes:\n", " roulette_wheel += pow(self.edges[self.tour[-1]][unvisited_node].pheromone, self.alpha) * \\\n", " pow((heuristic_total / self.edges[self.tour[-1]][unvisited_node].weight), self.beta)\n", " random_value = random.uniform(0.0, roulette_wheel)\n", " wheel_position = 0.0\n", " for unvisited_node in unvisited_nodes:\n", " wheel_position += pow(self.edges[self.tour[-1]][unvisited_node].pheromone, self.alpha) * \\\n", " pow((heuristic_total / self.edges[self.tour[-1]][unvisited_node].weight), self.beta)\n", " if wheel_position >= random_value:\n", " return unvisited_node\n", "\n", " def find_tour(self):\n", " self.tour = [random.randint(0, self.num_nodes - 1)]\n", " while len(self.tour) < self.num_nodes:\n", " self.tour.append(self._select_node())\n", " return self.tour\n", "\n", " def get_distance(self):\n", " self.distance = 0.0\n", " for i in range(self.num_nodes):\n", " self.distance += self.edges[self.tour[i]][self.tour[(i + 1) % self.num_nodes]].weight\n", " return self.distance\n", "\n", " def init(self, nodes, *args):\n", " super().init(nodes)\n", " mode, colony_size, elitist_weight, min_scaling_factor, alpha, beta, rho, pheromone_deposit_weight, initial_pheromone, labels = args\n", "\n", " self.mode = mode[0]\n", " self.colony_size = colony_size\n", " self.elitist_weight = elitist_weight\n", " self.min_scaling_factor = min_scaling_factor\n", " self.rho = rho\n", " self.pheromone_deposit_weight = pheromone_deposit_weight\n", " self.num_nodes = len(nodes)\n", " self.nodes = nodes\n", "\n", " if labels is not None:\n", " self.labels = labels\n", " else:\n", " self.labels = range(1, self.num_nodes + 1)\n", " self.edges = [[None] * self.num_nodes for _ in range(self.num_nodes)]\n", " for i in range(self.num_nodes):\n", " for j in range(i + 1, self.num_nodes):\n", " self.edges[i][j] = self.edges[j][i] = self.Edge(i, j, math.sqrt(\n", " pow(self.nodes[i][0] - self.nodes[j][0], 2.0) + pow(self.nodes[i][1] - self.nodes[j][1], 2.0)),\n", " initial_pheromone)\n", " self.ants = [self.Ant(alpha, beta, self.num_nodes, self.edges) for _ in range(self.colony_size)]\n", " self.global_best_tour = None\n", " self.global_best_distance = float(\"inf\")\n", "\n", " def _add_pheromone(self, tour, distance, weight=1.0):\n", " pheromone_to_add = self.pheromone_deposit_weight / distance\n", " for i in range(self.num_nodes):\n", " self.edges[tour[i]][tour[(i + 1) % self.num_nodes]].pheromone += weight * pheromone_to_add\n", "\n", " def _acs(self, max_it):\n", " for step in range(0, max_it):\n", "# print('[step]', step)\n", " for ant in self.ants:\n", " self._add_pheromone(ant.find_tour(), ant.get_distance())\n", " if ant.distance < self.global_best_distance:\n", " self.global_best_tour = ant.tour\n", " self.global_best_distance = ant.distance\n", " self.fitness_list.append(ant.distance)\n", " for i in range(self.num_nodes):\n", " for j in range(i + 1, self.num_nodes):\n", " self.edges[i][j].pheromone *= (1.0 - self.rho)\n", "\n", " def _elitist(self, max_it):\n", " for step in range(0, max_it):\n", "# print('[step]', step)\n", " for ant in self.ants:\n", " self._add_pheromone(ant.find_tour(), ant.get_distance())\n", " if ant.distance < self.global_best_distance:\n", " self.global_best_tour = ant.tour\n", " self.global_best_distance = ant.distance\n", " self.fitness_list.append(ant.distance)\n", " self._add_pheromone(self.global_best_tour, self.global_best_distance, weight=self.elitist_weight)\n", " for i in range(self.num_nodes):\n", " for j in range(i + 1, self.num_nodes):\n", " self.edges[i][j].pheromone *= (1.0 - self.rho)\n", "\n", " def fit(self, max_it=1000):\n", " \"\"\"\n", " Execute simulated annealing algorithm.\n", " \"\"\"\n", " # Initialize with the greedy solution.\n", " if self.mode == 'ACS':\n", " self._acs(max_it)\n", " elif self.mode == 'Elitist':\n", " self._elitist(max_it)\n", " else:\n", " print(\"Un supported\")\n", "# print('Sequence : <- {0} ->'.format(' - '.join(str(self.labels[i]) for i in self.global_best_tour)))\n", "# print('Total distance travelled to complete the tour : {0}\\n'.format(round(self.global_best_distance, 2)))\n", "\n", " return self.global_best_tour, 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": [ "# Set hypter-parameters\n", "mode='ACS', \n", "colony_size=10\n", "elitist_weight=1.0\n", "min_scaling_factor=0.001\n", "alpha=1.0\n", "beta=3.0\n", "rho=0.1\n", "pheromone_deposit_weight=1.0\n", "initial_pheromone=1.0\n", "labels = None" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "best_solution, fitness_list, time = TSP_Bench(tsp_file, MyACOModel, mode, colony_size, elitist_weight, min_scaling_factor, alpha, beta, rho, pheromone_deposit_weight, initial_pheromone, labels, max_it=1000, timeout=300)" ] }, { "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", " 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.log(\"Naive Random Solution\")\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": {}, "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": [ "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": {}, "outputs": [], "source": [ "tsp_path = './template/data'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Random Search\")\n", "best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyRandomModel)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_results(best_solutions, times, \"Random Model\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Set hypter-parameters\n", "mode='ACS', \n", "colony_size=10\n", "elitist_weight=1.0\n", "min_scaling_factor=0.001\n", "alpha=1.0\n", "beta=3.0\n", "rho=0.1\n", "pheromone_deposit_weight=1.0\n", "initial_pheromone=1.0\n", "labels = None" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Ant Colony Optimization\")\n", "best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MyACOModel, mode, colony_size, elitist_weight, min_scaling_factor, alpha, beta, rho, pheromone_deposit_weight, initial_pheromone, labels, max_it=100, timeout=600)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_results(best_solutions, times, \"Ant Colony Optimization\")" ] }, { "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 }