Previously my model has a disjunction function which is useful to allow dropping nodes and partial solution if any constraints are not met.
def allow_dropping(routing,data,manager):
# Allow to drop nodes.
global penalty
penalty = 1000000
for node in range(1, len(data.locations)-1):
routing.AddDisjunction([manager.NodeToIndex(node)], penalty)
Recently, I have added an exra constraint by limiting the types of vehicle visiting certain nodes (suggested approach is presented here: #2718) and the actual full codes implemented in my model is below:
def genTruckSubList(data):
global truck_all
global truck_4M2
global truck_5M2
global truck_6M8
global truck_7M6
global truck_9M6
global truck_13M
truck_all = []
truck_4M2 = []
truck_5M2 = []
truck_6M8 = []
truck_7M6 = []
truck_9M6 = []
truck_13M = []
global dist_all
global dist_4M2
global dist_5M2
global dist_6M8
global dist_7M6
global dist_9M6
global dist_13M
dist_all = []
dist_4M2 = []
dist_5M2 = []
dist_6M8 = []
dist_7M6 = []
dist_9M6 = []
dist_13M = []
for index, capacity in enumerate(capacity_vector()):
truck_all.append(index)
if capacity <= CAP_4M2:
#generate list of nodes that can be visited by 4.2meter truck
truck_4M2.append(index)
#generate the max driving distance for 4.2meter truck
dist_4M2.append(180)
if capacity > CAP_4M2 and capacity <= CAP_5M2:
truck_5M2.append(index)
dist_5M2.append(180)
if capacity > CAP_5M2 and capacity <= CAP_6M8:
truck_6M8.append(index)
dist_6M8.append(220)
if capacity > CAP_6M8 and capacity <= CAP_7M6:
truck_7M6.append(index)
dist_7M6.append(250)
if capacity > CAP_7M6 and capacity <= CAP_9M6:
truck_9M6.append(index)
dist_9M6.append(250)
if capacity > CAP_9M6 and capacity <= CAP_13M:
truck_13M.append(index)
dist_13M.append(220)
dist_all = dist_4M2+dist_5M2+dist_6M8+dist_7M6+dist_9M6+dist_13M
def limitMaxLength(data,manager,routing):
if "cargo_max_length" in data.df.columns and len(data.df["cargo_max_length"].tolist()) > 0:
for i, row in data.df.iterrows():
model_id = mapID_reverse(data.map, row["ID"])
index = manager.NodeToIndex(model_id)
length = row["cargo_max_length"]
if length < 4.2:
routing.VehicleVar(index).SetValues(truck_all)
continue
if length >= 4.2 and length < 5.2:
routing.VehicleVar(index).SetValues(truck_5M2+truck_6M8+truck_7M6+truck_9M6+truck_13M)
continue
if length >= 5.2 and length < 6.8:
routing.VehicleVar(index).SetValues(truck_6M8+truck_7M6+truck_9M6+truck_13M)
continue
if length >= 6.8 and length < 7.6:
routing.VehicleVar(index).SetValues(truck_7M6+truck_9M6+truck_13M)
continue
if length >= 7.6 and length < 9.6:
routing.VehicleVar(index).SetValues(truck_9M6+truck_13M)
continue
if length >= 9.6 and length < 13:
routing.VehicleVar(index).SetValues(truck_13M)
continue
if length >= 13:
routing.VehicleVar(index).SetValues([-1])
continue
The model main function settings are as below:
def main():
"""Entry point of the program"""
# Instantiate the data problem.
data = DataProblem()
# Create Routing Model
manager = pywrapcp.RoutingIndexManager(data.num_locations, data.num_vehicles, data.starts, data.ends)
# manager = pywrapcp.RoutingIndexManager(data.num_locations, data.num_vehicles, data.depot)
routing = pywrapcp.RoutingModel(manager)
# Add Max Length Limit constraint
genTruckSubList(data)
limitMaxLength(data,manager,routing)
# Add Distance constraint
dist_evaluator = CreateDistanceEvaluator(data,manager)
distance_callback_index = routing.RegisterTransitCallback(dist_evaluator.distance_evaluator)
dist_evaluator.define_total_cost(data,routing,manager)
add_distance_dimension(routing, distance_callback_index, data, manager)
# Add Capacity constraint
demand_evaluator = CreateDemandEvaluator(data, manager).demand_evaluator
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_evaluator)
add_capacity_constraints(routing, data, demand_callback_index, manager)
# Add Cubic Meters constraint
cm3_evaluator = CreateCM3Evaluator(data, manager).cm3_evaluator
cm3_callback_index = routing.RegisterUnaryTransitCallback(cm3_evaluator)
add_cm3_constraints(routing, data, cm3_callback_index)
# Add Time Window constraint
time_evaluator = CreateTimeEvaluator(data, manager).time_evaluator
time_callback_index = routing.RegisterTransitCallback(time_evaluator)
add_time_window_constraints(routing, data, time_callback_index, manager)
# Allow dropping nodes if solution infeasible
allow_dropping(routing, data, manager)
I found if the constaint function "limitMaxLength(data,manager,routing)" is in place, the model will not find a solution, even the allow_dropping() is there. However, if I try to comment out the "limitMaxLength(data,manager,routing)", the model works perfectly and dropped nodes sucessfully that with distance exceed the allowed vehicle driving distance.
Therefore, I suspect there is some conflicts between the use of "routing.AddDisjunction" and "routing.VehicleVar" in imitMaxLength(data,manager,routing), which prevents the model running in correct way to find the solution.