younadi commited on
Commit
e09b20c
·
verified ·
1 Parent(s): 6628237

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +8 -0
  2. .vscode/settings.json +4 -0
  3. miscellaneous/kool/attention-learn-to-route/.gitignore +11 -0
  4. miscellaneous/kool/attention-learn-to-route/LICENSE +21 -0
  5. miscellaneous/kool/attention-learn-to-route/README.md +117 -0
  6. miscellaneous/kool/attention-learn-to-route/data/tsp/.ipynb +110 -0
  7. miscellaneous/kool/attention-learn-to-route/environment.yml +14 -0
  8. miscellaneous/kool/attention-learn-to-route/eval.py +216 -0
  9. miscellaneous/kool/attention-learn-to-route/generate_data.py +167 -0
  10. miscellaneous/kool/attention-learn-to-route/images/cvrp_1.png +0 -0
  11. miscellaneous/kool/attention-learn-to-route/images/cvrp_5.png +0 -0
  12. miscellaneous/kool/attention-learn-to-route/images/cvrp_7.png +0 -0
  13. miscellaneous/kool/attention-learn-to-route/images/cvrp_8.png +0 -0
  14. miscellaneous/kool/attention-learn-to-route/images/cvrp_9.png +0 -0
  15. miscellaneous/kool/attention-learn-to-route/nets/__init__.py +0 -0
  16. miscellaneous/kool/attention-learn-to-route/nets/attention_model.py +513 -0
  17. miscellaneous/kool/attention-learn-to-route/nets/critic_network.py +40 -0
  18. miscellaneous/kool/attention-learn-to-route/nets/graph_encoder.py +215 -0
  19. miscellaneous/kool/attention-learn-to-route/nets/pointer_network.py +353 -0
  20. miscellaneous/kool/attention-learn-to-route/options.py +87 -0
  21. miscellaneous/kool/attention-learn-to-route/plot_vrp.ipynb +0 -0
  22. miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_10/args.json +39 -0
  23. miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_100/args.json +39 -0
  24. miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_20/args.json +39 -0
  25. miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_50/args.json +39 -0
  26. miscellaneous/kool/attention-learn-to-route/pretrained/op_const_100/args.json +40 -0
  27. miscellaneous/kool/attention-learn-to-route/pretrained/op_const_20/args.json +40 -0
  28. miscellaneous/kool/attention-learn-to-route/pretrained/op_const_50/args.json +40 -0
  29. miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_100/args.json +40 -0
  30. miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_20/args.json +40 -0
  31. miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_50/args.json +40 -0
  32. miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_100/args.json +40 -0
  33. miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_20/args.json +40 -0
  34. miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_50/args.json +40 -0
  35. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_100/args.json +40 -0
  36. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_20/args.json +40 -0
  37. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_50/args.json +40 -0
  38. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_100/args.json +40 -0
  39. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_20/args.json +40 -0
  40. miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_50/args.json +40 -0
  41. miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_10/args.json +39 -0
  42. miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_100/args.json +39 -0
  43. miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_20/args.json +39 -0
  44. miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_50/args.json +39 -0
  45. miscellaneous/kool/attention-learn-to-route/pretrained/tsp_100/args.json +39 -0
  46. miscellaneous/kool/attention-learn-to-route/pretrained/tsp_20/args.json +39 -0
  47. miscellaneous/kool/attention-learn-to-route/pretrained/tsp_50/args.json +39 -0
  48. miscellaneous/kool/attention-learn-to-route/problems/__init__.py +4 -0
  49. miscellaneous/kool/attention-learn-to-route/problems/op/.gitignore +1 -0
  50. miscellaneous/kool/attention-learn-to-route/problems/op/__init__.py +0 -0
