├── .gitignore ├── README.md ├── TSP ├── __init__.py └── tsp_utils.py ├── VRP ├── __init__.py ├── vrp_attention.py └── vrp_utils.py ├── configs.py ├── data └── .data ├── main.py ├── misc_utils.py ├── model ├── __init__.py └── attention_agent.py ├── shared ├── __init__.py ├── attention.py ├── decode_step.py ├── embeddings.py └── misc_utils.py └── task_specific_params.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Add any directories, files, or patterns you don't want to be tracked by version control 2 | *.pdf 3 | *.pyc 4 | *.npy 5 | *.txt 6 | *.out 7 | *.err 8 | *.mat 9 | *.jpg 10 | *.aux 11 | *.log 12 | *.blb 13 | *.gz 14 | *.xls 15 | *.synctex 16 | *.synctex.gz 17 | *.synctex.gz(busy) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Reinforcement Learning for Solving the Vehicle Routing Problem 3 | 4 | We use Reinforcement for solving Travelling Salesman Problem (TSP) and Vehicle Routing Problem (VRP). 5 | 6 | 7 | ## Paper 8 | Implementation of our paper: [Reinforcement Learning for Solving the Vehicle Routing Problem](https://arxiv.org/abs/1802.04240v2). 9 | 10 | ## Dependencies 11 | 12 | 13 | * Numpy 14 | * [tensorflow](https://www.tensorflow.org/)>=1.2 15 | * tqdm 16 | 17 | ## How to Run 18 | ### Train 19 | By default, the code is running in the training mode on a single gpu. For running the code, one can use the following command: 20 | ```bash 21 | python main.py --task=vrp10 22 | ``` 23 | 24 | It is possible to add other config parameters like: 25 | ```bash 26 | python main.py --task=vrp10 --gpu=0 --n_glimpses=1 --use_tanh=False 27 | ``` 28 | There is a full list of all configs in the ``config.py`` file. Also, task specific parameters are available in ``task_specific_params.py`` 29 | ### Inference 30 | For running the trained model for inference, it is possible to turn off the training mode. For this, you need to specify the directory of the trained model, otherwise random model will be used for decoding: 31 | ```bash 32 | python main.py --task=vrp10 --is_train=False --model_dir=./path_to_your_saved_checkpoint 33 | ``` 34 | The default inference is run in batch mode, meaning that all testing instances are fed simultanously. It is also possible to do inference in single mode, which means that we decode instances one-by-one. The latter case is used for reporting the runtimes and it will display detailed reports. For running the inference with single mode, you can try: 35 | ```bash 36 | python main.py --task=vrp10 --is_train=False --infer_type=single --model_dir=./path_to_your_saved_checkpoint 37 | ``` 38 | ### Logs 39 | All logs are stored in ``result.txt`` file stored in ``./logs/task_date_time`` directory. 40 | ## Sample CVRP solution 41 | 42 | ![enter image description here](https://lh3.googleusercontent.com/eUh69ZQsIV4SIE6RjwasAEkdw2VZaTmaeR8Fqk33di70-BGU62fvmcp6HLeGLE61lJDS7jLMpFf2 "Sample VRP") 43 | 44 | ## Acknowledgements 45 | Thanks to [pemami4911/neural-combinatorial-rl-pytorch](https://github.com/pemami4911/neural-combinatorial-rl-pytorch) for getting the idea of restructuring the code. -------------------------------------------------------------------------------- /TSP/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OptMLGroup/VRP-RL/b794fb1e4c4bb70a62cfa54504ee7a247adbc2a0/TSP/__init__.py -------------------------------------------------------------------------------- /TSP/tsp_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | import os 4 | import warnings 5 | import collections 6 | 7 | def create_TSP_dataset( 8 | n_problems, 9 | n_nodes, 10 | data_dir, 11 | seed=None, 12 | data_type='train'): 13 | ''' 14 | This function creates TSP instances and saves them on disk. If a file is already available, 15 | it will load the file. 16 | Input: 17 | n_problems: number of problems to generate. 18 | n_nodes: number of nodes in the problem. 19 | data_dir: the directory to save or load the file. 20 | seed: random seed for generating the data. 21 | data_type: the purpose for generating the data. It can be 'train', 'val', or any string. 22 | output: 23 | data: a numpy array with shape [n_problems x n_nodes x 2] 24 | ''' 25 | 26 | # set random number generator 27 | if seed == None: 28 | rnd = np.random 29 | else: 30 | rnd = np.random.RandomState(seed) 31 | 32 | # build task name and datafiles 33 | task_name = 'tsp-size-{}-len-{}-{}.txt'.format(n_problems, n_nodes,data_type) 34 | fname = os.path.join(data_dir, task_name) 35 | 36 | # cteate/load data 37 | if os.path.exists(fname): 38 | print('Loading dataset for {}...'.format(task_name)) 39 | data = np.loadtxt(fname) 40 | data = data.reshape(-1, n_nodes,2) 41 | else: 42 | print('Creating dataset for {}...'.format(task_name)) 43 | # Generate a training set of size n_problems 44 | data= rnd.uniform(0,1,size=(n_problems,n_nodes,2)) 45 | np.savetxt(fname, data.reshape(-1, n_nodes*2)) 46 | 47 | return data 48 | 49 | class DataGenerator(object): 50 | def __init__(self, 51 | args): 52 | 53 | ''' 54 | This class generates TSP problems for training and test 55 | Inputs: 56 | args: the parameter dictionary. It should include: 57 | args['random_seed']: random seed 58 | args['test_size']: number of problems to test 59 | args['n_nodes': number of nodes 60 | args['batch_size']: batchsize for training 61 | 62 | ''' 63 | self.args = args 64 | self.rnd = np.random.RandomState(seed= args['random_seed']) 65 | print('Created train iterator.') 66 | 67 | # create test data 68 | self.n_problems = args['test_size'] 69 | self.test_data = create_TSP_dataset(self.n_problems,args['n_nodes'],'./data', 70 | seed = args['random_seed']+1,data_type='test') 71 | 72 | self.reset() 73 | 74 | def reset(self): 75 | self.count = 0 76 | 77 | def get_train_next(self): 78 | ''' 79 | Get next batch of problems for training 80 | ''' 81 | input_data = self.rnd.uniform(0,1, 82 | size=[self.args['batch_size'],self.args['n_nodes'],2]) 83 | 84 | return input_data 85 | 86 | def get_test_next(self): 87 | ''' 88 | Get next batch of problems for testing 89 | ''' 90 | if self.count