Skip to content

Pickup & Delivery

TQrouting supports mixed pickup and delivery operations on the same route. Vehicles can deliver goods to some customers and pick up goods from others — or do both at the same customer.


How It Works

The Pickup and Delivery mode is automatically activated when at least one customer has pickup_load greater than zero. If all customers only have delivery_load (and no pickup_load), the problem is treated as a standard Capacitated VRP.

In the pickup and delivery model:

  1. A vehicle starts its route fully loaded with all goods to be delivered along that route
  2. At delivery customers, the onboard load decreases
  3. At pickup customers, the vehicle picks up goods and the onboard load increases
  4. A customer may have both delivery_load and pickup_load — the delivery is performed first, then the pickup
  5. The capacity constraint ensures that the peak onboard load at any point along the route does not exceed the vehicle's capacity_load

Example

from tqrouting import TQRouter, VRPInstance, Customer, VehicleType, Location

router = TQRouter()

instance = VRPInstance(
    customers=[
        Customer(location=Location(matrix_id=1), delivery_load=8),
        Customer(location=Location(matrix_id=2), pickup_load=5),
        Customer(location=Location(matrix_id=3), delivery_load=6),
        Customer(location=Location(matrix_id=4), delivery_load=3, pickup_load=4),
        Customer(location=Location(matrix_id=5), delivery_load=7),
    ],
    fleet=[
        VehicleType(
            start_location=Location(matrix_id=0),
            end_location=Location(matrix_id=0),
            capacity_load=25,
            n_vehicles=3,
        ),
    ],
    distance_matrix=[
        [0, 2, 5, 8, 4, 1],
        [2, 0, 7, 6, 3, 5],
        [5, 7, 0, 4, 3, 6],
        [8, 6, 4, 0, 2, 7],
        [4, 3, 3, 2, 0, 5],
        [1, 5, 6, 7, 5, 0],
    ],
)

solution = router.solve(instance)

for route in solution.routes:
    print(f"Route {route.id}: delivery load = {route.route_delivery_load}")
    for visit in route.visits:
        print(f"  Customer {visit.customer_id}")

Peak Onboard Load

The key difference from standard capacity constraints is that the solver tracks the peak onboard load rather than the simple sum of demands.

Consider a vehicle with capacity_load=20 visiting two customers:

  • Customer A: delivery_load=15 (vehicle delivers 15 units)
  • Customer B: pickup_load=10 (vehicle picks up 10 units)

The vehicle starts with 15 units (to deliver to Customer A). After delivering to A, the load drops to 0. After picking up at B, the load rises to 10. The peak load is 15, which is within capacity.

If the order were reversed (pickup first, then deliver), the vehicle would start with 15 (for delivery) + 0 (no pickup goods yet), pick up 10 at B → peak is 25, exceeding capacity. The solver automatically finds the best order.


Mixed Load Customers

A single customer can have both delivery and pickup operations:

Customer(
    location=Location(matrix_id=1),
    delivery_load=10,    # Deliver 10 units
    pickup_load=5,       # Then pick up 5 units
)

At this customer, the delivery is performed first (onboard load decreases by 10), then the pickup (onboard load increases by 5). The net change in onboard load is pickup_load - delivery_load = -5.


Reading Results

When pickup and delivery loads are used, the solution includes separate load breakdowns:

Field Description
route.route_delivery_load Delivery load on this route
route.route_pickup_load Pickup load on this route
route.route_peak_load Maximum vehicle load at any point during the route
solution.total_delivery_load Total delivery load across all routes
solution.total_pickup_load Total pickup load across all routes

Note

Unless the problem has a prize-collecting character, each customer's delivery_load or pickup_load should not exceed the capacity_load of the largest vehicle type.

Volume as a second dimension

Pickup and delivery work in two dimensions too. Add delivery_volume / pickup_volume on customers and capacity_volume on vehicles, and the peak-onboard-load rule is enforced independently per dimension. The load fields above are then returned as [weight, volume] lists. See Two-dimensional capacity.


Interactive Example

See the constraints notebook for a runnable pickup-and-delivery example.