A cross-platform C++17 implementation of a Genetic Algorithm to solve the Traveling Salesman Problem (TSP), with matplotlib visualization, TSPLIB support, and multiple selectable GA operators.
- π― High-performance C++17 GA core β Tournament selection, elitism, and configurable operators
- π Matplotlib visualizations β Route plots, convergence curves, and combined dashboards
- πΊοΈ TSPLIB support β Load real-world benchmark instances (EUC_2D)
- π CSV import/export β Load custom city sets and export results
- π¬ Multiple GA operators β Three crossover (OX, PMX, Cycle) and three mutation (Swap, Inversion, Scramble) operators
- π Multiple independent runs β Aggregate statistics (best/worst/avg/stddev) across runs
- π Convergence tracking β Per-generation best/average fitness export
- π² Reproducible runs β Fixed seed support for deterministic results
- π§ͺ Unit tested β 18 tests across 5 test suites
- π₯οΈ Cross-platform β Linux, macOS, Windows via CMake
git clone https://github.com/RezaSparks/tsp-ga-solver.git
cd tsp-ga-solver
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config ReleaseRequirements: CMake 3.16+, C++17 compiler (GCC, Clang, or MSVC)
# Random cities
./build/tsp_solver --cities 30 --population 100 --generations 500
# TSPLIB benchmark
./build/tsp_solver --tsplib examples/berlin52.tsp --population 200 --generations 1000
# Multiple runs with statistics
./build/tsp_solver --tsplib examples/berlin52.tsp --runs 10 --seed 42# Install Python dependencies
pip install -r scripts/requirements.txt
# Plot the best route
python scripts/plot_route.py output_<timestamp>/
# Plot convergence curve
python scripts/plot_convergence.py output_<timestamp>/
# Combined dashboard
python scripts/plot_dashboard.py output_<timestamp>/All plots are saved as high-resolution PNG files automatically.
Best route found for the berlin52 TSPLIB instance (52 cities) using OX crossover + inversion mutation, seed 42. Generated via matplotlib from solver output data.
./tsp_solver [OPTIONS]| Flag | Default | Description |
|---|---|---|
--cities, -n |
20 | Number of random cities to generate |
--population, -p |
100 | Population size (min: 4) |
--generations, -g |
500 | Number of generations |
--mutation-rate, -m |
0.02 | Mutation probability (0.0β1.0) |
--crossover |
ox |
Crossover: ox, pmx, cycle |
--mutation |
swap |
Mutation: swap, inversion, scramble |
--tsplib |
β | Load from TSPLIB .tsp file (EUC_2D) |
--csv |
β | Load from CSV file (header: x,y) |
--runs |
1 | Number of independent runs |
--seed |
0 | Random seed (0 = auto) |
--output-csv |
β | Export convergence data to CSV |
--help, -h |
β | Show help |
Random cities with custom operators:
./tsp_solver --cities 50 --population 200 --generations 1000 \
--crossover ox --mutation inversion --mutation-rate 0.03TSPLIB with convergence export:
./tsp_solver --tsplib examples/berlin52.tsp \
--population 300 --generations 2000 --output-csv convergence.csvReproducible benchmark run:
./tsp_solver --tsplib examples/berlin52.tsp --runs 10 \
--population 300 --generations 2000 --seed 200The scripts/ directory contains Python tools for analyzing solver output:
| Script | Purpose | Output |
|---|---|---|
plot_route.py |
Plot the best-found route | route.png |
plot_convergence.py |
Plot fitness over generations | convergence.png |
plot_dashboard.py |
Combined route + convergence | dashboard.png |
All scripts accept either a directory path (auto-detects files) or individual CSV files:
python scripts/plot_dashboard.py output_1234567890/ # directory mode
python scripts/plot_route.py best_route.csv # file modetsp-ga-solver/
βββ src/ # C++ source files
β βββ main.cpp # Entry point
β βββ city.cpp # City/distance logic
β βββ population.cpp # GA implementation
β βββ tsplib_parser.cpp # TSPLIB parser
β βββ cli.cpp # CLI parsing
βββ include/ # Header files
β βββ ga/ # GA core (selection, crossover, mutation, elitism)
β βββ tsp/ # City/distance logic, loaders
β βββ cli/ # CLI argument parsing
βββ scripts/ # Python visualization tools
β βββ plot_route.py
β βββ plot_convergence.py
β βββ plot_dashboard.py
β βββ requirements.txt
βββ tests/ # Unit tests (GoogleTest)
β βββ test_core.cpp
βββ examples/ # Sample input files
β βββ berlin52.tsp
β βββ cities_20.csv
βββ CMakeLists.txt
βββ README.md
cmake -B build -DTSP_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure18 tests across 5 suites:
| Suite | Tests | Coverage |
|---|---|---|
DistanceTest |
4 | Euclidean distance: zero, known value, symmetry, finiteness |
CrossoverValidity |
4 | OX, PMX, Cycle produce valid permutations; multi-seed stress test |
MutationValidity |
4 | Swap, Inversion, Scramble preserve validity; multi-seed stress test |
ElitismProperty |
3 | Best fitness monotonicity under OX+Swap, PMX+Inversion, high mutation |
CsvLoader |
3 | Valid CSV load, reject too few cities, reject missing file |
Results against berlin52.tsp (52 cities, optimal: 7542), 10 runs each:
| Crossover | Mutation | Pop | Gens | Best | Avg | Worst | Std Dev | Gap |
|---|---|---|---|---|---|---|---|---|
| OX | Swap | 200 | 1000 | 8874.27 | 9636.52 | 10007.33 | 294.00 | +17.7% |
| PMX | Swap | 200 | 1000 | 9498.83 | 10538.72 | 11426.32 | 563.57 | +25.9% |
| Cycle | Swap | 200 | 1000 | 10554.94 | 11197.54 | 11947.18 | 477.54 | +39.9% |
| OX | Inversion | 300 | 2000 | 7825.42 | 8188.91 | 8470.43 | 225.92 | +3.8% |
Key findings:
- OX consistently outperforms PMX and Cycle
- Inversion mutation significantly improves results over Swap
- Within ~3.8% of optimal for berlin52 without local search
Reproduce:
./build/tsp_solver --tsplib examples/berlin52.tsp \
--population 300 --generations 2000 --mutation-rate 0.03 \
--crossover ox --mutation inversion --runs 10 --seed 200- C++17 GA core with multiple operators
- TSPLIB & CSV support
- Convergence data export
- Unit tests (GoogleTest)
- Matplotlib visualization suite
- Additional TSPLIB distance types (ATT, CEIL_2D, GEO)
- 2-opt local search hybrid
- Animated evolution GIF generation
- Live web demo
Contributions are welcome! Please feel free to submit issues or pull requests.
MIT License β see LICENSE for details.
