-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Use PrepareCHGraph adapter for CH preparation #1818
Conversation
# Conflicts: # core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java
…e edge-based witness path searcher
# Conflicts: # core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java # core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java # core/src/test/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcherTest.java
# Conflicts: # core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
core/src/main/java/com/graphhopper/routing/ch/PrepareCHEdgeIterator.java
Show resolved
Hide resolved
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 really like that the accessEnc usage and FlagEncoder usage is reduced. (For #1776 we need this)
But isn't it actually just a bit moved around? E.g. it is now used directly in PrepareCHEdgeIterator.java instead of the EdgeFilter ... hmmh, no in PrepareCHGraph still the EdgeFilter is used. And why are there isForward & isBackward methods directly in the iterator? (in the future I would prefer to rely only on the Weighting for this)
So I'm a bit unsure about the goal of this refactoring - can you explain? Or is this mainly driven by #1818 and then the changes become more clear?
core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java
Show resolved
Hide resolved
Yes, its just moved around. I would argue it is now easier to use the weighting to determine the fwd/bwd access, because we have it right there in the iterator. But this is not very crucial here (it would not yet justify all this refactoring).
I think its only needed for some heuristic in edge-based CH preparation, but its probably possible to remove it. But implementing this via the weighting would be very easy, because the iterator now has access to the weighting.
Yes. This is because at the moment
Yes I understand, I am sure its hard to see the benefits at the moment :) The main goal is separating the concerns of CHGraph, because it does (at least) the four things I mentioned above and they all can be optimized in a different way:
The migration path I see is to change all the code that uses |
Ok, but how ? I am thinking the easiest way is what I did here: // the graph is weighted: it knows the weighting.
MyGraph g = new MyGraph(baseGraph, weighting);
// when the explorer is created it also gets a reference to the weighting internally
MyIter iter = g.createOutEdgeExplorer().setBaseNode();
// in next() the iterator is able to use the weighting and check if the access is blocked/weight is infinite etc.
while (iter.next()) {
// this is an outgoing edge
} I am not saying we should put the weighting into the base graph, but to me it seems to make a lot of sense to pass a 'weighted graph' (a wrapper of the base graph + weighting) to the code that traverses the graph (at least as long as it is code that should run on a weighted graph, such as the shortest path algorithms). The |
Thanks, I do not understand everything but already more :) ... also the flexible CH is something I have not thought of.
For BaseGraph I think we can have it very simple: while(edgeIter.next()) {
// in a separate PR we move the accessEnc check into the weighting:
double weight = weighting.calcWeight(edgeIter, reverse, prevEdge);
if(Double.isInfinite(weight)) continue; // block access
// do something with edgeIter
} For PrepareCHGraph we could put this logic into the |
Ok, so your biggest concern is calling |
Ok I understand. That makes sense and also your 'very' simple method works, because in many cases we do not even need to check for |
Thanks for the changes - feel free to merge this :)
Yes, but also conceptionally asking for "accessible?" and then again does not seem to be "good". But if it is cached it wouldn't matter much, yes.
Yes, interesting idea to do this only for the preparation.
In case of "infinite" it is indeed called just once, but if access=true (should be most of the time) it is called twice (?) |
I think the/your biggest concern is that with
I have this in mind, but would rather postpone this a bit. Do you (already) have weightings in mind that might be expensive to evaluate ? Especially when running some user defined scripts etc. to calculate the weight this might become important.
Ah I thought like this its not called twice: while(edgeIter.next()) {
// in a separate PR we move the accessEnc check into the weighting:
double weight = weighting.calcWeight(edgeIter, reverse, prevEdge);
if(Double.isInfinite(weight)) continue; // block access
// use weight here
} |
Yes, I think we need to refactor this, but in a later PR and likely with all the other usages of EdgeFilter and EdgeExplorer.
👍
Probably we should indeed benchmark before optimizing :)
I meant the way it was before with isForward. If isForward is true, then we have called calcWeigh and then again for evaluating the actual weighting. |
This PR adds a new class
PrepareCHGraph
(+ correspondingPrepareCHEdgeExplorer+Iterator
) that basically wrapsCHGraph
to provide a smaller interface used byPrepareContractionHierarchies
and theNodeContractor
s.These are the most important changes:
There is
PrepareCHIterator.getWeight()
, which either returns the shortcut weight or calculates the weight of an original edge. Previously we exposed theEdgeIteratorState
interface of the iterator, and passed it intoPrepareWeighting
again to make this distinction. Now that theWeighting
is available inside the iterator we can achieve the same without exposing all theEdgeIteratorState
methods. Maybe this approach has benefits in other places as well like checking foredgeWeight==infinity
instead of usingaccessEnc
inside the edge iterator).There is a new class
NodeBasedWitnessPathSearcher
, which at the moment is a copy ofDijkstraOneToMany
minus some methods that are not needed for CH preparation likecalcPaths()
etc. This is partly necessary, becausePrepareCHGraph
is not aGraph
and partly useful, because we can adjust it to the CH preparation without having to worry about other uses ofDijkstraOneToMany
. It sure means there is some kind of code duplication, but I think this is not so much of a problem here/its better than making the code overly generic in this case (imo, happy to hear other opinions).PrepareCHGraph
has methods likecreateIn/OutEdgeExplorer
, so we no longer need to pass the edge filter/access enc when creating the explorer.Why is this useful ? Its maybe not so apparent here already, but it takes some 'load' off
CHGraph
, becauseCHGraph
is used in multiple ways that can be optimized for their specific use. Currently we use it to:PrepareCHGraph
does 2) and 3) for now, even though this will probably be split further into two separate parts soon. Most importantlyPrepareCHGraph
is not (compared to the previousCHGraph
) concerned with 1) and 4) anymore.So far there is no new functionality, but this PR is part of a bigger refactoring: #1780. Its even very likely that the code changed here will change again very soon. I still thought it might be useful to merge this already to prevent a giant pull request at the end of this refactoring. What do you think ?