com2014-template/Workshop - 4 (TSP SA).ipynb

651 lines
16 KiB
Plaintext

{
"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.visualize_tsp import plotTSP\n",
"\n",
"from tsp import TSP_Bench_ONE\n",
"from tsp import TSP_Bench_PATH\n",
"from tsp import TSP_Bench_ALL"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Workshop Starts Here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src=\"images/tsp.jpg\" alt=\"TSP\" style=\"width: 900px;\"/>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src=\"images/solutions.png\" alt=\"solutions\" style=\"width: 900px;\"/>"
]
},
{
"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=1000):\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": {},
"outputs": [],
"source": [
"tsp_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyRandomModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.plot(fitness_list, 'o-')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulated Annealing"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import random\n",
"from model.base_model import Model\n",
"\n",
"class MySAModel(Model):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" self.iteration = 0\n",
"\n",
" def init(self, nodes):\n",
" super().init(nodes)\n",
" \n",
" # Set hyper-parameters\n",
" T = -1\n",
" stopping_temperature = -1\n",
" alpha = 0.99\n",
"\n",
" self.T = math.sqrt(self.N) if T == -1 else T\n",
" self.alpha = 0.995 if alpha == -1 else alpha\n",
" self.stopping_temperature = 1e-8 if stopping_temperature == -1 else stopping_temperature\n",
"\n",
" self.T_save = self.T # save inital T to reset if batch annealing is used\n",
"\n",
" def initial_solution(self):\n",
" \"\"\"\n",
" Greedy algorithm to get an initial solution (closest-neighbour).\n",
" \"\"\"\n",
" cur_node = random.choice(self.nodes) # start from a random node\n",
" solution = [cur_node]\n",
"\n",
" free_nodes = set(self.nodes)\n",
" free_nodes.remove(cur_node)\n",
" while free_nodes:\n",
" next_node = min(free_nodes, key=lambda x: self.dist(cur_node, x)) # nearest neighbour\n",
" free_nodes.remove(next_node)\n",
" solution.append(next_node)\n",
" cur_node = next_node\n",
"\n",
" cur_fit = self.fitness(solution)\n",
" if cur_fit < self.best_fitness: # If best found so far, update best fitness\n",
" self.best_fitness = cur_fit\n",
" self.best_solution = solution\n",
" self.fitness_list.append(cur_fit)\n",
" return solution, cur_fit\n",
"\n",
" def p_accept(self, candidate_fitness):\n",
" \"\"\"\n",
" Probability of accepting if the candidate is worse than current.\n",
" Depends on the current temperature and difference between candidate and current.\n",
" \"\"\"\n",
" return math.exp(-abs(candidate_fitness - self.cur_fitness) / self.T)\n",
"\n",
" def accept(self, candidate):\n",
" \"\"\"\n",
" Accept with probability 1 if candidate is better than current.\n",
" Accept with probabilty p_accept(..) if candidate is worse.\n",
" \"\"\"\n",
" candidate_fitness = self.fitness(candidate)\n",
" if candidate_fitness < self.cur_fitness:\n",
" self.cur_fitness, self.cur_solution = candidate_fitness, candidate\n",
" if candidate_fitness < self.best_fitness:\n",
" self.best_fitness, self.best_solution = candidate_fitness, candidate\n",
" else:\n",
" if random.random() < self.p_accept(candidate_fitness):\n",
" self.cur_fitness, self.cur_solution = candidate_fitness, candidate\n",
"\n",
" def fit(self, max_it=1000):\n",
" \"\"\"\n",
" Execute simulated annealing algorithm.\n",
" \"\"\"\n",
" # Initialize with the greedy solution.\n",
" self.cur_solution, self.cur_fitness = self.initial_solution()\n",
"\n",
" self.log(\"Starting annealing.\")\n",
" while self.T >= self.stopping_temperature and self.iteration < max_it:\n",
" candidate = list(self.cur_solution)\n",
" l = random.randint(1, self.N - 1)\n",
" i = random.randint(0, self.N - l)\n",
" candidate[i : (i + l)] = reversed(candidate[i : (i + l)])\n",
" self.accept(candidate)\n",
" self.T *= self.alpha\n",
" self.iteration += 1\n",
"\n",
" self.fitness_list.append(self.cur_fitness)\n",
"\n",
" self.log(f\"Best fitness obtained: {self.best_fitness}\")\n",
" improvement = 100 * (self.fitness_list[0] - self.best_fitness) / (self.fitness_list[0])\n",
" self.log(f\"Improvement over greedy heuristic: {improvement : .2f}%\")\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": {
"scrolled": false
},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MySAModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"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=1000):\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_file = './template/data/simple/ulysses16.tsp'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"best_solution, fitness_list, time = TSP_Bench_ONE(tsp_file, MyModel)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test All Dataset"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsp_path = './template'\n",
"for root, _, files in os.walk(tsp_path + '/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": [
"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": {
"scrolled": true
},
"outputs": [],
"source": [
"print(\"Simulated Annealing\")\n",
"best_solutions, fitness_lists, times = TSP_Bench_ALL(tsp_path, MySAModel)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"plot_results(best_solutions, times, \"Simulated Annealing Model\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conclusions"
]
},
{
"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.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}