/// <summary> /// Calculates a path. /// </summary> /// <returns>The path.</returns> public Path Run(RouterDb routerDb, SnapPoint source, SnapPoint target, Func <RouterDbEdgeEnumerator, uint> getWeight, Func <VertexId, bool> settled = null, Func <VertexId, bool> queued = null) { var enumerator = routerDb.GetEdgeEnumerator(); _tree.Clear(); _visits.Clear(); _heap.Clear(); // add sources. // add forward. if (!enumerator.MoveToEdge(source.EdgeId, true)) { throw new Exception($"Edge in source {source} not found!"); } var sourceCostForward = getWeight(enumerator); if (sourceCostForward > 0) { // can traverse edge in the forward direction. var sourceOffsetCostForward = sourceCostForward * (1 - source.OffsetFactor()); var p = _tree.AddVisit(enumerator.To, source.EdgeId, uint.MaxValue); _heap.Push(p, sourceOffsetCostForward); } // add backward. if (!enumerator.MoveToEdge(source.EdgeId, false)) { throw new Exception($"Edge in source {source} not found!"); } var sourceCostBackward = getWeight(enumerator); if (sourceCostBackward > 0) { // can traverse edge in the backward direction. var sourceOffsetCostBackward = sourceCostBackward * source.OffsetFactor(); var p = _tree.AddVisit(enumerator.To, source.EdgeId, uint.MaxValue); _heap.Push(p, sourceOffsetCostBackward); } // add targets. (uint pointer, float cost, bool forward, SnapPoint target)bestTarget = (uint.MaxValue, float.MaxValue, false,
public void Run(DirectedGraph graph, DirectedGraph witnessGraph, uint vertex, HashSet <uint> dirty) { try { forwardSettled.Clear(); backwardSettled.Clear(); pathTree.Clear(); pointerHeap.Clear(); witnesses.Clear(); var p = pathTree.AddSettledVertex(vertex, new WeightAndDir <float>() { Direction = new Dir(true, true), Weight = 0 }, 0, Constants.NO_VERTEX); pointerHeap.Push(p, 0); // dequeue vertices until stopping conditions are reached. var enumerator = graph.GetEdgeEnumerator(); // calculate max forward/backward weight. var forwardMax = 0f; var backwardMax = 0f; enumerator.MoveTo(vertex); var nextEnumerator = graph.GetEdgeEnumerator(); while (enumerator.MoveNext()) { var nVertex1 = enumerator.Neighbour; if (dirty != null && !dirty.Contains(nVertex1)) { // this is not a hop-2 to consider. continue; } ContractedEdgeDataSerializer.Deserialize(enumerator.Data0, out Dir dir1, out float weight1); var p1 = pathTree.AddSettledVertex(nVertex1, weight1, dir1, 1, vertex); pointerHeap.Push(p1, weight1); nextEnumerator.MoveTo(enumerator.Neighbour); while (nextEnumerator.MoveNext()) { var nVertex2 = nextEnumerator.Neighbour; if (nVertex2 == vertex) { // no u-turns. continue; } ContractedEdgeDataSerializer.Deserialize(nextEnumerator.Data0, out Dir dir2, out float weight2); dir2._val = (byte)(dir1._val & dir2._val); if (dir2._val == 0) { continue; } var weight2Hops = weight1 + weight2; var p2 = pathTree.AddSettledVertex(nVertex2, weight2Hops, dir2, 2, nVertex1); pointerHeap.Push(p2, weight2Hops); if (dir2.F && weight2Hops > forwardMax) { forwardMax = weight2Hops; } if (dir2.B && weight2Hops > backwardMax) { backwardMax = weight2Hops; } } } if (forwardMax == 0 && backwardMax == 0) { return; } while (pointerHeap.Count > 0) { var cPointer = pointerHeap.Pop(); pathTree.GetSettledVertex(cPointer, out uint cVertex, out WeightAndDir <float> cWeight, out uint cHops, out uint pVertex); if (cHops == 2) { // check if the search can stop or not. var witness = new Shortcut <float>(); witness.Forward = float.MaxValue; witness.Backward = float.MaxValue; if (cWeight.Direction.F && forwardSettled.TryGetValue(cVertex, out float best) && best < cWeight.Weight) { // this is a 2-hop and vertex was settled before, we have a witness! witness.Forward = best; } if (cWeight.Direction.B && backwardSettled.TryGetValue(cVertex, out best) && best < cWeight.Weight) { // this is a 2-hop and vertex was settled before, we have a witness! witness.Backward = best; } if (witness.Backward != float.MaxValue || witness.Forward != float.MaxValue) { // report witness here. if (vertex != cVertex) { // TODO: check this, how can they ever be the same? witnesses.Add(new Witness() { Vertex1 = vertex, Vertex2 = cVertex, Forward = witness.Forward, Backward = witness.Backward }); } } } if (forwardSettled.Count > _maxSettles || backwardSettled.Count > _maxSettles) { // over settled count. break; } if (cWeight.Weight > backwardMax && cWeight.Weight > forwardMax) { // over max weights. break; } if (forwardSettled.ContainsKey(cVertex) || cWeight.Weight > forwardMax) { cWeight.Direction = new Dir(false, cWeight.Direction.B); } if (backwardSettled.ContainsKey(cVertex) || cWeight.Weight > backwardMax) { cWeight.Direction = new Dir(cWeight.Direction.F, false); } var isRelevant = false; if (cWeight.Direction.F) { forwardSettled.Add(cVertex, cWeight.Weight); isRelevant = true; } if (cWeight.Direction.B) { backwardSettled.Add(cVertex, cWeight.Weight); isRelevant = true; } if (!isRelevant) { // not forward, not backwards. continue; } cHops++; if (cHops >= _hopLimit) { // over hop limit, don't queue. continue; } if (cHops == 1) { if (dirty == null) { // all hops 1 are already queued. continue; } } else if (cHops == 2) { if (dirty == null || dirty.Contains(cVertex)) { // all these hops 2 are already queue. continue; } } enumerator.MoveTo(cVertex); while (enumerator.MoveNext()) { var nVertex = enumerator.Neighbour; if (nVertex == pVertex) { // no going back. continue; } if (cHops == 1) { // check if the neighbour is dirty. if (dirty.Contains(nVertex)) { // skip dirty vertices, they are already in the queue. continue; } } Dir nDir; float nWeight; ContractedEdgeDataSerializer.Deserialize(enumerator.Data0, out nDir, out nWeight); nDir._val = (byte)(cWeight.Direction._val & nDir._val); if (nDir._val == 0) { continue; } nWeight = nWeight + cWeight.Weight; if (nDir.F && nWeight > forwardMax) { nDir = new Dir(false, nDir.B); if (nDir._val == 0) { continue; } } if (nDir.B && nWeight > backwardMax) { nDir = new Dir(nDir.F, false); if (nDir._val == 0) { continue; } } var nPoiner = pathTree.AddSettledVertex(nVertex, nWeight, nDir, cHops, cVertex); pointerHeap.Push(nPoiner, nWeight); } } if (witnesses.Count > 0) { lock (witnessGraph) { foreach (var witness in witnesses) { //witnessGraph.AddOrUpdateEdge(witness.Item1, witness.Item2, witness.Item3.Forward, // witness.Item3.Backward); witnessGraph.AddOrUpdateEdge(witness.Vertex1, witness.Vertex2, witness.Forward, witness.Backward); } } } } catch (Exception ex) { throw ex; } }
/// <summary> /// Calculates witness paths. /// </summary> public virtual void Calculate(DirectedGraph graph, WeightHandler <T> weightHandler, uint vertex, uint source, Dictionary <uint, Shortcut <T> > targets, int maxSettles, int hopLimit) { pathTree.Clear(); pointerHeap.Clear(); var forwardSettled = new HashSet <uint>(); var backwardSettled = new HashSet <uint>(); var forwardTargets = new HashSet <uint>(); var backwardTargets = new HashSet <uint>(); var maxWeight = 0f; foreach (var targetPair in targets) { var target = targetPair.Key; var shortcut = targetPair.Value; var e = new OriginalEdge(source, target); var shortcutForward = weightHandler.GetMetric(shortcut.Forward); if (shortcutForward > 0 && shortcutForward < float.MaxValue) { forwardTargets.Add(e.Vertex2); if (shortcutForward > maxWeight) { maxWeight = shortcutForward; } } var shortcutBackward = weightHandler.GetMetric(shortcut.Backward); if (shortcutBackward > 0 && shortcutBackward < float.MaxValue) { backwardTargets.Add(e.Vertex2); if (shortcutBackward > maxWeight) { maxWeight = shortcutBackward; } } } // queue the source. pathTree.Clear(); pointerHeap.Clear(); var p = pathTree.AddSettledVertex(source, new WeightAndDir <float>() { Direction = new Dir(true, true), Weight = 0 }, 0); pointerHeap.Push(p, 0); // dequeue vertices until stopping conditions are reached. var cVertex = Constants.NO_VERTEX; WeightAndDir <float> cWeight; var cHops = uint.MaxValue; var enumerator = graph.GetEdgeEnumerator(); while (pointerHeap.Count > 0) { var cPointer = pointerHeap.Pop(); pathTree.GetSettledVertex(cPointer, out cVertex, out cWeight, out cHops); if (cVertex == vertex) { continue; } if (cWeight.Weight >= maxWeight) { break; } if (forwardSettled.Contains(cVertex) || forwardTargets.Count == 0 || forwardSettled.Count > maxSettles) { cWeight.Direction = new Dir(false, cWeight.Direction.B); } if (backwardSettled.Contains(cVertex) || backwardTargets.Count == 0 || backwardSettled.Count > maxSettles) { cWeight.Direction = new Dir(cWeight.Direction.F, false); } if (cWeight.Direction.F) { forwardSettled.Add(cVertex); if (forwardTargets.Contains(cVertex)) { // target reached, evaluate it as a shortcut. Shortcut <T> shortcut; if (targets.TryGetValue(cVertex, out shortcut)) { var shortcutForward = weightHandler.GetMetric(shortcut.Forward); if (shortcutForward > cWeight.Weight) { // a witness path was found, don't add a shortcut. shortcut.Forward = weightHandler.Zero; targets[cVertex] = shortcut; } } forwardTargets.Remove(cVertex); if (forwardTargets.Count == 0) { if (backwardTargets.Count == 0) { break; } cWeight.Direction = new Dir(false, cWeight.Direction.B); if (!cWeight.Direction.F && !cWeight.Direction.B) { continue; } } } } if (cWeight.Direction.B) { backwardSettled.Add(cVertex); if (backwardTargets.Contains(cVertex)) { // target reached, evaluate it as a shortcut. Shortcut <T> shortcut; if (targets.TryGetValue(cVertex, out shortcut)) { var shortcutBackward = weightHandler.GetMetric(shortcut.Backward); if (shortcutBackward > cWeight.Weight) { // a witness path was found, don't add a shortcut. shortcut.Backward = weightHandler.Zero; targets[cVertex] = shortcut; } } backwardTargets.Remove(cVertex); if (backwardTargets.Count == 0) { if (forwardTargets.Count == 0) { break; } cWeight.Direction = new Dir(cWeight.Direction.F, false); if (!cWeight.Direction.F && !cWeight.Direction.B) { continue; } } } } if (cHops + 1 >= hopLimit) { continue; } if (forwardSettled.Count > maxSettles && backwardSettled.Count > maxSettles) { continue; } enumerator.MoveTo(cVertex); while (enumerator.MoveNext()) { var nVertex = enumerator.Neighbour; var nWeight = ContractedEdgeDataSerializer.Deserialize(enumerator.Data0); nWeight = new WeightAndDir <float>() { Direction = Dir.Combine(cWeight.Direction, nWeight.Direction), Weight = cWeight.Weight + nWeight.Weight }; if (nWeight.Direction.F && forwardSettled.Contains(nVertex)) { nWeight.Direction = new Dir(false, nWeight.Direction.B); } if (nWeight.Direction.B && backwardSettled.Contains(nVertex)) { nWeight.Direction = new Dir(nWeight.Direction.F, false); } if (!nWeight.Direction.F && !nWeight.Direction.B) { continue; } var nPoiner = pathTree.AddSettledVertex(nVertex, nWeight, cHops + 1); pointerHeap.Push(nPoiner, nWeight.Weight); } } }
/// <summary> /// Finds loops and merges them together. /// </summary> /// <param name="maxSettles">The maximum labels to settle.</param> /// <param name="updateLabel">A callback to update label.</param> internal void FindLoops(uint maxSettles, IslandLabels islandLabels, Action <uint, uint> updateLabel) { // TODO: it's probably better to call reduce here when too much has changed. var pathTree = new PathTree(); var enumerator = _graph.GetEdgeEnumerator(); var settled = new HashSet <uint>(); var queue = new Queue <uint>(); var loop = new HashSet <uint>(); // keeps all with a path back to label, initially only label. uint label = 0; while (label < _graph.VertexCount) { if (!enumerator.MoveTo(label)) { label++; continue; } if (islandLabels[label] != label) { label++; continue; } queue.Clear(); pathTree.Clear(); settled.Clear(); loop.Add(label); queue.Enqueue(pathTree.Add(label, uint.MaxValue)); while (queue.Count > 0 && settled.Count < maxSettles) { var pointer = queue.Dequeue(); pathTree.Get(pointer, out var current, out var previous); if (settled.Contains(current)) { continue; } settled.Add(current); if (!enumerator.MoveTo(current)) { continue; } while (enumerator.MoveNext()) { var n = enumerator.Neighbour; n = islandLabels[n]; if (loop.Contains(n)) { // yay, a loop! loop.Add(current); while (previous != uint.MaxValue) { pathTree.Get(previous, out current, out previous); loop.Add(current); } } if (settled.Contains(n)) { continue; } queue.Enqueue(pathTree.Add(n, pointer)); } } if (loop.Count > 0) { this.Merge(loop, updateLabel); } loop.Clear(); // move to the next label. label++; } }