Employee Transportation Cost Optimizer using Heterogeneous Vehicle Routing with Soft Time Windows (HVRPSTW)
π¬ YouTube Demo Β |Β π Live Frontend Β |Β βοΈ Live Backend API
Velora finds optimal employee pickup/dropoff routes across a heterogeneous vehicle fleet, minimizing total transportation cost while respecting capacity, precedence, and soft time-window constraints. It combines a C++ 4-phase metaheuristic solver with a Node.js REST API and a React + Vite frontend.
VeloraMobilityOptimizer/
βββ solver/ # C++ optimizer binary
β βββ src/
β β βββ main.cpp # 4-phase metaheuristic solver
β β βββ map_distance.cpp # OSRM / Haversine distance client
β βββ include/
β β βββ map_distance.hpp
β β βββ json.hpp # nlohmann/json (header-only)
β βββ CMakeLists.txt
β βββ build/velora_solver # Compiled binary (gitignored)
β
βββ backend/ # Node.js REST API
β βββ src/
β β βββ app.js # Express setup, CORS, routes
β β βββ routes/
β β β βββ optimization.js # Validate β prepare β solve β post-process
β β βββ services/
β β β βββ solver.js # Spawns the C++ subprocess
β β β βββ excelParser.js # Bridges to Python Excel parser
β β βββ controllers/
β β β βββ optimizationController.js # Test-case runner
β β βββ middleware/
β β β βββ upload.js # Multer file upload
β β βββ utils/
β β βββ processRunner.js # Async subprocess helper
β βββ uploads/ # Temp uploaded files (gitignored)
β βββ outputs/ # Solver output files (gitignored)
β
βββ frontend/ # React + Vite SPA
β βββ src/
β β βββ App.jsx # Root component β tabs, state, orchestration
β β βββ api.js # Fetch wrappers for backend API
β β βββ components/
β β βββ RouteMap.jsx # Leaflet map + OSRM road geometry
β β βββ ResultsPanel.jsx # Cost analytics, constraint compliance
β β βββ EmployeeResults.jsx
β β βββ AddEmployeeModal.jsx
β β βββ SolverTimeForm.jsx
β β βββ DistanceModeForm.jsx
β β βββ PenaltyForm.jsx
β βββ .env.production # VITE_API_BASE for production build
β βββ vite.config.js
β
βββ parser/
β βββ excel_to_json.py # Excel β JSON input converter
β
βββ data/
β βββ json/ # Test case inputs + best-known outputs (tc01βtc05)
β βββ *.txt # Human-readable solution reports
β
βββ Dockerfile # Docker build: compiles solver + runs backend
βββ build.sh # Local build script (compile + start)
βββ render.yaml # Render.com deployment config
The solver is a 4-phase metaheuristic for HVRPSTW, implemented in C++ with a fixed random seed (42) for fully reproducible results.
Minimize: Ξ£ (distance_ij Γ costPerKm_v) Γ w_cost
+ Ξ£ (travelTime_route) Γ w_time
+ Penalty terms
Default weights:
w_cost = 0.7,w_time = 0.3
| Type | Constraint |
|---|---|
| Hard | Vehicle capacity; pickup before dropoff (precedence) |
| Soft | Time windows [earlyTime, lateTime] with priority-based tolerance; sharing limits; vehicle type preference |
Soft constraints are penalized rather than strictly enforced, allowing the optimizer to trade off constraint violations against route cost.
Up to 20 restarts of a greedy insertion heuristic. Each restart shuffles requests within priority buckets and inserts them one by one into the cheapest feasible position across all vehicles. The best result across all restarts is kept.
Deterministic SA with 5 move operators. Terminates by maxNoImprove count (not wall-clock), so results are identical every run with seed=42.
| Operator | Weight | Description |
|---|---|---|
| Greedy Relocate | 28% | Move a request to its cheapest route (all vehicles) |
| Intra-Route Relocate | 28% | Re-insert a request at its best position within the same route |
| Exchange | 18% | Swap requests between two routes |
| 2-Opt | 13% | Reverse a segment within a route |
| Or-Opt | 13% | Move a chain of 1β2 stops to another position |
SA parameters scale with problem complexity (avgRouteStops): initial temperature Tβ = min(0.3 Γ cost, 1000), cooling Ξ± = 0.99, adaptive reheating every maxNoImprove/3 stagnant steps.
Time-bounded destroy-repair loop using Ropke & Pisinger adaptive scoring (Οβ=33, Οβ=9, Οβ=3, Ξ»=0.8).
Destroy operators:
| Operator | Purpose |
|---|---|
| Shaw Removal | Remove geographically related requests (seed + nearest neighbors) |
| Random Removal | Uniform random selection for diversification |
| Route Removal | Evict the entire most-penalized route (plateau escape) |
Repair: Regret-2 insertion heuristic β inserts requests with the highest regret (2nd-best minus best insertion cost) first, prioritizing tightly-constrained requests.
Restarts SA from the ALNS best solution with a shorter maxNoImprove for fine-grained local improvement. Keeps whichever solution (ALNS vs. Phase 4) is better.
| Mode | Method | Speed |
|---|---|---|
osrm |
OSRM Table API β single HTTP call for full NΓN matrix | ~5β20s pre-compute, then O(1) lookups |
haversine |
Straight-line air distance | Instant |
All SA/ALNS distance lookups are O(1) reads from a pre-computed NΓN matrix.
- C++ compiler with C++17 support (GCC 10+ or Clang 12+)
- CMake 3.14+
- libcurl (for OSRM API calls)
- Node.js 18+
- Python 3.9+ (for Excel parsing only)
cd solver/build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4
# Binary: solver/build/velora_solverOr use the convenience script from the project root:
bash build.shcd backend
npm install
# Create backend/.env:
# PORT=3001
# FRONTEND_URL=http://localhost:5173
node src/app.js
# API available at http://localhost:3001cd frontend
npm install
npm run dev
# App available at http://localhost:5173The Vite dev server proxies
/apirequests tolocalhost:3001automatically.
Run the solver directly for debugging:
./solver/build/velora_solver input.json output.json [convergence.csv]Submit a JSON optimization request.
Request body:
{
"config": {
"distance_method": "osrm",
"solver_time_seconds": 30,
"force_assign": true,
"weights": { "cost": 0.7, "time": 0.3 },
"tolerances": { "1": 5, "2": 10, "3": 15, "4": 20, "5": 30 },
"penalty_weights": { "sharingViolationPenalty": 500 }
},
"vehicles": [
{
"vehicle_id": "V01",
"capacity": 4,
"costPerKm": 15,
"avg_speed_kmph": 30,
"startLoc": { "lat": 12.9716, "lon": 77.5946 },
"type": "4w",
"fuel_type": "petrol",
"category": "normal"
}
],
"requests": [
{
"employee_id": "E01",
"priority": 2,
"pickup": { "lat": 12.9352, "lon": 77.6245 },
"dropoff": { "lat": 12.9716, "lon": 77.5946 },
"earlyTime": 480,
"lateTime": 510,
"load": 1,
"vehiclePreference": "any",
"sharingLimit": 3
}
]
}Times are minutes from midnight (e.g.
480= 08:00,540= 09:00).
Response: { jobId, status: "success", result: { routes, unassigned, summary, constraintAnalysis } }
Upload an Excel file (.xlsx) β returns parsed JSON preview.
Returns solver binary status.
The project deploys to Render.com via render.yaml:
| Service | Type | Details |
|---|---|---|
| Backend | Docker runtime | Builds the C++ solver inside the container, then starts the Node.js API |
| Frontend | Static site | npm run build β serves frontend/dist/ |
All environment variables for production are configured in render.yaml.
Five pre-built test cases are in data/json/tc0X_input.json:
| TC | Employees | Vehicles |
|---|---|---|
| tc01 | 5 | 3 |
| tc02 | 8 | 3 |
| tc03 | 10 | 4 |
| tc04 | 15 | 5 |
Run a test case via the Test Cases tab in the UI, or directly:
./solver/build/velora_solver data/json/tc01_input.json out.json- Solomon, M.M. (1987). Algorithms for the Vehicle Routing and Scheduling Problems with Time Window Constraints. Operations Research, 35(2), 254β265.
- Ropke, S. & Pisinger, D. (2006). An Adaptive Large Neighborhood Search Heuristic for the Pickup and Delivery Problem with Time Windows. Transportation Science, 40(4), 455β472.