New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple origin and destination candidates results in strange detours in unidirectional A* #3585
Conversation
- Adding tests for mutlipe origin and destination candidates - Fixing unidirectional A*
Dear reviewers, the basic idea is to wrap label creation into a lambda ( Cheers Daniel |
The test EDIT: My fault, found the reason... |
valhalla/sif/edgelabel.h
Outdated
} | ||
|
||
/** | ||
* Sets this edge as an origin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: origin seems like the wrong word maybe? maybe you meant that it can be either and origin or destination since we have both forward and reverse search algorithms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An edge can be both origin and destination at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw "Sets this edge as an origin." is already part of the untouched code, see here:
valhalla/valhalla/sif/edgelabel.h
Line 267 in a5ea654
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry im just pointing out that the comment says "set this edge as an origin" but the code sets the destination bit. that seems contradictory though yes i understand that its possible an edge is both a destination and an origin for certain routes. really i just wonder why the comment isnt "sets this edge as a destination"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, my bad!!!! Sorry
@dgearhart it looks like the pencilpoint tests are failing on this one with finding completely different routes? i just looked at ci and saw the logs and thats what it looked like. do you have a second to take a look? otherwise i can look maybe in a couple of days. @derolf thanks very much for the massive refactor of these algorithms. i'm hoping that @dnesbitt61 and i can take a look in the next few days. anyone else who has time (maybe @purew ? ) would also be much appreciated! |
@kevinkreiser will not have any time today |
@derolf apologies its taken so long to get to this, but i just wanted to say its still on my radar. just have to appease the tests at this point.. bear with us |
Hey @kevinkreiser, Any idea how to fix the tests? Assuming I would fix them in the next days, would it be ready to get merged? cheers Daniel |
@derolf we were just starting to look at them last week to see what is going on. As soon as I can qualify the exact cause I'll post back to this issue. All I see at the moment is it takes a different path. It might be that the test itself should change |
@nilsnolde @kevinkreiser Coverage went down a tiny bit. Any idea for an easy gain here? |
Ah that's fine @derolf. Can happen with bigger refactors where lots of (apparently very well tested) code is removed. As for me, the more important thing is the patch diff and that looks as good as it can be. Though I have a hard time wrapping my head around the overall decrease if the patch diff is so high. I could understand if the patch diff was like 84.9 or so, then I could reason that code was removed that was covered at a much higher degree. But with 96% coverage.. Anyways, maybe I understand those metrics wrong. Again, it's fine with me at least. |
@nilsnolde Ship it! |
jep, we'll review it again in a bit. I'm pretty sure it's good though! was just saying it's fine to decrease coverage. thanks again and sorry for the long wait. |
assert(opp_edge_id); | ||
assert(opp_edge); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we have the tile, it's not possible that we don't have the edge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally had some time to look into this. Looks good, thanks @derolf . I think the assert
are redundant, other than that all good. Will go over it with @kevinkreiser tmrw, then we can merge.
if (FORWARD ? costing_->AvoidAsDestinationEdge(edgeid, edge.percent_along()) | ||
: costing_->AvoidAsOriginEdge(edgeid, edge.percent_along())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the changes in this function:)
// edge (check has to be done BEFORE we invert the edge if !FORWARD) | ||
if (FORWARD ? costing_->AvoidAsOriginEdge(edgeid, percent_along) | ||
: costing_->AvoidAsDestinationEdge(edgeid, percent_along)) { | ||
continue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, not so convinced anymore after all.. For reverse this might not do what we'd expect I think. Basically it seems this relies on the edge as it's being traversed by the algorithm, not its opposing. That exact edge might not even be in the avoid_edges
set if the current mode isn't allowed on it. However, its opposing (the one we actually care about) might've been. That is to say, it's better than before, where we didn't look at avoided edges at all for the reverse tree, but might still need some more fiddling to get it really right, so we should leave a TODO.
Thanks for approving the PR. What would you like me to do before you merge the PR? |
I'd still like to test the pr. We need to run a suite of routes through it and look at the differences vs master. I'll do that and report back. I'd also like to reason about the changes myself i just haven't had the time. I started looking weeks ago but other things came up. |
Maybe we can do that tmrw together if there’s enough time. |
// Destinations, id and percent used along the edge | ||
std::unordered_map<uint64_t, float> destinations_percent_along_; | ||
// Mark if edge is a destination | ||
std::unordered_multimap<valhalla::baldr::GraphId, std::reference_wrapper<const valhalla::PathEdge>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im not too sure that we need a multimap here. the correlated edges can only have one copy of each unique edge id so you wont ever push in the same edge twice as a destination. unless im missing something here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not correct. The map-matcher could match the same edge multiple times. Just think about a hard loop shaped like a U with the car between.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while it is true that map matching needs to handle this case, this algorithm has nothing to do with our map matching implementation. all our map matching code is in the meili
namespace (and a little bit of it in thor/map_matcher thor/trace_*). this is because we do a one-to-many routing between map matching candidates and this algorithm only does a one-to-one routing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean if the incoming destination set has the same edge multiple times in the list, then we would need to add complex logic for tie breaking. Using a multimap usually nicely handles that case without any other checks required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep sorry what im saying is that that cannot happen. the list is populated by this line:
valhalla/src/thor/unidirectional_astar.cc
Line 822 in 4532f7b
for (const auto& edge : dest.correlation().edges()) { |
that list in turn is populated by loki search and im just telling you that loki search will only have unique edge ids in the list. this is because the whole job of loki search is to find the closest point on a given edge and return that as a candidate. there can be only 1 closest point on a given edge per location. so im just saying that multimap is an unneeded complication. i can see you are using it to nicely handle the event that there would be the same id more than once but what im saying is that there will never be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One should not couple implementation details of two algorithms, esp. across public APIs.
So relying on the fact that LOKI behaves that way is not good because the A* could also be used in a different way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one should probably also not constantly change the interface to those public APIs but i guess it depends on the guarantees one wants to provide with a given API.
since its inception the project has not really concerned itself with maintaining compatibility (commit to commit) between the APIs exposed in the headers. we pretty much only gaurantee the json API. in fact it is a long term goal to vastly reduce the scope of methods exposed in the library headers (a battle for another day.. or maybe slowly plugging away at it rather).
furthermore, i can tell you for a fact that other parts of thor assume that there are no duplicates in the list of a locations edge candidates (eg: https://github.com/valhalla/valhalla/blob/master/src/thor/triplegbuilder.cc#L411 if there were duplicates here it would randomly pick one candidate, not necessarily the right one)
anyway the whole reason i was telling you this was because i saw the multimap
and i saw the call to equal_range
and i was wondering: why is he protecting against this case that doesn't occur? which lead to me really just trying to do a polite: FYI you dont need to worry about this, it cant happen. but i think from your round about answers afterward i could understand that you may be calling this algorithm with your own set of edge candidates which may indeed have duplicates?
my point is i understand how one could put more than one correlation point for the same edge in the list, it just doesnt happen in any of the code here on github. if you have other designs elsewhere or future plans, best to just say that and shortcut the discussion 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nay, no plans right now, but could happen.
I did that change for the simple reason of my edge-cases-tuned brain :-)
@@ -441,6 +457,7 @@ class EdgeLabel { | |||
uint64_t shortcut_ : 1; | |||
uint64_t dest_only_ : 1; | |||
uint64_t origin_ : 1; | |||
uint64_t destination_ : 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is convenient good call
so we ran master and this branch on a 14k route file and the diff results were good for depart at. only 351 diffs total. we will run arrive by and also look at differences in performance. i'll post some of the screen shots of the differences so we can see what kinds of diffs we have. the routes i ran were with a 10m radius so that the logic to select different destination candidates would be triggered. looking good so far! thanks again for all the work @derolf |
ok i've checked performance and as expected there is no impact (we are doing the same amount of unordered::finds as we were before basically and the lambda is basically free). @nilsnolde and i looked at a bunch of the diffs and just about all of them make sense and are better results. there are a few where i'd say the results are worse and not intuitive but on the whole the ones we checked (maybe 20 manually reviewed) id say only a couple were noticeably worse. algorithmically the change makes sense to me so i think we are good to merge. |
Issue
#3582
#1952
Tasklist