.gitattributes CHANGED
@@ -34,3 +34,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  short_paper_submission_icml_2026_workshop_CombiLatent.pdf filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  short_paper_submission_icml_2026_workshop_CombiLatent.pdf filter=lfs diff=lfs merge=lfs -text
37
+ src/xp0/.xdp-metrics_final.jpg filter=lfs diff=lfs merge=lfs -text
38
+ src/xp0/.xdp-metropolized_filling.jpg filter=lfs diff=lfs merge=lfs -text
39
+ src/xp0/datasets/flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
40
+ src/xp0/datasets/no_sinkhorn_flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
41
+ src/xp0/datasets/abla_1_flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
42
+ src/xp0/datasets/abla_2_no_sinkhorn_flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
43
+ src/xp0/datasets/abla_3_no_langevin_flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
44
+ src/xp0/datasets/no_langevin_flowshop_transformer_heatmaps.png filter=lfs diff=lfs merge=lfs -text
.vscode/settings.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "python-envs.defaultEnvManager": "ms-python.python:conda",
3
+ "python-envs.defaultPackageManager": "ms-python.python:conda"
4
+ }
miscellaneous/kool/attention-learn-to-route/.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ data/
2
+ outputs/
3
+ logs/
4
+ results/
5
+ __pycache__/
6
+ .idea
7
+ */.ipynb_checkpoints
8
+ *.tc.*
9
+ *.tc_backward.*
10
+ *.log
11
+ *.bak
miscellaneous/kool/attention-learn-to-route/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Wouter Kool
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
miscellaneous/kool/attention-learn-to-route/README.md ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ > Note: I am currently not able to actively maintain this repository. Please also checkout more recent implementations, e.g. https://github.com/ai4co/rl4co and https://github.com/cpwan/RLOR.
2
+
3
+ # Attention, Learn to Solve Routing Problems!
4
+
5
+ Attention based model for learning to solve the Travelling Salesman Problem (TSP) and the Vehicle Routing Problem (VRP), Orienteering Problem (OP) and (Stochastic) Prize Collecting TSP (PCTSP). Training with REINFORCE with greedy rollout baseline.
6
+
7
+ ![TSP100](images/tsp.gif)
8
+
9
+ ## Paper
10
+ For more details, please see our paper [Attention, Learn to Solve Routing Problems!](https://openreview.net/forum?id=ByxBFsRqYm) which has been accepted at [ICLR 2019](https://iclr.cc/Conferences/2019). If this code is useful for your work, please cite our paper:
11
+
12
+ ```
13
+ @inproceedings{
14
+ kool2018attention,
15
+ title={Attention, Learn to Solve Routing Problems!},
16
+ author={Wouter Kool and Herke van Hoof and Max Welling},
17
+ booktitle={International Conference on Learning Representations},
18
+ year={2019},
19
+ url={https://openreview.net/forum?id=ByxBFsRqYm},
20
+ }
21
+ ```
22
+
23
+ ## Dependencies
24
+
25
+ * Python>=3.8
26
+ * NumPy
27
+ * SciPy
28
+ * [PyTorch](http://pytorch.org/)>=1.7
29
+ * tqdm
30
+ * [tensorboard_logger](https://github.com/TeamHG-Memex/tensorboard_logger)
31
+ * Matplotlib (optional, only for plotting)
32
+
33
+ ## Quick start
34
+
35
+ For training TSP instances with 20 nodes and using rollout as REINFORCE baseline:
36
+ ```bash
37
+ python run.py --graph_size 20 --baseline rollout --run_name 'tsp20_rollout'
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Generating data
43
+
44
+ Training data is generated on the fly. To generate validation and test data (same as used in the paper) for all problems:
45
+ ```bash
46
+ python generate_data.py --problem all --name validation --seed 4321
47
+ python generate_data.py --problem all --name test --seed 1234
48
+ ```
49
+
50
+ ### Training
51
+
52
+ For training TSP instances with 20 nodes and using rollout as REINFORCE baseline and using the generated validation set:
53
+ ```bash
54
+ python run.py --graph_size 20 --baseline rollout --run_name 'tsp20_rollout' --val_dataset data/tsp/tsp20_validation_seed4321.pkl
55
+ ```
56
+
57
+ #### Multiple GPUs
58
+ By default, training will happen *on all available GPUs*. To disable CUDA at all, add the flag `--no_cuda`.
59
+ Set the environment variable `CUDA_VISIBLE_DEVICES` to only use specific GPUs:
60
+ ```bash
61
+ CUDA_VISIBLE_DEVICES=2,3 python run.py
62
+ ```
63
+ Note that using multiple GPUs has limited efficiency for small problem sizes (up to 50 nodes).
64
+
65
+ #### Warm start
66
+ You can initialize a run using a pretrained model by using the `--load_path` option:
67
+ ```bash
68
+ python run.py --graph_size 100 --load_path pretrained/tsp_100/epoch-99.pt
69
+ ```
70
+
71
+ The `--load_path` option can also be used to load an earlier run, in which case also the optimizer state will be loaded:
72
+ ```bash
73
+ python run.py --graph_size 20 --load_path 'outputs/tsp_20/tsp20_rollout_{datetime}/epoch-0.pt'
74
+ ```
75
+
76
+ The `--resume` option can be used instead of the `--load_path` option, which will try to resume the run, e.g. load additionally the baseline state, set the current epoch/step counter and set the random number generator state.
77
+
78
+ ### Evaluation
79
+ To evaluate a model, you can add the `--eval-only` flag to `run.py`, or use `eval.py`, which will additionally measure timing and save the results:
80
+ ```bash
81
+ python eval.py data/tsp/tsp20_test_seed1234.pkl --model pretrained/tsp_20 --decode_strategy greedy
82
+ ```
83
+ If the epoch is not specified, by default the last one in the folder will be used.
84
+
85
+ #### Sampling
86
+ To report the best of 1280 sampled solutions, use
87
+ ```bash
88
+ python eval.py data/tsp/tsp20_test_seed1234.pkl --model pretrained/tsp_20 --decode_strategy sample --width 1280 --eval_batch_size 1
89
+ ```
90
+ Beam Search (not in the paper) is also recently added and can be used using `--decode_strategy bs --width {beam_size}`.
91
+
92
+ #### To run baselines
93
+ Baselines for different problems are within the corresponding folders and can be ran (on multiple datasets at once) as follows
94
+ ```bash
95
+ python -m problems.tsp.tsp_baseline farthest_insertion data/tsp/tsp20_test_seed1234.pkl data/tsp/tsp50_test_seed1234.pkl data/tsp/tsp100_test_seed1234.pkl
96
+ ```
97
+ To run baselines, you need to install [Compass](https://github.com/bcamath-ds/compass) by running the `install_compass.sh` script from within the `problems/op` directory and [Concorde](http://www.math.uwaterloo.ca/tsp/concorde.html) using the `install_concorde.sh` script from within `problems/tsp`. [LKH3](http://akira.ruc.dk/~keld/research/LKH-3/) should be automatically downloaded and installed when required. To use [Gurobi](http://www.gurobi.com), obtain a ([free academic](http://www.gurobi.com/registration/academic-license-reg)) license and follow the [installation instructions](https://www.gurobi.com/documentation/8.1/quickstart_windows/installing_the_anaconda_py.html).
98
+
99
+ ### Other options and help
100
+ ```bash
101
+ python run.py -h
102
+ python eval.py -h
103
+ ```
104
+
105
+ ### Example CVRP solution
106
+ See `plot_vrp.ipynb` for an example of loading a pretrained model and plotting the result for Capacitated VRP with 100 nodes.
107
+
108
+ ![CVRP100](images/cvrp_0.png)
109
+
110
+ ## Acknowledgements
111
+ Thanks to [pemami4911/neural-combinatorial-rl-pytorch](https://github.com/pemami4911/neural-combinatorial-rl-pytorch) for getting me started with the code for the Pointer Network.
112
+
113
+ This repository includes adaptions of the following repositories as baselines:
114
+ * https://github.com/MichelDeudon/encode-attend-navigate
115
+ * https://github.com/mc-ride/orienteering
116
+ * https://github.com/jordanamecler/PCTSP
117
+ * https://github.com/rafael2reis/salesman
miscellaneous/kool/attention-learn-to-route/data/tsp/.ipynb ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 16,
6
+ "id": "05cb491c",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "name": "stdout",
11
+ "output_type": "stream",
12
+ "text": [
13
+ "(10000, 100, 2)\n"
14
+ ]
15
+ }
16
+ ],
17
+ "source": [
18
+ "import pickle\n",
19
+ "import numpy as np\n",
20
+ "import matplotlib.pyplot as plt\n",
21
+ "\n",
22
+ "nb_spots = 100\n",
23
+ "\n",
24
+ "with open(f\"tsp{nb_spots}_test_seed1234.pkl\", \"rb\") as f:\n",
25
+ " tsp_instances = pickle.load(f)\n",
26
+ "\n",
27
+ "tsp_instances = np.array(tsp_instances)\n",
28
+ "print(tsp_instances.shape)"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": 19,
34
+ "id": "40577ec0",
35
+ "metadata": {},
36
+ "outputs": [
37
+ {
38
+ "name": "stdout",
39
+ "output_type": "stream",
40
+ "text": [
41
+ "(100, 100)\n",
42
+ "55.11399205595661\n"
43
+ ]
44
+ }
45
+ ],
46
+ "source": [
47
+ "# compute the pairwise distances between the spots\n",
48
+ "distances = np.linalg.norm(tsp_instances[0][:, None, :] - tsp_instances[0][None, :, :], axis=-1)\n",
49
+ "print(distances.shape)\n",
50
+ "\n",
51
+ "# compute the tour length of the tour that visits the spots in the order they are given\n",
52
+ "# tour_schedule = np.arange(nb_spots)\n",
53
+ "tour_length = np.sum(distances[np.arange(nb_spots), np.arange(1, nb_spots + 1) % nb_spots])\n",
54
+ "print(tour_length)\n",
55
+ "\n",
56
+ "# plt.scatter(tsp_instances[0][:, 0], tsp_instances[0][:, 1])"
57
+ ]
58
+ },
59
+ {
60
+ "cell_type": "code",
61
+ "execution_count": null,
62
+ "id": "a32e82e8",
63
+ "metadata": {},
64
+ "outputs": [
65
+ {
66
+ "data": {
67
+ "text/plain": [
68
+ "<matplotlib.collections.PathCollection at 0x764d6b3549d0>"
69
+ ]
70
+ },
71
+ "execution_count": 6,
72
+ "metadata": {},
73
+ "output_type": "execute_result"
74
+ },
75
+ {
76
+ "data": {
77
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi0AAAGdCAYAAADey0OaAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAJqtJREFUeJzt3X9sW/W9//GXbUgM3yWmoTex2/muofcOlptB1ZaEABXalK4RV976x9XtF+iPW3FBC72oIrr30vKjXsZd041d1K9Gl4peuEPKWKqLxli2yrssrLpCZIpu00rLTQtqG9beYictuThZuWla+3z/qGLqxSmx6/j4c/x8SP7DJ59jv80B+cV5fz4fuyzLsgQAAFDk3HYXAAAAMBeEFgAAYARCCwAAMAKhBQAAGIHQAgAAjEBoAQAARiC0AAAAIxBaAACAEa6zu4C5SCaT+vDDD1VRUSGXy2V3OQAAYA4sy9LExIQWLVokt/va75MYEVo+/PBDBYNBu8sAAAA5OH36tD7/+c9f8+sYEVoqKiokXf7QlZWVNlcDAADmYnx8XMFgMPU9fq2MCC3TLaHKykpCCwAAhsnX1A4m4gIAACMQWgAAgBEILQAAwAiEFgAAYARCCwAAMAKhBQAAGIHQAgAAjEBoAQAARjBicznYJ5G01D88ptGJSVVXeNVQWyWPm99/AgAUHqEFs4oMRtXeM6RofDJ1LODzKhyqU0t9wMbKAACliPYQMooMRtXaNZAWWCQpFp9Ua9eAIoNRmyoDAJQqQgtmSCQttfcMycrwt+lj7T1DSiQzjQAAYH4QWjBD//DYjDssV7IkReOT6h8eK1xRAICSR2jBDKMTsweWXMYBAJAPTMTFDNUV3ryOg/1YBQbACQgtmKGhtkoBn1ex+GTGeS0uSX7f5S8+FD9WgQFwCtpDmMHjdikcqpN0OaBcafp5OFTH/6kbgFVgAJyE0IKMWuoD6ly/XH5fegvI7/Oqc/1y/g/dAKwCA+A0tIcwq5b6gFbX+ZkLYahsVoE1Lb25cIUBQI4ILbgqj9vFF5qhWAUGwGloDwEOxSowAE5DaAEcanoV2GzNPJcuryJiFRgAUxBaAIdiFRgApyG0AA7GKjAATsJEXMDhWAUGE7BrM+aC0AKUAFaBoZixazPmivYQAMA27NqMbBBaAAC2YNdm+yWSlvpOfKQ3j5xR34mPiv6fNe0hAIAt2LXZXia25bjTAgCwBbs228fUthyhBQBgC3ZttofJbTlCCwDAFuzabI9s2nLFhtACALAFuzbbw+S2HKEFAGAbdm0uPJPbcqweAgDYil2bC2u6LReLT2ac1+LS5dBYjG05QgsAwHbs2lw402251q4BuaS04FLsbTnaQwAAlBhT23LcaQEAoASZ2JYjtAAAUKJMa8vRHgIAAEYgtAAAACMQWgAAgBEILQAAwAiEFgAAYARCCwAAMAKhBQAAGIHQAgAAjEBoAQAARiC0AAAAIxBaAACAEQgtAADACIQWAABgBEILAAAwAqEFAAAY4Tq7CwD+WCJpqX94TKMTk6qu8Kqhtkoet8vusgAANsvpTsuePXu0ZMkSeb1eNTY2qr+//6rjd+/erVtvvVU33HCDgsGgnnjiCU1OTuZUMJwtMhjVvd99Ww/s+622dh/RA/t+q3u/+7Yig1G7SwMA2Czr0LJ//361tbUpHA5rYGBAd9xxh9asWaPR0dGM41977TVt27ZN4XBYR48e1csvv6z9+/frqaeeuubi4SyRwahauwYUjacH2lh8Uq1dAwQXAChxWYeWF154QY888og2b96suro67d27VzfeeKNeeeWVjOPfffdd3XPPPXrwwQe1ZMkSfe1rX9MDDzzwmXdnUFoSSUvtPUOyMvxt+lh7z5ASyUwjAAClIKvQMjU1pUOHDqm5ufnTF3C71dzcrL6+vozn3H333Tp06FAqpJw8eVIHDhzQ/ffffw1lw2n6h8dm3GG5kiUpGp9U//BY4YoCABSVrCbinjt3TolEQjU1NWnHa2pqdOzYsYznPPjggzp37pzuvfdeWZalS5cu6Zvf/OZV20MXLlzQhQsXUs/Hx8ezKRMGGp2Y2xynuY4zEROQAeDq5n310MGDB7Vz50798Ic/VGNjo44fP66tW7fqueee07PPPpvxnI6ODrW3t893aSgi1RXevI4zTWQwqvaeobS7TQGfV+FQnVrqAzm9JiEIgNO4LMua8ySBqakp3XjjjXr99de1du3a1PFNmzbp448/1ptvvjnjnFWrVumuu+7S888/nzrW1dWlRx99VH/4wx/kds/sUGW60xIMBhWPx1VZWTnXcmGQRNLSvd99W7H4ZMZ5LS5Jfp9X7zz5Vcd98U5PQP7jzz39KTvXL886uMxHCAKAbI2Pj8vn8+Xt+zurOS1lZWVasWKFent7U8eSyaR6e3vV1NSU8ZxPPvlkRjDxeDySpNnyUnl5uSorK9MecDaP26VwqE7Sp1/W06afh0N1jgss8zEBmVVYAJwq69VDbW1t2rdvn1599VUdPXpUra2tOn/+vDZv3ixJ2rhxo7Zv354aHwqF1NnZqe7ubg0PD+utt97Ss88+q1AolAovgCS11AfUuX65/L70FpDf583pboMJ8j0BmVVYAJws6zkt69at09mzZ7Vjxw7FYjEtW7ZMkUgkNTn31KlTaXdWnnnmGblcLj3zzDM6c+aM/uRP/kShUEjf+c538vcp4Bgt9QGtrvOXzFyMfE9AziYENS29eU6vCQDFIqs5LXbJd08MKBZ9Jz7SA/t++5njfvLIXXMKGW8eOaOt3Uc+c9z/+7/L9I1li+dSIgDkzNY5LQDyq6G2SgGfd8Y8nmkuXZ5A21BbNafXK/VVWACcjdAC2CjfE5DzHYIAoJgQWgCb5XMCcqmuwgJQGpjTAhSJfG4Gxz4tAIpBvr+/CS2AQ7EjLgC75fv7e9638QdgD4/bxbJmAI7CnBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAiEFgAAYARCCwAAMAKhBQAAGIHQAgAAjEBoAQAARiC0AAAAIxBaAACAEa6zuwAAQP4kkpb6h8c0OjGp6gqvGmqr5HG77C4LyAtCCwA4RGQwqvaeIUXjk6ljAZ9X4VCdWuoDNlYG5AftIQBwgMhgVK1dA2mBRZJi8Um1dg0oMhi1qTIgfwgtAGC4RNJSe8+QrAx/mz7W3jOkRNJKO6fvxEd688gZ9Z34KO1vQLGiPQQAhusfHptxh+VKlqRofFL9w2NqWnozbSQYizstAGC40YnZA8sfj6ONBJMRWgDAcNUV3jmNW/h/yrNuIwHFhNACAIZrqK1SwOfVbAubXbrc/pFLc24jAcWI0AIAhvO4XQqH6iRpRnCZfh4O1encHy7M6fXm2m4CCo3QAgAO0FIfUOf65fL70ltFfp9XneuXq6U+MOc20lzHAYXG6iEAcIiW+oBW1/ln3RF3uo0Ui09mnNfi0uWQ01BbVdC6gbkitACAg3jcLjUtvXnWv4VDdWrtGpBLSgsuV7aR2PYfxYr2EACUkLm0kYBixZ0WACgxn9VGAooVoQUAStDV2khAsaI9BAAAjEBoAQAARiC0AAAAIxBaAACAEQgtAADACIQWAABgBEILAAAwAvu0AADgAImk5fgNAwktAAAYLjIYVXvPkKLxydSxgM+rcKjOUT/NQHsIAACDRQajau0aSAsskhSLT6q1a0CRwahNleUfoQUAAEMlkpbae4bSfrF72vSx9p4hJZKZRpiH0AIAgKH6h8dm3GG5kiUpGp9U//BY4YqaR4QWAAAMNToxe2DJZVyxI7QAAGCo6gpvXscVO0ILAACGaqitUsDn1WwLm126vIqoobaqkGXNG0ILAACG8rhdCofqJGlGcJl+Hg7VOWa/FkILAAAGa6kPqHP9cvl96S0gv8+rzvXLHbVPC5vLAQBguJb6gFbX+dkRF85QCts7A0Ap87hdalp6s91lzCtCSwkole2dAQDOxpwWhyul7Z0BAM5GaHGwUtve2U6JpKW+Ex/pzSNn1HfiI/6ZAsA8oD3kYNls7+z0Puh8ov0GAIXBnRYHK7Xtne1A+w0ACofQ4mCltr1zodF+A4DCIrQ4WKlt71xopfbrqgBgN0KLg5Xa9s6FRvsNAAorp9CyZ88eLVmyRF6vV42Njerv77/q+I8//lhbtmxRIBBQeXm5vvjFL+rAgQM5FYzslNL2zoVG+w0ACivr1UP79+9XW1ub9u7dq8bGRu3evVtr1qzRe++9p+rq6hnjp6amtHr1alVXV+v111/X4sWL9fvf/1433XRTPurHHJTK9s6FNt1+i8UnM85rcelyOKT9BgD54bIsK6tZgo2Njbrzzjv14osvSpKSyaSCwaAef/xxbdu2bcb4vXv36vnnn9exY8d0/fXX51Tk+Pi4fD6f4vG4Kisrc3oNYD5Mrx6SlBZcpuMgd7MAlLJ8f39n1R6amprSoUOH1Nzc/OkLuN1qbm5WX19fxnN+/vOfq6mpSVu2bFFNTY3q6+u1c+dOJRKJWd/nwoULGh8fT3sAxYj2GwAUTlbtoXPnzimRSKimpibteE1NjY4dO5bxnJMnT+rtt9/WQw89pAMHDuj48eN67LHHdPHiRYXD4YzndHR0qL29PZvSANvQfgOAwpj3HXGTyaSqq6v10ksvyePxaMWKFTpz5oyef/75WUPL9u3b1dbWlno+Pj6uYDA436UCOSuFX1cFALtlFVoWLlwoj8ejkZGRtOMjIyPy+/0ZzwkEArr++uvl8XhSx770pS8pFotpampKZWVlM84pLy9XeXl5NqUBAACHy2pOS1lZmVasWKHe3t7UsWQyqd7eXjU1NWU855577tHx48eVTCZTx95//30FAoGMgQUAACCTrPdpaWtr0759+/Tqq6/q6NGjam1t1fnz57V582ZJ0saNG7V9+/bU+NbWVo2NjWnr1q16//339ctf/lI7d+7Uli1b8vcpAACA42U9p2XdunU6e/asduzYoVgspmXLlikSiaQm5546dUpu96dZKBgM6le/+pWeeOIJ3X777Vq8eLG2bt2qJ598Mn+fAgAAOF7W+7TYgX1aAAAwT76/v+d99RBQDBJJiyXJAGA4QgscLzIYVXvPUNovMgd8XoVDdWz+BgAG4Vee4WjT2+xfGVgkKRafVGvXgCKDUZsqAwBki9ACx0okLbX3DGX8McPpY+09Q0ok8zutK5G01HfiI7155Iz6TnyU99cHgFJFewiO1T88NuMOy5UsSdH4pPqHx/K2my2tKACYP9xpgWONTsweWHIZ91loRQHA/CK0wLGqK7yfPSiLcVdjVysKAEoJoQWO1VBbpYDPq9kWNrt0uXXTUFt1ze+VTSsKAJAbQgscy+N2KRyqk6QZwWX6eThUl5f9WgrdigKAUkRogaO11AfUuX65/L70FpDf51Xn+uV5mxxbyFYUAJQqVg/B8VrqA1pd55/XHXGnW1Gx+GTGeS0uXQ5K+WhFAUCpIrSgJHjcrrwta57t9cOhOrV2DcglpQWXfLeiAKBU0R4C8qRQrSgAKFXcaQHyqBCtKAAoVYQWIM/muxUFAKWK9hAAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAiEFgAAYARCCwAAMAKhBQAAGIFt/IEcJZIWvzEEAAVEaAFyEBmMqr1nSNH4ZOpYwOdVOFTHrzkDwDyhPQRkKTIYVWvXQFpgkaRYfFKtXQOKDEZtqgwAnI3QAmQhkbTU3jMkK8Pfpo+19wwpkcw0AgBwLQgtQBb6h8dm3GG5kiUpGp9U//BY4YoCgBJBaAGyMDoxe2DJZRwAYO4ILUAWqiu8eR0HAJg7QguQhYbaKgV8Xs22sNmly6uIGmqrClkWAJQEQguQBY/bpXCoTpJmBJfp5+FQHfu1AMA8ILQAWWqpD6hz/XL5fektIL/Pq871y9mnBQDmCZvLATloqQ9odZ2fHXEBoIAILUCOPG6XmpbebHcZAFAyaA8BAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAiEFgAAYAR+MBEAgDxIJC1++X2eEVoAALhGkcGo2nuGFI1Ppo4FfF6FQ3VqqQ/YWJmz0B4CAOAaRAajau0aSAsskhSLT6q1a0CRwahNlTkPoQUAgBwlkpbae4ZkZfjb9LH2niElkplGIFuEFgAActQ/PDbjDsuVLEnR+KT6h8cKV5SDEVoAAMjR6MTsgSWXcbg6QgsAADmqrvDmdRyujtACAECOGmqrFPB5NdvCZpcuryJqqK0qZFmORWgBACBHHrdL4VCdJM0ILtPPw6E69mvJE0ILAADXoKU+oM71y+X3pbeA/D6vOtcvZ5+WPGJzOQAArlFLfUCr6/zsiDvPCC0AAOSBx+1S09Kb7S7D0WgPAQAAIxBaAACAEXIKLXv27NGSJUvk9XrV2Nio/v7+OZ3X3d0tl8ultWvX5vK2AACghGUdWvbv36+2tjaFw2ENDAzojjvu0Jo1azQ6OnrV8z744AP9/d//vVatWpVzsQAAoHRlHVpeeOEFPfLII9q8ebPq6uq0d+9e3XjjjXrllVdmPSeRSOihhx5Se3u7brnllmsqGAAAlKasQsvU1JQOHTqk5ubmT1/A7VZzc7P6+vpmPe/b3/62qqur9fDDD8/pfS5cuKDx8fG0BwAAKG1ZhZZz584pkUiopqYm7XhNTY1isVjGc9555x29/PLL2rdv35zfp6OjQz6fL/UIBoPZlAkAABxoXlcPTUxMaMOGDdq3b58WLlw45/O2b9+ueDyeepw+fXoeqwQAACbIanO5hQsXyuPxaGRkJO34yMiI/H7/jPEnTpzQBx98oFAolDqWTCYvv/F11+m9997T0qVLZ5xXXl6u8vLybEoDAAAOl9WdlrKyMq1YsUK9vb2pY8lkUr29vWpqapox/rbbbtPvfvc7HTlyJPX4+te/rq985Ss6cuQIbR8AADBnWW/j39bWpk2bNmnlypVqaGjQ7t27df78eW3evFmStHHjRi1evFgdHR3yer2qr69PO/+mm26SpBnHAQAAribr0LJu3TqdPXtWO3bsUCwW07JlyxSJRFKTc0+dOiW3m412AQBAfrksy7LsLuKzjI+Py+fzKR6Pq7Ky0u5yAADAHOT7+7tkf+U5kbT4CXEAAAxSkqElMhhVe8+QovHJ1LGAz6twqE4t9QEbKwMAALMpucknkcGoWrsG0gKLJMXik2rtGlBkMGpTZQAA4GpKKrQkkpbae4aUaRLP9LH2niElkkU/zQcAgJJTUqGlf3hsxh2WK1mSovFJ9Q+PFa4oAAAwJyUVWkYnZg8suYwDAACFU1KhpbrCm9dxAACgcEoqtDTUVing82q2hc0uXV5F1FBbVciyAADAHJRUaPG4XQqH6iRpRnCZfh4O1bFfCwAARaikQosktdQH1Ll+ufy+9BaQ3+dV5/rl7NMCAECRKsnN5VrqA1pd52dHXAAADFKSoUW63CpqWnqz3WUAAIA5Krn2EAAAMBOhBQAAGIHQAgAAjEBoAQAARiC0AAAAIxBaAACAEQgtAADACIQWAABgBEILAAAwAqEFAAAYgdACAACMQGgBAABGILQAAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAjX2V0AAABXSiQt9Q+PaXRiUtUVXjXUVsnjdtldFooAoQUAUDQig1G19wwpGp9MHQv4vAqH6tRSH7CxMhQD2kMAgKIQGYyqtWsgLbBIUiw+qdauAUUGozZVhmJBaAEA2C6RtNTeMyQrw9+mj7X3DCmRzDQCpYLQAgCwXf/w2Iw7LFeyJEXjk+ofHitcUSg6hBYAgO1GJ2YPLLmMgzMRWgAAtquu8OZ1HJyJ0AIAsF1DbZUCPq9mW9js0uVVRA21VYUsC0WG0AIAsJ3H7VI4VCdJM4LL9PNwqI79WkocoQUAUBRa6gPqXL9cfl96C8jv86pz/XL2aQGbywEAikdLfUCr6/zsiIuMCC0AgKLicbvUtPRmu8tAEaI9BAAAjEBoAQAARiC0AAAAIxBaAACAEQgtAADACIQWAABgBEILAAAwAqEFAAAYgdACAACMQGgBAABGILQAAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABghp9CyZ88eLVmyRF6vV42Njerv75917L59+7Rq1SotWLBACxYsUHNz81XHAwAAZJJ1aNm/f7/a2toUDoc1MDCgO+64Q2vWrNHo6GjG8QcPHtQDDzyg3/zmN+rr61MwGNTXvvY1nTlz5pqLBwAApcNlWZaVzQmNjY2688479eKLL0qSksmkgsGgHn/8cW3btu0zz08kElqwYIFefPFFbdy4cU7vOT4+Lp/Pp3g8rsrKymzKBQAANsn393dWd1qmpqZ06NAhNTc3f/oCbream5vV19c3p9f45JNPdPHiRVVVVc065sKFCxofH097AACA0pZVaDl37pwSiYRqamrSjtfU1CgWi83pNZ588kktWrQoLfj8sY6ODvl8vtQjGAxmUyYAAHCggq4e2rVrl7q7u/XGG2/I6/XOOm779u2Kx+Opx+nTpwtYJQAAKEbXZTN44cKF8ng8GhkZSTs+MjIiv99/1XO///3va9euXfr1r3+t22+//apjy8vLVV5enk1pAADA4bK601JWVqYVK1aot7c3dSyZTKq3t1dNTU2znve9731Pzz33nCKRiFauXJl7tQAAoGRldadFktra2rRp0yatXLlSDQ0N2r17t86fP6/NmzdLkjZu3KjFixero6NDkvTd735XO3bs0GuvvaYlS5ak5r587nOf0+c+97k8fhQAAOBkWYeWdevW6ezZs9qxY4disZiWLVumSCSSmpx76tQpud2f3sDp7OzU1NSU/uqv/irtdcLhsL71rW9dW/UAAKBkZL1Pix3YpwUAAPPYuk8LAACAXQgtAADACIQWAABgBEILAAAwAqEFAAAYgdACAACMQGgBAABGILQAAAAjEFoAAIARst7GHwCAYpFIWuofHtPoxKSqK7xqqK2Sx+2yuyzME0ILAMBIkcGo2nuGFI1Ppo4FfF6FQ3VqqQ/YWBnmC+0hAIBxIoNRtXYNpAUWSYrFJ9XaNaDIYNSmyjCfCC0AAKMkkpbae4aU6dd+p4+19wwpkSz63wNGlggtAACj9A+PzbjDciVLUjQ+qf7hscIVhYIgtAAAjDI6MXtgyWUczEFoAQAYpbrCm9dxMAehBQBglIbaKgV8Xs22sNmly6uIGmqrClkWCoDQAgAwisftUjhUJ0kzgsv083Cojv1aHIjQAgAwTkt9QJ3rl8vvS28B+X1eda5fzj4tDsXmcgAAI7XUB7S6zs+OuCWE0AIAMJbH7VLT0pvtLgMFQnsIAAAYgdACAACMQGgBAABGILQAAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAiEFgAAYARCCwAAMAKhBQAAGIHQAgAAjEBoAQAARiC0AAAAIxBaAACAEQgtAADACIQWAABgBEILAAAwAqEFAAAYgdACAACMQGgBAABGILQAAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEa4zu4CADhbImmpf3hMoxOTqq7wqqG2Sh63y+6yABiI0AJg3kQGo2rvGVI0Ppk6FvB5FQ7VqaU+YGNlAExEewjAvIgMRtXaNZAWWCQpFp9Ua9eAIoNRmyoDYCpCC4C8SyQttfcMycrwt+lj7T1DSiQzjQCAzAgtAPKuf3hsxh2WK1mSovFJ9Q+PFa4oAMYjtADIu9GJ2QNLLuMAQMoxtOzZs0dLliyR1+tVY2Oj+vv7rzr+3/7t33TbbbfJ6/Xqy1/+sg4cOJBTsQDMUF3hzes4AJByCC379+9XW1ubwuGwBgYGdMcdd2jNmjUaHR3NOP7dd9/VAw88oIcffliHDx/W2rVrtXbtWg0ODl5z8QCKU0NtlQI+r2Zb2OzS5VVEDbVVhSwLgOFclmVlNROusbFRd955p1588UVJUjKZVDAY1OOPP65t27bNGL9u3TqdP39ev/jFL1LH7rrrLi1btkx79+6d03uOj4/L5/MpHo+rsrIym3IB2GR69ZCktAm500Gmc/1ylj0DDpfv7++s7rRMTU3p0KFDam5u/vQF3G41Nzerr68v4zl9fX1p4yVpzZo1s44H4Awt9QF1rl8uvy+9BeT3eQksAHKS1eZy586dUyKRUE1NTdrxmpoaHTt2LOM5sVgs4/hYLDbr+1y4cEEXLlxIPR8fH8+mTABFoqU+oNV1fnbEBZAXRbkjbkdHh9rb2+0uA0AeeNwuNS292e4yADhAVu2hhQsXyuPxaGRkJO34yMiI/H5/xnP8fn9W4yVp+/btisfjqcfp06ezKRMAADhQVqGlrKxMK1asUG9vb+pYMplUb2+vmpqaMp7T1NSUNl6S3nrrrVnHS1J5ebkqKyvTHgAAoLRl3R5qa2vTpk2btHLlSjU0NGj37t06f/68Nm/eLEnauHGjFi9erI6ODknS1q1bdd999+mf//mf9Zd/+Zfq7u7Wf/7nf+qll17K7ycBAACOlnVoWbdunc6ePasdO3YoFotp2bJlikQiqcm2p06dktv96Q2cu+++W6+99pqeeeYZPfXUU/rzP/9z/exnP1N9fX3+PgUAAHC8rPdpsQP7tAAAYB5b92kBAACwC6EFAAAYgdACAACMQGgBAABGKModcf/Y9FxhtvMHAMAc09/b+VrzY0RomZiYkCQFg0GbKwEAANmamJiQz+e75tcxYslzMpnUhx9+qIqKCrlc/NBavoyPjysYDOr06dMsJS8iXJfixHUpTlyX4jV9bYaGhnTrrbem7eGWKyPutLjdbn3+85+3uwzH4qcSihPXpThxXYoT16V4LV68OC+BRWIiLgAAMAShBQAAGIHQUsLKy8sVDodVXl5udym4AtelOHFdihPXpXjNx7UxYiIuAAAAd1oAAIARCC0AAMAIhBYAAGAEQgsAADACocXh9uzZoyVLlsjr9aqxsVH9/f2zjt23b59WrVqlBQsWaMGCBWpubr7qeOQum+type7ubrlcLq1du3Z+CyxR2V6Xjz/+WFu2bFEgEFB5ebm++MUv6sCBAwWqtnRke112796tW2+9VTfccIOCwaCeeOIJTU5OFqja0vAf//EfCoVCWrRokVwul372s5995jkHDx7U8uXLVV5erj/7sz/Tj370o+zf2IJjdXd3W2VlZdYrr7xi/dd//Zf1yCOPWDfddJM1MjKScfyDDz5o7dmzxzp8+LB19OhR62/+5m8sn89n/fd//3eBK3e2bK/LtOHhYWvx4sXWqlWrrG984xuFKbaEZHtdLly4YK1cudK6//77rXfeeccaHh62Dh48aB05cqTAlTtbttflxz/+sVVeXm79+Mc/toaHh61f/epXViAQsJ544okCV+5sBw4csJ5++mnrpz/9qSXJeuONN646/uTJk9aNN95otbW1WUNDQ9YPfvADy+PxWJFIJKv3JbQ4WENDg7Vly5bU80QiYS1atMjq6OiY0/mXLl2yKioqrFdffXW+SixJuVyXS5cuWXfffbf1L//yL9amTZsILfMg2+vS2dlp3XLLLdbU1FShSixJ2V6XLVu2WF/96lfTjrW1tVn33HPPvNZZyuYSWv7xH//R+ou/+Iu0Y+vWrbPWrFmT1XvRHnKoqakpHTp0SM3Nzaljbrdbzc3N6uvrm9NrfPLJJ7p48aKqqqrmq8ySk+t1+fa3v63q6mo9/PDDhSiz5ORyXX7+85+rqalJW7ZsUU1Njerr67Vz504lEolCle14uVyXu+++W4cOHUq1kE6ePKkDBw7o/vvvL0jNyKyvry/tOkrSmjVr5vx9NM2IH0xE9s6dO6dEIqGampq04zU1NTp27NicXuPJJ5/UokWLZvyLhtzlcl3eeecdvfzyyzpy5EgBKixNuVyXkydP6u2339ZDDz2kAwcO6Pjx43rsscd08eJFhcPhQpTteLlclwcffFDnzp3TvffeK8uydOnSJX3zm9/UU089VYiSMYtYLJbxOo6Pj+t///d/dcMNN8zpdbjTgox27dql7u5uvfHGG/J6vXaXU7ImJia0YcMG7du3TwsXLrS7HFwhmUyqurpaL730klasWKF169bp6aef1t69e+0uraQdPHhQO3fu1A9/+EMNDAzopz/9qX75y1/queees7s05AF3Whxq4cKF8ng8GhkZSTs+MjIiv99/1XO///3va9euXfr1r3+t22+/fT7LLDnZXpcTJ07ogw8+UCgUSh1LJpOSpOuuu07vvfeeli5dOr9Fl4Bc/nsJBAK6/vrr5fF4Use+9KUvKRaLaWpqSmVlZfNacynI5bo8++yz2rBhg/72b/9WkvTlL39Z58+f16OPPqqnn35abjf/r24Hv9+f8TpWVlbO+S6LxJ0WxyorK9OKFSvU29ubOpZMJtXb26umpqZZz/ve976n5557TpFIRCtXrixEqSUl2+ty22236Xe/+52OHDmSenz961/XV77yFR05ckTBYLCQ5TtWLv+93HPPPTp+/HgqRErS+++/r0AgQGDJk1yuyyeffDIjmEwHS4uf2rNNU1NT2nWUpLfeeuuq30cZZTdHGCbp7u62ysvLrR/96EfW0NCQ9eijj1o33XSTFYvFLMuyrA0bNljbtm1Ljd+1a5dVVlZmvf7661Y0Gk09JiYm7PoIjpTtdfljrB6aH9lel1OnTlkVFRXW3/3d31nvvfee9Ytf/MKqrq62/umf/smuj+BI2V6XcDhsVVRUWD/5yU+skydPWv/+7/9uLV261Prrv/5ruz6CI01MTFiHDx+2Dh8+bEmyXnjhBevw4cPW73//e8uyLGvbtm3Whg0bUuOnlzz/wz/8g3X06FFrz549LHnGTD/4wQ+sP/3TP7XKysqshoYG67e//W3qb/fdd5+1adOm1PMvfOELlqQZj3A4XPjCHS6b6/LHCC3zJ9vr8u6771qNjY1WeXm5dcstt1jf+c53rEuXLhW4aufL5rpcvHjR+ta3vmUtXbrU8nq9VjAYtB577DHrf/7nfwpfuIP95je/yfh9MX0tNm3aZN13330zzlm2bJlVVlZm3XLLLda//uu/Zv2+LsvifhkAACh+zGkBAABGILQAAAAjEFoAAIARCC0AAMAIhBYAAGAEQgsAADACoQUAABiB0AIAAIxAaAEAAEYgtAAAACMQWgAAgBEILQAAwAj/HwPAsrA23U14AAAAAElFTkSuQmCC",
78
+ "text/plain": [
79
+ "<Figure size 640x480 with 1 Axes>"
80
+ ]
81
+ },
82
+ "metadata": {},
83
+ "output_type": "display_data"
84
+ }
85
+ ],
86
+ "source": []
87
+ }
88
+ ],
89
+ "metadata": {
90
+ "kernelspec": {
91
+ "display_name": "ai",
92
+ "language": "python",
93
+ "name": "python3"
94
+ },
95
+ "language_info": {
96
+ "codemirror_mode": {
97
+ "name": "ipython",
98
+ "version": 3
99
+ },
100
+ "file_extension": ".py",
101
+ "mimetype": "text/x-python",
102
+ "name": "python",
103
+ "nbconvert_exporter": "python",
104
+ "pygments_lexer": "ipython3",
105
+ "version": "3.11.13"
106
+ }
107
+ },
108
+ "nbformat": 4,
109
+ "nbformat_minor": 5
110
+ }
miscellaneous/kool/attention-learn-to-route/environment.yml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run: conda env create --file environment.yml
2
+ name: attention_tsp
3
+ channels:
4
+ - pytorch
5
+ dependencies:
6
+ - python>=3.8
7
+ - anaconda
8
+ - tqdm
9
+ - pytorch
10
+ - torchvision
11
+ - cuda91
12
+ - pip
13
+ - pip:
14
+ - tensorboard_logger
miscellaneous/kool/attention-learn-to-route/eval.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import os
4
+ import argparse
5
+ import numpy as np
6
+ import itertools
7
+ from tqdm import tqdm
8
+ from utils import load_model, move_to
9
+ from utils.data_utils import save_dataset
10
+ from torch.utils.data import DataLoader
11
+ import time
12
+ from datetime import timedelta
13
+ from utils.functions import parse_softmax_temperature
14
+ mp = torch.multiprocessing.get_context('spawn')
15
+
16
+
17
+ def get_best(sequences, cost, ids=None, batch_size=None):
18
+ """
19
+ Ids contains [0, 0, 0, 1, 1, 2, ..., n, n, n] if 3 solutions found for 0th instance, 2 for 1st, etc
20
+ :param sequences:
21
+ :param lengths:
22
+ :param ids:
23
+ :return: list with n sequences and list with n lengths of solutions
24
+ """
25
+ if ids is None:
26
+ idx = cost.argmin()
27
+ return sequences[idx:idx+1, ...], cost[idx:idx+1, ...]
28
+
29
+ splits = np.hstack([0, np.where(ids[:-1] != ids[1:])[0] + 1])
30
+ mincosts = np.minimum.reduceat(cost, splits)
31
+
32
+ group_lengths = np.diff(np.hstack([splits, len(ids)]))
33
+ all_argmin = np.flatnonzero(np.repeat(mincosts, group_lengths) == cost)
34
+ result = np.full(len(group_lengths) if batch_size is None else batch_size, -1, dtype=int)
35
+
36
+ result[ids[all_argmin[::-1]]] = all_argmin[::-1]
37
+
38
+ return [sequences[i] if i >= 0 else None for i in result], [cost[i] if i >= 0 else math.inf for i in result]
39
+
40
+
41
+ def eval_dataset_mp(args):
42
+ (dataset_path, width, softmax_temp, opts, i, num_processes) = args
43
+
44
+ model, _ = load_model(opts.model)
45
+ val_size = opts.val_size // num_processes
46
+ dataset = model.problem.make_dataset(filename=dataset_path, num_samples=val_size, offset=opts.offset + val_size * i)
47
+ device = torch.device("cuda:{}".format(i))
48
+
49
+ return _eval_dataset(model, dataset, width, softmax_temp, opts, device)
50
+
51
+
52
+ def eval_dataset(dataset_path, width, softmax_temp, opts):
53
+ # Even with multiprocessing, we load the model here since it contains the name where to write results
54
+ model, _ = load_model(opts.model)
55
+ use_cuda = torch.cuda.is_available() and not opts.no_cuda
56
+ if opts.multiprocessing:
57
+ assert use_cuda, "Can only do multiprocessing with cuda"
58
+ num_processes = torch.cuda.device_count()
59
+ assert opts.val_size % num_processes == 0
60
+
61
+ with mp.Pool(num_processes) as pool:
62
+ results = list(itertools.chain.from_iterable(pool.map(
63
+ eval_dataset_mp,
64
+ [(dataset_path, width, softmax_temp, opts, i, num_processes) for i in range(num_processes)]
65
+ )))
66
+
67
+ else:
68
+ device = torch.device("cuda:0" if use_cuda else "cpu")
69
+ dataset = model.problem.make_dataset(filename=dataset_path, num_samples=opts.val_size, offset=opts.offset)
70
+ results = _eval_dataset(model, dataset, width, softmax_temp, opts, device)
71
+
72
+ # This is parallelism, even if we use multiprocessing (we report as if we did not use multiprocessing, e.g. 1 GPU)
73
+ parallelism = opts.eval_batch_size
74
+
75
+ costs, tours, durations = zip(*results) # Not really costs since they should be negative
76
+
77
+ print("Average cost: {} +- {}".format(np.mean(costs), 2 * np.std(costs) / np.sqrt(len(costs))))
78
+ print("Average serial duration: {} +- {}".format(
79
+ np.mean(durations), 2 * np.std(durations) / np.sqrt(len(durations))))
80
+ print("Average parallel duration: {}".format(np.mean(durations) / parallelism))
81
+ print("Calculated total duration: {}".format(timedelta(seconds=int(np.sum(durations) / parallelism))))
82
+
83
+ dataset_basename, ext = os.path.splitext(os.path.split(dataset_path)[-1])
84
+ model_name = "_".join(os.path.normpath(os.path.splitext(opts.model)[0]).split(os.sep)[-2:])
85
+ if opts.o is None:
86
+ results_dir = os.path.join(opts.results_dir, model.problem.NAME, dataset_basename)
87
+ os.makedirs(results_dir, exist_ok=True)
88
+
89
+ out_file = os.path.join(results_dir, "{}-{}-{}{}-t{}-{}-{}{}".format(
90
+ dataset_basename, model_name,
91
+ opts.decode_strategy,
92
+ width if opts.decode_strategy != 'greedy' else '',
93
+ softmax_temp, opts.offset, opts.offset + len(costs), ext
94
+ ))
95
+ else:
96
+ out_file = opts.o
97
+
98
+ assert opts.f or not os.path.isfile(
99
+ out_file), "File already exists! Try running with -f option to overwrite."
100
+
101
+ save_dataset((results, parallelism), out_file)
102
+
103
+ return costs, tours, durations
104
+
105
+
106
+ def _eval_dataset(model, dataset, width, softmax_temp, opts, device):
107
+
108
+ model.to(device)
109
+ model.eval()
110
+
111
+ model.set_decode_type(
112
+ "greedy" if opts.decode_strategy in ('bs', 'greedy') else "sampling",
113
+ temp=softmax_temp)
114
+
115
+ dataloader = DataLoader(dataset, batch_size=opts.eval_batch_size)
116
+
117
+ results = []
118
+ for batch in tqdm(dataloader, disable=opts.no_progress_bar):
119
+ batch = move_to(batch, device)
120
+
121
+ start = time.time()
122
+ with torch.no_grad():
123
+ if opts.decode_strategy in ('sample', 'greedy'):
124
+ if opts.decode_strategy == 'greedy':
125
+ assert width == 0, "Do not set width when using greedy"
126
+ assert opts.eval_batch_size <= opts.max_calc_batch_size, \
127
+ "eval_batch_size should be smaller than calc batch size"
128
+ batch_rep = 1
129
+ iter_rep = 1
130
+ elif width * opts.eval_batch_size > opts.max_calc_batch_size:
131
+ assert opts.eval_batch_size == 1
132
+ assert width % opts.max_calc_batch_size == 0
133
+ batch_rep = opts.max_calc_batch_size
134
+ iter_rep = width // opts.max_calc_batch_size
135
+ else:
136
+ batch_rep = width
137
+ iter_rep = 1
138
+ assert batch_rep > 0
139
+ # This returns (batch_size, iter_rep shape)
140
+ sequences, costs = model.sample_many(batch, batch_rep=batch_rep, iter_rep=iter_rep)
141
+ batch_size = len(costs)
142
+ ids = torch.arange(batch_size, dtype=torch.int64, device=costs.device)
143
+ else:
144
+ assert opts.decode_strategy == 'bs'
145
+
146
+ cum_log_p, sequences, costs, ids, batch_size = model.beam_search(
147
+ batch, beam_size=width,
148
+ compress_mask=opts.compress_mask,
149
+ max_calc_batch_size=opts.max_calc_batch_size
150
+ )
151
+
152
+ if sequences is None:
153
+ sequences = [None] * batch_size
154
+ costs = [math.inf] * batch_size
155
+ else:
156
+ sequences, costs = get_best(
157
+ sequences.cpu().numpy(), costs.cpu().numpy(),
158
+ ids.cpu().numpy() if ids is not None else None,
159
+ batch_size
160
+ )
161
+ duration = time.time() - start
162
+ for seq, cost in zip(sequences, costs):
163
+ if model.problem.NAME == "tsp":
164
+ seq = seq.tolist() # No need to trim as all are same length
165
+ elif model.problem.NAME in ("cvrp", "sdvrp"):
166
+ seq = np.trim_zeros(seq).tolist() + [0] # Add depot
167
+ elif model.problem.NAME in ("op", "pctsp"):
168
+ seq = np.trim_zeros(seq) # We have the convention to exclude the depot
169
+ else:
170
+ assert False, "Unkown problem: {}".format(model.problem.NAME)
171
+ # Note VRP only
172
+ results.append((cost, seq, duration))
173
+
174
+ return results
175
+
176
+
177
+ if __name__ == "__main__":
178
+
179
+ parser = argparse.ArgumentParser()
180
+ parser.add_argument("datasets", nargs='+', help="Filename of the dataset(s) to evaluate")
181
+ parser.add_argument("-f", action='store_true', help="Set true to overwrite")
182
+ parser.add_argument("-o", default=None, help="Name of the results file to write")
183
+ parser.add_argument('--val_size', type=int, default=10000,
184
+ help='Number of instances used for reporting validation performance')
185
+ parser.add_argument('--offset', type=int, default=0,
186
+ help='Offset where to start in dataset (default 0)')
187
+ parser.add_argument('--eval_batch_size', type=int, default=1024,
188
+ help="Batch size to use during (baseline) evaluation")
189
+ # parser.add_argument('--decode_type', type=str, default='greedy',
190
+ # help='Decode type, greedy or sampling')
191
+ parser.add_argument('--width', type=int, nargs='+',
192
+ help='Sizes of beam to use for beam search (or number of samples for sampling), '
193
+ '0 to disable (default), -1 for infinite')
194
+ parser.add_argument('--decode_strategy', type=str,
195
+ help='Beam search (bs), Sampling (sample) or Greedy (greedy)')
196
+ parser.add_argument('--softmax_temperature', type=parse_softmax_temperature, default=1,
197
+ help="Softmax temperature (sampling or bs)")
198
+ parser.add_argument('--model', type=str)
199
+ parser.add_argument('--no_cuda', action='store_true', help='Disable CUDA')
200
+ parser.add_argument('--no_progress_bar', action='store_true', help='Disable progress bar')
201
+ parser.add_argument('--compress_mask', action='store_true', help='Compress mask into long')
202
+ parser.add_argument('--max_calc_batch_size', type=int, default=10000, help='Size for subbatches')
203
+ parser.add_argument('--results_dir', default='results', help="Name of results directory")
204
+ parser.add_argument('--multiprocessing', action='store_true',
205
+ help='Use multiprocessing to parallelize over multiple GPUs')
206
+
207
+ opts = parser.parse_args()
208
+
209
+ assert opts.o is None or (len(opts.datasets) == 1 and len(opts.width) <= 1), \
210
+ "Cannot specify result filename with more than one dataset or more than one width"
211
+
212
+ widths = opts.width if opts.width is not None else [0]
213
+
214
+ for width in widths:
215
+ for dataset_path in opts.datasets:
216
+ eval_dataset(dataset_path, width, opts.softmax_temperature, opts)
miscellaneous/kool/attention-learn-to-route/generate_data.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import numpy as np
4
+ from utils.data_utils import check_extension, save_dataset
5
+
6
+
7
+ def generate_tsp_data(dataset_size, tsp_size):
8
+ return np.random.uniform(size=(dataset_size, tsp_size, 2)).tolist()
9
+
10
+
11
+ def generate_vrp_data(dataset_size, vrp_size):
12
+ CAPACITIES = {
13
+ 10: 20.,
14
+ 20: 30.,
15
+ 50: 40.,
16
+ 100: 50.
17
+ }
18
+ return list(zip(
19
+ np.random.uniform(size=(dataset_size, 2)).tolist(), # Depot location
20
+ np.random.uniform(size=(dataset_size, vrp_size, 2)).tolist(), # Node locations
21
+ np.random.randint(1, 10, size=(dataset_size, vrp_size)).tolist(), # Demand, uniform integer 1 ... 9
22
+ np.full(dataset_size, CAPACITIES[vrp_size]).tolist() # Capacity, same for whole dataset
23
+ ))
24
+
25
+
26
+ def generate_op_data(dataset_size, op_size, prize_type='const'):
27
+ depot = np.random.uniform(size=(dataset_size, 2))
28
+ loc = np.random.uniform(size=(dataset_size, op_size, 2))
29
+
30
+ # Methods taken from Fischetti et al. 1998
31
+ if prize_type == 'const':
32
+ prize = np.ones((dataset_size, op_size))
33
+ elif prize_type == 'unif':
34
+ prize = (1 + np.random.randint(0, 100, size=(dataset_size, op_size))) / 100.
35
+ else: # Based on distance to depot
36
+ assert prize_type == 'dist'
37
+ prize_ = np.linalg.norm(depot[:, None, :] - loc, axis=-1)
38
+ prize = (1 + (prize_ / prize_.max(axis=-1, keepdims=True) * 99).astype(int)) / 100.
39
+
40
+ # Max length is approximately half of optimal TSP tour, such that half (a bit more) of the nodes can be visited
41
+ # which is maximally difficult as this has the largest number of possibilities
42
+ MAX_LENGTHS = {
43
+ 20: 2.,
44
+ 50: 3.,
45
+ 100: 4.
46
+ }
47
+
48
+ return list(zip(
49
+ depot.tolist(),
50
+ loc.tolist(),
51
+ prize.tolist(),
52
+ np.full(dataset_size, MAX_LENGTHS[op_size]).tolist() # Capacity, same for whole dataset
53
+ ))
54
+
55
+
56
+ def generate_pctsp_data(dataset_size, pctsp_size, penalty_factor=3):
57
+ depot = np.random.uniform(size=(dataset_size, 2))
58
+ loc = np.random.uniform(size=(dataset_size, pctsp_size, 2))
59
+
60
+ # For the penalty to make sense it should be not too large (in which case all nodes will be visited) nor too small
61
+ # so we want the objective term to be approximately equal to the length of the tour, which we estimate with half
62
+ # of the nodes by half of the tour length (which is very rough but similar to op)
63
+ # This means that the sum of penalties for all nodes will be approximately equal to the tour length (on average)
64
+ # The expected total (uniform) penalty of half of the nodes (since approx half will be visited by the constraint)
65
+ # is (n / 2) / 2 = n / 4 so divide by this means multiply by 4 / n,
66
+ # However instead of 4 we use penalty_factor (3 works well) so we can make them larger or smaller
67
+ MAX_LENGTHS = {
68
+ 20: 2.,
69
+ 50: 3.,
70
+ 100: 4.
71
+ }
72
+ penalty_max = MAX_LENGTHS[pctsp_size] * (penalty_factor) / float(pctsp_size)
73
+ penalty = np.random.uniform(size=(dataset_size, pctsp_size)) * penalty_max
74
+
75
+ # Take uniform prizes
76
+ # Now expectation is 0.5 so expected total prize is n / 2, we want to force to visit approximately half of the nodes
77
+ # so the constraint will be that total prize >= (n / 2) / 2 = n / 4
78
+ # equivalently, we divide all prizes by n / 4 and the total prize should be >= 1
79
+ deterministic_prize = np.random.uniform(size=(dataset_size, pctsp_size)) * 4 / float(pctsp_size)
80
+
81
+ # In the deterministic setting, the stochastic_prize is not used and the deterministic prize is known
82
+ # In the stochastic setting, the deterministic prize is the expected prize and is known up front but the
83
+ # stochastic prize is only revealed once the node is visited
84
+ # Stochastic prize is between (0, 2 * expected_prize) such that E(stochastic prize) = E(deterministic_prize)
85
+ stochastic_prize = np.random.uniform(size=(dataset_size, pctsp_size)) * deterministic_prize * 2
86
+
87
+ return list(zip(
88
+ depot.tolist(),
89
+ loc.tolist(),
90
+ penalty.tolist(),
91
+ deterministic_prize.tolist(),
92
+ stochastic_prize.tolist()
93
+ ))
94
+
95
+
96
+ if __name__ == "__main__":
97
+ parser = argparse.ArgumentParser()
98
+ parser.add_argument("--filename", help="Filename of the dataset to create (ignores datadir)")
99
+ parser.add_argument("--data_dir", default='data', help="Create datasets in data_dir/problem (default 'data')")
100
+ parser.add_argument("--name", type=str, required=True, help="Name to identify dataset")
101
+ parser.add_argument("--problem", type=str, default='all',
102
+ help="Problem, 'tsp', 'vrp', 'pctsp' or 'op_const', 'op_unif' or 'op_dist'"
103
+ " or 'all' to generate all")
104
+ parser.add_argument('--data_distribution', type=str, default='all',
105
+ help="Distributions to generate for problem, default 'all'.")
106
+
107
+ parser.add_argument("--dataset_size", type=int, default=10000, help="Size of the dataset")
108
+ parser.add_argument('--graph_sizes', type=int, nargs='+', default=[20, 50, 100],
109
+ help="Sizes of problem instances (default 20, 50, 100)")
110
+ parser.add_argument("-f", action='store_true', help="Set true to overwrite")
111
+ parser.add_argument('--seed', type=int, default=1234, help="Random seed")
112
+
113
+ opts = parser.parse_args()
114
+
115
+ assert opts.filename is None or (len(opts.problems) == 1 and len(opts.graph_sizes) == 1), \
116
+ "Can only specify filename when generating a single dataset"
117
+
118
+ distributions_per_problem = {
119
+ 'tsp': [None],
120
+ 'vrp': [None],
121
+ 'pctsp': [None],
122
+ 'op': ['const', 'unif', 'dist']
123
+ }
124
+ if opts.problem == 'all':
125
+ problems = distributions_per_problem
126
+ else:
127
+ problems = {
128
+ opts.problem:
129
+ distributions_per_problem[opts.problem]
130
+ if opts.data_distribution == 'all'
131
+ else [opts.data_distribution]
132
+ }
133
+
134
+ for problem, distributions in problems.items():
135
+ for distribution in distributions or [None]:
136
+ for graph_size in opts.graph_sizes:
137
+
138
+ datadir = os.path.join(opts.data_dir, problem)
139
+ os.makedirs(datadir, exist_ok=True)
140
+
141
+ if opts.filename is None:
142
+ filename = os.path.join(datadir, "{}{}{}_{}_seed{}.pkl".format(
143
+ problem,
144
+ "_{}".format(distribution) if distribution is not None else "",
145
+ graph_size, opts.name, opts.seed))
146
+ else:
147
+ filename = check_extension(opts.filename)
148
+
149
+ assert opts.f or not os.path.isfile(check_extension(filename)), \
150
+ "File already exists! Try running with -f option to overwrite."
151
+
152
+ np.random.seed(opts.seed)
153
+ if problem == 'tsp':
154
+ dataset = generate_tsp_data(opts.dataset_size, graph_size)
155
+ elif problem == 'vrp':
156
+ dataset = generate_vrp_data(
157
+ opts.dataset_size, graph_size)
158
+ elif problem == 'pctsp':
159
+ dataset = generate_pctsp_data(opts.dataset_size, graph_size)
160
+ elif problem == "op":
161
+ dataset = generate_op_data(opts.dataset_size, graph_size, prize_type=distribution)
162
+ else:
163
+ assert False, "Unknown problem: {}".format(problem)
164
+
165
+ print(dataset[0])
166
+
167
+ save_dataset(dataset, filename)
miscellaneous/kool/attention-learn-to-route/images/cvrp_1.png ADDED
miscellaneous/kool/attention-learn-to-route/images/cvrp_5.png ADDED
miscellaneous/kool/attention-learn-to-route/images/cvrp_7.png ADDED
miscellaneous/kool/attention-learn-to-route/images/cvrp_8.png ADDED
miscellaneous/kool/attention-learn-to-route/images/cvrp_9.png ADDED
miscellaneous/kool/attention-learn-to-route/nets/__init__.py ADDED
File without changes
miscellaneous/kool/attention-learn-to-route/nets/attention_model.py ADDED
@@ -0,0 +1,513 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from torch.utils.checkpoint import checkpoint
4
+ import math
5
+ from typing import NamedTuple
6
+ from utils.tensor_functions import compute_in_batches
7
+
8
+ from nets.graph_encoder import GraphAttentionEncoder
9
+ from torch.nn import DataParallel
10
+ from utils.beam_search import CachedLookup
11
+ from utils.functions import sample_many
12
+
13
+
14
+ def set_decode_type(model, decode_type):
15
+ if isinstance(model, DataParallel):
16
+ model = model.module
17
+ model.set_decode_type(decode_type)
18
+
19
+
20
+ class AttentionModelFixed(NamedTuple):
21
+ """
22
+ Context for AttentionModel decoder that is fixed during decoding so can be precomputed/cached
23
+ This class allows for efficient indexing of multiple Tensors at once
24
+ """
25
+ node_embeddings: torch.Tensor
26
+ context_node_projected: torch.Tensor
27
+ glimpse_key: torch.Tensor
28
+ glimpse_val: torch.Tensor
29
+ logit_key: torch.Tensor
30
+
31
+ def __getitem__(self, key):
32
+ assert torch.is_tensor(key) or isinstance(key, slice)
33
+ return AttentionModelFixed(
34
+ node_embeddings=self.node_embeddings[key],
35
+ context_node_projected=self.context_node_projected[key],
36
+ glimpse_key=self.glimpse_key[:, key], # dim 0 are the heads
37
+ glimpse_val=self.glimpse_val[:, key], # dim 0 are the heads
38
+ logit_key=self.logit_key[key]
39
+ )
40
+
41
+
42
+ class AttentionModel(nn.Module):
43
+
44
+ def __init__(self,
45
+ embedding_dim,
46
+ hidden_dim,
47
+ problem,
48
+ n_encode_layers=2,
49
+ tanh_clipping=10.,
50
+ mask_inner=True,
51
+ mask_logits=True,
52
+ normalization='batch',
53
+ n_heads=8,
54
+ checkpoint_encoder=False,
55
+ shrink_size=None):
56
+ super(AttentionModel, self).__init__()
57
+
58
+ self.embedding_dim = embedding_dim
59
+ self.hidden_dim = hidden_dim
60
+ self.n_encode_layers = n_encode_layers
61
+ self.decode_type = None
62
+ self.temp = 1.0
63
+ self.allow_partial = problem.NAME == 'sdvrp'
64
+ self.is_vrp = problem.NAME == 'cvrp' or problem.NAME == 'sdvrp'
65
+ self.is_orienteering = problem.NAME == 'op'
66
+ self.is_pctsp = problem.NAME == 'pctsp'
67
+
68
+ self.tanh_clipping = tanh_clipping
69
+
70
+ self.mask_inner = mask_inner
71
+ self.mask_logits = mask_logits
72
+
73
+ self.problem = problem
74
+ self.n_heads = n_heads
75
+ self.checkpoint_encoder = checkpoint_encoder
76
+ self.shrink_size = shrink_size
77
+
78
+ # Problem specific context parameters (placeholder and step context dimension)
79
+ if self.is_vrp or self.is_orienteering or self.is_pctsp:
80
+ # Embedding of last node + remaining_capacity / remaining length / remaining prize to collect
81
+ step_context_dim = embedding_dim + 1
82
+
83
+ if self.is_pctsp:
84
+ node_dim = 4 # x, y, expected_prize, penalty
85
+ else:
86
+ node_dim = 3 # x, y, demand / prize
87
+
88
+ # Special embedding projection for depot node
89
+ self.init_embed_depot = nn.Linear(2, embedding_dim)
90
+
91
+ if self.is_vrp and self.allow_partial: # Need to include the demand if split delivery allowed
92
+ self.project_node_step = nn.Linear(1, 3 * embedding_dim, bias=False)
93
+ else: # TSP
94
+ assert problem.NAME == "tsp", "Unsupported problem: {}".format(problem.NAME)
95
+ step_context_dim = 2 * embedding_dim # Embedding of first and last node
96
+ node_dim = 2 # x, y
97
+
98
+ # Learned input symbols for first action
99
+ self.W_placeholder = nn.Parameter(torch.Tensor(2 * embedding_dim))
100
+ self.W_placeholder.data.uniform_(-1, 1) # Placeholder should be in range of activations
101
+
102
+ self.init_embed = nn.Linear(node_dim, embedding_dim)
103
+
104
+ self.embedder = GraphAttentionEncoder(
105
+ n_heads=n_heads,
106
+ embed_dim=embedding_dim,
107
+ n_layers=self.n_encode_layers,
108
+ normalization=normalization
109
+ )
110
+
111
+ # For each node we compute (glimpse key, glimpse value, logit key) so 3 * embedding_dim
112
+ self.project_node_embeddings = nn.Linear(embedding_dim, 3 * embedding_dim, bias=False)
113
+ self.project_fixed_context = nn.Linear(embedding_dim, embedding_dim, bias=False)
114
+ self.project_step_context = nn.Linear(step_context_dim, embedding_dim, bias=False)
115
+ assert embedding_dim % n_heads == 0
116
+ # Note n_heads * val_dim == embedding_dim so input to project_out is embedding_dim
117
+ self.project_out = nn.Linear(embedding_dim, embedding_dim, bias=False)
118
+
119
+ def set_decode_type(self, decode_type, temp=None):
120
+ self.decode_type = decode_type
121
+ if temp is not None: # Do not change temperature if not provided
122
+ self.temp = temp
123
+
124
+ def forward(self, input, return_pi=False):
125
+ """
126
+ :param input: (batch_size, graph_size, node_dim) input node features or dictionary with multiple tensors
127
+ :param return_pi: whether to return the output sequences, this is optional as it is not compatible with
128
+ using DataParallel as the results may be of different lengths on different GPUs
129
+ :return:
130
+ """
131
+
132
+ if self.checkpoint_encoder and self.training: # Only checkpoint if we need gradients
133
+ embeddings, _ = checkpoint(self.embedder, self._init_embed(input))
134
+ else:
135
+ embeddings, _ = self.embedder(self._init_embed(input))
136
+
137
+ _log_p, pi = self._inner(input, embeddings)
138
+
139
+ cost, mask = self.problem.get_costs(input, pi)
140
+ # Log likelyhood is calculated within the model since returning it per action does not work well with
141
+ # DataParallel since sequences can be of different lengths
142
+ ll = self._calc_log_likelihood(_log_p, pi, mask)
143
+ if return_pi:
144
+ return cost, ll, pi
145
+
146
+ return cost, ll
147
+
148
+ def beam_search(self, *args, **kwargs):
149
+ return self.problem.beam_search(*args, **kwargs, model=self)
150
+
151
+ def precompute_fixed(self, input):
152
+ embeddings, _ = self.embedder(self._init_embed(input))
153
+ # Use a CachedLookup such that if we repeatedly index this object with the same index we only need to do
154
+ # the lookup once... this is the case if all elements in the batch have maximum batch size
155
+ return CachedLookup(self._precompute(embeddings))
156
+
157
+ def propose_expansions(self, beam, fixed, expand_size=None, normalize=False, max_calc_batch_size=4096):
158
+ # First dim = batch_size * cur_beam_size
159
+ log_p_topk, ind_topk = compute_in_batches(
160
+ lambda b: self._get_log_p_topk(fixed[b.ids], b.state, k=expand_size, normalize=normalize),
161
+ max_calc_batch_size, beam, n=beam.size()
162
+ )
163
+
164
+ assert log_p_topk.size(1) == 1, "Can only have single step"
165
+ # This will broadcast, calculate log_p (score) of expansions
166
+ score_expand = beam.score[:, None] + log_p_topk[:, 0, :]
167
+
168
+ # We flatten the action as we need to filter and this cannot be done in 2d
169
+ flat_action = ind_topk.view(-1)
170
+ flat_score = score_expand.view(-1)
171
+ flat_feas = flat_score > -1e10 # != -math.inf triggers
172
+
173
+ # Parent is row idx of ind_topk, can be found by enumerating elements and dividing by number of columns
174
+ flat_parent = torch.arange(flat_action.size(-1), out=flat_action.new()) // ind_topk.size(-1)
175
+
176
+ # Filter infeasible
177
+ feas_ind_2d = torch.nonzero(flat_feas)
178
+
179
+ if len(feas_ind_2d) == 0:
180
+ # Too bad, no feasible expansions at all :(
181
+ return None, None, None
182
+
183
+ feas_ind = feas_ind_2d[:, 0]
184
+
185
+ return flat_parent[feas_ind], flat_action[feas_ind], flat_score[feas_ind]
186
+
187
+ def _calc_log_likelihood(self, _log_p, a, mask):
188
+
189
+ # Get log_p corresponding to selected actions
190
+ log_p = _log_p.gather(2, a.unsqueeze(-1)).squeeze(-1)
191
+
192
+ # Optional: mask out actions irrelevant to objective so they do not get reinforced
193
+ if mask is not None:
194
+ log_p[mask] = 0
195
+
196
+ assert (log_p > -1000).data.all(), "Logprobs should not be -inf, check sampling procedure!"
197
+
198
+ # Calculate log_likelihood
199
+ return log_p.sum(1)
200
+
201
+ def _init_embed(self, input):
202
+
203
+ if self.is_vrp or self.is_orienteering or self.is_pctsp:
204
+ if self.is_vrp:
205
+ features = ('demand', )
206
+ elif self.is_orienteering:
207
+ features = ('prize', )
208
+ else:
209
+ assert self.is_pctsp
210
+ features = ('deterministic_prize', 'penalty')
211
+ return torch.cat(
212
+ (
213
+ self.init_embed_depot(input['depot'])[:, None, :],
214
+ self.init_embed(torch.cat((
215
+ input['loc'],
216
+ *(input[feat][:, :, None] for feat in features)
217
+ ), -1))
218
+ ),
219
+ 1
220
+ )
221
+ # TSP
222
+ return self.init_embed(input)
223
+
224
+ def _inner(self, input, embeddings):
225
+
226
+ outputs = []
227
+ sequences = []
228
+
229
+ state = self.problem.make_state(input)
230
+
231
+ # Compute keys, values for the glimpse and keys for the logits once as they can be reused in every step
232
+ fixed = self._precompute(embeddings)
233
+
234
+ batch_size = state.ids.size(0)
235
+
236
+ # Perform decoding steps
237
+ i = 0
238
+ while not (self.shrink_size is None and state.all_finished()):
239
+
240
+ if self.shrink_size is not None:
241
+ unfinished = torch.nonzero(state.get_finished() == 0)
242
+ if len(unfinished) == 0:
243
+ break
244
+ unfinished = unfinished[:, 0]
245
+ # Check if we can shrink by at least shrink_size and if this leaves at least 16
246
+ # (otherwise batch norm will not work well and it is inefficient anyway)
247
+ if 16 <= len(unfinished) <= state.ids.size(0) - self.shrink_size:
248
+ # Filter states
249
+ state = state[unfinished]
250
+ fixed = fixed[unfinished]
251
+
252
+ log_p, mask = self._get_log_p(fixed, state)
253
+
254
+ # Select the indices of the next nodes in the sequences, result (batch_size) long
255
+ selected = self._select_node(log_p.exp()[:, 0, :], mask[:, 0, :]) # Squeeze out steps dimension
256
+
257
+ state = state.update(selected)
258
+
259
+ # Now make log_p, selected desired output size by 'unshrinking'
260
+ if self.shrink_size is not None and state.ids.size(0) < batch_size:
261
+ log_p_, selected_ = log_p, selected
262
+ log_p = log_p_.new_zeros(batch_size, *log_p_.size()[1:])
263
+ selected = selected_.new_zeros(batch_size)
264
+
265
+ log_p[state.ids[:, 0]] = log_p_
266
+ selected[state.ids[:, 0]] = selected_
267
+
268
+ # Collect output of step
269
+ outputs.append(log_p[:, 0, :])
270
+ sequences.append(selected)
271
+
272
+ i += 1
273
+
274
+ # Collected lists, return Tensor
275
+ return torch.stack(outputs, 1), torch.stack(sequences, 1)
276
+
277
+ def sample_many(self, input, batch_rep=1, iter_rep=1):
278
+ """
279
+ :param input: (batch_size, graph_size, node_dim) input node features
280
+ :return:
281
+ """
282
+ # Bit ugly but we need to pass the embeddings as well.
283
+ # Making a tuple will not work with the problem.get_cost function
284
+ return sample_many(
285
+ lambda input: self._inner(*input), # Need to unpack tuple into arguments
286
+ lambda input, pi: self.problem.get_costs(input[0], pi), # Don't need embeddings as input to get_costs
287
+ (input, self.embedder(self._init_embed(input))[0]), # Pack input with embeddings (additional input)
288
+ batch_rep, iter_rep
289
+ )
290
+
291
+ def _select_node(self, probs, mask):
292
+
293
+ assert (probs == probs).all(), "Probs should not contain any nans"
294
+
295
+ if self.decode_type == "greedy":
296
+ _, selected = probs.max(1)
297
+ assert not mask.gather(1, selected.unsqueeze(
298
+ -1)).data.any(), "Decode greedy: infeasible action has maximum probability"
299
+
300
+ elif self.decode_type == "sampling":
301
+ selected = probs.multinomial(1).squeeze(1)
302
+
303
+ # Check if sampling went OK, can go wrong due to bug on GPU
304
+ # See https://discuss.pytorch.org/t/bad-behavior-of-multinomial-function/10232
305
+ while mask.gather(1, selected.unsqueeze(-1)).data.any():
306
+ print('Sampled bad values, resampling!')
307
+ selected = probs.multinomial(1).squeeze(1)
308
+
309
+ else:
310
+ assert False, "Unknown decode type"
311
+ return selected
312
+
313
+ def _precompute(self, embeddings, num_steps=1):
314
+
315
+ # The fixed context projection of the graph embedding is calculated only once for efficiency
316
+ graph_embed = embeddings.mean(1)
317
+ # fixed context = (batch_size, 1, embed_dim) to make broadcastable with parallel timesteps
318
+ fixed_context = self.project_fixed_context(graph_embed)[:, None, :]
319
+
320
+ # The projection of the node embeddings for the attention is calculated once up front
321
+ glimpse_key_fixed, glimpse_val_fixed, logit_key_fixed = \
322
+ self.project_node_embeddings(embeddings[:, None, :, :]).chunk(3, dim=-1)
323
+
324
+ # No need to rearrange key for logit as there is a single head
325
+ fixed_attention_node_data = (
326
+ self._make_heads(glimpse_key_fixed, num_steps),
327
+ self._make_heads(glimpse_val_fixed, num_steps),
328
+ logit_key_fixed.contiguous()
329
+ )
330
+ return AttentionModelFixed(embeddings, fixed_context, *fixed_attention_node_data)
331
+
332
+ def _get_log_p_topk(self, fixed, state, k=None, normalize=True):
333
+ log_p, _ = self._get_log_p(fixed, state, normalize=normalize)
334
+
335
+ # Return topk
336
+ if k is not None and k < log_p.size(-1):
337
+ return log_p.topk(k, -1)
338
+
339
+ # Return all, note different from torch.topk this does not give error if less than k elements along dim
340
+ return (
341
+ log_p,
342
+ torch.arange(log_p.size(-1), device=log_p.device, dtype=torch.int64).repeat(log_p.size(0), 1)[:, None, :]
343
+ )
344
+
345
+ def _get_log_p(self, fixed, state, normalize=True):
346
+
347
+ # Compute query = context node embedding
348
+ query = fixed.context_node_projected + \
349
+ self.project_step_context(self._get_parallel_step_context(fixed.node_embeddings, state))
350
+
351
+ # Compute keys and values for the nodes
352
+ glimpse_K, glimpse_V, logit_K = self._get_attention_node_data(fixed, state)
353
+
354
+ # Compute the mask
355
+ mask = state.get_mask()
356
+
357
+ # Compute logits (unnormalized log_p)
358
+ log_p, glimpse = self._one_to_many_logits(query, glimpse_K, glimpse_V, logit_K, mask)
359
+
360
+ if normalize:
361
+ log_p = torch.log_softmax(log_p / self.temp, dim=-1)
362
+
363
+ assert not torch.isnan(log_p).any()
364
+
365
+ return log_p, mask
366
+
367
+ def _get_parallel_step_context(self, embeddings, state, from_depot=False):
368
+ """
369
+ Returns the context per step, optionally for multiple steps at once (for efficient evaluation of the model)
370
+
371
+ :param embeddings: (batch_size, graph_size, embed_dim)
372
+ :param prev_a: (batch_size, num_steps)
373
+ :param first_a: Only used when num_steps = 1, action of first step or None if first step
374
+ :return: (batch_size, num_steps, context_dim)
375
+ """
376
+
377
+ current_node = state.get_current_node()
378
+ batch_size, num_steps = current_node.size()
379
+
380
+ if self.is_vrp:
381
+ # Embedding of previous node + remaining capacity
382
+ if from_depot:
383
+ # 1st dimension is node idx, but we do not squeeze it since we want to insert step dimension
384
+ # i.e. we actually want embeddings[:, 0, :][:, None, :] which is equivalent
385
+ return torch.cat(
386
+ (
387
+ embeddings[:, 0:1, :].expand(batch_size, num_steps, embeddings.size(-1)),
388
+ # used capacity is 0 after visiting depot
389
+ self.problem.VEHICLE_CAPACITY - torch.zeros_like(state.used_capacity[:, :, None])
390
+ ),
391
+ -1
392
+ )
393
+ else:
394
+ return torch.cat(
395
+ (
396
+ torch.gather(
397
+ embeddings,
398
+ 1,
399
+ current_node.contiguous()
400
+ .view(batch_size, num_steps, 1)
401
+ .expand(batch_size, num_steps, embeddings.size(-1))
402
+ ).view(batch_size, num_steps, embeddings.size(-1)),
403
+ self.problem.VEHICLE_CAPACITY - state.used_capacity[:, :, None]
404
+ ),
405
+ -1
406
+ )
407
+ elif self.is_orienteering or self.is_pctsp:
408
+ return torch.cat(
409
+ (
410
+ torch.gather(
411
+ embeddings,
412
+ 1,
413
+ current_node.contiguous()
414
+ .view(batch_size, num_steps, 1)
415
+ .expand(batch_size, num_steps, embeddings.size(-1))
416
+ ).view(batch_size, num_steps, embeddings.size(-1)),
417
+ (
418
+ state.get_remaining_length()[:, :, None]
419
+ if self.is_orienteering
420
+ else state.get_remaining_prize_to_collect()[:, :, None]
421
+ )
422
+ ),
423
+ -1
424
+ )
425
+ else: # TSP
426
+
427
+ if num_steps == 1: # We need to special case if we have only 1 step, may be the first or not
428
+ if state.i.item() == 0:
429
+ # First and only step, ignore prev_a (this is a placeholder)
430
+ return self.W_placeholder[None, None, :].expand(batch_size, 1, self.W_placeholder.size(-1))
431
+ else:
432
+ return embeddings.gather(
433
+ 1,
434
+ torch.cat((state.first_a, current_node), 1)[:, :, None].expand(batch_size, 2, embeddings.size(-1))
435
+ ).view(batch_size, 1, -1)
436
+ # More than one step, assume always starting with first
437
+ embeddings_per_step = embeddings.gather(
438
+ 1,
439
+ current_node[:, 1:, None].expand(batch_size, num_steps - 1, embeddings.size(-1))
440
+ )
441
+ return torch.cat((
442
+ # First step placeholder, cat in dim 1 (time steps)
443
+ self.W_placeholder[None, None, :].expand(batch_size, 1, self.W_placeholder.size(-1)),
444
+ # Second step, concatenate embedding of first with embedding of current/previous (in dim 2, context dim)
445
+ torch.cat((
446
+ embeddings_per_step[:, 0:1, :].expand(batch_size, num_steps - 1, embeddings.size(-1)),
447
+ embeddings_per_step
448
+ ), 2)
449
+ ), 1)
450
+
451
+ def _one_to_many_logits(self, query, glimpse_K, glimpse_V, logit_K, mask):
452
+
453
+ batch_size, num_steps, embed_dim = query.size()
454
+ key_size = val_size = embed_dim // self.n_heads
455
+
456
+ # Compute the glimpse, rearrange dimensions so the dimensions are (n_heads, batch_size, num_steps, 1, key_size)
457
+ glimpse_Q = query.view(batch_size, num_steps, self.n_heads, 1, key_size).permute(2, 0, 1, 3, 4)
458
+
459
+ # Batch matrix multiplication to compute compatibilities (n_heads, batch_size, num_steps, graph_size)
460
+ compatibility = torch.matmul(glimpse_Q, glimpse_K.transpose(-2, -1)) / math.sqrt(glimpse_Q.size(-1))
461
+ if self.mask_inner:
462
+ assert self.mask_logits, "Cannot mask inner without masking logits"
463
+ compatibility[mask[None, :, :, None, :].expand_as(compatibility)] = -math.inf
464
+
465
+ # Batch matrix multiplication to compute heads (n_heads, batch_size, num_steps, val_size)
466
+ heads = torch.matmul(torch.softmax(compatibility, dim=-1), glimpse_V)
467
+
468
+ # Project to get glimpse/updated context node embedding (batch_size, num_steps, embedding_dim)
469
+ glimpse = self.project_out(
470
+ heads.permute(1, 2, 3, 0, 4).contiguous().view(-1, num_steps, 1, self.n_heads * val_size))
471
+
472
+ # Now projecting the glimpse is not needed since this can be absorbed into project_out
473
+ # final_Q = self.project_glimpse(glimpse)
474
+ final_Q = glimpse
475
+ # Batch matrix multiplication to compute logits (batch_size, num_steps, graph_size)
476
+ # logits = 'compatibility'
477
+ logits = torch.matmul(final_Q, logit_K.transpose(-2, -1)).squeeze(-2) / math.sqrt(final_Q.size(-1))
478
+
479
+ # From the logits compute the probabilities by clipping, masking and softmax
480
+ if self.tanh_clipping > 0:
481
+ logits = torch.tanh(logits) * self.tanh_clipping
482
+ if self.mask_logits:
483
+ logits[mask] = -math.inf
484
+
485
+ return logits, glimpse.squeeze(-2)
486
+
487
+ def _get_attention_node_data(self, fixed, state):
488
+
489
+ if self.is_vrp and self.allow_partial:
490
+
491
+ # Need to provide information of how much each node has already been served
492
+ # Clone demands as they are needed by the backprop whereas they are updated later
493
+ glimpse_key_step, glimpse_val_step, logit_key_step = \
494
+ self.project_node_step(state.demands_with_depot[:, :, :, None].clone()).chunk(3, dim=-1)
495
+
496
+ # Projection of concatenation is equivalent to addition of projections but this is more efficient
497
+ return (
498
+ fixed.glimpse_key + self._make_heads(glimpse_key_step),
499
+ fixed.glimpse_val + self._make_heads(glimpse_val_step),
500
+ fixed.logit_key + logit_key_step,
501
+ )
502
+
503
+ # TSP or VRP without split delivery
504
+ return fixed.glimpse_key, fixed.glimpse_val, fixed.logit_key
505
+
506
+ def _make_heads(self, v, num_steps=None):
507
+ assert num_steps is None or v.size(1) == 1 or v.size(1) == num_steps
508
+
509
+ return (
510
+ v.contiguous().view(v.size(0), v.size(1), v.size(2), self.n_heads, -1)
511
+ .expand(v.size(0), v.size(1) if num_steps is None else num_steps, v.size(2), self.n_heads, -1)
512
+ .permute(3, 0, 1, 2, 4) # (n_heads, batch_size, num_steps, graph_size, head_dim)
513
+ )
miscellaneous/kool/attention-learn-to-route/nets/critic_network.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch import nn
2
+ from nets.graph_encoder import GraphAttentionEncoder
3
+
4
+
5
+ class CriticNetwork(nn.Module):
6
+
7
+ def __init__(
8
+ self,
9
+ input_dim,
10
+ embedding_dim,
11
+ hidden_dim,
12
+ n_layers,
13
+ encoder_normalization
14
+ ):
15
+ super(CriticNetwork, self).__init__()
16
+
17
+ self.hidden_dim = hidden_dim
18
+
19
+ self.encoder = GraphAttentionEncoder(
20
+ node_dim=input_dim,
21
+ n_heads=8,
22
+ embed_dim=embedding_dim,
23
+ n_layers=n_layers,
24
+ normalization=encoder_normalization
25
+ )
26
+
27
+ self.value_head = nn.Sequential(
28
+ nn.Linear(embedding_dim, hidden_dim),
29
+ nn.ReLU(),
30
+ nn.Linear(hidden_dim, 1)
31
+ )
32
+
33
+ def forward(self, inputs):
34
+ """
35
+
36
+ :param inputs: (batch_size, graph_size, input_dim)
37
+ :return:
38
+ """
39
+ _, graph_embeddings = self.encoder(inputs)
40
+ return self.value_head(graph_embeddings)
miscellaneous/kool/attention-learn-to-route/nets/graph_encoder.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ from torch import nn
4
+ import math
5
+
6
+
7
+ class SkipConnection(nn.Module):
8
+
9
+ def __init__(self, module):
10
+ super(SkipConnection, self).__init__()
11
+ self.module = module
12
+
13
+ def forward(self, input):
14
+ return input + self.module(input)
15
+
16
+
17
+ class MultiHeadAttention(nn.Module):
18
+ def __init__(
19
+ self,
20
+ n_heads,
21
+ input_dim,
22
+ embed_dim,
23
+ val_dim=None,
24
+ key_dim=None
25
+ ):
26
+ super(MultiHeadAttention, self).__init__()
27
+
28
+ if val_dim is None:
29
+ val_dim = embed_dim // n_heads
30
+ if key_dim is None:
31
+ key_dim = val_dim
32
+
33
+ self.n_heads = n_heads
34
+ self.input_dim = input_dim
35
+ self.embed_dim = embed_dim
36
+ self.val_dim = val_dim
37
+ self.key_dim = key_dim
38
+
39
+ self.norm_factor = 1 / math.sqrt(key_dim) # See Attention is all you need
40
+
41
+ self.W_query = nn.Parameter(torch.Tensor(n_heads, input_dim, key_dim))
42
+ self.W_key = nn.Parameter(torch.Tensor(n_heads, input_dim, key_dim))
43
+ self.W_val = nn.Parameter(torch.Tensor(n_heads, input_dim, val_dim))
44
+
45
+ self.W_out = nn.Parameter(torch.Tensor(n_heads, val_dim, embed_dim))
46
+
47
+ self.init_parameters()
48
+
49
+ def init_parameters(self):
50
+
51
+ for param in self.parameters():
52
+ stdv = 1. / math.sqrt(param.size(-1))
53
+ param.data.uniform_(-stdv, stdv)
54
+
55
+ def forward(self, q, h=None, mask=None):
56
+ """
57
+
58
+ :param q: queries (batch_size, n_query, input_dim)
59
+ :param h: data (batch_size, graph_size, input_dim)
60
+ :param mask: mask (batch_size, n_query, graph_size) or viewable as that (i.e. can be 2 dim if n_query == 1)
61
+ Mask should contain 1 if attention is not possible (i.e. mask is negative adjacency)
62
+ :return:
63
+ """
64
+ if h is None:
65
+ h = q # compute self-attention
66
+
67
+ # h should be (batch_size, graph_size, input_dim)
68
+ batch_size, graph_size, input_dim = h.size()
69
+ n_query = q.size(1)
70
+ assert q.size(0) == batch_size
71
+ assert q.size(2) == input_dim
72
+ assert input_dim == self.input_dim, "Wrong embedding dimension of input"
73
+
74
+ hflat = h.contiguous().view(-1, input_dim)
75
+ qflat = q.contiguous().view(-1, input_dim)
76
+
77
+ # last dimension can be different for keys and values
78
+ shp = (self.n_heads, batch_size, graph_size, -1)
79
+ shp_q = (self.n_heads, batch_size, n_query, -1)
80
+
81
+ # Calculate queries, (n_heads, n_query, graph_size, key/val_size)
82
+ Q = torch.matmul(qflat, self.W_query).view(shp_q)
83
+ # Calculate keys and values (n_heads, batch_size, graph_size, key/val_size)
84
+ K = torch.matmul(hflat, self.W_key).view(shp)
85
+ V = torch.matmul(hflat, self.W_val).view(shp)
86
+
87
+ # Calculate compatibility (n_heads, batch_size, n_query, graph_size)
88
+ compatibility = self.norm_factor * torch.matmul(Q, K.transpose(2, 3))
89
+
90
+ # Optionally apply mask to prevent attention
91
+ if mask is not None:
92
+ mask = mask.view(1, batch_size, n_query, graph_size).expand_as(compatibility)
93
+ compatibility[mask] = -np.inf
94
+
95
+ attn = torch.softmax(compatibility, dim=-1)
96
+
97
+ # If there are nodes with no neighbours then softmax returns nan so we fix them to 0
98
+ if mask is not None:
99
+ attnc = attn.clone()
100
+ attnc[mask] = 0
101
+ attn = attnc
102
+
103
+ heads = torch.matmul(attn, V)
104
+
105
+ out = torch.mm(
106
+ heads.permute(1, 2, 0, 3).contiguous().view(-1, self.n_heads * self.val_dim),
107
+ self.W_out.view(-1, self.embed_dim)
108
+ ).view(batch_size, n_query, self.embed_dim)
109
+
110
+ # Alternative:
111
+ # headst = heads.transpose(0, 1) # swap the dimensions for batch and heads to align it for the matmul
112
+ # # proj_h = torch.einsum('bhni,hij->bhnj', headst, self.W_out)
113
+ # projected_heads = torch.matmul(headst, self.W_out)
114
+ # out = torch.sum(projected_heads, dim=1) # sum across heads
115
+
116
+ # Or:
117
+ # out = torch.einsum('hbni,hij->bnj', heads, self.W_out)
118
+
119
+ return out
120
+
121
+
122
+ class Normalization(nn.Module):
123
+
124
+ def __init__(self, embed_dim, normalization='batch'):
125
+ super(Normalization, self).__init__()
126
+
127
+ normalizer_class = {
128
+ 'batch': nn.BatchNorm1d,
129
+ 'instance': nn.InstanceNorm1d
130
+ }.get(normalization, None)
131
+
132
+ self.normalizer = normalizer_class(embed_dim, affine=True)
133
+
134
+ # Normalization by default initializes affine parameters with bias 0 and weight unif(0,1) which is too large!
135
+ # self.init_parameters()
136
+
137
+ def init_parameters(self):
138
+
139
+ for name, param in self.named_parameters():
140
+ stdv = 1. / math.sqrt(param.size(-1))
141
+ param.data.uniform_(-stdv, stdv)
142
+
143
+ def forward(self, input):
144
+
145
+ if isinstance(self.normalizer, nn.BatchNorm1d):
146
+ return self.normalizer(input.view(-1, input.size(-1))).view(*input.size())
147
+ elif isinstance(self.normalizer, nn.InstanceNorm1d):
148
+ return self.normalizer(input.permute(0, 2, 1)).permute(0, 2, 1)
149
+ else:
150
+ assert self.normalizer is None, "Unknown normalizer type"
151
+ return input
152
+
153
+
154
+ class MultiHeadAttentionLayer(nn.Sequential):
155
+
156
+ def __init__(
157
+ self,
158
+ n_heads,
159
+ embed_dim,
160
+ feed_forward_hidden=512,
161
+ normalization='batch',
162
+ ):
163
+ super(MultiHeadAttentionLayer, self).__init__(
164
+ SkipConnection(
165
+ MultiHeadAttention(
166
+ n_heads,
167
+ input_dim=embed_dim,
168
+ embed_dim=embed_dim
169
+ )
170
+ ),
171
+ Normalization(embed_dim, normalization),
172
+ SkipConnection(
173
+ nn.Sequential(
174
+ nn.Linear(embed_dim, feed_forward_hidden),
175
+ nn.ReLU(),
176
+ nn.Linear(feed_forward_hidden, embed_dim)
177
+ ) if feed_forward_hidden > 0 else nn.Linear(embed_dim, embed_dim)
178
+ ),
179
+ Normalization(embed_dim, normalization)
180
+ )
181
+
182
+
183
+ class GraphAttentionEncoder(nn.Module):
184
+ def __init__(
185
+ self,
186
+ n_heads,
187
+ embed_dim,
188
+ n_layers,
189
+ node_dim=None,
190
+ normalization='batch',
191
+ feed_forward_hidden=512
192
+ ):
193
+ super(GraphAttentionEncoder, self).__init__()
194
+
195
+ # To map input to embedding space
196
+ self.init_embed = nn.Linear(node_dim, embed_dim) if node_dim is not None else None
197
+
198
+ self.layers = nn.Sequential(*(
199
+ MultiHeadAttentionLayer(n_heads, embed_dim, feed_forward_hidden, normalization)
200
+ for _ in range(n_layers)
201
+ ))
202
+
203
+ def forward(self, x, mask=None):
204
+
205
+ assert mask is None, "TODO mask not yet supported!"
206
+
207
+ # Batch multiply to get initial embeddings of nodes
208
+ h = self.init_embed(x.view(-1, x.size(-1))).view(*x.size()[:2], -1) if self.init_embed is not None else x
209
+
210
+ h = self.layers(h)
211
+
212
+ return (
213
+ h, # (batch_size, graph_size, embed_dim)
214
+ h.mean(dim=1), # average to get embedding of graph, (batch_size, embed_dim)
215
+ )
miscellaneous/kool/attention-learn-to-route/nets/pointer_network.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torch.autograd import Variable
4
+ import math
5
+ import numpy as np
6
+
7
+
8
+ class Encoder(nn.Module):
9
+ """Maps a graph represented as an input sequence
10
+ to a hidden vector"""
11
+ def __init__(self, input_dim, hidden_dim):
12
+ super(Encoder, self).__init__()
13
+ self.hidden_dim = hidden_dim
14
+ self.lstm = nn.LSTM(input_dim, hidden_dim)
15
+ self.init_hx, self.init_cx = self.init_hidden(hidden_dim)
16
+
17
+ def forward(self, x, hidden):
18
+ output, hidden = self.lstm(x, hidden)
19
+ return output, hidden
20
+
21
+ def init_hidden(self, hidden_dim):
22
+ """Trainable initial hidden state"""
23
+ std = 1. / math.sqrt(hidden_dim)
24
+ enc_init_hx = nn.Parameter(torch.FloatTensor(hidden_dim))
25
+ enc_init_hx.data.uniform_(-std, std)
26
+
27
+ enc_init_cx = nn.Parameter(torch.FloatTensor(hidden_dim))
28
+ enc_init_cx.data.uniform_(-std, std)
29
+ return enc_init_hx, enc_init_cx
30
+
31
+
32
+ class Attention(nn.Module):
33
+ """A generic attention module for a decoder in seq2seq"""
34
+ def __init__(self, dim, use_tanh=False, C=10):
35
+ super(Attention, self).__init__()
36
+ self.use_tanh = use_tanh
37
+ self.project_query = nn.Linear(dim, dim)
38
+ self.project_ref = nn.Conv1d(dim, dim, 1, 1)
39
+ self.C = C # tanh exploration
40
+ self.tanh = nn.Tanh()
41
+
42
+ self.v = nn.Parameter(torch.FloatTensor(dim))
43
+ self.v.data.uniform_(-(1. / math.sqrt(dim)), 1. / math.sqrt(dim))
44
+
45
+ def forward(self, query, ref):
46
+ """
47
+ Args:
48
+ query: is the hidden state of the decoder at the current
49
+ time step. batch x dim
50
+ ref: the set of hidden states from the encoder.
51
+ sourceL x batch x hidden_dim
52
+ """
53
+ # ref is now [batch_size x hidden_dim x sourceL]
54
+ ref = ref.permute(1, 2, 0)
55
+ q = self.project_query(query).unsqueeze(2) # batch x dim x 1
56
+ e = self.project_ref(ref) # batch_size x hidden_dim x sourceL
57
+ # expand the query by sourceL
58
+ # batch x dim x sourceL
59
+ expanded_q = q.repeat(1, 1, e.size(2))
60
+ # batch x 1 x hidden_dim
61
+ v_view = self.v.unsqueeze(0).expand(
62
+ expanded_q.size(0), len(self.v)).unsqueeze(1)
63
+ # [batch_size x 1 x hidden_dim] * [batch_size x hidden_dim x sourceL]
64
+ u = torch.bmm(v_view, self.tanh(expanded_q + e)).squeeze(1)
65
+ if self.use_tanh:
66
+ logits = self.C * self.tanh(u)
67
+ else:
68
+ logits = u
69
+ return e, logits
70
+
71
+
72
+ class Decoder(nn.Module):
73
+ def __init__(self,
74
+ embedding_dim,
75
+ hidden_dim,
76
+ tanh_exploration,
77
+ use_tanh,
78
+ n_glimpses=1,
79
+ mask_glimpses=True,
80
+ mask_logits=True):
81
+ super(Decoder, self).__init__()
82
+
83
+ self.embedding_dim = embedding_dim
84
+ self.hidden_dim = hidden_dim
85
+ self.n_glimpses = n_glimpses
86
+ self.mask_glimpses = mask_glimpses
87
+ self.mask_logits = mask_logits
88
+ self.use_tanh = use_tanh
89
+ self.tanh_exploration = tanh_exploration
90
+ self.decode_type = None # Needs to be set explicitly before use
91
+
92
+ self.lstm = nn.LSTMCell(embedding_dim, hidden_dim)
93
+ self.pointer = Attention(hidden_dim, use_tanh=use_tanh, C=tanh_exploration)
94
+ self.glimpse = Attention(hidden_dim, use_tanh=False)
95
+ self.sm = nn.Softmax(dim=1)
96
+
97
+ def update_mask(self, mask, selected):
98
+ return mask.clone().scatter_(1, selected.unsqueeze(-1), True)
99
+
100
+ def recurrence(self, x, h_in, prev_mask, prev_idxs, step, context):
101
+
102
+ logit_mask = self.update_mask(prev_mask, prev_idxs) if prev_idxs is not None else prev_mask
103
+
104
+ logits, h_out = self.calc_logits(x, h_in, logit_mask, context, self.mask_glimpses, self.mask_logits)
105
+
106
+ # Calculate log_softmax for better numerical stability
107
+ log_p = torch.log_softmax(logits, dim=1)
108
+ probs = log_p.exp()
109
+
110
+ if not self.mask_logits:
111
+ # If self.mask_logits, this would be redundant, otherwise we must mask to make sure we don't resample
112
+ # Note that as a result the vector of probs may not sum to one (this is OK for .multinomial sampling)
113
+ # But practically by not masking the logits, a model is learned over all sequences (also infeasible)
114
+ # while only during sampling feasibility is enforced (a.k.a. by setting to 0. here)
115
+ probs[logit_mask] = 0.
116
+ # For consistency we should also mask out in log_p, but the values set to 0 will not be sampled and
117
+ # Therefore not be used by the reinforce estimator
118
+
119
+ return h_out, log_p, probs, logit_mask
120
+
121
+ def calc_logits(self, x, h_in, logit_mask, context, mask_glimpses=None, mask_logits=None):
122
+
123
+ if mask_glimpses is None:
124
+ mask_glimpses = self.mask_glimpses
125
+
126
+ if mask_logits is None:
127
+ mask_logits = self.mask_logits
128
+
129
+ hy, cy = self.lstm(x, h_in)
130
+ g_l, h_out = hy, (hy, cy)
131
+
132
+ for i in range(self.n_glimpses):
133
+ ref, logits = self.glimpse(g_l, context)
134
+ # For the glimpses, only mask before softmax so we have always an L1 norm 1 readout vector
135
+ if mask_glimpses:
136
+ logits[logit_mask] = -np.inf
137
+ # [batch_size x h_dim x sourceL] * [batch_size x sourceL x 1] =
138
+ # [batch_size x h_dim x 1]
139
+ g_l = torch.bmm(ref, self.sm(logits).unsqueeze(2)).squeeze(2)
140
+ _, logits = self.pointer(g_l, context)
141
+
142
+ # Masking before softmax makes probs sum to one
143
+ if mask_logits:
144
+ logits[logit_mask] = -np.inf
145
+
146
+ return logits, h_out
147
+
148
+ def forward(self, decoder_input, embedded_inputs, hidden, context, eval_tours=None):
149
+ """
150
+ Args:
151
+ decoder_input: The initial input to the decoder
152
+ size is [batch_size x embedding_dim]. Trainable parameter.
153
+ embedded_inputs: [sourceL x batch_size x embedding_dim]
154
+ hidden: the prev hidden state, size is [batch_size x hidden_dim].
155
+ Initially this is set to (enc_h[-1], enc_c[-1])
156
+ context: encoder outputs, [sourceL x batch_size x hidden_dim]
157
+ """
158
+
159
+ batch_size = context.size(1)
160
+ outputs = []
161
+ selections = []
162
+ steps = range(embedded_inputs.size(0))
163
+ idxs = None
164
+ mask = Variable(
165
+ embedded_inputs.data.new().byte().new(embedded_inputs.size(1), embedded_inputs.size(0)).zero_(),
166
+ requires_grad=False
167
+ )
168
+
169
+ for i in steps:
170
+ hidden, log_p, probs, mask = self.recurrence(decoder_input, hidden, mask, idxs, i, context)
171
+ # select the next inputs for the decoder [batch_size x hidden_dim]
172
+ idxs = self.decode(
173
+ probs,
174
+ mask
175
+ ) if eval_tours is None else eval_tours[:, i]
176
+
177
+ idxs = idxs.detach() # Otherwise pytorch complains it want's a reward, todo implement this more properly?
178
+
179
+ # Gather input embedding of selected
180
+ decoder_input = torch.gather(
181
+ embedded_inputs,
182
+ 0,
183
+ idxs.contiguous().view(1, batch_size, 1).expand(1, batch_size, *embedded_inputs.size()[2:])
184
+ ).squeeze(0)
185
+
186
+ # use outs to point to next object
187
+ outputs.append(log_p)
188
+ selections.append(idxs)
189
+ return (torch.stack(outputs, 1), torch.stack(selections, 1)), hidden
190
+
191
+ def decode(self, probs, mask):
192
+ if self.decode_type == "greedy":
193
+ _, idxs = probs.max(1)
194
+ assert not mask.gather(1, idxs.unsqueeze(-1)).data.any(), \
195
+ "Decode greedy: infeasible action has maximum probability"
196
+ elif self.decode_type == "sampling":
197
+ idxs = probs.multinomial(1).squeeze(1)
198
+ # Check if sampling went OK, can go wrong due to bug on GPU
199
+ while mask.gather(1, idxs.unsqueeze(-1)).data.any():
200
+ print(' [!] resampling due to race condition')
201
+ idxs = probs.multinomial().squeeze(1)
202
+ else:
203
+ assert False, "Unknown decode type"
204
+
205
+ return idxs
206
+
207
+
208
+ class CriticNetworkLSTM(nn.Module):
209
+ """Useful as a baseline in REINFORCE updates"""
210
+ def __init__(self,
211
+ embedding_dim,
212
+ hidden_dim,
213
+ n_process_block_iters,
214
+ tanh_exploration,
215
+ use_tanh):
216
+ super(CriticNetworkLSTM, self).__init__()
217
+
218
+ self.hidden_dim = hidden_dim
219
+ self.n_process_block_iters = n_process_block_iters
220
+
221
+ self.encoder = Encoder(embedding_dim, hidden_dim)
222
+
223
+ self.process_block = Attention(hidden_dim, use_tanh=use_tanh, C=tanh_exploration)
224
+ self.sm = nn.Softmax(dim=1)
225
+ self.decoder = nn.Sequential(
226
+ nn.Linear(hidden_dim, hidden_dim),
227
+ nn.ReLU(),
228
+ nn.Linear(hidden_dim, 1)
229
+ )
230
+
231
+ def forward(self, inputs):
232
+ """
233
+ Args:
234
+ inputs: [embedding_dim x batch_size x sourceL] of embedded inputs
235
+ """
236
+ inputs = inputs.transpose(0, 1).contiguous()
237
+
238
+ encoder_hx = self.encoder.init_hx.unsqueeze(0).repeat(inputs.size(1), 1).unsqueeze(0)
239
+ encoder_cx = self.encoder.init_cx.unsqueeze(0).repeat(inputs.size(1), 1).unsqueeze(0)
240
+
241
+ # encoder forward pass
242
+ enc_outputs, (enc_h_t, enc_c_t) = self.encoder(inputs, (encoder_hx, encoder_cx))
243
+
244
+ # grab the hidden state and process it via the process block
245
+ process_block_state = enc_h_t[-1]
246
+ for i in range(self.n_process_block_iters):
247
+ ref, logits = self.process_block(process_block_state, enc_outputs)
248
+ process_block_state = torch.bmm(ref, self.sm(logits).unsqueeze(2)).squeeze(2)
249
+ # produce the final scalar output
250
+ out = self.decoder(process_block_state)
251
+ return out
252
+
253
+
254
+ class PointerNetwork(nn.Module):
255
+
256
+ def __init__(self,
257
+ embedding_dim,
258
+ hidden_dim,
259
+ problem,
260
+ n_encode_layers=None,
261
+ tanh_clipping=10.,
262
+ mask_inner=True,
263
+ mask_logits=True,
264
+ normalization=None,
265
+ **kwargs):
266
+ super(PointerNetwork, self).__init__()
267
+
268
+ self.problem = problem
269
+ assert problem.NAME == "tsp", "Pointer Network only supported for TSP"
270
+ self.input_dim = 2
271
+
272
+ self.encoder = Encoder(
273
+ embedding_dim,
274
+ hidden_dim)
275
+
276
+ self.decoder = Decoder(
277
+ embedding_dim,
278
+ hidden_dim,
279
+ tanh_exploration=tanh_clipping,
280
+ use_tanh=tanh_clipping > 0,
281
+ n_glimpses=1,
282
+ mask_glimpses=mask_inner,
283
+ mask_logits=mask_logits
284
+ )
285
+
286
+ # Trainable initial hidden states
287
+ std = 1. / math.sqrt(embedding_dim)
288
+ self.decoder_in_0 = nn.Parameter(torch.FloatTensor(embedding_dim))
289
+ self.decoder_in_0.data.uniform_(-std, std)
290
+
291
+ self.embedding = nn.Parameter(torch.FloatTensor(self.input_dim, embedding_dim))
292
+ self.embedding.data.uniform_(-std, std)
293
+
294
+ def set_decode_type(self, decode_type):
295
+ self.decoder.decode_type = decode_type
296
+
297
+ def forward(self, inputs, eval_tours=None, return_pi=False):
298
+ batch_size, graph_size, input_dim = inputs.size()
299
+
300
+ embedded_inputs = torch.mm(
301
+ inputs.transpose(0, 1).contiguous().view(-1, input_dim),
302
+ self.embedding
303
+ ).view(graph_size, batch_size, -1)
304
+
305
+ # query the actor net for the input indices
306
+ # making up the output, and the pointer attn
307
+ _log_p, pi = self._inner(embedded_inputs, eval_tours)
308
+
309
+ cost, mask = self.problem.get_costs(inputs, pi)
310
+ # Log likelyhood is calculated within the model since returning it per action does not work well with
311
+ # DataParallel since sequences can be of different lengths
312
+ ll = self._calc_log_likelihood(_log_p, pi, mask)
313
+ if return_pi:
314
+ return cost, ll, pi
315
+
316
+ return cost, ll
317
+
318
+ def _calc_log_likelihood(self, _log_p, a, mask):
319
+
320
+ # Get log_p corresponding to selected actions
321
+ log_p = _log_p.gather(2, a.unsqueeze(-1)).squeeze(-1)
322
+
323
+ # Optional: mask out actions irrelevant to objective so they do not get reinforced
324
+ if mask is not None:
325
+ log_p[mask] = 0
326
+
327
+ assert (log_p > -1000).data.all(), "Logprobs should not be -inf, check sampling procedure!"
328
+
329
+ # Calculate log_likelihood
330
+ return log_p.sum(1)
331
+
332
+ def _inner(self, inputs, eval_tours=None):
333
+
334
+ encoder_hx = encoder_cx = Variable(
335
+ torch.zeros(1, inputs.size(1), self.encoder.hidden_dim, out=inputs.data.new()),
336
+ requires_grad=False
337
+ )
338
+
339
+ # encoder forward pass
340
+ enc_h, (enc_h_t, enc_c_t) = self.encoder(inputs, (encoder_hx, encoder_cx))
341
+
342
+ dec_init_state = (enc_h_t[-1], enc_c_t[-1])
343
+
344
+ # repeat decoder_in_0 across batch
345
+ decoder_input = self.decoder_in_0.unsqueeze(0).repeat(inputs.size(1), 1)
346
+
347
+ (pointer_probs, input_idxs), dec_hidden_t = self.decoder(decoder_input,
348
+ inputs,
349
+ dec_init_state,
350
+ enc_h,
351
+ eval_tours)
352
+
353
+ return pointer_probs, input_idxs
miscellaneous/kool/attention-learn-to-route/options.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import argparse
4
+ import torch
5
+
6
+
7
+ def get_options(args=None):
8
+ parser = argparse.ArgumentParser(
9
+ description="Attention based model for solving the Travelling Salesman Problem with Reinforcement Learning")
10
+
11
+ # Data
12
+ parser.add_argument('--problem', default='tsp', help="The problem to solve, default 'tsp'")
13
+ parser.add_argument('--graph_size', type=int, default=20, help="The size of the problem graph")
14
+ parser.add_argument('--batch_size', type=int, default=512, help='Number of instances per batch during training')
15
+ parser.add_argument('--epoch_size', type=int, default=1280000, help='Number of instances per epoch during training')
16
+ parser.add_argument('--val_size', type=int, default=10000,
17
+ help='Number of instances used for reporting validation performance')
18
+ parser.add_argument('--val_dataset', type=str, default=None, help='Dataset file to use for validation')
19
+
20
+ # Model
21
+ parser.add_argument('--model', default='attention', help="Model, 'attention' (default) or 'pointer'")
22
+ parser.add_argument('--embedding_dim', type=int, default=128, help='Dimension of input embedding')
23
+ parser.add_argument('--hidden_dim', type=int, default=128, help='Dimension of hidden layers in Enc/Dec')
24
+ parser.add_argument('--n_encode_layers', type=int, default=3,
25
+ help='Number of layers in the encoder/critic network')
26
+ parser.add_argument('--tanh_clipping', type=float, default=10.,
27
+ help='Clip the parameters to within +- this value using tanh. '
28
+ 'Set to 0 to not perform any clipping.')
29
+ parser.add_argument('--normalization', default='batch', help="Normalization type, 'batch' (default) or 'instance'")
30
+
31
+ # Training
32
+ parser.add_argument('--lr_model', type=float, default=1e-4, help="Set the learning rate for the actor network")
33
+ parser.add_argument('--lr_critic', type=float, default=1e-4, help="Set the learning rate for the critic network")
34
+ parser.add_argument('--lr_decay', type=float, default=1.0, help='Learning rate decay per epoch')
35
+ parser.add_argument('--eval_only', action='store_true', help='Set this value to only evaluate model')
36
+ parser.add_argument('--n_epochs', type=int, default=100, help='The number of epochs to train')
37
+ parser.add_argument('--seed', type=int, default=1234, help='Random seed to use')
38
+ parser.add_argument('--max_grad_norm', type=float, default=1.0,
39
+ help='Maximum L2 norm for gradient clipping, default 1.0 (0 to disable clipping)')
40
+ parser.add_argument('--no_cuda', action='store_true', help='Disable CUDA')
41
+ parser.add_argument('--exp_beta', type=float, default=0.8,
42
+ help='Exponential moving average baseline decay (default 0.8)')
43
+ parser.add_argument('--baseline', default=None,
44
+ help="Baseline to use: 'rollout', 'critic' or 'exponential'. Defaults to no baseline.")
45
+ parser.add_argument('--bl_alpha', type=float, default=0.05,
46
+ help='Significance in the t-test for updating rollout baseline')
47
+ parser.add_argument('--bl_warmup_epochs', type=int, default=None,
48
+ help='Number of epochs to warmup the baseline, default None means 1 for rollout (exponential '
49
+ 'used for warmup phase), 0 otherwise. Can only be used with rollout baseline.')
50
+ parser.add_argument('--eval_batch_size', type=int, default=1024,
51
+ help="Batch size to use during (baseline) evaluation")
52
+ parser.add_argument('--checkpoint_encoder', action='store_true',
53
+ help='Set to decrease memory usage by checkpointing encoder')
54
+ parser.add_argument('--shrink_size', type=int, default=None,
55
+ help='Shrink the batch size if at least this many instances in the batch are finished'
56
+ ' to save memory (default None means no shrinking)')
57
+ parser.add_argument('--data_distribution', type=str, default=None,
58
+ help='Data distribution to use during training, defaults and options depend on problem.')
59
+
60
+ # Misc
61
+ parser.add_argument('--log_step', type=int, default=50, help='Log info every log_step steps')
62
+ parser.add_argument('--log_dir', default='logs', help='Directory to write TensorBoard information to')
63
+ parser.add_argument('--run_name', default='run', help='Name to identify the run')
64
+ parser.add_argument('--output_dir', default='outputs', help='Directory to write output models to')
65
+ parser.add_argument('--epoch_start', type=int, default=0,
66
+ help='Start at epoch # (relevant for learning rate decay)')
67
+ parser.add_argument('--checkpoint_epochs', type=int, default=1,
68
+ help='Save checkpoint every n epochs (default 1), 0 to save no checkpoints')
69
+ parser.add_argument('--load_path', help='Path to load model parameters and optimizer state from')
70
+ parser.add_argument('--resume', help='Resume from previous checkpoint file')
71
+ parser.add_argument('--no_tensorboard', action='store_true', help='Disable logging TensorBoard files')
72
+ parser.add_argument('--no_progress_bar', action='store_true', help='Disable progress bar')
73
+
74
+ opts = parser.parse_args(args)
75
+
76
+ opts.use_cuda = torch.cuda.is_available() and not opts.no_cuda
77
+ opts.run_name = "{}_{}".format(opts.run_name, time.strftime("%Y%m%dT%H%M%S"))
78
+ opts.save_dir = os.path.join(
79
+ opts.output_dir,
80
+ "{}_{}".format(opts.problem, opts.graph_size),
81
+ opts.run_name
82
+ )
83
+ if opts.bl_warmup_epochs is None:
84
+ opts.bl_warmup_epochs = 1 if opts.baseline == 'rollout' else 0
85
+ assert (opts.bl_warmup_epochs == 0) or (opts.baseline == 'rollout')
86
+ assert opts.epoch_size % opts.batch_size == 0, "Epoch size must be integer multiple of batch size!"
87
+ return opts
miscellaneous/kool/attention-learn-to-route/plot_vrp.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_10/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "log_dir": "logs",
3
+ "eval_only": false,
4
+ "tanh_clipping": 10.0,
5
+ "lr_model": 0.0001,
6
+ "val_size": 10000,
7
+ "epoch_size": 1280000,
8
+ "graph_size": 10,
9
+ "batch_size": 512,
10
+ "val_dataset": "",
11
+ "checkpoint_epochs": 50,
12
+ "output_dir": "outputs",
13
+ "save_dir": "",
14
+ "n_epochs": 100,
15
+ "resume": null,
16
+ "exp_beta": 0.8,
17
+ "seed": 1234,
18
+ "embedding_dim": 128,
19
+ "no_cuda": false,
20
+ "eval_batch_size": 2048,
21
+ "use_cuda": true,
22
+ "lr_critic": 0.0001,
23
+ "n_encode_layers": 3,
24
+ "baseline": "rollout",
25
+ "epoch_start": 0,
26
+ "no_progress_bar": true,
27
+ "bl_alpha": 0.05,
28
+ "run_name": "cvrp_10",
29
+ "log_step": 500,
30
+ "normalization": "batch",
31
+ "max_grad_norm": 1.0,
32
+ "model": "attention",
33
+ "no_tensorboard": false,
34
+ "bl_warmup_epochs": 1,
35
+ "lr_decay": 1.0,
36
+ "load_path": null,
37
+ "hidden_dim": 128,
38
+ "problem": "cvrp"
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_100/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "val_size": 10000,
3
+ "log_step": 500,
4
+ "exp_beta": 0.8,
5
+ "tanh_clipping": 10.0,
6
+ "run_name": "cvrp_100",
7
+ "no_tensorboard": false,
8
+ "resume": null,
9
+ "use_cuda": true,
10
+ "log_dir": "logs",
11
+ "bl_warmup_epochs": 1,
12
+ "no_cuda": false,
13
+ "eval_only": false,
14
+ "seed": 1234,
15
+ "max_grad_norm": 1.0,
16
+ "graph_size": 100,
17
+ "problem": "cvrp",
18
+ "lr_model": 0.0001,
19
+ "lr_critic": 0.0001,
20
+ "normalization": "batch",
21
+ "no_progress_bar": true,
22
+ "save_dir": "",
23
+ "n_epochs": 100,
24
+ "bl_alpha": 0.05,
25
+ "baseline": "rollout",
26
+ "batch_size": 256,
27
+ "output_dir": "outputs",
28
+ "n_encode_layers": 3,
29
+ "val_dataset": "",
30
+ "epoch_start": 0,
31
+ "model": "attention",
32
+ "eval_batch_size": 2048,
33
+ "embedding_dim": 128,
34
+ "lr_decay": 1.0,
35
+ "hidden_dim": 128,
36
+ "load_path": null,
37
+ "checkpoint_epochs": 50,
38
+ "epoch_size": 640000
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_20/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "no_cuda": false,
3
+ "val_size": 10000,
4
+ "bl_alpha": 0.05,
5
+ "seed": 1234,
6
+ "batch_size": 512,
7
+ "no_tensorboard": false,
8
+ "log_step": 500,
9
+ "resume": null,
10
+ "lr_decay": 1.0,
11
+ "run_name": "cvrp_20",
12
+ "load_path": null,
13
+ "no_progress_bar": true,
14
+ "epoch_start": 0,
15
+ "problem": "cvrp",
16
+ "n_encode_layers": 3,
17
+ "bl_warmup_epochs": 1,
18
+ "exp_beta": 0.8,
19
+ "baseline": "rollout",
20
+ "epoch_size": 1280000,
21
+ "max_grad_norm": 1.0,
22
+ "val_dataset": "",
23
+ "eval_only": false,
24
+ "graph_size": 20,
25
+ "use_cuda": true,
26
+ "n_epochs": 100,
27
+ "tanh_clipping": 10.0,
28
+ "output_dir": "outputs",
29
+ "eval_batch_size": 2048,
30
+ "normalization": "batch",
31
+ "hidden_dim": 128,
32
+ "checkpoint_epochs": 50,
33
+ "embedding_dim": 128,
34
+ "model": "attention",
35
+ "lr_critic": 0.0001,
36
+ "log_dir": "logs",
37
+ "save_dir": "",
38
+ "lr_model": 0.0001
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/cvrp_50/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "graph_size": 50,
3
+ "lr_decay": 1.0,
4
+ "problem": "cvrp",
5
+ "no_progress_bar": true,
6
+ "run_name": "cvrp_50",
7
+ "embedding_dim": 128,
8
+ "use_cuda": true,
9
+ "save_dir": "",
10
+ "bl_alpha": 0.05,
11
+ "eval_only": false,
12
+ "output_dir": "outputs",
13
+ "no_cuda": false,
14
+ "max_grad_norm": 1.0,
15
+ "checkpoint_epochs": 50,
16
+ "n_epochs": 100,
17
+ "no_tensorboard": false,
18
+ "log_step": 500,
19
+ "baseline": "rollout",
20
+ "load_path": null,
21
+ "epoch_start": 0,
22
+ "tanh_clipping": 10.0,
23
+ "hidden_dim": 128,
24
+ "lr_critic": 0.0001,
25
+ "bl_warmup_epochs": 1,
26
+ "lr_model": 0.0001,
27
+ "val_size": 10000,
28
+ "epoch_size": 1280000,
29
+ "eval_batch_size": 2048,
30
+ "normalization": "batch",
31
+ "model": "attention",
32
+ "resume": null,
33
+ "log_dir": "logs",
34
+ "exp_beta": 0.8,
35
+ "n_encode_layers": 3,
36
+ "seed": 1235,
37
+ "batch_size": 512,
38
+ "val_dataset": ""
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_const_100/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_const",
3
+ "graph_size": 100,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 39,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": true,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_const_100",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": "outputs/op_const_100/op_const_100_rollout_20180910T180544/epoch-60.pt",
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_const_20/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_const",
3
+ "graph_size": 20,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_const_20",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_const_50/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_const",
3
+ "graph_size": 50,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_const_50",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_100/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_dist",
3
+ "graph_size": 100,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": true,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_dist_100",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_20/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_dist",
3
+ "graph_size": 20,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_dist_20",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_dist_50/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_dist",
3
+ "graph_size": 50,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_dist_50",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_100/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_unif",
3
+ "graph_size": 100,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 35,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": true,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_unif_100",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": "outputs/op_unif_100/op_unif_100_rollout_20180910T180547/epoch-64.pt",
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_20/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_unif",
3
+ "graph_size": 20,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_unif_20",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/op_unif_50/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "op_unif",
3
+ "graph_size": 50,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "op_unif_50",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_100/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_det",
3
+ "graph_size": 100,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_det_100",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_20/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_det",
3
+ "graph_size": 20,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1235,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_det_20",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_det_50/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_det",
3
+ "graph_size": 50,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_det_50",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_100/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_stoch",
3
+ "graph_size": 100,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_stoch_100",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_20/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_stoch",
3
+ "graph_size": 20,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_stoch_20",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/pctsp_stoch_50/args.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem": "pctsp_stoch",
3
+ "graph_size": 50,
4
+ "batch_size": 512,
5
+ "epoch_size": 1280000,
6
+ "val_size": 10000,
7
+ "val_dataset": "",
8
+ "model": "attention",
9
+ "embedding_dim": 128,
10
+ "hidden_dim": 128,
11
+ "n_encode_layers": 3,
12
+ "tanh_clipping": 10.0,
13
+ "normalization": "batch",
14
+ "lr_model": 0.0001,
15
+ "lr_critic": 0.0001,
16
+ "lr_decay": 1.0,
17
+ "eval_only": false,
18
+ "n_epochs": 100,
19
+ "seed": 1234,
20
+ "max_grad_norm": 1.0,
21
+ "no_cuda": false,
22
+ "exp_beta": 0.8,
23
+ "baseline": "rollout",
24
+ "bl_alpha": 0.05,
25
+ "bl_warmup_epochs": 1,
26
+ "eval_batch_size": 2048,
27
+ "checkpoint_encoder": false,
28
+ "log_step": 50,
29
+ "log_dir": "logs",
30
+ "run_name": "pctsp_stoch_50",
31
+ "output_dir": "outputs",
32
+ "epoch_start": 0,
33
+ "checkpoint_epochs": 1,
34
+ "load_path": null,
35
+ "resume": null,
36
+ "no_tensorboard": false,
37
+ "no_progress_bar": false,
38
+ "use_cuda": true,
39
+ "save_dir": ""
40
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_10/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "no_tensorboard": false,
3
+ "exp_beta": 0.8,
4
+ "model": "attention",
5
+ "max_grad_norm": 1.0,
6
+ "no_progress_bar": true,
7
+ "resume": null,
8
+ "eval_batch_size": 2048,
9
+ "epoch_size": 1280000,
10
+ "n_encode_layers": 3,
11
+ "log_dir": "logs",
12
+ "hidden_dim": 128,
13
+ "val_dataset": "",
14
+ "seed": 1235,
15
+ "normalization": "batch",
16
+ "log_step": 500,
17
+ "no_cuda": false,
18
+ "batch_size": 512,
19
+ "bl_warmup_epochs": 1,
20
+ "problem": "sdvrp",
21
+ "val_size": 10000,
22
+ "lr_critic": 0.0001,
23
+ "epoch_start": 0,
24
+ "checkpoint_epochs": 50,
25
+ "load_path": null,
26
+ "embedding_dim": 128,
27
+ "baseline": "rollout",
28
+ "graph_size": 10,
29
+ "eval_only": false,
30
+ "lr_model": 0.0001,
31
+ "save_dir": "",
32
+ "use_cuda": true,
33
+ "lr_decay": 1.0,
34
+ "output_dir": "outputs",
35
+ "run_name": "sdvrp_10",
36
+ "tanh_clipping": 10.0,
37
+ "bl_alpha": 0.05,
38
+ "n_epochs": 100
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_100/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "hidden_dim": 128,
3
+ "val_dataset": "",
4
+ "model": "attention",
5
+ "baseline": "rollout",
6
+ "checkpoint_epochs": 50,
7
+ "bl_warmup_epochs": 1,
8
+ "log_dir": "logs",
9
+ "run_name": "sdvrp_100",
10
+ "n_epochs": 100,
11
+ "output_dir": "outputs",
12
+ "val_size": 10000,
13
+ "bl_alpha": 0.05,
14
+ "graph_size": 100,
15
+ "normalization": "batch",
16
+ "load_path": null,
17
+ "batch_size": 256,
18
+ "lr_decay": 1.0,
19
+ "max_grad_norm": 1.0,
20
+ "epoch_start": 0,
21
+ "no_tensorboard": false,
22
+ "epoch_size": 640000,
23
+ "exp_beta": 0.8,
24
+ "tanh_clipping": 10.0,
25
+ "eval_only": false,
26
+ "resume": null,
27
+ "n_encode_layers": 3,
28
+ "log_step": 500,
29
+ "lr_model": 0.0001,
30
+ "embedding_dim": 128,
31
+ "seed": 1235,
32
+ "use_cuda": true,
33
+ "no_progress_bar": true,
34
+ "lr_critic": 0.0001,
35
+ "save_dir": "",
36
+ "no_cuda": false,
37
+ "problem": "sdvrp",
38
+ "eval_batch_size": 2048
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_20/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "baseline": "rollout",
3
+ "checkpoint_epochs": 50,
4
+ "n_epochs": 100,
5
+ "no_tensorboard": false,
6
+ "eval_batch_size": 2048,
7
+ "no_progress_bar": true,
8
+ "normalization": "batch",
9
+ "output_dir": "outputs",
10
+ "epoch_size": 1280000,
11
+ "model": "attention",
12
+ "tanh_clipping": 10.0,
13
+ "log_step": 500,
14
+ "n_encode_layers": 3,
15
+ "save_dir": "",
16
+ "max_grad_norm": 1.0,
17
+ "hidden_dim": 128,
18
+ "log_dir": "logs",
19
+ "lr_model": 0.0001,
20
+ "bl_warmup_epochs": 1,
21
+ "no_cuda": false,
22
+ "graph_size": 20,
23
+ "epoch_start": 0,
24
+ "problem": "sdvrp",
25
+ "eval_only": false,
26
+ "exp_beta": 0.8,
27
+ "embedding_dim": 128,
28
+ "run_name": "sdvrp_20",
29
+ "lr_critic": 0.0001,
30
+ "load_path": null,
31
+ "val_dataset": "",
32
+ "use_cuda": true,
33
+ "batch_size": 512,
34
+ "lr_decay": 1.0,
35
+ "bl_alpha": 0.05,
36
+ "seed": 1234,
37
+ "resume": null,
38
+ "val_size": 10000
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/sdvrp_50/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "lr_critic": 0.0001,
3
+ "val_dataset": "",
4
+ "n_encode_layers": 3,
5
+ "no_tensorboard": false,
6
+ "output_dir": "outputs",
7
+ "no_progress_bar": true,
8
+ "max_grad_norm": 1.0,
9
+ "epoch_size": 1280000,
10
+ "embedding_dim": 128,
11
+ "lr_decay": 1.0,
12
+ "tanh_clipping": 10.0,
13
+ "baseline": "rollout",
14
+ "lr_model": 0.0001,
15
+ "run_name": "sdvrp_50",
16
+ "load_path": null,
17
+ "bl_warmup_epochs": 1,
18
+ "save_dir": "",
19
+ "hidden_dim": 128,
20
+ "problem": "sdvrp",
21
+ "log_dir": "logs",
22
+ "no_cuda": false,
23
+ "bl_alpha": 0.05,
24
+ "use_cuda": true,
25
+ "normalization": "batch",
26
+ "checkpoint_epochs": 50,
27
+ "graph_size": 50,
28
+ "exp_beta": 0.8,
29
+ "epoch_start": 0,
30
+ "batch_size": 512,
31
+ "seed": 1234,
32
+ "eval_only": false,
33
+ "n_epochs": 100,
34
+ "model": "attention",
35
+ "log_step": 500,
36
+ "resume": null,
37
+ "eval_batch_size": 2048,
38
+ "val_size": 10000
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/tsp_100/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "eval_batch_size": 2048,
3
+ "val_dataset": "",
4
+ "val_size": 10000,
5
+ "load_path": null,
6
+ "n_encode_layers": 3,
7
+ "lr_critic": 0.0001,
8
+ "output_dir": "outputs",
9
+ "epoch_size": 1280000,
10
+ "tanh_clipping": 10.0,
11
+ "hidden_dim": 128,
12
+ "log_dir": "logs",
13
+ "seed": 1235,
14
+ "n_epochs": 100,
15
+ "no_tensorboard": false,
16
+ "eval_only": false,
17
+ "embedding_dim": 128,
18
+ "lr_model": 0.0001,
19
+ "baseline": "rollout",
20
+ "resume": null,
21
+ "bl_warmup_epochs": 1,
22
+ "no_progress_bar": true,
23
+ "run_name": "tsp_100",
24
+ "problem": "tsp",
25
+ "use_cuda": true,
26
+ "max_grad_norm": 1.0,
27
+ "epoch_start": 0,
28
+ "batch_size": 512,
29
+ "log_step": 500,
30
+ "save_dir": "",
31
+ "no_cuda": false,
32
+ "graph_size": 100,
33
+ "normalization": "batch",
34
+ "model": "attention",
35
+ "bl_alpha": 0.05,
36
+ "exp_beta": 0.8,
37
+ "lr_decay": 1.0,
38
+ "checkpoint_epochs": 50
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/tsp_20/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "save_dir": "",
3
+ "resume": null,
4
+ "baseline": "rollout",
5
+ "batch_size": 512,
6
+ "log_step": 500,
7
+ "exp_beta": 0.8,
8
+ "val_size": 10000,
9
+ "n_encode_layers": 3,
10
+ "load_path": null,
11
+ "embedding_dim": 128,
12
+ "eval_batch_size": 2048,
13
+ "seed": 1235,
14
+ "max_grad_norm": 1.0,
15
+ "output_dir": "outputs",
16
+ "graph_size": 20,
17
+ "no_progress_bar": true,
18
+ "lr_decay": 1.0,
19
+ "checkpoint_epochs": 50,
20
+ "run_name": "tsp_20",
21
+ "bl_alpha": 0.05,
22
+ "epoch_start": 0,
23
+ "log_dir": "logs",
24
+ "no_tensorboard": false,
25
+ "normalization": "batch",
26
+ "no_cuda": false,
27
+ "model": "attention",
28
+ "eval_only": false,
29
+ "use_cuda": true,
30
+ "problem": "tsp",
31
+ "lr_critic": 0.0001,
32
+ "hidden_dim": 128,
33
+ "val_dataset": "",
34
+ "n_epochs": 100,
35
+ "epoch_size": 1280000,
36
+ "lr_model": 0.0001,
37
+ "bl_warmup_epochs": 1,
38
+ "tanh_clipping": 10.0
39
+ }
miscellaneous/kool/attention-learn-to-route/pretrained/tsp_50/args.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "resume": null,
3
+ "use_cuda": true,
4
+ "lr_critic": 0.0001,
5
+ "output_dir": "outputs",
6
+ "log_step": 500,
7
+ "epoch_start": 0,
8
+ "run_name": "tsp_50",
9
+ "hidden_dim": 128,
10
+ "log_dir": "logs",
11
+ "model": "attention",
12
+ "n_encode_layers": 3,
13
+ "checkpoint_epochs": 50,
14
+ "epoch_size": 1280000,
15
+ "baseline": "rollout",
16
+ "problem": "tsp",
17
+ "graph_size": 50,
18
+ "n_epochs": 100,
19
+ "lr_decay": 1.0,
20
+ "no_cuda": false,
21
+ "eval_batch_size": 2048,
22
+ "eval_only": false,
23
+ "exp_beta": 0.8,
24
+ "seed": 1235,
25
+ "no_tensorboard": false,
26
+ "no_progress_bar": true,
27
+ "max_grad_norm": 1.0,
28
+ "batch_size": 512,
29
+ "val_dataset": "",
30
+ "load_path": null,
31
+ "bl_alpha": 0.05,
32
+ "val_size": 10000,
33
+ "save_dir": "",
34
+ "normalization": "batch",
35
+ "embedding_dim": 128,
36
+ "tanh_clipping": 10.0,
37
+ "lr_model": 0.0001,
38
+ "bl_warmup_epochs": 1
39
+ }
miscellaneous/kool/attention-learn-to-route/problems/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from problems.tsp.problem_tsp import TSP
2
+ from problems.vrp.problem_vrp import CVRP, SDVRP
3
+ from problems.op.problem_op import OP
4
+ from problems.pctsp.problem_pctsp import PCTSPDet, PCTSPStoch
miscellaneous/kool/attention-learn-to-route/problems/op/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ compass/
miscellaneous/kool/attention-learn-to-route/problems/op/__init__.py ADDED
File without changes