This is the C++ solver for the dynamic-ips project. It solves large-scale dynamic Dial-a-Ride Problems (DARP) within a rolling-horizon framework. The solver implements the two main workflows of the project plus supporting baselines.
- B-CG — Batch-based Column Generation.
- A-CG — Anytime Column Generation.
- Greedy / insertion baseline.
The selected algorithm and solution mode are selected via parameters / command-line options (see Configuration). The solver supports commercial backends (Gurobi and/or CPLEX) for solving the master problem.
You must have a valid Gurobi or CPLEX license to use the corresponding backend. This repository does not include CPLEX/Gurobi binaries. For best computational performance, the Gurobi backend is recommended.
For a high-level overview of the project and datasets, see the root README. The Python pipeline that prepares data and plots results is documented in the Python pipeline.
cpp/
├─ include/ # vendored third-party headers (json.hpp)
├─ src/
│ ├─ data/ # core objects (Instance, Request, Vehicle, Route, Parameters, Graph, ...)
│ │ ├─ Graph.cpp/.h
│ │ ├─ Greedy.cpp/.h
│ │ ├─ Instance.cpp/.h
│ │ ├─ Label.cpp/.h
│ │ ├─ Parameters.cpp/.h
│ │ ├─ Request.cpp/.h
│ │ ├─ Route.cpp/.h
│ │ ├─ SolutionMetrics.cpp/.h
│ │ └─ Vehicle.cpp/.h
│ ├─ solvers/ # algorithm implementations
│ │ ├─ CG_Algorithm.cpp/.h
│ │ ├─ LabelingSubProblem.cpp/.h
│ │ ├─ MasterAlgorithm.cpp/.h
│ │ ├─ SubproModeler.cpp/.h
│ │ ├─ GreedyModeler.cpp/.h
│ │ ├─ AnytimeSolver.cpp/.h
│ │ ├─ BatchSolver.cpp/.h
│ │ ├─ OfflineSolver.cpp/.h
│ │ └─ ...
│ ├─ CplexSolver/ # CPLEX-based modeling/solving
│ │ ├─ MP_Cplex.cpp/.h
│ │ ├─ RP_Cplex.cpp/.h
│ │ ├─ CP_Cplex.cpp/.h
│ │ ├─ MIPSolver_Cplex.cpp/.h
│ │ └─ ...
│ ├─ GurobiSolver/ # Gurobi-based modeling/solving
│ │ ├─ MP_Gurobi.cpp/.h
│ │ ├─ RP_Gurobi.cpp/.h
│ │ ├─ CPModeler.cpp/.h
│ │ ├─ MIPSolver_Gurobi.cpp/.h
│ │ └─ ...
│ ├─ utilities/ # config parsing, IO tools, helpers
│ │ ├─ ConfigParser.cpp/.h
│ │ ├─ ReadWrite.cpp/.h
│ │ ├─ Tools.cpp/.h
│ │ └─ ...
│ ├─ realTimeMain.cpp # entry point (main function)
│ └─ CMakeLists.txt
├─ parameters.md # parameter reference
├─ CMakeLists.txt
├─ FindEigen3.cmake
├─ FindCPLEX.cmake
└─ FindGUROBI.cmake
Benchmark instance sets live at the repository root under
../data/, and the experiment-generation and SLURM scripts under../computational_scripts/.
- C++17 (or newer)
- GCC / G++ with C++17 support (e.g. GCC 9+ recommended)
- CMake (≥ 3.16 recommended)
- Eigen3 (header-only linear-algebra library)
- Boost (development headers + libraries)
Eigen3 is header-only, so installing it system-wide is enough:
# macOS (Homebrew)
brew install eigen boost
# Debian / Ubuntu
sudo apt-get install libeigen3-dev libboost-all-devCMake locates Eigen3 in this order (see cpp/CMakeLists.txt):
find_package(Eigen3)— finds a system-wide install automatically (e.g. the Homebrew/apt packages above; on macOS Homebrew this is/usr/local/include/eigen3or/opt/homebrew/include/eigen3).-DEIGEN_DIR=/path/to/eigen-3.x.xpassed at configure time.- The
EIGEN_DIRenvironment variable.
If you see Eigen3 not found during configuration, install the package above or
point CMake at the headers explicitly:
cmake -S . -B build -DEIGEN_DIR=/path/to/eigen-3.x.x
# or
export EIGEN_DIR=/path/to/eigen-3.x.x- Gurobi Optimizer (license required) — preferred, and the backend used in the experiments, or
- IBM ILOG CPLEX (license required)
You are responsible for installing Gurobi/CPLEX and ensuring your license is valid. The custom find-modules
FindGUROBI.cmakeandFindCPLEX.cmakeare used to locate them.
Build from the cpp/ directory (the commands below assume this). You can also
build from the repository root — the top-level CMakeLists.txt forwards to
cpp/ — in which case the executable is still written to cpp/bin/.
The solver backend is selected with -DSOLVER=<GUROBI|CPLEX|NONE> (defaults to
GUROBI). NONE builds the solver-free libraries and tests only (no backend,
no license) — see Tests.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSOLVER=GUROBI
cmake --build build -jcmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSOLVER=CPLEX
cmake --build build -jThe build includes a small test suite (disable with -DBUILD_TESTING=OFF). After
building, run it from the build directory:
ctest --test-dir build --output-on-failureThe unit tests need no solver backend, so you can build and run them without a
Gurobi/CPLEX license by configuring with -DSOLVER=NONE (this is what CI does):
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSOLVER=NONE
cmake --build build --target unit_tests
ctest --test-dir build -R unit_tests --output-on-failureTwo kinds of tests are registered:
unit_tests— C++ unit and regression tests for the self-contained pieces (command-line parsing, the vehicle/zone file readers). They link only the solver-freeutilitiesanddatalibraries, so they build and run without a Gurobi/CPLEX license. Sources live intests/.toy_regression— an end-to-end check that the bundled toy example still solves correctly (3 vehicles serve all 8 requests, none rejected). It runs the compiled solver, so it is only registered when a backend is enabled and requires the corresponding license. Run just this one withctest --test-dir build -R toy_regression.
The unit_tests build and run on every push / pull request via the
build-and-test GitHub Actions
workflow, on Linux, macOS, and Windows (with SOLVER=NONE).
Running the executable with no arguments solves a tiny bundled instance
(data/ToyExample) with the B-CG workflow. This
is the fastest way to smoke-test a build:
# from the repository root (or cpp/, or the build/bin directory)
cpp/bin/realtime_DARPIt auto-locates data/ToyExample, solves 8 requests with 3 vehicles, and writes
outputs to data/ToyExample/runs/ (git-ignored). For real experiments, pass
options as below.
The executable is otherwise driven entirely by command-line options (no
positional arguments). Run with --help (or -h) to see the full usage message:
bin/realtime_DARP --help--inst-folder <path> Path to instance folder (relative to --data-dir)
--main-algo <int|name> Main algorithm: 0..4 or a name
(GREEDY, MIP, B_CG, F_ICG, A_CG)
--sol-mode <int|name> Solution mode: 0..2 or a name (STATIC, DYNAMIC, ANYTIME)
--paramfile <string> Parameter file name (e.g. AnyParameters or BatchParameters)
--scenario <string> Scenario name (must match an entry in the parameter JSON)
--main-algo and --sol-mode accept either the integer code (backward
compatible) or the case-insensitive enum name. The mapping from these settings
to the B-CG / A-CG / greedy / MIP / offline workflows is documented in the
Parameter reference.
--data-dir <path> Root data directory (default: current directory)
--vehicle-folder <path> Vehicle folder, relative to --data-dir (default: vehicles)
--num-vehicles <int> Fleet size; selects vehicles_<N>_4.txt in the folder.
Omit to read the fleet from <folder>/vehicles.txt
--vehicle-capacity <int> Vehicle capacity (default: the per-vehicle value in the file)
--initial-state <int> Fleet state at the start of the simulation (default: 0):
0 = fresh start (vehicles idle at their depots)
1 = warm start (precomputed state + onboard passengers)
2 = resume from a saved mid-simulation state
--instance-name <string> Specific instance (default: read instance list from file)
--output-dir <path> Output root (default: next to the instance data;
or set DARP_OUTPUT_DIR)
Fleet size and capacity come from the vehicle file, not its name. With
--num-vehicles N, the file vehicles_<N>_4.txt is selected (the benchmark
convention for choosing a fleet size). When --num-vehicles is omitted, the
solver reads <vehicle-folder>/vehicles.txt and takes the fleet size from its
row count. Likewise, --vehicle-capacity overrides the capacity only when given;
otherwise the per-vehicle capacity column of the file is used as-is.
--initial-state selects how the fleet is initialized:
| Value | Meaning |
|---|---|
0 |
Fresh start — vehicles begin idle at their depots with no passengers. |
1 |
Warm start — load a precomputed initial fleet state and onboard passengers from the general ONBOARDS_<file> in the vehicle folder (e.g. the vehicles_warmStart_11 sets). |
2 |
Resume — continue from a saved mid-simulation state, using the instance-specific VEHICLES_/ONBOARDS_/WaitRequests_ files. |
# fleet size from the instance, algo/mode by name
bin/realtime_DARP --data-dir ../data/NYC-DARP-Benchmark --vehicle-folder vehicles_warmStart_11 \
--inst-folder Instances_4h-11 --instance-name 20150917_11-240m --num-vehicles 1450 \
--main-algo B_CG --sol-mode DYNAMIC --paramfile ../computational_scripts/parameters/AnyParameters \
--scenario Basis_warm_keep --initial-state 1Run
bin/realtime_DARP --helpfor the complete and authoritative list of options, including any optional flags shown in the example above.
Solver behavior is controlled via JSON parameter files (located in
computational_scripts/parameters/, e.g. AnyParameters.json or
BatchParameters.json). A scenario is selected on the command line with
--paramfile <name> --scenario <key>; each scenario overrides only the keys it
needs, and all other parameters fall back to their code defaults.
- Full parameter reference (types, defaults, descriptions, and the mapping from parameters to the B-CG / A-CG settings): Parameter reference.
- To inspect the exact parameter schema in code:
src/data/Parameters.handsrc/data/Parameters.cppsrc/utilities/ConfigParser.handsrc/utilities/ConfigParser.cpp
Experiment generation and execution (local and SLURM) are managed from
computational_scripts/. The Python helper there does not implement the
algorithm; it only expands experiment matrices into command lines that call this
C++ binary.
See the Reproducibility guide for the full workflow: generating command files, running them locally (including dry runs and command-range filters), and submitting SLURM array jobs.
Both benchmarks are built on NYC TLC taxi trip records (2015–2016) using a
Manhattan network of virtual stop locations. The travel-time matrix between stop
locations is provided as edge_time_matrix.txt (constructed from OpenStreetMap
routing data).
The benchmark folders live at the repository root under data/ and are tracked
with Git LFS; run git lfs pull after cloning to fetch
them. Instructions for downloading the data from Zenodo or regenerating
instances are in the Python pipeline.
Riley, C., Legrain, A., & Van Hentenryck, P. (2026). Real-time ride-sharing operations — NYC TLC sub Dataset 2015–2016 [Data set]. Zenodo. https://doi.org/10.5281/zenodo.18745880
| Folder | Description |
|---|---|
Instances_2h-7 |
24 instances, 7:00–9:00 AM (2-hour window) |
Instances_2h-11 |
24 instances, 11:00 AM–1:00 PM (2-hour window) |
Instances_30s |
Reduced instances (30 epochs, 12:00–1:00 PM) for sensitivity analysis |
vehicles_uniform |
Initial vehicle positions for 7:00 AM instances (uniform distribution) |
vehicles_byDemand |
Initial vehicle positions for 7:00 AM instances (random distribution by demand) |
vehicles_warmStart_11 |
Warm-start vehicle states for 11:00 AM instances |
Amiri, E., Legrain, A., & El Hallaoui, I. (2026). Manhattan Dial-a-Ride Benchmark Dataset with NYC TLC Taxi Trips, 2015–2016 (v1.0) [Data set]. Zenodo. https://doi.org/10.5281/zenodo.20452171
| Folder | Description |
|---|---|
Instances_2h-7 |
24 instances, 7:00–9:00 AM (2-hour window) |
Instances_2h-11 |
24 instances, 11:00 AM–1:00 PM (2-hour window) |
Instances_4h-11 |
24 instances, 11:00 AM–3:00 PM (4-hour window) |
Instances_16h-7 |
24 instances, 7:00 AM–11:00 PM (16-hour window) |
vehicles_uniform |
Initial vehicle positions (uniform distribution); used for 7:00 AM and 16-hour instances |
vehicles_warmStart_11 |
Warm-start vehicle states for 11:00 AM instances |
Naming. Instance folders follow YYYYMMDD_HH-Xm, where HH is the start
hour and X is the duration in minutes (e.g. 20150926_11-240m = Sept 26 2015,
11:00 AM start, 4-hour window).
Warm-up. For instances starting at 11:00 AM, a one-hour dry run (10:00–11:00 AM) generates a realistic initial vehicle state; the resulting vehicle positions and onboard passenger status are saved in the corresponding warm-start vehicle files.
For each run the solver writes the following files to --output-dir, named after the instance with a suffix encoding the solution mode and algorithm (e.g. <instance>_D_B_CG.*).
| File | Contents |
|---|---|
LogFinalResults_<tag>.txt |
Human-readable log: parameter echo, per-vehicle routes stop-by-stop, and a final totals/averages block. |
summary_<tag>.csv |
One row per run. Service-quality metrics (wait time and trip delay per request/passenger, ...), fleet utilization, CG iteration counts, and runtime breakdown by component. |
epochRuntime_<tag>.csv |
One row per epoch. Runtime breakdown (MP, SP, greedy, rebalancing), and labeling statistics (labels generated/dominated/eliminated, pruned arcs/paths). |
epochResults_<tag>.csv |
One row per CG iteration within each epoch. LP objective, relative improvement, column pool size, and iteration runtimes — the convergence data used to characterize anytime behavior. |
Requests_<tag>.csv |
One row per request. Realized pickup/drop-off times, wait time, trip delay, assigned vehicle, vehicle switches across epochs, and dual value range. |
Vehicles_<tag>.csv |
One row per vehicle. Requests served and time breakdown across service, driving full, driving empty, and idle. |
Routes_<tag>.csv |
One row per stop visit. Final route plan with planned and realized arrival/departure times, vehicle load, and travel time from the previous stop. |
Parameters_<tag>.csv |
One row per run. Machine-readable record of every solver parameter used. Useful for grouping and filtering result sets; columns match the Parameter reference. |
Released under the MIT License; see the root README. CPLEX/Gurobi remain under their respective licenses and are not included here.