Pickup and Delivery (PDP)
What’s added
Customers come in paired nodes: for each pair (p, d), p is a pickup location, d is a delivery. Two new constraints on top of the base VRP:
- Precedence. Along any vehicle’s route,
pmust be visited before its matchingd. - Same vehicle.
panddmust be served by the same vehicle, no transfers between trucks.
Pair count can be larger than vehicle count; multiple pairs can share a vehicle.
The picture
Pairs (p → d) with demand 1 each: (P1 → D1) (P2 → D2) (P3 → D3) (P4 → D4)
A valid route: DEPOT → P1 → P2 → D1 → D2 → P3 → D3 → P4 → D4 → DEPOT └─ Vehicle holds {1,2} here ─┘ └─ holds {3} ─┘ └─ holds {4} ─┘
Invariant: at every moment, the vehicle holds only items whosepickups have been visited and whose deliveries have not.
Infeasible (delivery before pickup): DEPOT → D1 → P1 → ... → DEPOT ← impossibleIn many real-world cases (ride-sharing, freight forwarding) there’s also an implicit capacity limit: a vehicle holds at most Q items at a time. CVRP-style capacity + PDP precedence is the most common real-world combination.
OR-Tools sketch
OR-Tools exposes the two PDP constraints via dedicated API calls:
# For each (pickup_index, delivery_index) pair:for pickup_node, delivery_node in pickup_delivery_pairs: pickup_index = manager.NodeToIndex(pickup_node) delivery_index = manager.NodeToIndex(delivery_node)
# 1. Pair the two stops routing.AddPickupAndDelivery(pickup_index, delivery_index)
# 2. Same vehicle constraint routing.solver().Add( routing.VehicleVar(pickup_index) == routing.VehicleVar(delivery_index) )
# 3. Precedence, pickup cumul ≤ delivery cumul on the time (or distance) dimension distance_dim = routing.GetDimensionOrDie("Distance") routing.solver().Add( distance_dim.CumulVar(pickup_index) <= distance_dim.CumulVar(delivery_index) )The CumulVar comparison is how precedence is expressed: the cumulative dimension value (distance traveled so far, or elapsed time) at the pickup must be ≤ that at the delivery, which forces pickup-first ordering.
Capacity + PDP
Layering CVRP capacity on top of PDP is common. The pickup node has demand +q; the delivery node has demand -q. The capacity dimension’s cumul at any point along the route equals the current load, positive on pickups, negative on deliveries, never exceeding Q at any intermediate step.
def demand_callback(from_index): from_node = manager.IndexToNode(from_index) return demands[from_node] # + on pickups, - on deliveries
demand_idx = routing.RegisterUnaryTransitCallback(demand_callback)routing.AddDimensionWithVehicleCapacity( demand_idx, slack_max=0, vehicle_capacities=[Q] * num_vehicles, fix_start_cumul_to_zero=True, name="Capacity",)OR-Tools automatically enforces the capacity constraint at every node along every route, so the negative demand at a delivery actually frees up capacity, and subsequent pickups can use that freed capacity.
Gotchas
- Pair indices are node indices, not arcs. Pass the numeric node indices from your manager, not location names.
- Zero-demand pickups/deliveries are OK. If a task is “move one person” with implicit demand of 1, make the demand explicit and let the capacity dimension handle it.
- Precedence is per-dimension. Use the time dimension for temporal precedence, distance for spatial. Mixing signals produces subtle bugs.
- Dropped-pair semantics. If dropping a pickup is allowed, you must also drop its matching delivery, OR-Tools handles this automatically when you use
AddDisjunctionwith a penalty on both nodes (see the “dropped visits” pattern on the main variants page). - Multi-leg transfers (cross-docking, hub-and-spoke with transshipment) are not standard PDP. The one-vehicle constraint is essential; multi-leg problems need specialized formulations (PDP with transshipment, PDPT).
Applications
- Ride-sharing, passenger pickups and dropoffs, same vehicle constraint is physically enforced.
- Same-day delivery / parcel relay, where items can’t transfer between couriers.
- Freight forwarding, load cross-dock, but the intra-route leg must be one truck.
- Home care scheduling, patient pickup from home, drop at clinic, then back home.
References
- Pickup and Delivery, OR-Tools
- Dial-a-Ride problem (related)
- Savelsbergh & Sol (1995). “The General Pickup and Delivery Problem.” Transportation Science 29(1):17–29.