Using OR-tools I am trying to model a multi pickup per delivery problem with disjunctions where delivery can be fulfiled only when all pickups were viseted prior to delivery arrival and can't get the solver to find partial solutions. In the toy example below the solver returns an empty solution while it can fulfil the first 2 pickups and its delivery within its given MAX_ROUTE_TIME limit. Am I setting the multi pickup per delivery correctly?
I tried without success the following approaches:
import numpy as np
from ortools.constraint_solver import routing_enums_pb2, pywrapcp
manager = pywrapcp.RoutingIndexManager(7, 1, 0)
routing = pywrapcp.RoutingModel(manager)
dim_name = 'Time'
durations = np.array(
[[ 0, 1, 1, 1, 100, 100, 100],
[ 1, 0, 1, 1, 100, 100, 100],
[ 1, 1, 0, 1, 100, 100, 100],
[ 1, 1, 1, 0, 100, 100, 100],
[100, 100, 100, 100, 0, 100, 100],
[100, 100, 100, 100, 100, 0, 100],
[100, 100, 100, 100, 100, 100, 0]])
def duration_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return durations[from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(duration_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
MAX_ROUTE_TIME = 400
routing.AddDimension(transit_callback_index, 0, MAX_ROUTE_TIME, True, dim_name)
time_dimension = routing.GetDimensionOrDie(dim_name)
pickups_deliveries = [
(1, 3),
(2, 3),
(4, 6),
(5, 6)
]
for pickup, delivery in pickups_deliveries:
pickup_index = manager.NodeToIndex(pickup)
delivery_index = manager.NodeToIndex(delivery)
routing.AddPickupAndDelivery(pickup_index, delivery_index)
routing.solver().Add(routing.VehicleVar(pickup_index) == routing.VehicleVar(delivery_index))
routing.solver().Add(time_dimension.CumulVar(pickup_index) <= time_dimension.CumulVar(delivery_index))
for node in range(1, 7):
routing.AddDisjunction([manager.NodeToIndex(node)], 10000000)
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.AUTOMATIC
search_parameters.local_search_metaheuristic = routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
search_parameters.lns_time_limit.seconds = 2
search_parameters.time_limit.seconds = 5
solution = routing.SolveWithParameters(search_parameters)
vehicle_id = 0
index = routing.Start(vehicle_id)
node = manager.IndexToNode(index)
while not routing.IsEnd(index):
previous_node = node
previous_index = index
index = solution.Value(routing.NextVar(index))
node = manager.IndexToNode(index)
print(previous_node, node ,durations[previous_node, node])
I would like to drop the whole delivery in such cases
In this case, instead of using one disjunction per node you can use one disjunction per set of node [1,2,3]
or [4,5,6]
.
routing.AddDisjunction([manager.NodeToIndex(i) for i in (1,2,3,7)], 10000000, 4)
routing.AddDisjunction([manager.NodeToIndex(i) for i in (4,5,6,8)], 10000000, 4)
#for node in range(1, 9):
# routing.AddDisjunction([manager.NodeToIndex(node)], 10000000)
possible output:
[127]─[~/work/tmp/issue]
[>_<]─mizux@nuc10i7 %./so_2020_09_06_2.py
objective: 10000004
Dropped nodes: 4 5 6 8
0 -> 2 (1)
2 -> 1 (1)
1 -> 7 (1)
7 -> 3 (0)
3 -> 0 (1)
[0]─[~/work/tmp/issue]
[^v^]─mizux@nuc10i7 %