Skip to content

Commit

Permalink
improve landmark algorithm regarding heuristic change
Browse files Browse the repository at this point in the history
  • Loading branch information
karussell committed Mar 20, 2017
1 parent 4c701bc commit 1152934
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 73 deletions.
112 changes: 72 additions & 40 deletions core/src/main/java/com/graphhopper/routing/AStarBidirection.java
Expand Up @@ -31,6 +31,8 @@
import com.graphhopper.storage.SPTEntry;
import com.graphhopper.util.*;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
Expand Down Expand Up @@ -67,10 +69,11 @@ public class AStarBidirection extends AbstractBidirAlgo implements Recalculation
protected IntObjectMap<AStarEntry> bestWeightMapTo;
private IntObjectMap<AStarEntry> bestWeightMapOther;
private ConsistentWeightApproximator weightApprox;
private PriorityQueue<AStarEntry> prioQueueOpenSetFrom;
private PriorityQueue<AStarEntry> prioQueueOpenSetTo;
private final IntHashSet ignoreExplorationFrom = new IntHashSet();
private final IntHashSet ignoreExplorationTo = new IntHashSet();
private PriorityQueue<AStarEntry> pqOpenSetFrom;
private PriorityQueue<AStarEntry> pqOpenSetTo;
private IntHashSet ignoreExplorationFrom = new IntHashSet();
private IntHashSet ignoreExplorationTo = new IntHashSet();
private boolean updateBestPath = true;

public AStarBidirection(Graph graph, Weighting weighting, TraversalMode tMode) {
super(graph, weighting, tMode);
Expand All @@ -82,10 +85,10 @@ public AStarBidirection(Graph graph, Weighting weighting, TraversalMode tMode) {
}

protected void initCollections(int size) {
prioQueueOpenSetFrom = new PriorityQueue<AStarEntry>(size);
pqOpenSetFrom = new PriorityQueue<AStarEntry>(size);
bestWeightMapFrom = new GHIntObjectHashMap<AStarEntry>(size);

prioQueueOpenSetTo = new PriorityQueue<AStarEntry>(size);
pqOpenSetTo = new PriorityQueue<AStarEntry>(size);
bestWeightMapTo = new GHIntObjectHashMap<AStarEntry>(size);
}

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

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

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

@Override
boolean fillEdgesFrom() {
if (prioQueueOpenSetFrom.isEmpty())
if (pqOpenSetFrom.isEmpty())
return false;

currFrom = prioQueueOpenSetFrom.poll();
currFrom = pqOpenSetFrom.poll();
bestWeightMapOther = bestWeightMapTo;
fillEdges(currFrom, prioQueueOpenSetFrom, bestWeightMapFrom, ignoreExplorationFrom, outEdgeExplorer, false);
fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, ignoreExplorationFrom, outEdgeExplorer, false);
visitedCountFrom++;
return true;
}

@Override
boolean fillEdgesTo() {
if (prioQueueOpenSetTo.isEmpty())
if (pqOpenSetTo.isEmpty())
return false;

currTo = prioQueueOpenSetTo.poll();
currTo = pqOpenSetTo.poll();
bestWeightMapOther = bestWeightMapFrom;
fillEdges(currTo, prioQueueOpenSetTo, bestWeightMapTo, ignoreExplorationTo, inEdgeExplorer, true);
fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, ignoreExplorationTo, inEdgeExplorer, true);
visitedCountTo++;
return true;
}
Expand Down Expand Up @@ -256,7 +259,9 @@ private void fillEdges(AStarEntry currEdge, PriorityQueue<AStarEntry> prioQueueO

ase.parent = currEdge;
prioQueueOpenSet.add(ase);
updateBestPath(iter, ase, traversalId);

if (updateBestPath)
updateBestPath(iter, ase, traversalId);
}
}
}
Expand Down Expand Up @@ -290,42 +295,69 @@ public void updateBestPath(EdgeIteratorState edgeState, AStarEntry entryCurrent,
}
}

