Closed Westlife1002 closed 3 years ago
After a very quick look at your code...
You must convert your index into node in your distance callback
def distance_evaluator(self, from_index, to_index):
"""Returns the Harversine Distance between the two nodes"""
return self._distances[from_index][to_index]
should be:
def distance_evaluator(self, from_index, to_index):
"""Returns the Harversine Distance between the two nodes"""
from_node = self.manager.IndexToNode(from_index)
to_node = self.manager.IndexToNode(to_index)
return self._distances[from_node][to_node]
notice the use of manager
so you may need to pass it to your class constructor...
note: it will be the same issue for demand and time callback/evaluator
note2: for capacity you should use a UnaryTransitCallback than using del to_node
e.g.
https://github.com/google/or-tools/blob/b37d9c786b69128f3505f15beca09e89bf078a89/ortools/constraint_solver/samples/vrp_capacity.py#L176-L184
After a very quick look at your code...
You must convert your index into node in your distance callback
def distance_evaluator(self, from_index, to_index): """Returns the Harversine Distance between the two nodes""" return self._distances[from_index][to_index]
should be:
def distance_evaluator(self, from_index, to_index): """Returns the Harversine Distance between the two nodes""" from_node = self.manager.IndexToNode(from_index) to_node = self.manager.IndexToNode(to_index) return self._distances[from_node][to_node]
notice the use of
manager
so you may need to pass it to your class constructor...note: it will be the same issue for demand and time callback/evaluator note2: for capacity you should use a UnaryTransitCallback than using
del to_node
e.g. https://github.com/google/or-tools/blob/b37d9c786b69128f3505f15beca09e89bf078a89/ortools/constraint_solver/samples/vrp_capacity.py#L176-L184
Sorry I did not copy the full codes ( I have updated in the main function). I actually did add the manager to do the index convention, but I still got the same error report. could you please help to investigate further? thank you
Looking at the trace
Traceback (most recent call last):
File "C:/Users/xxx/VRP_9.0.py", line 194, in distance_evaluator
return self._distances[from_index][to_index]
KeyError: 144
it seems you didn't performed the convertion otherwise you should have return self._distances[from_node][to_node]
instead...
Can you double check, you did the index -> node
convertion in ALL your evaluator (and update the trace/source code accordingly ?)
ps: you can use gist to share the code...
this is the most updated code which I just run: https://gist.github.com/Westlife1002/905722fcca425f3538544d6c1404fcf9
the reported error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:/Users/Jie/Dropbox/Jie/VRP_9.0.py", line 572, in distance_evaluator
from_node = manager.IndexToNode(from_index)
File "C:\Users\Jie\AppData\Roaming\Python\Python36\site-packages\ortools\constraint_solver\pywrapcp.py", line 2818, in IndexToNode
return _pywrapcp.RoutingIndexManager_IndexToNode(self, index)
OverflowError: in method 'RoutingIndexManager_IndexToNode', argument 2 of type 'int64_t'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\Jie\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "C:/Users/Jie/Dropbox/Jie/VRP_9.0.py", line 617, in main
solution = routing.SolveWithParameters(search_parameters)
File "C:\Users\Jie\AppData\Roaming\Python\Python36\site-packages\ortools\constraint_solver\pywrapcp.py", line 3150, in SolveWithParameters
return _pywrapcp.RoutingModel_SolveWithParameters(self, search_parameters, solutions)
SystemError: <built-in function RoutingModel_SolveWithParameters> returned a result with an error set
IIRC this is due to the fact your are using float instead of int (ed std::int64_t). All your callback must return int not float, so try to use something like this
self._distances[from_node][to_node] = (
int(distance(x1,y1,x2,y2)))
temp.append(int(distance(x1,y1,x2,y2)))
ps: in your gist you should use filename.py to have the correct syntax coloration
EDIT: In you code you must fix:
int
not float
here:
https://gist.github.com/Westlife1002/905722fcca425f3538544d6c1404fcf9#file-gistfile1-txt-L174-L177def demand_evaluator(self, from_node, to_node):
solver will call it with (from_index, to_index)
parametersstill it's reporting same issue.... I have shared with the input file, maybe you could run the code to see what exactly happens.. https://www.dropbox.com/s/unhzamero0napnj/VRP_input.csv?dl=0
don't hesitate to reach me on discord (see the badge in the readme) On my way to try to run your code on my machine..
my diff %diff -u --color gistfile1.txt test.py
--- gistfile1.txt 2021-05-13 11:49:44.396042395 +0200
+++ test.py 2021-05-13 11:51:44.707026400 +0200
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
"""Capacitated Vehicle Routing Problem"""
from __future__ import print_function
from six.moves import xrange
@@ -171,9 +172,9 @@
# print(y1)
x2 = data.locations[to_node][0] # Ycor of end2
y2 = data.locations[to_node][1] # Xcor of end2
- self._distances[from_node][to_node] = (
+ self._distances[from_node][to_node] = int(
distance(x1,y1,x2,y2))
- temp.append(distance(x1,y1,x2,y2))
+ temp.append(int(distance(x1,y1,x2,y2)))
self.dist_matrix.append(temp)
@@ -202,13 +203,14 @@
class CreateDemandEvaluator(object):
"""Creates callback to get demands at each location."""
- def __init__(self, data):
+ def __init__(self, data, manager):
"""Initializes the demand array."""
self._demands = data.demands
+ self._manager = manager
- def demand_evaluator(self, from_node, to_node):
+ def demand_evaluator(self, from_index):
"""Returns the demand of the current node"""
- del to_node
+ from_node = self._manager.IndexToNode(from_index)
return self._demands[from_node]
@@ -234,9 +236,10 @@
x1,y1,x2,y2) / data.getvehicle.speed()
return travel_time
- def __init__(self, data):
+ def __init__(self, data, manager):
"""Initializes the total time matrix."""
self._total_time = {}
+ self._manager = manager
# precompute total time to have time callback in O(1)
for from_node in xrange(data.num_locations):
self._total_time[from_node] = {} #under total_time {}, add key = this from_node, value ={}
@@ -251,8 +254,10 @@
# print(self._total_time)
- def time_evaluator(self, from_node, to_node):
+ def time_evaluator(self, from_index, to_index):
"""Returns the total time between the two nodes"""
+ from_node = self._manager.IndexToNode(from_index)
+ to_node = self._manager.IndexToNode(to_index)
return self._total_time[from_node][to_node]
@@ -586,11 +591,11 @@
# Add Distance constraint
add_distance_dimension(routing, transit_callback_index)
# Add Capacity constraint
- demand_evaluator = CreateDemandEvaluator(data).demand_evaluator
- demand_callback_index = routing.RegisterTransitCallback(demand_evaluator)
+ demand_evaluator = CreateDemandEvaluator(data, manager).demand_evaluator
+ demand_callback_index = routing.RegisterUnaryTransitCallback(demand_evaluator)
add_capacity_constraints(routing, data, demand_callback_index)
# Add Time Window constraint
- time_evaluator = CreateTimeEvaluator(data).time_evaluator
+ time_evaluator = CreateTimeEvaluator(data, manager).time_evaluator
time_callback_index = routing.RegisterTransitCallback(time_evaluator)
add_time_window_constraints(routing, data, time_callback_index, manager)
@@ -600,8 +605,10 @@
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# 2).Guided Local Search Heuristics
- # search_parameters.local_search_metaheuristic = (
- # routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
+ search_parameters.local_search_metaheuristic = (
+ routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
+ search_parameters.log_search = True
+ search_parameters.time_limit.FromSeconds(10)
# search_parameters.time_limit_ms = 3000
# 3).Simulated Annealing Heuristics
# search_parameters.local_search_metaheuristic = (
@@ -622,6 +629,8 @@
DrawNetwork()
Reporting(data)
plt.show()
+ else:
+ print("No solution found !")
# if __name__ == '__main__':
@@ -688,4 +697,4 @@
b.pack()
-window.mainloop()
\ No newline at end of file
+window.mainloop()
Manage to run it and found a solution if i use ~5 vehicles of each type, if using only 5 "Sprinter" I can see the "No solution found !" -> DONE on my side, please give us some feedback if it's working on your side and if yes please close this issue !
my diff %diff -u --color gistfile1.txt test.py
--- gistfile1.txt 2021-05-13 11:49:44.396042395 +0200 +++ test.py 2021-05-13 11:51:44.707026400 +0200 @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """Capacitated Vehicle Routing Problem""" from __future__ import print_function from six.moves import xrange @@ -171,9 +172,9 @@ # print(y1) x2 = data.locations[to_node][0] # Ycor of end2 y2 = data.locations[to_node][1] # Xcor of end2 - self._distances[from_node][to_node] = ( + self._distances[from_node][to_node] = int( distance(x1,y1,x2,y2)) - temp.append(distance(x1,y1,x2,y2)) + temp.append(int(distance(x1,y1,x2,y2))) self.dist_matrix.append(temp) @@ -202,13 +203,14 @@ class CreateDemandEvaluator(object): """Creates callback to get demands at each location.""" - def __init__(self, data): + def __init__(self, data, manager): """Initializes the demand array.""" self._demands = data.demands + self._manager = manager - def demand_evaluator(self, from_node, to_node): + def demand_evaluator(self, from_index): """Returns the demand of the current node""" - del to_node + from_node = self._manager.IndexToNode(from_index) return self._demands[from_node] @@ -234,9 +236,10 @@ x1,y1,x2,y2) / data.getvehicle.speed() return travel_time - def __init__(self, data): + def __init__(self, data, manager): """Initializes the total time matrix.""" self._total_time = {} + self._manager = manager # precompute total time to have time callback in O(1) for from_node in xrange(data.num_locations): self._total_time[from_node] = {} #under total_time {}, add key = this from_node, value ={} @@ -251,8 +254,10 @@ # print(self._total_time) - def time_evaluator(self, from_node, to_node): + def time_evaluator(self, from_index, to_index): """Returns the total time between the two nodes""" + from_node = self._manager.IndexToNode(from_index) + to_node = self._manager.IndexToNode(to_index) return self._total_time[from_node][to_node] @@ -586,11 +591,11 @@ # Add Distance constraint add_distance_dimension(routing, transit_callback_index) # Add Capacity constraint - demand_evaluator = CreateDemandEvaluator(data).demand_evaluator - demand_callback_index = routing.RegisterTransitCallback(demand_evaluator) + demand_evaluator = CreateDemandEvaluator(data, manager).demand_evaluator + demand_callback_index = routing.RegisterUnaryTransitCallback(demand_evaluator) add_capacity_constraints(routing, data, demand_callback_index) # Add Time Window constraint - time_evaluator = CreateTimeEvaluator(data).time_evaluator + time_evaluator = CreateTimeEvaluator(data, manager).time_evaluator time_callback_index = routing.RegisterTransitCallback(time_evaluator) add_time_window_constraints(routing, data, time_callback_index, manager) @@ -600,8 +605,10 @@ search_parameters.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # 2).Guided Local Search Heuristics - # search_parameters.local_search_metaheuristic = ( - # routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + search_parameters.local_search_metaheuristic = ( + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + search_parameters.log_search = True + search_parameters.time_limit.FromSeconds(10) # search_parameters.time_limit_ms = 3000 # 3).Simulated Annealing Heuristics # search_parameters.local_search_metaheuristic = ( @@ -622,6 +629,8 @@ DrawNetwork() Reporting(data) plt.show() + else: + print("No solution found !") # if __name__ == '__main__': @@ -688,4 +697,4 @@ b.pack() -window.mainloop() \ No newline at end of file +window.mainloop()
Manage to run it and found a solution if i use ~5 vehicles of each type, if using only 5 "Sprinter" I can see the "No solution found !" -> DONE on my side, please give us some feedback if it's working on your side and if yes please close this issue !
It's running ok now. there seems to be quite a lot of changes since the version I used about 3 years ago. Great Thanks for your help! I am picking up this model again for another usage. Instead of users to define how many /which type of vehicle used, can the model compute the optimal number and types of vehicles needed, given the demand volume and all other constraints? if so, how do we edit the current codes? many thanks for your advice
BTW, when I run the code, if there is no solution, it's simply stuck in the control panel, not showing "no solution found". How did u see it on your machine?
running from a terminal on my archlinux (call it test.py, chmod a+x test.py; ./test.py), I can see it in the terminal...
note: I use Awesome WM manager which is a tilling windows manager so I have on the left your "gui" and on the right my urxvt term with any python print()
issued...
Concerning vehicle, you can try to add a "VehicleFixedCost" (which can be higher for heavier vehicle) so solver will have incentive to use less vehicle if possible + due to the usage of arc cost solver will have incentive to use one vehicle instead of two since usually you'll have cost(D -> A -> B -> D) << cost(D -> A -> D) + cost(D -> B -> D)
Also you can avoid to use distance_dimension.SetGlobalSpanCostCoefficient(100)
so solver will have incentive to use vehicle as much as possible than "splitting the route" across vehicles to reduce the longest route...
well if I want the model to decide the optimal&types of trucks, I can simply place all 150 trucks as candidate for model to select. The problem is: how to retrive the truck capacity (type) information from the solution? since the capacity information is given in the capacity_vector, not stored in the vehicle object.
don't get it, Why not creating a custom structure with the solution AND the capacity_vector or passing both instances to your own "print_solution()" method ? note: in the solution vehicle N should be same than capacity_vector[N] i.e. indices are same so you can retrieve the vehicle capacity etc..
What version of OR-Tools and what language are you using? Version: lastest Language: python 3.6.5
Which solver are you using (e.g. CP-SAT, Routing Solver, GLOP, BOP, Gurobi) standard vrp suit
What operating system (Linux, Windows, ...) and version? windows
My VRP model was developed 3 years ago, and now is not compatible with the lastest or-tools configurations. I tried many many times to fix the problems. Still I have errors which I have no idea where is wrong:
This is the complete code: