Closed rastislavkopal closed 7 months ago
Hi @rastislavkopal! 👋
Your prize vector has values between 5 and 10. Your distances, in meters, are orders of magnitude larger. PyVRP optimizes an objective of the form "minize distance - prizes" (the docs contain the exact formulation). To get a prize of 5 to 10, a vehicle needs to spend many thousands in distance to get there. That's not a good decision to make, so the algorithm decides that not visiting anything is the best it can do.
The solution here is to also scale the prizes, to make their values comparable to the distance values. That should make the prizes interesting enough for PyVRP to consider visiting the clients.
Oh, I see. Thanks for pointing that out @N-Wouda .
Now when I scale the prizes (I tried multiple scaling_factors with no success)
def scale_scores(scores, distance_matrix, scale_factor=1):
distances = [distance for row in distance_matrix for distance in row if distance != 0] # Flatten and remove 0
median_distance = sorted(distances)[len(distances) // 2]
median_price = sorted(scores)[len(scores) // 2]
scale_factor = (scale_factor * median_distance) / median_price
return [round(prize * scale_factor) for prize in scores]
The solution visits everything if I do not set any duration to edges and max_duration to vehicle. That makes sense.
When I set both duration and distance to each edge:
for frm_idx, frm in enumerate(locations):
for to_idx, to in enumerate(locations):
if frm_idx == to_idx:
continue
distance = DISTANCE_MATRIX[frm_idx][to_idx]
walking_duration = round(distance / 83.33)
m.add_edge(frm, to, distance=distance, duration=walking_duration)
And max distance to the only vehicle I have:
m.add_vehicle_type(1, depot=depots[0], max_duration=12 * 60)
There is always just INFEASIBLE solutions. Am I still missing something?
@rastislavkopal could you provide me with a full script that I can run locally? I find it somewhat difficult to piece this together into a full, running example. Just copy pasting your local script should be sufficient, if it is not too large.
@rastislavkopal you're running into an issue that's been reported previously in #461 and #508. Basically, our default time windows are [0, 0]
for depots and clients. That's been fixed in main to [0, +inf)
, and will be part of 0.8.0 when we release that version in a few weeks.
For now, if you want to use durations, I suggest you provide time windows for depots and clients explicitly. In your sample something like tw_early=0
and tw_late=24 * 60
should work. With those settings I find a feasible solution within a second:
@rastislavkopal does that resolve the issue?
Yes it did actually, thanks so much for that @N-Wouda .
@rastislavkopal great!
I was also wondering if it is possible to add something like a vehicle breaks which must happen during the day. Let's say one hour for a lunch break within a specified time window.
For example using or-tools: https://github.com/google/or-tools/blob/stable/examples/cpp/cvrptw_with_breaks.cc
Btw. In your example in docs, you use
res = m.solve(stop=MaxRuntime(1), display=False) # one second
But it throws an error
TypeError: Model.solve() got an unexpected keyword argument 'display
@rastislavkopal you're looking at the dev docs for 0.8.0, not the docs for the latest release (0.7.0). Those are here. We still need to update pyvrp.org to default to the latest stable release rather than the development version.
I really appreciate your help @N-Wouda . I know it's probably outside of the scope of the library and this discussion. But do you think it's possible to model the break for the driver during the day? As I mention, I need to set a for example 60 minute lunch break anywhere in time window 11:00 to 15:00. I was thinking about adding dummy nodes with zero distances which will be required. Not sure what is the standard way to handle this scenario.
I was thinking about adding dummy nodes with zero distances which will be required.
This is probably your best bet for now. We do not have any formal support for lunch breaks (#352) or time-based breaks (#415) yet.
What is the correct usage?
I am implementing a Prize Collecting VRP, where I have list of clients with their geoLocation. Each client has a prize that we will collect, and the service time (duration spent at a client). I have one vehicle that needs to maximize the prize collected within a day. Each client will also have a time window (availability time). We need to maximize the prize collected.
as the library does not support float coordinates, I tried to just multiply location by a constant (1000) to get INTs. I calculated the distances with geopy library. Now I am using UTM representation instead and just calculate the Manhattan distance between them. I actually do not know what is the standard way to encode the geoLocations as INT the best way.
Now when I create the model with the scores, coordinates, there is no found solution
The model:
The solver returns either an empty solution or sometimes the warning:
An empty solution is being added to the population. This typically indicates that there is a significant difference between the values of the prizes and the other objective terms, which hints at a data problem. Note that not every part of PyVRP can work gracefully with empty solutions.
What can be the reason ? Thank you for any insights.