Capacity Constraints¶
Each vehicle has a limited capacity, and customer demands must not exceed it.
How It Works¶
- Each customer has a
delivery_loadrepresenting its demand - Each vehicle type has a
capacity_loadrepresenting the maximum load it can carry - The solver ensures that the total delivery load on any route does not exceed its vehicle's capacity
Example¶
from tqrouting import TQRouter, VRPInstance, Customer, VehicleType, Location
router = TQRouter()
instance = VRPInstance(
customers=[
Customer(location=Location(matrix_id=1), delivery_load=15),
Customer(location=Location(matrix_id=2), delivery_load=12),
Customer(location=Location(matrix_id=3), delivery_load=8),
Customer(location=Location(matrix_id=4), delivery_load=20),
],
fleet=[
VehicleType(
start_location=Location(matrix_id=0),
end_location=Location(matrix_id=0),
capacity_load=30,
n_vehicles=3,
),
],
distance_matrix=[
[0, 5, 8, 12, 6],
[5, 0, 4, 10, 9],
[8, 4, 0, 7, 11],
[12, 10, 7, 0, 3],
[6, 9, 11, 3, 0],
],
)
solution = router.solve(instance)
for route in solution.routes:
print(f"Route {route.id}: delivery load = {route.route_delivery_load} / 30")
Note
Unless the problem has a prize-collecting character, each customer's delivery_load should not exceed the capacity_load of the largest vehicle type.
Two-Dimensional Capacity (weight + volume)¶
Capacity can constrain two independent dimensions at once — typically weight and volume. Add the *_volume fields:
- customers:
delivery_volume/pickup_volume - vehicle types:
capacity_volume
instance = VRPInstance(
customers=[
Customer(location=Location(matrix_id=1), delivery_load=15, delivery_volume=1.2),
Customer(location=Location(matrix_id=2), delivery_load=12, delivery_volume=2.0),
],
fleet=[
VehicleType(
start_location=Location(matrix_id=0),
end_location=Location(matrix_id=0),
capacity_load=30, # weight limit
capacity_volume=4.0, # volume limit
n_vehicles=3,
),
],
distance_matrix=...,
)
You can constrain just weight, just volume, or both — set the fields you need. When both quantities appear in the instance — in customer demands or in vehicle capacities — every vehicle type must declare the capacity of each quantity your customers carry; omitting one raises a ValidationError. With a single quantity, a vehicle type without its capacity carries unlimited load, and the SDK warns about it. The two limits are enforced independently.
When you use both, the output loads (route_delivery_load, route_peak_load, total_delivery_load, …) are returned as [weight, volume] lists, and capacity constraint violations carry a dimension field (0 = weight, 1 = volume).
Pickup & Delivery¶
If customers also have pickup demands, the capacity model changes — see Pickup & Delivery for details.
Tuning¶
If the solver struggles to find feasible solutions with tight capacity constraints, you can increase the capacity penalty weights — each dimension has its own:
Different Rules per Dimension¶
Weight and volume are fully independent: each has its own penalty, and either can be soft while the other stays hard. A common setup — weight is a legal limit (hard), volume is a packing preference (soft, priced per m³ of overflow):
solution = router.solve(
instance,
soft_constraints=["capacity_volume"], # volume may overflow, at a price
penalty_capacity_volume=200.0, # objective units per unit of overflow
)
for v in solution.constraint_violations:
print(f"{v.constraint} (dimension {v.dimension}): excess {v.excess_value}")
Weight violations still make a solution infeasible; volume violations are priced into the objective and reported in constraint_violations.
Interactive Example
See the quickstart notebook for a runnable CVRP example with visualization, and the constraints notebook for 2-D capacity.