Heterogeneous Fleet¶
TQrouting supports heterogeneous fleets — you can define multiple vehicle types with different capacities, constraints, depot locations, and fleet sizes. The solver assigns customers to the best vehicle type automatically.
Different Vehicle Types¶
Define vehicle types with varying capabilities:
from tqrouting import TQRouter, VRPInstance, Customer, VehicleType, Location
router = TQRouter()
# Two depots: the trucks' warehouse (matrix_id 0) in the city centre and the
# vans' hub (matrix_id 1) in a suburb half an hour away. Durations in seconds.
instance = VRPInstance(
customers=[
Customer(location=Location(matrix_id=2), delivery_load=50), # near the warehouse
Customer(location=Location(matrix_id=3), delivery_load=15), # near the warehouse
Customer(location=Location(matrix_id=4), delivery_load=8), # near the van hub
Customer(location=Location(matrix_id=5), delivery_load=40), # near the van hub
],
fleet=[
VehicleType(
id="large-truck",
start_location=Location(matrix_id=0),
end_location=Location(matrix_id=0),
capacity_load=200,
max_duration="10:00:00",
n_vehicles=2,
),
VehicleType(
id="small-van",
start_location=Location(matrix_id=1),
end_location=Location(matrix_id=1),
capacity_load=50,
max_duration="06:00:00",
n_vehicles=4,
),
],
duration_matrix=[
[0, 1800, 600, 900, 2400, 2700],
[1800, 0, 1500, 1200, 720, 480],
[600, 1500, 0, 300, 2100, 2400],
[900, 1200, 300, 0, 1800, 2100],
[2400, 720, 2100, 1800, 0, 360],
[2700, 480, 2400, 2100, 360, 0],
],
)
solution = router.solve(instance, time_limit=2)
for route in solution.routes:
customers = [v.customer_id for v in route.visits]
print(f"Route {route.id} ({route.vehicle_type_id}): customers {customers}, "
f"load {route.route_delivery_load:g}, {route.route_duration / 60:.0f} min")
Output:
Route 0 (large-truck): customers [0, 1], load 65, 30 min
Route 1 (small-van): customers [3, 2], load 48, 26 min
The two customers near the warehouse go on a truck; the two near the suburban hub fit into one van (48 of 50 units), which is 40 minutes closer to them than any truck. Nothing forces this split — the solver picks it because it is the cheapest total.
Each vehicle type can have its own:
capacity_load(andcapacity_volumefor 2-D) — different load limitsfixed_vehicle_cost— different per-vehicle costs (drives fleet minimization)max_duration— different shift lengthsmax_distance— different range limitsshift_start/shift_end— different operating hoursn_vehicles— different fleet sizesstart_location/end_location— different depots
Minimizing fleet size¶
Set fixed_vehicle_cost on a vehicle type to charge a fixed cost each time one of its vehicles is used. The solver then balances that cost against routing cost, preferring to serve all customers with fewer vehicles when the saved fixed cost outweighs the extra travel:
With mixed types, giving each type a different fixed_vehicle_cost lets the solver choose the cheaper fleet composition.
Multiple Depots¶
Assign different vehicle types to different starting locations. The solver decides which depot should serve which customers:
instance = VRPInstance(
customers=[
Customer(location=Location(matrix_id=2), delivery_load=10),
Customer(location=Location(matrix_id=3), delivery_load=8),
Customer(location=Location(matrix_id=4), delivery_load=15),
Customer(location=Location(matrix_id=5), delivery_load=12),
],
fleet=[
VehicleType(
id="depot-north",
start_location=Location(matrix_id=0),
end_location=Location(matrix_id=0),
capacity_load=30,
n_vehicles=2,
),
VehicleType(
id="depot-south",
start_location=Location(matrix_id=1),
end_location=Location(matrix_id=1),
capacity_load=30,
n_vehicles=2,
),
],
distance_matrix=[
[0, 50, 10, 15, 40, 45],
[50, 0, 45, 40, 12, 8],
[10, 45, 0, 5, 35, 40],
[15, 40, 5, 0, 30, 35],
[40, 12, 35, 30, 0, 6],
[45, 8, 40, 35, 6, 0],
],
)
solution = router.solve(instance)
for route in solution.routes:
print(f"Route {route.id} (depot: {route.vehicle_type_id}):")
customers = [v.customer_id for v in route.visits]
print(f" Customers: {customers}")
Open Routes (Different Start and End)¶
Vehicles can end at a different location than where they started:
VehicleType(
start_location=Location(matrix_id=0), # Starts at warehouse
end_location=Location(matrix_id=5), # Ends at parking lot
n_vehicles=3,
)
One-Way Routes (No Return)¶
If you don't specify end_location, vehicles finish at their last customer — no return trip is added. This models scenarios like one-way deliveries or field workers who go home after their last stop.
VehicleType(
start_location=Location(matrix_id=0),
capacity_load=30,
n_vehicles=5,
# No end_location → route ends at last customer
)
Info
When end_location is not set, the solver does not add return travel to the route duration or distance.
Fleet Size¶
n_vehicles sets the maximum number of vehicles of that type. The solver uses as few vehicles as needed to serve all customers — it won't create empty routes.
fleet=[
VehicleType(
start_location=Location(matrix_id=0),
capacity_load=30,
n_vehicles=10, # Up to 10 available, solver uses only what's needed
),
]
Interactive Example
See the constraints notebook for runnable multi-depot and heterogeneous fleet examples.