VROOM-Project / pyvroom

Vehicle Routing Open-source Optimization Machine
BSD 2-Clause "Simplified" License
67 stars 14 forks source link

Providing callback functions rather than fixed matrices #62

Open iedmrc opened 2 years ago

iedmrc commented 2 years ago

Hi all,

Could we add a new future such:

Providing callback functions rather than fixed matrices. With this feature, we would be able to produce custom costs, durations etc.. on the air by looking at some parameters, for example, the onway duration may vary according to traffic at that specific time. Or we would like to punish moving forward from one specific place to another if it's coming from another, etc..

Edit: Miss-click to enter button, sorry :)

jcoupey commented 2 years ago

I guess having this matrix-adjusting logic live in the same place as the python bindings could be handy. But since you have to maintain that on top of the bindings, maybe it would be easier in the long run to have it as an isolated component?

Also another question raised here is that it would define something different from the upstream API while the primary goal for this repo was to provide python bindings that are real close to the upstream C++ API.

@jonathf since you wrote the python bindings, what's your take on this?

jonathf commented 2 years ago

Having the code on the pyvroom side of things is posible, but I feel it would be hacky, as I would need to circumvent a lot of the C++ infrastructure, and I don't see any reason why it should be a Python-only feature.

I think that the way to go here is to have the functionality on the C++ side, like you are indicating. Perhaps a new routing class that accepts a std::function<Cost(Location, Location)> as an arg to the constructor. If that existed, it would be trivial to link up such functionality in python with two-three lines of glue code in pyvroom.

jcoupey commented 2 years ago

I don't see any reason why it should be a Python-only feature.

Good point, it's probably better to keep features in line in the bindings.

On the other hand I don't think it would be a good idea to bloat the C++ API with a new (complex) way to adjust cost matrices. We already provide a low-level way to pass in custom matrices and optimize based on those. Of course it means users have to build their own cost logic outside VROOM if they want to, but this provides a good modularity.

iedmrc commented 2 years ago

Actually, I also don't see any reason why it should be a Python-only feature. But to have a quick solution, I say why not :)

And also at this point:

On the other hand I don't think it would be a good idea to bloat the C++ API with a new (complex) way to adjust cost matrices. We already provide a low-level way to pass in custom matrices and optimize based on those.

I think such functionality would improve VROOM much. With this functionality, it would be more extensible. At least through bindings or a library version of it. Google's OR-tools can be a good example here.

jcoupey commented 2 years ago

Using VROOM as a library is already a thing, see this example of defining a custom matrix directly from C++.

You can already add any custom cost logic to compute matrices, then call the low-level set_durations_matrix function. Having clear and separate roles for different software components is usually considered a good practice.

Baking your matrix-building logic inside VROOM ties you with this software component, whereas if you build your own logic outside you could re-use that part with other engines if need be, or easily switch to a different logic without having to touch VROOM at all.

iedmrc commented 2 years ago

I opened this ticket here because I agree with you @jcoupey here:

Baking your matrix-building logic inside VROOM ties you with this software component, whereas if you build your own logic outside you could re-use that part with other engines if need be, or easily switch to a different logic without having to touch VROOM at all.

And also I actually know VROOM has a library version too. But making the development in such a project is hard because:

But to make 3rd party bindings easy to implement such "callback function rather than static matrix" feature, seems like we need a development at VROOM part. So I think we should consider it.

jonathf commented 2 years ago

The high-level approach to what you want can be done outside of VROOM I think. Like:

import numpy
import vroom

jobs = [vroom.Job(1414, location=0), vroom.Job(1515, location=1),
        vroom.Job(1616, location=2), vroom.Job(1717, location=3)]

def callback(int: loc1, int: loc2) -> int:
    # logic for callculating distance/cost between locations

# Part that you want built into pyvroom:
matrix = numpy.zeros((len(jobs), len(jobs)), dtype=int)
for i in range(len(jobs)):
    for j in range(i + 1, len(jobs)):
      matrix[i, j] = matrix[j, i] = callback(i, j)

# Define problem to be solved:
problem_instance = vroom.Input()
problem_instance.set_duration_matrix(matrix)
problem_instance.add_job(jobs)
...

In other words, you won't gain much code wise from such a logic. From a code implementation point of view, a lot of interception has to be done to do this properly on the Python end. So I think this snippet is your best bet for now.

iedmrc commented 2 years ago

Hey @jonathf, thank you for the offer. I think it's a good approach but doesn't work much as you mentioned because the callback function needs some parameters to be passed to it such as what is the current route, what is the current cost etc..

I understand for both pyvroom and vroom this is not a case that wanted to be placed in the roadmap. But in my opinion, it would be a powerful feature for the vroom. You can assume this as a feature request for a long-term roadmap :). Hence maybe some other parties who need such a feature can find this issue and place their thoughts.

SkyTik commented 1 month ago

Hi, I came to this issue since I had the same problem as @iedmrc. Are there any updates on this issue? My use case is that I need to define a cost matrix for different types of vehicles. I can easily achieve it with OR-Tools like this.

for i in range(len(vehicles_list)):
    cost_callback = arc_cost_callbacks[i]
    transit_callback_index = routing.RegisterTransitCallback(cost_callback)
    routing.SetArcCostEvaluatorOfVehicle(transit_callback_index, i)
jonathf commented 1 month ago

No, sorry. Not on the horizon for planned features.

As for different cost matrixes for different types of vehicles, that is already supported. Just make multiple profiles. See the example in the root README.rst

jonathf commented 1 month ago

When you have multiple profiles, you will need to explicitly define the profile for each Vehicle with the profile arg:

car  = vroom.Vehicle(..., profile="car")
truck = vroom.Vehicle(..., profile="truck")
...