Skip to content

Commit 1152934

Browse files
committed
improve landmark algorithm regarding heuristic change
1 parent 4c701bc commit 1152934

File tree

6 files changed

+110
-73
lines changed

6 files changed

+110
-73
lines changed

core/src/main/java/com/graphhopper/routing/AStarBidirection.java

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.graphhopper.storage.SPTEntry;
3232
import com.graphhopper.util.*;
3333

34+
import java.util.ArrayList;
35+
import java.util.List;
3436
import java.util.PriorityQueue;
3537

3638
/**
@@ -67,10 +69,11 @@ public class AStarBidirection extends AbstractBidirAlgo implements Recalculation
6769
protected IntObjectMap<AStarEntry> bestWeightMapTo;
6870
private IntObjectMap<AStarEntry> bestWeightMapOther;
6971
private ConsistentWeightApproximator weightApprox;
70-
private PriorityQueue<AStarEntry> prioQueueOpenSetFrom;
71-
private PriorityQueue<AStarEntry> prioQueueOpenSetTo;
72-
private final IntHashSet ignoreExplorationFrom = new IntHashSet();
73-
private final IntHashSet ignoreExplorationTo = new IntHashSet();
72+
private PriorityQueue<AStarEntry> pqOpenSetFrom;
73+
private PriorityQueue<AStarEntry> pqOpenSetTo;
74+
private IntHashSet ignoreExplorationFrom = new IntHashSet();
75+
private IntHashSet ignoreExplorationTo = new IntHashSet();
76+
private boolean updateBestPath = true;
7477

7578
public AStarBidirection(Graph graph, Weighting weighting, TraversalMode tMode) {
7679
super(graph, weighting, tMode);
@@ -82,10 +85,10 @@ public AStarBidirection(Graph graph, Weighting weighting, TraversalMode tMode) {
8285
}
8386

8487
protected void initCollections(int size) {
85-
prioQueueOpenSetFrom = new PriorityQueue<AStarEntry>(size);
88+
pqOpenSetFrom = new PriorityQueue<AStarEntry>(size);
8689
bestWeightMapFrom = new GHIntObjectHashMap<AStarEntry>(size);
8790

88-
prioQueueOpenSetTo = new PriorityQueue<AStarEntry>(size);
91+
pqOpenSetTo = new PriorityQueue<AStarEntry>(size);
8992
bestWeightMapTo = new GHIntObjectHashMap<AStarEntry>(size);
9093
}
9194

@@ -110,7 +113,7 @@ protected SPTEntry createSPTEntry(int node, double weight) {
110113
public void initFrom(int from, double weight) {
111114
currFrom = new AStarEntry(EdgeIterator.NO_EDGE, from, weight, weight);
112115
weightApprox.setFrom(from);
113-
prioQueueOpenSetFrom.add(currFrom);
116+
pqOpenSetFrom.add(currFrom);
114117

115118
if (currTo != null) {
116119
currFrom.weight += weightApprox.approximate(currFrom.adjNode, false);
@@ -136,7 +139,7 @@ public void initFrom(int from, double weight) {
136139
public void initTo(int to, double weight) {
137140
currTo = new AStarEntry(EdgeIterator.NO_EDGE, to, weight, weight);
138141
weightApprox.setTo(to);
139-
prioQueueOpenSetTo.add(currTo);
142+
pqOpenSetTo.add(currTo);
140143

141144
if (currFrom != null) {
142145
currFrom.weight += weightApprox.approximate(currFrom.adjNode, false);
@@ -193,24 +196,24 @@ protected boolean finished() {
193196

194197
@Override
195198
boolean fillEdgesFrom() {
196-
if (prioQueueOpenSetFrom.isEmpty())
199+
if (pqOpenSetFrom.isEmpty())
197200
return false;
198201

199-
currFrom = prioQueueOpenSetFrom.poll();
202+
currFrom = pqOpenSetFrom.poll();
200203
bestWeightMapOther = bestWeightMapTo;
201-
fillEdges(currFrom, prioQueueOpenSetFrom, bestWeightMapFrom, ignoreExplorationFrom, outEdgeExplorer, false);
204+
fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, ignoreExplorationFrom, outEdgeExplorer, false);
202205
visitedCountFrom++;
203206
return true;
204207
}
205208

206209
@Override
207210
boolean fillEdgesTo() {
208-
if (prioQueueOpenSetTo.isEmpty())
211+
if (pqOpenSetTo.isEmpty())
209212
return false;
210213

211-
currTo = prioQueueOpenSetTo.poll();
214+
currTo = pqOpenSetTo.poll();
212215
bestWeightMapOther = bestWeightMapFrom;
213-
fillEdges(currTo, prioQueueOpenSetTo, bestWeightMapTo, ignoreExplorationTo, inEdgeExplorer, true);
216+
fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, ignoreExplorationTo, inEdgeExplorer, true);
214217
visitedCountTo++;
215218
return true;
216219
}
@@ -256,7 +259,9 @@ private void fillEdges(AStarEntry currEdge, PriorityQueue<AStarEntry> prioQueueO
256259

257260
ase.parent = currEdge;
258261
prioQueueOpenSet.add(ase);
259-
updateBestPath(iter, ase, traversalId);
262+
263+
if (updateBestPath)
264+
updateBestPath(iter, ase, traversalId);
260265
}
261266
}
262267
}
@@ -290,42 +295,69 @@ public void updateBestPath(EdgeIteratorState edgeState, AStarEntry entryCurrent,
290295
}
291296
}
292297

298+
IntObjectMap<AStarEntry> getBestFromMap() {
299+
return bestWeightMapFrom;
300+
}
301+
302+
IntObjectMap<AStarEntry> getBestToMap() {
303+
return bestWeightMapTo;
304+
}
305+
306+
void setBestOtherMap(IntObjectMap<AStarEntry> other) {
307+
bestWeightMapOther = other;
308+
}
309+
310+
void setFromDataStructures(AStarBidirection astar) {
311+
pqOpenSetFrom = astar.pqOpenSetFrom;
312+
bestWeightMapFrom = astar.bestWeightMapFrom;
313+
finishedFrom = astar.finishedFrom;
314+
currFrom = astar.currFrom;
315+
visitedCountFrom = astar.visitedCountFrom;
316+
ignoreExplorationFrom = astar.ignoreExplorationFrom;
317+
weightApprox.setFrom(astar.currFrom.adjNode);
318+
// outEdgeExplorer
319+
}
320+
321+
void setToDataStructures(AStarBidirection astar) {
322+
pqOpenSetTo = astar.pqOpenSetTo;
323+
bestWeightMapTo = astar.bestWeightMapTo;
324+
finishedTo = astar.finishedTo;
325+
currTo = astar.currTo;
326+
visitedCountTo = astar.visitedCountTo;
327+
ignoreExplorationTo = astar.ignoreExplorationTo;
328+
weightApprox.setTo(astar.currTo.adjNode);
329+
// inEdgeExplorer
330+
}
331+
293332
@Override
294333
public void afterHeuristicChange(boolean forward, boolean backward) {
295334
if (forward) {
296-
ignoreExplorationFrom.ensureCapacity(bestWeightMapFrom.size());
297-
bestWeightMapFrom.forEach(new IntObjectPredicate<AStarEntry>() {
298-
@Override
299-
public boolean apply(int key, AStarEntry value) {
335+
336+
// update PQ due to heuristic change (i.e. weight changed)
337+
if (!pqOpenSetFrom.isEmpty()) {
338+
// copy into temporary array to avoid pointer change of PQ
339+
AStarEntry[] entries = pqOpenSetFrom.toArray(new AStarEntry[pqOpenSetFrom.size()]);
340+
pqOpenSetFrom.clear();
341+
for (AStarEntry value : entries) {
300342
value.weight = value.weightOfVisitedPath + weightApprox.approximate(value.adjNode, false);
301-
ignoreExplorationFrom.add(key);
302-
return true;
303-
}
304-
});
343+
// does not work for edge based
344+
// ignoreExplorationFrom.add(value.adjNode);
305345

306-
// update PQ with new entries
307-
if (!prioQueueOpenSetFrom.isEmpty()) {
308-
final PriorityQueue<AStarEntry> tmpFrom = new PriorityQueue<>(prioQueueOpenSetFrom.size());
309-
tmpFrom.addAll(prioQueueOpenSetFrom);
310-
prioQueueOpenSetFrom = tmpFrom;
346+
pqOpenSetFrom.add(value);
347+
}
311348
}
312349
}
313350

314351
if (backward) {
315-
ignoreExplorationTo.ensureCapacity(bestWeightMapTo.size());
316-
bestWeightMapTo.forEach(new IntObjectPredicate<AStarEntry>() {
317-
@Override
318-
public boolean apply(int key, AStarEntry value) {
352+
if (!pqOpenSetTo.isEmpty()) {
353+
AStarEntry[] entries = pqOpenSetTo.toArray(new AStarEntry[pqOpenSetTo.size()]);
354+
pqOpenSetTo.clear();
355+
for (AStarEntry value : entries) {
319356
value.weight = value.weightOfVisitedPath + weightApprox.approximate(value.adjNode, true);
320-
ignoreExplorationTo.add(key);
321-
return true;
322-
}
323-
});
357+
// ignoreExplorationTo.add(value.adjNode);
324358

325-
if (!prioQueueOpenSetTo.isEmpty()) {
326-
final PriorityQueue<AStarEntry> tmpTo = new PriorityQueue<>(prioQueueOpenSetTo.size());
327-
tmpTo.addAll(prioQueueOpenSetTo);
328-
prioQueueOpenSetTo = tmpTo;
359+
pqOpenSetTo.add(value);
360+
}
329361
}
330362
}
331363
}

core/src/main/java/com/graphhopper/routing/DijkstraBidirectionRef.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public class DijkstraBidirectionRef extends AbstractBidirAlgo {
4242
protected SPTEntry currFrom;
4343
protected SPTEntry currTo;
4444
protected PathBidirRef bestPath;
45-
private PriorityQueue<SPTEntry> openSetFrom;
46-
private PriorityQueue<SPTEntry> openSetTo;
45+
private PriorityQueue<SPTEntry> pqOpenSetFrom;
46+
private PriorityQueue<SPTEntry> pqOpenSetTo;
4747
private boolean updateBestPath = true;
4848

4949
public DijkstraBidirectionRef(Graph graph, Weighting weighting, TraversalMode tMode) {
@@ -53,17 +53,17 @@ public DijkstraBidirectionRef(Graph graph, Weighting weighting, TraversalMode tM
5353
}
5454

5555
protected void initCollections(int size) {
56-
openSetFrom = new PriorityQueue<SPTEntry>(size);
56+
pqOpenSetFrom = new PriorityQueue<SPTEntry>(size);
5757
bestWeightMapFrom = new GHIntObjectHashMap<SPTEntry>(size);
5858

59-
openSetTo = new PriorityQueue<SPTEntry>(size);
59+
pqOpenSetTo = new PriorityQueue<SPTEntry>(size);
6060
bestWeightMapTo = new GHIntObjectHashMap<SPTEntry>(size);
6161
}
6262

6363
@Override
6464
public void initFrom(int from, double weight) {
6565
currFrom = createSPTEntry(from, weight);
66-
openSetFrom.add(currFrom);
66+
pqOpenSetFrom.add(currFrom);
6767
if (!traversalMode.isEdgeBased()) {
6868
bestWeightMapFrom.put(from, currFrom);
6969
if (currTo != null) {
@@ -82,7 +82,7 @@ public void initFrom(int from, double weight) {
8282
@Override
8383
public void initTo(int to, double weight) {
8484
currTo = createSPTEntry(to, weight);
85-
openSetTo.add(currTo);
85+
pqOpenSetTo.add(currTo);
8686
if (!traversalMode.isEdgeBased()) {
8787
bestWeightMapTo.put(to, currTo);
8888
if (currFrom != null) {
@@ -124,23 +124,23 @@ protected double getCurrentToWeight() {
124124

125125
@Override
126126
public boolean fillEdgesFrom() {
127-
if (openSetFrom.isEmpty())
127+
if (pqOpenSetFrom.isEmpty())
128128
return false;
129129

130-
currFrom = openSetFrom.poll();
130+
currFrom = pqOpenSetFrom.poll();
131131
bestWeightMapOther = bestWeightMapTo;
132-
fillEdges(currFrom, openSetFrom, bestWeightMapFrom, outEdgeExplorer, false);
132+
fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, outEdgeExplorer, false);
133133
visitedCountFrom++;
134134
return true;
135135
}
136136

137137
@Override
138138
public boolean fillEdgesTo() {
139-
if (openSetTo.isEmpty())
139+
if (pqOpenSetTo.isEmpty())
140140
return false;
141-
currTo = openSetTo.poll();
141+
currTo = pqOpenSetTo.poll();
142142
bestWeightMapOther = bestWeightMapFrom;
143-
fillEdges(currTo, openSetTo, bestWeightMapTo, inEdgeExplorer, true);
143+
fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, inEdgeExplorer, true);
144144
visitedCountTo++;
145145
return true;
146146
}
@@ -232,7 +232,7 @@ void setBestOtherMap(IntObjectMap<SPTEntry> other) {
232232
}
233233

234234
void setFromDataStructures(DijkstraBidirectionRef dijkstra) {
235-
openSetFrom = dijkstra.openSetFrom;
235+
pqOpenSetFrom = dijkstra.pqOpenSetFrom;
236236
bestWeightMapFrom = dijkstra.bestWeightMapFrom;
237237
finishedFrom = dijkstra.finishedFrom;
238238
currFrom = dijkstra.currFrom;
@@ -241,7 +241,7 @@ void setFromDataStructures(DijkstraBidirectionRef dijkstra) {
241241
}
242242

243243
void setToDataStructures(DijkstraBidirectionRef dijkstra) {
244-
openSetTo = dijkstra.openSetTo;
244+
pqOpenSetTo = dijkstra.pqOpenSetTo;
245245
bestWeightMapTo = dijkstra.bestWeightMapTo;
246246
finishedTo = dijkstra.finishedTo;
247247
currTo = dijkstra.currTo;

core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ public void createPreparations(GraphHopperStorage ghStorage, TraversalMode trave
327327
for (Weighting weighting : getWeightings()) {
328328
Double maximumWeight = maximumWeights.get(weighting.getName());
329329
if (maximumWeight == null)
330-
throw new IllegalStateException("maximumWeight cannot be null. Default should be just negative");
330+
throw new IllegalStateException("maximumWeight cannot be null. Default should be just negative. " +
331+
"Couldn't find " + weighting.getName() + " in " + maximumWeights);
331332

332333
PrepareLandmarks tmpPrepareLM = new PrepareLandmarks(ghStorage.getDirectory(), ghStorage,
333334
weighting, traversalMode, landmarkCount, activeLandmarkCount).

core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,16 @@ public String toString() {
4949
private int[] activeFromIntWeights;
5050
private int[] activeToIntWeights;
5151
private double epsilon = 1;
52-
private int to = -1, counter;
52+
private int to = -1;
53+
// do activate landmark recalculation
54+
private boolean doALMRecalc = true;
5355
private final double factor;
5456
private final boolean reverse;
5557
private final int maxBaseNodes;
5658
private final Graph graph;
5759
private final WeightApproximator fallBackApproximation;
5860
private boolean fallback = false;
5961
private final GHIntObjectHashMap<VirtEntry> virtNodeMap;
60-
private boolean changeActiveLandmarks = true;
6162

6263
public LMApproximator(Graph graph, int maxBaseNodes, LandmarkStorage lms, int activeCount,
6364
double factor, boolean reverse) {
@@ -115,7 +116,7 @@ public LMApproximator setEpsilon(double epsilon) {
115116

116117
@Override
117118
public double approximate(final int queryNode) {
118-
if (fallback || lms.isEmpty())
119+
if (!doALMRecalc && fallback || lms.isEmpty())
119120
return fallBackApproximation.approximate(queryNode);
120121

121122
int node = queryNode;
@@ -130,19 +131,16 @@ public double approximate(final int queryNode) {
130131
if (node == to)
131132
return 0;
132133

133-
if (changeActiveLandmarks) {
134-
// select better active landmarks, LATER: use 'success' statistics about last active landmark
135-
// we have to update the priority queues and the maps if done in the middle of the search http://cstheory.stackexchange.com/q/36355/13229
136-
if (counter == 0) {
137-
boolean res = lms.initActiveLandmarks(node, to, activeLandmarks, activeFromIntWeights, activeToIntWeights, reverse);
138-
if (!res) {
139-
// note: fallback==true means forever true!
140-
fallback = true;
141-
return fallBackApproximation.approximate(queryNode);
142-
}
134+
// select better active landmarks, LATER: use 'success' statistics about last active landmark
135+
// we have to update the priority queues and the maps if done in the middle of the search http://cstheory.stackexchange.com/q/36355/13229
136+
if (doALMRecalc) {
137+
doALMRecalc = false;
138+
boolean res = lms.initActiveLandmarks(node, to, activeLandmarks, activeFromIntWeights, activeToIntWeights, reverse);
139+
if (!res) {
140+
// note: fallback==true means forever true!
141+
fallback = true;
142+
return fallBackApproximation.approximate(queryNode);
143143
}
144-
145-
counter++;
146144
}
147145

148146
int maxWeightInt = getMaxWeight(node, virtEdgeWeightInt, activeLandmarks, activeFromIntWeights, activeToIntWeights);
@@ -210,6 +208,13 @@ public WeightApproximator reverse() {
210208
return new LMApproximator(graph, maxBaseNodes, lms, activeLandmarks.length, factor, !reverse);
211209
}
212210

211+
/**
212+
* This method forces a lazy recalculation of the active landmark set e.g. necessary after the 'to' node changed.
213+
*/
214+
public void triggerActiveLandmarkRecalculation() {
215+
doALMRecalc = true;
216+
}
217+
213218
@Override
214219
public String toString() {
215220
return "landmarks";

core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ public void createLandmarks() {
231231

232232
LOGGER.info("init landmarks for subnetworks with node count greater than " + minimumNodes + " with factor:" + factor + additionalInfo);
233233

234-
// special subnetwork 0
235234
int[] empty = new int[landmarks];
236235
Arrays.fill(empty, UNSET_SUBNETWORK);
237236
landmarkIDs.add(empty);
@@ -531,7 +530,7 @@ boolean initActiveLandmarks(int fromNode, int toNode, int[] activeLandmarkIndice
531530

532531
int subnetworkFrom = subnetworkStorage.getSubnetwork(fromNode);
533532
int subnetworkTo = subnetworkStorage.getSubnetwork(toNode);
534-
if (subnetworkFrom == UNCLEAR_SUBNETWORK || subnetworkTo == UNCLEAR_SUBNETWORK)
533+
if (subnetworkFrom <= UNCLEAR_SUBNETWORK || subnetworkTo <= UNCLEAR_SUBNETWORK)
535534
return false;
536535
if (subnetworkFrom != subnetworkTo) {
537536
throw new ConnectionNotFoundException("Connection between locations not found. Different subnetworks " + subnetworkFrom + " vs. " + subnetworkTo, new HashMap<String, Object>());

web/src/test/java/com/graphhopper/http/BaseServletTester.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class BaseServletTester {
4949
private static final MediaType MT_JSON = MediaType.parse("application/json; charset=utf-8");
5050
private static final MediaType MT_XML = MediaType.parse("application/gpx+xml; charset=utf-8");
5151
protected static final Logger LOGGER = LoggerFactory.getLogger(BaseServletTester.class);
52-
private final OkHttpClient client = new OkHttpClient.Builder().connectTimeout(1020, TimeUnit.SECONDS).build();
52+
private final OkHttpClient client = new OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
5353
protected static int port;
5454
private static GHServer server;
5555
protected Injector injector;

0 commit comments

Comments
 (0)