IntObjectMap<AStarEntry> getBestFromMap() {
return bestWeightMapFrom;
}

IntObjectMap<AStarEntry> getBestToMap() {
return bestWeightMapTo;
}

void setBestOtherMap(IntObjectMap<AStarEntry> other) {
bestWeightMapOther = other;
}

void setFromDataStructures(AStarBidirection astar) {
pqOpenSetFrom = astar.pqOpenSetFrom;
bestWeightMapFrom = astar.bestWeightMapFrom;
finishedFrom = astar.finishedFrom;
currFrom = astar.currFrom;
visitedCountFrom = astar.visitedCountFrom;
ignoreExplorationFrom = astar.ignoreExplorationFrom;
weightApprox.setFrom(astar.currFrom.adjNode);
// outEdgeExplorer
}

void setToDataStructures(AStarBidirection astar) {
pqOpenSetTo = astar.pqOpenSetTo;
bestWeightMapTo = astar.bestWeightMapTo;
finishedTo = astar.finishedTo;
currTo = astar.currTo;
visitedCountTo = astar.visitedCountTo;
ignoreExplorationTo = astar.ignoreExplorationTo;
weightApprox.setTo(astar.currTo.adjNode);
// inEdgeExplorer
}

@Override
public void afterHeuristicChange(boolean forward, boolean backward) {
if (forward) {
ignoreExplorationFrom.ensureCapacity(bestWeightMapFrom.size());
bestWeightMapFrom.forEach(new IntObjectPredicate<AStarEntry>() {
@Override
public boolean apply(int key, AStarEntry value) {

// update PQ due to heuristic change (i.e. weight changed)
if (!pqOpenSetFrom.isEmpty()) {
// copy into temporary array to avoid pointer change of PQ
AStarEntry[] entries = pqOpenSetFrom.toArray(new AStarEntry[pqOpenSetFrom.size()]);
pqOpenSetFrom.clear();
for (AStarEntry value : entries) {
value.weight = value.weightOfVisitedPath + weightApprox.approximate(value.adjNode, false);
ignoreExplorationFrom.add(key);
return true;
}
});
// does not work for edge based
// ignoreExplorationFrom.add(value.adjNode);

// update PQ with new entries
if (!prioQueueOpenSetFrom.isEmpty()) {
final PriorityQueue<AStarEntry> tmpFrom = new PriorityQueue<>(prioQueueOpenSetFrom.size());
tmpFrom.addAll(prioQueueOpenSetFrom);
prioQueueOpenSetFrom = tmpFrom;
pqOpenSetFrom.add(value);
}
}
}

if (backward) {
ignoreExplorationTo.ensureCapacity(bestWeightMapTo.size());
bestWeightMapTo.forEach(new IntObjectPredicate<AStarEntry>() {
@Override
public boolean apply(int key, AStarEntry value) {
if (!pqOpenSetTo.isEmpty()) {
AStarEntry[] entries = pqOpenSetTo.toArray(new AStarEntry[pqOpenSetTo.size()]);
pqOpenSetTo.clear();
for (AStarEntry value : entries) {
value.weight = value.weightOfVisitedPath + weightApprox.approximate(value.adjNode, true);
ignoreExplorationTo.add(key);
return true;
}
});
// ignoreExplorationTo.add(value.adjNode);

if (!prioQueueOpenSetTo.isEmpty()) {
final PriorityQueue<AStarEntry> tmpTo = new PriorityQueue<>(prioQueueOpenSetTo.size());
tmpTo.addAll(prioQueueOpenSetTo);
prioQueueOpenSetTo = tmpTo;
pqOpenSetTo.add(value);
}
}
}
}
Expand Down
Expand Up @@ -42,8 +42,8 @@ public class DijkstraBidirectionRef extends AbstractBidirAlgo {
protected SPTEntry currFrom;
protected SPTEntry currTo;
protected PathBidirRef bestPath;
private PriorityQueue<SPTEntry> openSetFrom;
private PriorityQueue<SPTEntry> openSetTo;
private PriorityQueue<SPTEntry> pqOpenSetFrom;
private PriorityQueue<SPTEntry> pqOpenSetTo;
private boolean updateBestPath = true;

public DijkstraBidirectionRef(Graph graph, Weighting weighting, TraversalMode tMode) {
Expand All @@ -53,17 +53,17 @@ public DijkstraBidirectionRef(Graph graph, Weighting weighting, TraversalMode tM
}

protected void initCollections(int size) {
openSetFrom = new PriorityQueue<SPTEntry>(size);
pqOpenSetFrom = new PriorityQueue<SPTEntry>(size);
bestWeightMapFrom = new GHIntObjectHashMap<SPTEntry>(size);

openSetTo = new PriorityQueue<SPTEntry>(size);
pqOpenSetTo = new PriorityQueue<SPTEntry>(size);
bestWeightMapTo = new GHIntObjectHashMap<SPTEntry>(size);
}

@Override
public void initFrom(int from, double weight) {
currFrom = createSPTEntry(from, weight);
openSetFrom.add(currFrom);
pqOpenSetFrom.add(currFrom);
if (!traversalMode.isEdgeBased()) {
bestWeightMapFrom.put(from, currFrom);
if (currTo != null) {
Expand All @@ -82,7 +82,7 @@ public void initFrom(int from, double weight) {
@Override
public void initTo(int to, double weight) {
currTo = createSPTEntry(to, weight);
openSetTo.add(currTo);
pqOpenSetTo.add(currTo);
if (!traversalMode.isEdgeBased()) {
bestWeightMapTo.put(to, currTo);
if (currFrom != null) {
Expand Down Expand Up @@ -124,23 +124,23 @@ protected double getCurrentToWeight() {

@Override
public boolean fillEdgesFrom() {
if (openSetFrom.isEmpty())
if (pqOpenSetFrom.isEmpty())
return false;

currFrom = openSetFrom.poll();
currFrom = pqOpenSetFrom.poll();
bestWeightMapOther = bestWeightMapTo;
fillEdges(currFrom, openSetFrom, bestWeightMapFrom, outEdgeExplorer, false);
fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, outEdgeExplorer, false);
visitedCountFrom++;
return true;
}

@Override
public boolean fillEdgesTo() {
if (openSetTo.isEmpty())
if (pqOpenSetTo.isEmpty())
return false;
currTo = openSetTo.poll();
currTo = pqOpenSetTo.poll();
bestWeightMapOther = bestWeightMapFrom;
fillEdges(currTo, openSetTo, bestWeightMapTo, inEdgeExplorer, true);
fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, inEdgeExplorer, true);
visitedCountTo++;
return true;
}
Expand Down Expand Up @@ -232,7 +232,7 @@ void setBestOtherMap(IntObjectMap<SPTEntry> other) {
}

void setFromDataStructures(DijkstraBidirectionRef dijkstra) {
openSetFrom = dijkstra.openSetFrom;
pqOpenSetFrom = dijkstra.pqOpenSetFrom;
bestWeightMapFrom = dijkstra.bestWeightMapFrom;
finishedFrom = dijkstra.finishedFrom;
currFrom = dijkstra.currFrom;
Expand All @@ -241,7 +241,7 @@ void setFromDataStructures(DijkstraBidirectionRef dijkstra) {
}

void setToDataStructures(DijkstraBidirectionRef dijkstra) {
openSetTo = dijkstra.openSetTo;
pqOpenSetTo = dijkstra.pqOpenSetTo;
bestWeightMapTo = dijkstra.bestWeightMapTo;
finishedTo = dijkstra.finishedTo;
currTo = dijkstra.currTo;
Expand Down
Expand Up @@ -327,7 +327,8 @@ public void createPreparations(GraphHopperStorage ghStorage, TraversalMode trave
for (Weighting weighting : getWeightings()) {
Double maximumWeight = maximumWeights.get(weighting.getName());
if (maximumWeight == null)
throw new IllegalStateException("maximumWeight cannot be null. Default should be just negative");
throw new IllegalStateException("maximumWeight cannot be null. Default should be just negative. " +
"Couldn't find " + weighting.getName() + " in " + maximumWeights);

PrepareLandmarks tmpPrepareLM = new PrepareLandmarks(ghStorage.getDirectory(), ghStorage,
weighting, traversalMode, landmarkCount, activeLandmarkCount).
Expand Down
35 changes: 20 additions & 15 deletions core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java
Expand Up @@ -49,15 +49,16 @@ public String toString() {
private int[] activeFromIntWeights;
private int[] activeToIntWeights;
private double epsilon = 1;
private int to = -1, counter;
private int to = -1;
// do activate landmark recalculation
private boolean doALMRecalc = true;
private final double factor;
private final boolean reverse;
private final int maxBaseNodes;
private final Graph graph;
private final WeightApproximator fallBackApproximation;
private boolean fallback = false;
private final GHIntObjectHashMap<VirtEntry> virtNodeMap;
private boolean changeActiveLandmarks = true;

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

@Override
public double approximate(final int queryNode) {
if (fallback || lms.isEmpty())
if (!doALMRecalc && fallback || lms.isEmpty())
return fallBackApproximation.approximate(queryNode);

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

if (changeActiveLandmarks) {
// select better active landmarks, LATER: use 'success' statistics about last active landmark
// 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
if (counter == 0) {
boolean res = lms.initActiveLandmarks(node, to, activeLandmarks, activeFromIntWeights, activeToIntWeights, reverse);
if (!res) {
// note: fallback==true means forever true!
fallback = true;
return fallBackApproximation.approximate(queryNode);
}
// select better active landmarks, LATER: use 'success' statistics about last active landmark
// 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
if (doALMRecalc) {
doALMRecalc = false;
boolean res = lms.initActiveLandmarks(node, to, activeLandmarks, activeFromIntWeights, activeToIntWeights, reverse);
if (!res) {
// note: fallback==true means forever true!
fallback = true;
return fallBackApproximation.approximate(queryNode);
}

counter++;
}

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

/**
* This method forces a lazy recalculation of the active landmark set e.g. necessary after the 'to' node changed.
*/
public void triggerActiveLandmarkRecalculation() {
doALMRecalc = true;
}

@Override
public String toString() {
return "landmarks";
Expand Down
Expand Up @@ -231,7 +231,6 @@ public void createLandmarks() {

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

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

int subnetworkFrom = subnetworkStorage.getSubnetwork(fromNode);
int subnetworkTo = subnetworkStorage.getSubnetwork(toNode);
if (subnetworkFrom == UNCLEAR_SUBNETWORK || subnetworkTo == UNCLEAR_SUBNETWORK)
if (subnetworkFrom <= UNCLEAR_SUBNETWORK || subnetworkTo <= UNCLEAR_SUBNETWORK)
return false;
if (subnetworkFrom != subnetworkTo) {
throw new ConnectionNotFoundException("Connection between locations not found. Different subnetworks " + subnetworkFrom + " vs. " + subnetworkTo, new HashMap<String, Object>());
Expand Down
Expand Up @@ -49,7 +49,7 @@ public class BaseServletTester {
private static final MediaType MT_JSON = MediaType.parse("application/json; charset=utf-8");
private static final MediaType MT_XML = MediaType.parse("application/gpx+xml; charset=utf-8");
protected static final Logger LOGGER = LoggerFactory.getLogger(BaseServletTester.class);
private final OkHttpClient client = new OkHttpClient.Builder().connectTimeout(1020, TimeUnit.SECONDS).build();
private final OkHttpClient client = new OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
protected static int port;
private static GHServer server;
protected Injector injector;
Expand Down

0 comments on commit 1152934

Please sign in to comment.