/** Introduces the necessary shortcuts for endNode v in the graph. */ int addShortcuts(int v) { shortcuts.clear(); findShortcuts(addScHandler.setNode(v)); int tmpNewShortcuts = 0; for (Shortcut sc : shortcuts.keySet()) { boolean updatedInGraph = false; // check if we need to update some existing shortcut in the graph EdgeSkipIterator iter = vehicleOutExplorer.setBaseNode(sc.from); while (iter.next()) { if (iter.isShortcut() && iter.getAdjNode() == sc.to && prepareEncoder.canBeOverwritten(iter.getFlags(), sc.flags) && iter.getDistance() > sc.weight) { iter.setFlags(sc.flags); iter.setSkippedEdges(sc.skippedEdge1, sc.skippedEdge2); iter.setDistance(sc.weight); setOrigEdgeCount(iter.getEdge(), sc.originalEdges); updatedInGraph = true; break; } } if (!updatedInGraph) { EdgeSkipIterState edgeState = g.shortcut(sc.from, sc.to); edgeState.setDistance(sc.weight).setFlags(sc.flags); edgeState.setSkippedEdges(sc.skippedEdge1, sc.skippedEdge2); setOrigEdgeCount(edgeState.getEdge(), sc.originalEdges); tmpNewShortcuts++; } } return tmpNewShortcuts; }
/** * Calculates the priority of endNode v without changing the graph. Warning: the calculated * priority must NOT depend on priority(v) and therefor findShortcuts should also not depend on * the priority(v). Otherwise updating the priority before contracting in contractNodes() could * lead to a slowishor even endless loop. */ int calculatePriority(int v) { // set of shortcuts that would be added if endNode v would be contracted next. findShortcuts(calcScHandler.setNode(v)); // System.out.println(v + "\t " + tmpShortcuts); // # huge influence: the bigger the less shortcuts gets created and the faster is the // preparation // // every endNode has an 'original edge' number associated. initially it is r=1 // when a new shortcut is introduced then r of the associated edges is summed up: // r(u,w)=r(u,v)+r(v,w) now we can define // originalEdgesCount = σ(v) := sum_{ (u,w) ∈ shortcuts(v) } of r(u, w) int originalEdgesCount = calcScHandler.originalEdgesCount; // for (Shortcut sc : tmpShortcuts) { // originalEdgesCount += sc.originalEdges; // } // # lowest influence on preparation speed or shortcut creation count // (but according to paper should speed up queries) // // number of already contracted neighbors of v int contractedNeighbors = 0; int degree = 0; EdgeSkipIterator iter = calcPrioAllExplorer.setBaseNode(v); while (iter.next()) { degree++; if (iter.isShortcut()) contractedNeighbors++; } // from shortcuts we can compute the edgeDifference // # low influence: with it the shortcut creation is slightly faster // // |shortcuts(v)| − |{(u, v) | v uncontracted}| − |{(v, w) | v uncontracted}| // meanDegree is used instead of outDegree+inDegree as if one endNode is in both directions // only one bucket memory is used. Additionally one shortcut could also stand for two // directions. int edgeDifference = calcScHandler.shortcuts - degree; // according to the paper do a simple linear combination of the properties to get the priority. // this is the current optimum for unterfranken: return 10 * edgeDifference + originalEdgesCount + contractedNeighbors; }
@Test public void testExpandMultipleSkippedNodes() { LevelGraphStorage g = (LevelGraphStorage) createGraph(20); EdgeSkipIterator iter = g.edge(0, 1, 10, true); // 1 g.edge(1, 2, 10, true); // 2 g.edge(2, 3, 10, true); // 3 g.edge(3, 4, 10, true); // 4 g.setLevel(1, -1); g.setLevel(2, -1); g.edge(0, 3, 30, CarStreetType.flagsDefault(true)).skippedEdge(iter.edge()); // 5 Path4Shortcuts path = new Path4Shortcuts(g, ShortestCarCalc.DEFAULT); path.edgeEntry = new EdgeEntry(5, 3, 30); path.edgeEntry.parent = new EdgeEntry(-1, 0, 0); path.edgeTo = new EdgeEntry(4, 3, 10); path.edgeTo.parent = new EdgeEntry(EdgeIterator.NO_EDGE, 4, 0); Path p = path.extract(); assertEquals(Helper.createTList(0, 1, 2, 3, 4), p.calcNodes()); }
void initUnpackingGraph(LevelGraphStorage g, WeightCalculation w) { double dist = 1; int flags = carEncoder.flags(30, false); g.edge(10, 0, w.getWeight(dist, flags), flags); EdgeSkipIterator iter1 = g.edge(0, 1, w.getWeight(dist, flags), flags); EdgeSkipIterator iter2 = g.edge(1, 2, w.getWeight(dist, flags), flags); EdgeSkipIterator iter3 = g.edge(2, 3, w.getWeight(dist, flags), flags); EdgeSkipIterator iter4 = g.edge(3, 4, w.getWeight(dist, flags), flags); EdgeSkipIterator iter5 = g.edge(4, 5, w.getWeight(dist, flags), flags); EdgeSkipIterator iter6 = g.edge(5, 6, w.getWeight(dist, flags), flags); int oneDirFlags = new PrepareContractionHierarchies().scOneDir(); int tmp = iter1.edge(); iter1 = g.edge(0, 2, 2, oneDirFlags); iter1.skippedEdges(tmp, iter2.edge()); tmp = iter1.edge(); iter1 = g.edge(0, 3, 3, oneDirFlags); iter1.skippedEdges(tmp, iter3.edge()); tmp = iter1.edge(); iter1 = g.edge(0, 4, 4, oneDirFlags); iter1.skippedEdges(tmp, iter4.edge()); tmp = iter1.edge(); iter1 = g.edge(0, 5, 5, oneDirFlags); iter1.skippedEdges(tmp, iter5.edge()); tmp = iter1.edge(); iter1 = g.edge(0, 6, 6, oneDirFlags); iter1.skippedEdges(tmp, iter6.edge()); g.setLevel(0, 10); g.setLevel(6, 9); g.setLevel(5, 8); g.setLevel(4, 7); g.setLevel(3, 6); g.setLevel(2, 5); g.setLevel(1, 4); g.setLevel(10, 3); }
@Test public void testFindShortcuts_Roundabout() { LevelGraphStorage g = (LevelGraphStorage) createGraph(); EdgeSkipIterator iter1_1 = g.edge(1, 3, 1, true); EdgeSkipIterator iter1_2 = g.edge(3, 4, 1, true); EdgeSkipIterator iter2_1 = g.edge(4, 5, 1, false); EdgeSkipIterator iter2_2 = g.edge(5, 6, 1, false); EdgeSkipIterator iter3_1 = g.edge(6, 7, 1, true); EdgeSkipIterator iter3_2 = g.edge(6, 8, 2, false); g.edge(8, 4, 1, false); g.setLevel(3, 3); g.setLevel(5, 5); g.setLevel(7, 7); g.setLevel(8, 8); PrepareContractionHierarchies prepare = new PrepareContractionHierarchies().graph(g); g.edge(1, 4, 2, prepare.scBothDir()).skippedEdges(iter1_1.edge(), iter1_2.edge()); int f = prepare.scOneDir(); g.edge(4, 6, 2, f).skippedEdges(iter2_1.edge(), iter2_2.edge()); g.edge(6, 4, 3, f).skippedEdges(iter3_1.edge(), iter3_2.edge()); prepare.initFromGraph(); // there should be two different shortcuts for both directions! Collection<Shortcut> sc = prepare.testFindShortcuts(4); assertEquals(2, sc.size()); }
void contractNodes() { meanDegree = g.getAllEdges().getMaxId() / g.getNodes(); int level = 1; counter = 0; int initSize = sortedNodes.getSize(); int logSize = (int) Math.round(Math.max(10, sortedNodes.getSize() / 100 * logMessagesPercentage)); if (logMessagesPercentage == 0) logSize = Integer.MAX_VALUE; // preparation takes longer but queries are slightly faster with preparation // => enable it but call not so often boolean periodicUpdate = true; StopWatch periodSW = new StopWatch(); int updateCounter = 0; int periodicUpdatesCount = Math.max(10, sortedNodes.getSize() / 100 * periodicUpdatesPercentage); if (periodicUpdatesPercentage == 0) periodicUpdate = false; // disable as preparation is slower and query time does not benefit int lastNodesLazyUpdates = lastNodesLazyUpdatePercentage == 0 ? 0 : sortedNodes.getSize() / 100 * lastNodesLazyUpdatePercentage; StopWatch lazySW = new StopWatch(); // Recompute priority of uncontracted neighbors. // Without neighborupdates preparation is faster but we need them // to slightly improve query time. Also if not applied too often it decreases the shortcut // number. boolean neighborUpdate = true; if (neighborUpdatePercentage == 0) neighborUpdate = false; StopWatch neighborSW = new StopWatch(); LevelGraphStorage lg = ((LevelGraphStorage) g); while (!sortedNodes.isEmpty()) { // periodically update priorities of ALL nodes if (periodicUpdate && counter > 0 && counter % periodicUpdatesCount == 0) { periodSW.start(); sortedNodes.clear(); int len = g.getNodes(); for (int node = 0; node < len; node++) { if (g.getLevel(node) != 0) continue; int priority = oldPriorities[node] = calculatePriority(node); sortedNodes.insert(node, priority); } periodSW.stop(); updateCounter++; } if (counter % logSize == 0) { // TODO necessary? System.gc(); logger.info( Helper.nf(counter) + ", updates:" + updateCounter + ", nodes: " + Helper.nf(sortedNodes.getSize()) + ", shortcuts:" + Helper.nf(newShortcuts) + ", dijkstras:" + Helper.nf(dijkstraCount) + ", t(dijk):" + (int) dijkstraSW.getSeconds() + ", t(period):" + (int) periodSW.getSeconds() + ", t(lazy):" + (int) lazySW.getSeconds() + ", t(neighbor):" + (int) neighborSW.getSeconds() + ", meanDegree:" + (long) meanDegree + ", algo:" + algo.getMemoryUsageAsString() + ", " + Helper.getMemInfo()); dijkstraSW = new StopWatch(); periodSW = new StopWatch(); lazySW = new StopWatch(); neighborSW = new StopWatch(); } counter++; int polledNode = sortedNodes.pollKey(); if (sortedNodes.getSize() < lastNodesLazyUpdates) { lazySW.start(); int priority = oldPriorities[polledNode] = calculatePriority(polledNode); if (!sortedNodes.isEmpty() && priority > sortedNodes.peekValue()) { // current node got more important => insert as new value and contract it later sortedNodes.insert(polledNode, priority); lazySW.stop(); continue; } lazySW.stop(); } // contract! newShortcuts += addShortcuts(polledNode); g.setLevel(polledNode, level); level++; EdgeSkipIterator iter = vehicleAllExplorer.setBaseNode(polledNode); while (iter.next()) { int nn = iter.getAdjNode(); if (g.getLevel(nn) != 0) // already contracted no update necessary continue; if (neighborUpdate && rand.nextInt(100) < neighborUpdatePercentage) { neighborSW.start(); int oldPrio = oldPriorities[nn]; int priority = oldPriorities[nn] = calculatePriority(nn); if (priority != oldPrio) sortedNodes.update(nn, oldPrio, priority); neighborSW.stop(); } if (removesHigher2LowerEdges) lg.disconnect(vehicleAllTmpExplorer, iter); } } // Preparation works only once so we can release temporary data. // The preparation object itself has to be intact to create the algorithm. close(); logger.info( "took:" + (int) allSW.stop().getSeconds() + ", new shortcuts: " + newShortcuts + ", " + prepareWeighting + ", " + prepareEncoder + ", removeHigher2LowerEdges:" + removesHigher2LowerEdges + ", dijkstras:" + dijkstraCount + ", t(dijk):" + (int) dijkstraSW.getSeconds() + ", t(period):" + (int) periodSW.getSeconds() + ", t(lazy):" + (int) lazySW.getSeconds() + ", t(neighbor):" + (int) neighborSW.getSeconds() + ", meanDegree:" + (long) meanDegree + ", initSize:" + initSize + ", periodic:" + periodicUpdatesPercentage + ", lazy:" + lastNodesLazyUpdatePercentage + ", neighbor:" + neighborUpdatePercentage); }