How do I assign a different arc cost function for each vehicle type? (Python/RoutingModel)

1,712 views
Skip to first unread message

Oliver Wilkie

unread,
Jun 16, 2015, 2:10:21 AM6/16/15
to or-tools...@googlegroups.com

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


Oliver Wilkie

unread,
Jun 16, 2015, 2:56:55 PM6/16/15
to or-tools...@googlegroups.com

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!

Oliver Wilkie

unread,
Jun 16, 2015, 4:12:56 PM6/16/15
to or-tools...@googlegroups.com

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)

Michael Powell

unread,
Jun 17, 2015, 7:24:34 AM6/17/15
to or-tools...@googlegroups.com
If you did it via callback methods like I did in C# then that's quite likely the case. It is necessary to maintain references to those unmanaged instances, or at minimum mark them not for GC, between callbacks.

Benoît Boucher

unread,
Mar 13, 2018, 8:39:46 AM3/13/18
to or-tools-discuss
Thank you so much Oliver! I spent the whole day trying to figure it out!
Store the distance function outside of the forloop containing the evaluator that uses it... Genius!
Reply all
Reply to author
Forward
0 new messages