Associated paper: On Some Relations Between Optimal TSP Solutions and Proximity Graphs in the Plane with Logan D. Graham, Joseph S. B. Mitchell, and Gaurish Telang.
TSP-vs-Graphs is a repository for researching relations between the traveling salesman problem (TSP) and proximity graphs. It includes
- a simulation API
- a GUI for plotting point sets and computing the TSP and proximity graphs
- the experimental results of our paper
- a highly optimized implementation of the TSP-NNG enumeration algorithm
- a collection of Euclidean TSPLIB instances ready for plotting
The TSP tour, TSP path, and bitonic TSP tour are implemented. Proximity graphs of interest include the k-nearest neighbor graph (k-NNG), minimum spanning tree (MST), Gabriel graph, Urquhart graph, and order-k Delaunay (primarily k=0, 1, 2).
git clone https://github.com/samvanderpoel/TSP-vs-Graphs
cd TSP-vs-Graphs
The most reliable way to set up the necessary libraries is by creating the following Conda environment:
conda env create -f tsp_env.yml
conda activate tsp
conda install matplotlib-base=3.5.0
Alternatively, you may use pip (with Python 3.7), which is sufficient for the simulation API and GUI. If you use pip, we recommend initializing a virtual environment first (this could be a conda virtual environment).
pip install -r requirements.txt
Another (arguably better) way to setup the necessary libraries using pip is to navigate to the top of the TSP-vs-Graphs directory and run
pip install .
(If you intend to make edits to the TSP-vs-Graphs source code, then run pip install -e . instead).
If you use pip, you will need to install a separate Rust compiler for the TSP-NNG algorithm.
Next, set up PyConcorde:
git clone https://github.com/jvkersch/pyconcorde
cd pyconcorde
pip install .
cd ..
(If you intend to make edits to the PyConcorde source code, then run pip install -e . instead).
GNU Parallel is used in our parallel TSP-NNG implementation, and it is optionally used in the Simulation code.
We illustrate usage of the simulation API by way of an example simulation called test. The test simulation is specified by the following items:
- a top-level script sim/test.sh
- a simulation config file sim/config_simul/test.yaml
- a plotting config file sim/config_plots/test.yaml
Run the simulation with bash sim/test.sh. Several simulation jobs can run concurrently as long as they have distinct jobnames as specified in their config files.
The results of the test simulation will appear in results/test, and they are organized by point set distributions (e.g. uniform on [0,1]x[0,1], bivariate normal, etc.). Graph comparison results are stored in files 'data.txt', simulation meta-data are stored in files 'meta.txt', and simulation compute times are stored in files 'compute-times.txt'.
In simulation and plotting scripts, each graph type has a unique string IDs to track comparisons and organize results. The graph IDs are:
- TSP tour:
tour - TSP path:
path - Bitonic TSP tour:
bito - k-NNG:
{k}nng(e.g. 1-NNG:1nng; 2-NNG:2nng) - 20%-NNG:
20pt - MST:
mst - Urquhart graph:
urq - Gabriel graph:
gab - Delaunay triangulation:
del - Order-k Delaunay:
{k}del(e.g. 1-Delaunay:1del; 2-Delaunay:2del)
Existing data from the test simulation are printed to standard output with
python sim/report_existing_data.py --jobname test
The optional flag --all verbosely prints data for each point set size, and the flag --export writes statistics to .csv:
python sim/report_existing_data.py --jobname test --all --export
Plotting of simulation results can be done individually, as in
python sim/plot_simul_data.py --config sim/config_plots/test.yaml --cloudtype uniform-sqr
or in a batch, as in sim/gen_plots.sh.
Merge two simulation data files path/to/data1.txt and path/to/data2.txt into a new data file path/to/data.txt via the command
python sim/merge_data.py path/to/data1.txt path/to/data2.txt path/to/data.txt
The primary purpose of the simulation API is large-scale edgewise graph comparison, but it is sufficiently general that other graph theoretic or geometric properties can be tested at scale. The function sim.utils.compare tests whether simulated graphs satisfy a given criterion and, if so, saves the corresponding point set to a yaml file.
For example, one may wish to test whether it is possible for the TSP tour to properly cross the MST. One would test this in sim.utils.compare when comp=='tour_mst' by implementing a routine to check for graph crossing.
Simple anomalies to search for (and record) may be specified in the anoms section of a simulation config. For example,
anoms:
tour_1del: '<1'
path_mst: '==1'
records the coordinates of a point set if its TSP tour is not a subset of its 1-Delaunay graph, or if its TSP path is equal to its MST.
The GUI is launched with
python plot/plot.py --interactive
Points may be double-clicked directly onto the canvas. To compute graphs for a point set on the canvas, the current window must be the GUI canvas, and press i. You will see a set of options appear in the terminal; enter the string associated with the graph you would like to compute.
To clear all edges from the current point cloud, press x; to clear all points and edges, press c.
Points sets may be sampled from various distributions. Press n (with the GUI as the current window) to see a list of options; answer the prompts in your terminal to specify the distribution.
Pressing o activates automatic comparison of edges of each graph computed with edges of the Euclidean TSP tour and path.
To export the current figure to the plot directory, press e.
All point sets are stored in .yaml files in this repository. For example, to plot TSPLIB instance TSP225, run
python plot/plot.py --file tsplib/instances/euclidean_instances/tsp225.yaml
Compute and plot graphs as described above (by pressing i), or press t while the GUI is selected to display the following
Coordinates and figures are provided for salient point sets.
Scripts are provided to generate point sets that 'witness' a certain claim. For example, point-sets/kdel/gen_kdel.py generates a point set with the property that its Euclidean TSP tour or path is not a subset of its corresponding k-Delaunay graph, for any user-specified k. A typical usage is
python point-sets/kdel/gen_kdel.py --mode tour --k 3
python plot/plot.py --file point-sets/kdel/tour-3del.yaml
Similarly, point-sets/kgab/gen_kgab.py generates a point set of size n with the property that only two of its Euclidean TSP tour edges belong to its Gabriel graph:
python point-sets/kgab/gen_kgab.py --n 9
python plot/plot.py --file point-sets/kgab/gab9.yaml
The TSP-NNG enumeration algorithm is presented in tspnng/pseudocode.pdf and implemented in tspnng/main.py. The script tspnng/run_tour_nng.sh executes the algorithm for a specific value of n and user-specified degrees of parallelism (provided by GNU parallel and Python's multiprocessing). Execute run_tour_nng.sh from the tspnng directory.
The user may use NetworkX, graph-tool, or petgraph (Rust) as the underlying graph library for the algorithm. The specific method is set in tspnng/main.py.
The high-performance version of the algorithm uses Rust. To set up the Rust library,
cd tspnng/find_edge_swap
maturin develop
cd ..
If you wish to use the graph-tool method, first
conda install -c conda-forge graph-tool
The script tspnng/run_tour_nng_serial.py is a serial implementation of the enumeration algorithm for illustrative purposes. On a PC, it is reasonable to run this up to n=8 or so.

