/* * Licensed to GraphHopper GmbH under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * GraphHopper GmbH licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.graphhopper.routing; import com.carrotsearch.hppc.IntObjectMap; import com.graphhopper.coll.GHIntObjectHashMap; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; import com.graphhopper.storage.SPTEntry; import com.graphhopper.util.*; import java.util.PriorityQueue; /** * Calculates best path in bidirectional way. * <p> * 'Ref' stands for reference implementation and is using the normal Java-'reference'-way. * <p> * * @author Peter Karich */ public class DijkstraBidirectionRef extends AbstractBidirAlgo { protected IntObjectMap<SPTEntry> bestWeightMapFrom; protected IntObjectMap<SPTEntry> bestWeightMapTo; protected IntObjectMap<SPTEntry> bestWeightMapOther; protected SPTEntry currFrom; protected SPTEntry currTo; protected PathBidirRef bestPath; private PriorityQueue<SPTEntry> pqOpenSetFrom; private PriorityQueue<SPTEntry> pqOpenSetTo; private boolean updateBestPath = true; public DijkstraBidirectionRef(Graph graph, Weighting weighting, TraversalMode tMode) { super(graph, weighting, tMode); int size = Math.min(Math.max(200, graph.getNodes() / 10), 150_000); initCollections(size); } protected void initCollections(int size) { pqOpenSetFrom = new PriorityQueue<SPTEntry>(size); bestWeightMapFrom = new GHIntObjectHashMap<SPTEntry>(size); pqOpenSetTo = new PriorityQueue<SPTEntry>(size); bestWeightMapTo = new GHIntObjectHashMap<SPTEntry>(size); } @Override public void initFrom(int from, double weight) { currFrom = createSPTEntry(from, weight); pqOpenSetFrom.add(currFrom); if (!traversalMode.isEdgeBased()) { bestWeightMapFrom.put(from, currFrom); if (currTo != null) { bestWeightMapOther = bestWeightMapTo; updateBestPath(GHUtility.getEdge(graph, from, currTo.adjNode), currTo, from); } } else if (currTo != null && currTo.adjNode == from) { // special case of identical start and end bestPath.sptEntry = currFrom; bestPath.edgeTo = currTo; finishedFrom = true; finishedTo = true; } } @Override public void initTo(int to, double weight) { currTo = createSPTEntry(to, weight); pqOpenSetTo.add(currTo); if (!traversalMode.isEdgeBased()) { bestWeightMapTo.put(to, currTo); if (currFrom != null) { bestWeightMapOther = bestWeightMapFrom; updateBestPath(GHUtility.getEdge(graph, currFrom.adjNode, to), currFrom, to); } } else if (currFrom != null && currFrom.adjNode == to) { // special case of identical start and end bestPath.sptEntry = currFrom; bestPath.edgeTo = currTo; finishedFrom = true; finishedTo = true; } } @Override protected Path createAndInitPath() { bestPath = new PathBidirRef(graph, weighting); return bestPath; } @Override protected Path extractPath() { if (finished()) return bestPath.extract(); return bestPath; } @Override protected double getCurrentFromWeight() { return currFrom.weight; } @Override protected double getCurrentToWeight() { return currTo.weight; } @Override public boolean fillEdgesFrom() { if (pqOpenSetFrom.isEmpty()) return false; currFrom = pqOpenSetFrom.poll(); bestWeightMapOther = bestWeightMapTo; fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, outEdgeExplorer, false); visitedCountFrom++; return true; } @Override public boolean fillEdgesTo() { if (pqOpenSetTo.isEmpty()) return false; currTo = pqOpenSetTo.poll(); bestWeightMapOther = bestWeightMapFrom; fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, inEdgeExplorer, true); visitedCountTo++; return true; } // http://www.cs.princeton.edu/courses/archive/spr06/cos423/Handouts/EPP%20shortest%20path%20algorithms.pdf // a node from overlap may not be on the best path! // => when scanning an arc (v, w) in the forward search and w is scanned in the reverseOrder // search, update extractPath = μ if df (v) + (v, w) + dr (w) < μ @Override public boolean finished() { if (finishedFrom || finishedTo) return true; return currFrom.weight + currTo.weight >= bestPath.getWeight(); } void fillEdges(SPTEntry currEdge, PriorityQueue<SPTEntry> prioQueue, IntObjectMap<SPTEntry> bestWeightMap, EdgeExplorer explorer, boolean reverse) { EdgeIterator iter = explorer.setBaseNode(currEdge.adjNode); while (iter.next()) { if (!accept(iter, currEdge.edge)) continue; int traversalId = traversalMode.createTraversalId(iter, reverse); double tmpWeight = weighting.calcWeight(iter, reverse, currEdge.edge) + currEdge.weight; if (Double.isInfinite(tmpWeight)) continue; SPTEntry ee = bestWeightMap.get(traversalId); if (ee == null) { ee = new SPTEntry(iter.getEdge(), iter.getAdjNode(), tmpWeight); ee.parent = currEdge; bestWeightMap.put(traversalId, ee); prioQueue.add(ee); } else if (ee.weight > tmpWeight) { prioQueue.remove(ee); ee.edge = iter.getEdge(); ee.weight = tmpWeight; ee.parent = currEdge; prioQueue.add(ee); } else continue; if (updateBestPath) updateBestPath(iter, ee, traversalId); } } @Override protected void updateBestPath(EdgeIteratorState edgeState, SPTEntry entryCurrent, int traversalId) { SPTEntry entryOther = bestWeightMapOther.get(traversalId); if (entryOther == null) return; boolean reverse = bestWeightMapFrom == bestWeightMapOther; // update μ double newWeight = entryCurrent.weight + entryOther.weight; if (traversalMode.isEdgeBased()) { if (entryOther.edge != entryCurrent.edge) throw new IllegalStateException("cannot happen for edge based execution of " + getName()); if (entryOther.adjNode != entryCurrent.adjNode) { // prevents the path to contain the edge at the meeting point twice and subtract the weight (excluding turn weight => no previous edge) entryCurrent = entryCurrent.parent; newWeight -= weighting.calcWeight(edgeState, reverse, EdgeIterator.NO_EDGE); } else if (!traversalMode.hasUTurnSupport()) // we detected a u-turn at meeting point, skip if not supported return; } if (newWeight < bestPath.getWeight()) { bestPath.setSwitchToFrom(reverse); bestPath.setSPTEntry(entryCurrent); bestPath.setWeight(newWeight); bestPath.setSPTEntryTo(entryOther); } } IntObjectMap<SPTEntry> getBestFromMap() { return bestWeightMapFrom; } IntObjectMap<SPTEntry> getBestToMap() { return bestWeightMapTo; } void setBestOtherMap(IntObjectMap<SPTEntry> other) { bestWeightMapOther = other; } void setFromDataStructures(DijkstraBidirectionRef dijkstra) { pqOpenSetFrom = dijkstra.pqOpenSetFrom; bestWeightMapFrom = dijkstra.bestWeightMapFrom; finishedFrom = dijkstra.finishedFrom; currFrom = dijkstra.currFrom; visitedCountFrom = dijkstra.visitedCountFrom; // outEdgeExplorer } void setToDataStructures(DijkstraBidirectionRef dijkstra) { pqOpenSetTo = dijkstra.pqOpenSetTo; bestWeightMapTo = dijkstra.bestWeightMapTo; finishedTo = dijkstra.finishedTo; currTo = dijkstra.currTo; visitedCountTo = dijkstra.visitedCountTo; // inEdgeExplorer } protected void setUpdateBestPath(boolean b) { updateBestPath = b; } void setBestPath(PathBidirRef bestPath) { this.bestPath = bestPath; } @Override public String getName() { return Parameters.Algorithms.DIJKSTRA_BI; } }