Closed fbaierl closed 1 year ago
Thanks for reporting. Actually breaks are not supposed to have a location in output, see for example https://github.com/VROOM-Project/vroom/blob/master/docs/example_1_sol.json. It has always been this way, so the problem here is that we report a meaningless location_index
(the 0 value is a default value since no location has ever been set for the break step).
The problem is that we don't discriminate between steps types when serializing to json. No-one spotted this since it has been introduced back in #627.
Actually we should probably make the Step::location
member optional here, as it would be easy to check that upon serialization. Also it would provide a cleaner interface for users of the C++ interface.
Thanks for the fast reply (and happy new year). Originally, I assumed that the break always happens at the location of the previous step. This was true before I think - now it always seems to happen at the location of the next step for some reason.
What I find a bit strange in my example is that the the vehicle is driving to the end-location after the last delivery and before the break happens at that (end-) location, there is an additional waiting time before the break begins. While technically not wrong, this seems odd. Is there a reason for that?
Also, isn't it a bit tedious to have to check the 'distance' value of the break to determine its location?
Breaks can basically happen anywhere/anytime between the adjacent tasks. Even if they can stick to the previous or next task, we want to have a lot of flexibility here. The rationale is that in a T1 -> Break -> T2
setup, we have the ability to split the T1 -> T2
travel time across the break. Typically if the break and T2
have later TW, you don't want to wait before taking the break. In that case we'll do as much of the travel we can, start the break ASAP, then do the rest after the break.
The intermediate distance value for the break has no real significance since there is no actual location. It is just computed based on travel time split for consistency here.
For me it seems the distance value is always either the same as for the previous step or the following step. I have never seen a break with a distance in between.
Is there another way of recognizing at what part of the travel the break is taken? For our algorithm I think I would need to iterate over the breaks and manually shift them back or forth so that they are always exactly after/before another step.
For me it seems the distance value is always either the same as for the previous step or the following step.
It means you only encountered situations with the break right after or right before a job. Having the travel time split across a break only happens under special TW circumstances where fitting the break would otherwise be impossible.
Is there another way of recognizing at what part of the travel the break is taken?
The steps[].arrival
keys (combined with duration
values) will provide that information. If a break happens right after job j
, then the break duration
value is j.duration
and its arrival
will be j.arrival + j.waiting_time [+ j.setup] + j.service
.
the break happens at that (end-) location, there is an additional waiting time before the break begins. While technically not wrong, this seems odd. Is there a reason for that?
Breaks are mandatory so they'll show up in routes no matter what, even if everything can be done early and the vehicle reached its end location before the break available time. If you have a business logic where the break is only meaningful if the route is long enough, then you probably want to remove the "end break" from the route plan altogether in a post-processing step.
Alright, thank you very much. That explains a lot.
I have adapted our code to post-process the break accordingly (either attach it to previous or next step). Here is how I have done it (in case someone else has the same requirements):
// handles the vroom behavior described here: https://github.com/VROOM-Project/vroom/issues/877
// -> give break step a location
// -> attach the break to the previous or next step
private List<Step> postProcessBreakSteps(List<Step> steps) {
final var ss = new ArrayList<>(steps);
for (int i = 0; i < steps.size(); i++) {
if (ss.get(i).isBreak() &&
i > 0 && i < steps.size() - 1) { // actually not necessary, since start/end are always the first/last step! just to be extra-safe
final var breakStep = ss.get(i);
final var previousStep = ss.get(i - 1);
final var nextStep = ss.get(i + 1);
// decide, whether to attach the break to the previous or the next step by comparing the arrival difference
final var midOfBreak = breakStep.getArrival() + (breakStep.getService() * 0.5);
final var arrivalDifferenceToPrevious = Math.abs(previousStep.getArrival() - midOfBreak);
final var arrivalDifferenceToNext = Math.abs(nextStep.getArrival() - midOfBreak);
final var breakAfterPreviousStep = arrivalDifferenceToPrevious <= arrivalDifferenceToNext;
// then calculate the updated break values
final var breakLocation = breakAfterPreviousStep
? previousStep.getLocation()
: nextStep.getLocation();
final var breakArrival = breakAfterPreviousStep
? previousStep.getArrival() + previousStep.getWaitingTime() + previousStep.getService() + previousStep.getSetup() // right after previous step has finished
: nextStep.getArrival() - breakStep.getService(); // right before the next step
final var breakWaitingTime = breakAfterPreviousStep
? 0 // right after previous step -> no waiting time; add the waiting time to the next step
: breakStep.getWaitingTime(); // no need to update the waiting time if we just shift the break to a later time
final var nextStepWaitingTime = breakAfterPreviousStep
? breakWaitingTime + breakStep.getWaitingTime()
: nextStep.getWaitingTime();
final var nextStepArrival = breakAfterPreviousStep
? nextStep.getArrival() - breakStep.getWaitingTime()
: nextStep.getArrival();
final var breakDistance = breakAfterPreviousStep
? previousStep.getDistance()
: nextStep.getDistance();
final var breakDuration = breakAfterPreviousStep
? previousStep.getDuration()
: nextStep.getDuration();
// update break
ss.set(i, new Step(
breakStep.getType(),
breakArrival,
breakDuration,
breakStep.getSetup(),
breakStep.getLoad(),
breakStep.getService(),
breakWaitingTime,
breakStep.getViolations(),
breakStep.getDescription(),
breakStep.getId(),
breakDistance,
breakLocation
));
// update next step
ss.set(i + 1, new Step(
nextStep.getType(),
nextStepArrival,
nextStep.getDuration(),
nextStep.getSetup(),
nextStep.getLoad(),
nextStep.getService(),
nextStepWaitingTime,
nextStep.getViolations(),
nextStep.getDescription(),
nextStep.getId(),
nextStep.getDistance(),
nextStep.getLocation()
));
}
}
return ss;
}
Hello,
we noticed a strange behavior using VROOM v1.13-rc1. It seems that the break (sometimes?) does not get a location attached in the result json. Instead, a
"location_index": 0
is added to the break even though we did not provide a custom location matrice or something similar.For now we fix this issue by just assuming the breaks location to be at the step happening before or afterwards depending on the distance.
Input
Output