Reloading

Sometimes vehicles can execute multiple trips during their shift, by reloading at the depot when needed. This effectively mitigates the capacity constraint we have so far seen, because vehicles can always choose to return to the depot to reload if needed. PyVRP supports a very general form of reloading, with free depot selection from a list of alternatives. Optionally, the maximum number of reloads per vehicle type may also be restricted.

[1]:
import pyvrp
import pyvrp.plotting
import pyvrp.stop

We will investigate a simple example, with a single vehicle having a capacity of 15. Without reloading, the clients demand more than the vehicle can carry. By reloading at the depot, the vehicle can service all client demand in two trips.

[2]:
COORDS = [(456, 320), (312, 418), (114, 80), (570, 160), (684, 240)]
DEMANDS = [5, 8, 7, 6]
[3]:
m = pyvrp.Model()

depot = m.add_depot(x=COORDS[0][0], y=COORDS[0][1])
m.add_vehicle_type(
    1,
    capacity=15,
    reload_depots=[depot],  # where reloads may take place
    max_reloads=2,  # maximum number of reload depot visits on a route
)

for idx in range(1, len(COORDS)):
    m.add_client(
        x=COORDS[idx][0],
        y=COORDS[idx][1],
        delivery=DEMANDS[idx - 1],
    )

for frm in m.locations:
    for to in m.locations:
        distance = abs(frm.x - to.x) + abs(frm.y - to.y)
        m.add_edge(frm, to, distance=distance)

res = m.solve(stop=pyvrp.stop.MaxRuntime(1))
PyVRP v0.14.0a0

Solving an instance with:
    1 depot
    4 clients
    1 vehicle (1 vehicle type)

    Iters    Time |      Current OK    Candidate OK         Best OK

Search terminated in 1.02s after 38057 iterations.
Best-found solution has cost 2136.

Solution results
================
    # routes: 1
     # trips: 2
   # clients: 4
   objective: 2136
    distance: 2136
    duration: 0
# iterations: 38057
    run-time: 1.02 seconds

Returns to a reload depot are marked with a |, as follows:

[4]:
print(res.best)
Route #1: 4 3 | 1 2

Let’s investigate the route in more detail:

[5]:
route = res.best.routes()[0]

for idx, trip in enumerate(route.trips()):
    print(f"- Trip #{idx + 1}:")
    print(f"   | Client visits: {trip.visits()}.")
    print(f"   | Distance: {trip.distance()}.")
    print(f"   | Load: {trip.delivery()}.")
- Trip #1:
   | Client visits: [4, 3].
   | Distance: 776.
   | Load: [13].
- Trip #2:
   | Client visits: [1, 2].
   | Distance: 1360.
   | Load: [13].

A plot reveals the routing and reloading decisions that PyVRP determined:

[6]:
pyvrp.plotting.plot_solution(res.best, m.data(), plot_clients=True)
../_images/notebooks_reloading_10_0.png

Conclusion

You now know how to use PyVRP’s reloading feature. PyVRP’s support for reloading is very general, and is useful in many settings, like urban last-mile (return to the warehouse to pick up new goods), waste collection (to empty a full truck at a landfill or recycling centre), and more.