Defining a Problem¶
A routing problem in TQrouting consists of customers to visit, a fleet of vehicles, and distance/duration matrices describing travel costs between locations.
VRPInstance¶
VRPInstance is the main container for a complete problem definition.
from tqrouting import VRPInstance, Customer, VehicleType, Location
instance = VRPInstance(
customers=[...], # List of customers to serve
fleet=[...], # List of vehicle types available
duration_matrix=[...], # Travel times between locations
distance_matrix=[...], # Travel distances between locations
)
Distance vs. Duration Matrix
You must provide at least one of these two matrices. The solver always optimizes travel duration (the duration_matrix drives the objective function and all time-related scheduling). If you only provide distance_matrix, the solver treats it as the duration matrix as well. The max_distance constraint is measured on the distance_matrix — the only place the distance matrix is used on its own. Provide both matrices when your distances and durations differ.
Location¶
Every customer and vehicle depot references a location. You can either provide a precomputed distance/duration matrix and reference locations by their matrix index (matrix_id), or provide coordinates and let TQrouting compute the matrices for you.
TQrouting supports two types of coordinates: Cartesian coordinates (x, y) for use with Euclidean distances, and geographic coordinates (lat, lon) for routing within street networks. When geographic coordinates are used, you can use a matrix provider (OSRM or Google Maps) to compute road-network distances and travel times.
x/y coordinates are all-or-none: once one location has them, every location — customers, start and end locations — needs them as well, and a partial set raises a ValidationError naming the location without them. A location may carry a matrix_id and coordinates at the same time.
Option 1: Matrix Index (Direct)¶
Assign each location a position in your distance/duration matrix:
Location(matrix_id=0) # Row/column 0 in the matrix
Location(matrix_id=5) # Row/column 5 in the matrix
Option 2: Cartesian Coordinates¶
For problems common in academic benchmarks (Euclidean distances), synthetic instances, or intra-logistics scenarios (custom distance and duration matrices). Use with EuclideanMatrixProvider or custom distance and duration matrices.
Option 3: Geographic Coordinates¶
For routing within street networks. Use with a matrix provider (OSRM, Google Maps):
Location(lat=48.137, lon=11.575) # Munich city center
Location(lat=48.265, lon=11.671) # Munich airport
Customer¶
A Customer defines a stop that must (or may, in prize-collecting problems) be served. Each customer has one required attribute (location) and several optional attributes.
Customer Attributes¶
| Field | Type | Required | Description |
|---|---|---|---|
location |
Location |
Yes | Customer location |
id |
str or int |
No | Your custom identifier (order number, database ID, etc.) |
delivery_load |
float |
No | Units to deliver to this customer (weight dimension) |
pickup_load |
float |
No | Units to pick up from this customer (weight dimension) |
delivery_volume |
float |
No | Second capacity dimension (volume) to deliver — see 2-D capacity |
pickup_volume |
float |
No | Second capacity dimension (volume) to pick up |
service_time |
float, int, or str |
No | Time spent serving this customer |
time_window |
TimeWindow |
No | Allowed visit window |
profit |
float |
No | Reward for visiting (for prize-collecting problems) |
Attribute Details:
-
locationis required and specifies where the customer is located. See the Location section above for the supported formats. -
idis an optional custom identifier for this customer. If you setidon any customer, you must set it on all customers. IDs must be unique within customers and can be strings or integers. After solving, the solution uses your custom IDs (e.g.,visit.customer_idreturns"ORD-001"). -
delivery_loadrepresents the amount of goods to be delivered to this customer. If provided, it activates the Capacitated VRP: the total delivery load on a route must not exceed the vehicle'scapacity_load. A vehicle type withoutcapacity_loadis covered in Two-dimensional capacity. The value must be a non-negative float, measured in the same unit as the vehiclecapacity_load(e.g., kilograms). Unless the problem has a prize-collecting character, each customer'sdelivery_loadshould not exceed thecapacity_loadof the largest vehicle type. -
pickup_loadrepresents the amount of goods to be picked up from this customer. If at least one customer has apickup_loadgreater than zero, the Pickup and Delivery mode is automatically activated. In this mode, a vehicle starts its route fully loaded with all goods to be delivered. As it visits delivery customers, the onboard load decreases; at pickup customers, the onboard load increases. The capacity constraint ensures that the peak onboard load at any point along the route does not exceed the vehicle'scapacity_load. A customer may have bothdelivery_loadandpickup_load— in this case, the delivery is performed first, then the pickup. -
delivery_volume/pickup_volumecarry a second, independent quantity (typically volume) alongside the weight indelivery_load/pickup_load, constrained by the vehicle'scapacity_volume. See Two-dimensional capacity. -
service_timeis the time spent servicing this customer (e.g., unloading goods, walking from parking to delivery point, obtaining a signature, etc.). The time unit must be consistent with other time-related parameters (time_window,max_duration,shift_start,shift_end). Accepts seconds as a number or a string inHH:MM:SSformat. -
time_windowconstrains when service can begin at this customer. If a vehicle arrives before the window opens, it waits. If notime_windowis specified, the customer can be served at any time. See Time Windows for more details. -
profitactivates the prize-collecting mode when provided. The solver will select which customers to visit to maximize total collected profit. Profits must be non-negative values. Since the mode covers the whole instance, setprofiton every customer or on none — a mix raises aValidationError. See Prize-collecting for more details.
Example¶
from tqrouting import Customer, Location, TimeWindow
customer = Customer(
location=Location(matrix_id=1),
id="ORD-001",
delivery_load=15.0,
pickup_load=5.0,
service_time="00:05:00", # 5 minutes
time_window=TimeWindow(
start="08:00:00", # 8:00 AM
end="17:00:00", # 5:00 PM
),
profit=100.0,
)
VehicleType¶
A VehicleType defines a type of vehicle in your fleet. You can have multiple vehicle types with different capabilities.
VehicleType Attributes¶
| Field | Type | Required | Description |
|---|---|---|---|
id |
str or int |
No | Your custom identifier |
start_location |
Location |
Yes | Depot / starting point |
end_location |
Location |
No | Return point. If omitted, routes end at the last customer (open-ended). |
capacity_load |
float |
No | Maximum load the vehicle can carry (weight dimension) |
capacity_volume |
float |
No | Second capacity dimension (volume) — see 2-D capacity |
reload |
float, int, or str |
No | Allow mid-route depot reloads (multi-trip); the value is the turnaround time per reload |
fixed_vehicle_cost |
float |
No | Cost added to the objective per used vehicle of this type (fleet minimization) |
n_vehicles |
int |
No | Number of available vehicles of this type |
max_duration |
float, int, or str |
No | Maximum route duration |
max_distance |
float |
No | Maximum route distance |
shift_start |
float, int, or str |
No | Earliest time the vehicle can depart |
shift_end |
float, int, or str |
No | Latest time the vehicle must return |
Attribute Details:
-
idis an optional custom identifier. If you setidon any vehicle type, you must set it on all vehicle types. IDs must be unique within vehicle types and can be strings or integers. -
start_locationis required and specifies where vehicles of this type begin their routes. It follows the same format as customer locations (see Location). -
end_locationspecifies where vehicles return after completing their route. If omitted, routes are open-ended — vehicles finish at their last customer and no return travel is added to the route duration or distance. Ifend_locationis the same asstart_location, vehicles return to their depot (the most common case). -
capacity_loaddefines the maximum load the vehicle can carry. When set, the totaldelivery_loadof all customers on a route must not exceed thecapacity_load(or, in pickup-and-delivery mode, the peak onboard load must not exceed it). Should be measured in the same unit as customers'delivery_load/pickup_load. See Two-dimensional capacity for vehicle types without it. -
capacity_volumeis the vehicle's limit for the second quantity (volume). See Two-dimensional capacity. -
reloadallows vehicles of this type to return to theirstart_locationmid-route, reload (the carried load resets), and continue — one vehicle running several trips in a single route. The value is the turnaround time spent at the depot per reload stop (0= instant); accepts seconds or anHH:MM:SSstring. Requires a capacity. See Multi-Trip Routes. -
fixed_vehicle_costadds a fixed cost to the objective for each used vehicle of this type, in the same units as the routing objective. It lets the solver trade extra travel against using fewer vehicles (fleet minimization). Vehicles that stay unused cost nothing. Defaults to0(off). -
n_vehicleslimits how many vehicles of this type are available. The solver uses only as many vehicles as needed — it won't create empty routes. If not specified, the number of vehicles is unlimited. -
max_durationcaps the total time of each route of this vehicle type. Route duration includes driving time, service time, and waiting time. It must use the same time unit as other time-related parameters. Accepts seconds as a number or a string inHH:MM:SSformat. -
max_distancecaps the total travel distance of each route (sum of travel distances between consecutive stops, including from/to depot). Specified in the same unit as the distance matrix (e.g., kilometers). Does not accept time strings. -
shift_startdefines the earliest time vehicles of this type can depart. No vehicle of this type can start its route before this time. -
shift_enddefines the latest time vehicles of this type must return. No vehicle of this type can finish its route after this time.
Two-dimensional capacity¶
Demand and capacity can cover two independent quantities — for example weight and volume. Use the load fields for the first (delivery_load / pickup_load on customers, capacity_load on vehicles) and the volume fields for the second (delivery_volume / pickup_volume, capacity_volume). You can constrain just one of them or both.
An instance is two-dimensional when both quantities appear in it — in customer demands or in vehicle capacities. Then every vehicle type must set the capacity of each quantity your customers carry; leaving one out raises a ValidationError. With a single quantity, a vehicle type without its capacity carries unlimited load, and the SDK warns about it. Each quantity is enforced independently — a route is feasible only if it stays within every capacity you set.
# 2-D: each vehicle limited to 1000 kg AND 14 m³
Customer(location=Location(matrix_id=1), delivery_load=120, delivery_volume=1.8)
VehicleType(start_location=Location(matrix_id=0), capacity_load=1000, capacity_volume=14)
In pickup-and-delivery mode the peak-onboard-load rule is applied per dimension. See Capacity Constraints.
Example¶
from tqrouting import VehicleType, Location
vehicle = VehicleType(
start_location=Location(matrix_id=0),
id="truck-A",
end_location=Location(matrix_id=0),
capacity_load=1000,
n_vehicles=5,
max_duration="08:00:00", # 8 hours
max_distance=50000, # 50 km
shift_start="07:00:00", # 7:00 AM
shift_end="18:00:00", # 6:00 PM
)
Time Windows¶
Time windows constrain when a customer can be served:
from tqrouting import TimeWindow
tw = TimeWindow(start="08:00:00", end="12:00:00") # 8:00 AM to 12:00 PM
Time Format¶
Every time field takes either a number or a string in HH:MM:SS format, and one instance uses a single format throughout: all time fields as numbers, or all of them as strings. Mixing the two is a ValidationError that names both fields.
A string is a count of seconds written as hours:minutes:seconds. Hours are not limited to 24, so "30:00:00" is 108000 seconds, and durations use the same notation as instants. Because a string always means seconds, use strings only when your duration matrix is in seconds; with a matrix in any other unit, give times as numbers in that unit.
from tqrouting import Customer, VehicleType, Location, TimeWindow
# The same constraints in the two formats (one instance uses one of them):
customer_numeric = Customer(
location=Location(matrix_id=1),
service_time=900, # 900 seconds
time_window=TimeWindow(start=28800, end=61200), # 8:00–17:00
)
customer_strings = Customer(
location=Location(matrix_id=1),
service_time="00:15:00", # 15 minutes → 900 s
time_window=TimeWindow(start="08:00:00", end="17:00:00"),
)
# Works for vehicles too:
vehicle = VehicleType(
start_location=Location(matrix_id=0),
shift_start="07:30:00", # → 27000 seconds
shift_end="19:00:00", # → 68400 seconds
max_duration="10:00:00", # → 36000 seconds (10 hours)
)
The following fields accept time strings:
TimeWindow.start,TimeWindow.endCustomer.service_timeVehicleType.shift_start,VehicleType.shift_endVehicleType.max_durationVehicleType.reload
The solution is written in the format the instance used — see Time Formatting.
Custom IDs¶
By default, TQrouting uses 0-based positional indices for customers and vehicles. You can assign your own IDs to match your domain data:
instance = VRPInstance(
customers=[
Customer(id="ORD-001", location=Location(matrix_id=1), delivery_load=9),
Customer(id="ORD-002", location=Location(matrix_id=2), delivery_load=4),
],
fleet=[
VehicleType(id="truck-A", start_location=Location(matrix_id=0), capacity_load=20, n_vehicles=3),
VehicleType(id="van-B", start_location=Location(matrix_id=0), capacity_load=10, n_vehicles=2),
],
distance_matrix=[[0, 2, 5], [2, 0, 7], [5, 7, 0]],
)
solution = router.solve(instance)
for route in solution.routes:
print(f"Vehicle: {route.vehicle_type_id}") # "truck-A" or "van-B"
for visit in route.visits:
print(f" → {visit.customer_id}") # "ORD-001" or "ORD-002"
Rules for custom IDs:
- If you set
idon any customer, you must set it on all customers (same rule for vehicle types) - IDs must be unique within customers and within vehicle types
- IDs can be strings or integers
- Customers and vehicle types are separate namespaces, so the same ID may appear in both
- Breaking either rule raises a
ValidationErrornaming the entry — see Error Handling
Serialization¶
Convert between VRPInstance and dicts/JSON files:
# VRPInstance → dict
problem_dict = instance.to_dict()
# dict → VRPInstance
instance = VRPInstance.from_dict(problem_dict)
# VRPInstance → JSON file
instance.to_json("problem.json")
# JSON file → VRPInstance
instance = VRPInstance.from_json("problem.json")
The dict/JSON format uses the same field names as the Python classes (customers, fleet, duration_matrix, distance_matrix), so what you see in code is exactly what you get in JSON.
Unknown keys are an error: from_dict() / from_json() (and validate_input()) raise ValidationError naming the field and suggesting the closest valid name:
Keep your own metadata outside the instance document.
Next Steps¶
- Distance & Duration Matrices — how to provide or compute matrices
- Solver Configuration — control solver behavior
- Features — explore all constraint types