I'm trying to use the RoutingModel and I’m struggling to correctly set different cost function callbacks for each vehicle type.
This is a snippet from my function
# set costs
for courier in couriers:
distance_function = distance_matrix.distance
model.SetArcCostEvaluatorOfVehicle(distance_function, courier.vehicle_num)
When I run it, it produces a solution but the solution is incorrect. The end_node for the first vehicle isn’t a depot and this exception is thrown
File "/Users/oliverw/caviar/projects/caviar-ml-service/env/lib/python2.7/site-packages/ortools/constraint_solver/pywrapcp.py", line 3220, in SolveWithParameters
return _pywrapcp.RoutingModel_SolveWithParameters(self, parameters, assignment)
TypeError: in method 'RoutingModel_SolveWithParameters', argument 1 of type 'operations_research::RoutingModel *'
When I replace
# set costs
for courier in couriers:
distance_function = distance_matrix.distance
model.SetArcCostEvaluatorOfVehicle(distance_function, courier.vehicle_num)
with
model.SetArcCostEvaluatorOfAllVehicles(distance_function)
a valid solution is returned with no exceptions.
I’ve looked in the python examples but can’t find a use of SetArcCostEvaluatorOfVehicle in the python examples.
I saw in the Java examples that SetVehicleCost was another option but that didn’t help me either.
Do I need to set any specific parameters on the routing model in order for the SetArcCostEvaluatorOfVehicle to be used correctly?
Thanks in advance for any advice you guys have!
Oli
Using a C debugger I’ve found the point where this is going wrong.
The method in question is RoutingModel::GetFingerprintOfEvaluator. This method tries to build a fingerprint for each evaluator based on the distances they return for each node pairing. The error occurs when evaluator->Run() is called.
.....
if (from != to && !IsStart(to) && (!from_start || !IsEnd(to))) {
row[row_size] = evaluator->Run(IndexToNode(from), IndexToNode(to));
++row_size;
}
....
The stack trace is
(lldb) thread backtrace
* thread #1: tid = 0x2e80a, 0x000000010000f0a3 Python`PyObject_Call + 28, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x000000010000f0a3 Python`PyObject_Call + 28
frame #1: 0x000000010008ed8b Python`PyEval_CallObjectWithKeywords + 93
frame #2: 0x000000010434becb _pywrapcp.so`PyCallback2NodeIndexNodeIndex(_object*, IntType<operations_research::_RoutingModel_NodeIndex_tag_, int>, IntType<operations_research::_RoutingModel_NodeIndex_tag_, int>) + 43
frame #3: 0x000000010475839f libortools.dylib`operations_research::RoutingModel::GetFingerprintOfEvaluator(ResultCallback2<long long, IntType<operations_research::_RoutingModel_NodeIndex_tag_, int>, IntType<operations_research::_RoutingModel_NodeIndex_tag_, int> >*) const + 287
frame #4: 0x0000000104758dff libortools.dylib`operations_research::RoutingModel::ComputeCostClasses() + 2207
frame #5: 0x000000010475c063 libortools.dylib`operations_research::RoutingModel::CloseModel() + 147
frame #6: 0x000000010475fdc1 libortools.dylib`operations_research::RoutingModel::Solve(operations_research::Assignment const*) + 33
When I use SetArcCostEvaluatorOfAllVehicles instead of SetArcCostEvaluatorOfVehicle it never calls RoutingModel::GetFingerprintOfEvaluator and so the problem doesn’t happen.
I have no idea if this strange behaviour is
a) me not using it correctly
b) a bug in the source code
c) a bug resulting from building incorrectly
Would really be grateful for any tips!
Figured it out! Its to do with scoping in the for loop.
Because distance_function is inside the forloop (and not used again in pythonland) it must get deallocated by python’s garbage collector.
I used this instead and it worked
vehicle_call_backs = [lambda i, j: matrix.Distance(i, j) for vehicle in range(vehicles)]
for vehicle in range(vehicles):
routing.SetArcCostEvaluatorOfVehicle(vehicle_call_backs[vehicle], vehicle)