/// <summary> /// If possible, relax the given edge using the supplied base cost and edge-weight function. /// </summary> /// <param name="edge">The edge to relax.</param> /// <param name="cost">The base cost to reach the edge destination vertex.</param> /// <param name="ew">The edge weigher.</param> /// <param name="forbidNegatives">If true, negative values will forbid the link.</param> /// <returns>True if the edge was relaxed, otherwise false.</returns> public bool RelaxEdge(E edge, IWeight cost, IEdgeWeigher <V, E> ew, bool forbidNegatives = true) { V v = edge.Dst; IWeight hopCost = ew.GetWeight(edge); if ((!hopCost.IsViable) || (hopCost.IsNegative && forbidNegatives)) { return(false); } IWeight newCost = cost.Merge(hopCost); int compareResult = -1; if (HasCost(v)) { IWeight oldCost = GetCost(v); compareResult = newCost.CompareTo(oldCost); } if (compareResult <= 0) { UpdateVertex(v, edge, newCost, compareResult < 0); } return(compareResult < 0); }
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = -1) { if (maxPaths == AllPaths) { maxPaths = popSize; } if (useSuurballe) { return(new SuurballeGraphSearch <V, E>().Search(graph, src, dst, weigher, AllPaths)); } orig = graph; this.src = src; this.dst = dst; this.weigher = weigher; IList <Subset> best = new GaPopulation <Subset>().RunGa(iterations, popSize, maxPaths, new Subset(this, new bool[numGroups])); var dpps = new HashSet <DisjointPathPair <V, E> >(); foreach (Subset s in best) { dpps.UnionWith(s.BuildPaths()); } IResult <V, E> firstDijkstra = new DijkstraGraphSearch <V, E>().Search(orig, src, dst, weigher, 1); var result = new InternalResult(this, firstDijkstra, dpps); return(result); }
public ShortestPathEnumerator(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher) { this.graph = CheckNotNull(graph); CheckNotNull(src); this.dst = CheckNotNull(dst); this.weigher = CheckNotNull(weigher); maskingWeigher = new InnerEdgeWeigher(weigher); next = () => search.Search(graph, src, dst, weigher, 1).Paths.FirstOrDefault(); }
/// <summary> /// Sums the given edges using the given weigher. /// </summary> /// <param name="weigher">The weigher to use.</param> /// <param name="edges">The edges to sum.</param> /// <returns>The sum of path cost between the given edges.</returns> private IWeight CalculatePathCost(IEdgeWeigher <V, E> weigher, IEnumerable <E> edges) { IWeight totalCost = weigher.InitialWeight; foreach (E edge in edges) { totalCost = totalCost.Merge(weigher.GetWeight(edge)); } return(totalCost); }
/// <summary> /// <para> /// This implementation produces results augmented with information on SCCs within the graph. /// </para> /// <para> /// To prevent traversal of an edge, the <see cref="IEdgeWeigher{V, E}"/> should /// return a negative value as an edge weigher. /// </para> /// </summary> /// <param name="graph">The graph to search.</param> /// <param name="weigher">The weigher to use.</param> /// <returns>The SCC search result.</returns> public IResultBase <V, E> Search(IGraph <V, E> graph, IEdgeWeigher <V, E> weigher) { SccResult result = new SccResult(graph); foreach (V vertex in graph.Vertices) { VertexData data = result.GetData(vertex); if (data is null) { Connect(graph, vertex, weigher, result); } } return(result.Build()); }
protected void ExecuteSinglePathSearch(IGraphPathSearch <TestVertex, TestEdge> search, IGraph <TestVertex, TestEdge> graph, TestVertex src, TestVertex dst, IEdgeWeigher <TestVertex, TestEdge> weigher, int pathCount, IWeight pathCost) { IResult <TestVertex, TestEdge> result = search.Search(graph, src, dst, weigher, 1); ISet <IPath <TestVertex, TestEdge> > paths = result.Paths; PrintPaths(paths); Assert.Equal(Math.Min(pathCount, 1), paths.Count); if (pathCount > 0) { IPath <TestVertex, TestEdge> path = paths.First(); Assert.Equal(pathCost, path.Cost); } }
/// <summary> /// Scans the specified graph, using recursion, and produces SCC results. /// </summary> /// <param name="graph">The graph to search.</param> /// <param name="vertex">The current vertex to scan and connect.</param> /// <param name="weigher">The optional weigher to use.</param> /// <param name="result">The graph search result.</param> /// <returns>Augmentation vertex data for the current vertex.</returns> private VertexData Connect(IGraph <V, E> graph, V vertex, IEdgeWeigher <V, E> weigher, SccResult result) { VertexData data = result.AddData(vertex); // Scan through all egress edges of the current vertex. foreach (E edge in graph.GetEdgesFrom(vertex)) { V nextVertex = edge.Dst; // If edge is not viable, skip it. if (weigher != null && !weigher.GetWeight(edge).IsViable) { continue; } // Attempt to get the augmentation vertex data for the next vertex. VertexData nextData = result.GetData(nextVertex); if (nextData is null) { // Next vertex has not been visited yet, so do this now. nextData = Connect(graph, nextVertex, weigher, result); data.LowLink = Math.Min(data.LowLink, nextData.LowLink); } else if (result.Visited(nextData)) { // Next vertex has been visited, which means // it is in the same cluster as the current vertex. data.LowLink = Math.Min(data.LowLink, nextData.Index); } } if (data.LowLink == data.Index) { result.AddCluster(data); } return(data); }
/// <summary> /// Searches the given graph for paths between the given vertices. /// </summary> /// <param name="graph"></param> /// <param name="src"></param> /// <param name="dst"></param> /// <param name="weigher"></param> /// <returns></returns> public IEnumerable <IPath <V, E> > LazyPathSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher) { var enumerator = new ShortestPathEnumerator(graph, src, dst, weigher); var paths = new SortedSet <IPath <V, E> >(EnumeratePaths(enumerator), new PathComparer()); foreach (IPath <V, E> path in paths) { yield return(path); } }
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = -1) { // Prepare the graph result. var result = new DefaultResult(src, dst, maxPaths); // Set up the starting frontier with the source as the sole vertex. var frontier = new HashSet <V>(); result.UpdateVertex(src, default, weigher.InitialWeight, true);
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = -1) { // TODO: This method needs to be refactored as it is difficult to follow and debug. // TODO: There is a defect here triggered by 3+ edges between the same vertices which // makes an attempt to produce looping paths. Protection against this was added to // AbstractGraphPathSearch, but the root issue remains here. // TODO: There is a defect here where not all paths are truly disjoint. // This class needs to filter its own results to make sure that the paths // are indeed disjoint. Temporary fix for this is provided, but the issue still // needs to be addressed through refactoring. weightF = weigher; firstDijkstraS = (DefaultResult)base.InternalSearch(graph, src, dst, weigher, AllPaths); firstDijkstra = (DefaultResult)base.InternalSearch(graph, src, null, weigher, AllPaths); // Choose an arbitrary shortest path to run Suurballe on. IPath <V, E> shortPath = null; if (firstDijkstraS.Paths.Count == 0) { return(firstDijkstraS); } DisjointPathResult result = new DisjointPathResult(firstDijkstra, src, dst, maxPaths); foreach (IPath <V, E> p in firstDijkstraS.Paths) { shortPath = p; // Transforms the graph so tree edges have 0 weight. var modified = new ModifiedWeigher(this); // Create a residual graph g' by removing all source vertices and reversing 0 length path edges. IMutableGraph <V, E> gt = MutableCopy(graph); foreach (E edge in graph.GetEdgesTo(src)) { gt.RemoveEdge(edge); } foreach (E edge in shortPath.Edges) { gt.RemoveEdge(edge); E reverse = (E)Activator.CreateInstance(typeof(E), edge.Dst, edge.Src); revToEdge.AddOrSet(reverse, edge); gt.AddEdge(reverse); } // Rerun dijkstra on the temporary graph to get a second path. IResult <V, E> secondDijkstra = new DijkstraGraphSearch <V, E>().Search(gt, src, dst, modified); IPath <V, E> residualShortPath = null; if (secondDijkstra.Paths.Count == 0) { result.Dpps.Add(new DisjointPathPair <V, E>(shortPath, null)); continue; } foreach (IPath <V, E> p2 in secondDijkstra.Paths) { residualShortPath = p2; IMutableGraph <V, E> roundTrip = MutableCopy(graph); List <E> tmp = roundTrip.Edges.ToList(); tmp.ForEach(roundTrip.RemoveEdge); foreach (E edge in shortPath.Edges) { roundTrip.AddEdge(edge); } if (residualShortPath != null) { foreach (E edge in residualShortPath.Edges) { if (revToEdge.ContainsKey(edge)) { E edgeToRemove = revToEdge[edge]; if (roundTrip.Edges.Contains(edgeToRemove)) { roundTrip.RemoveEdge(edgeToRemove); } } else { roundTrip.AddEdge(edge); } } } // Actually build the final result. DefaultResult lastSearch = (DefaultResult)base.InternalSearch(roundTrip, src, dst, weigher, AllPaths); IPath <V, E> primary = lastSearch.Paths.First(); foreach (E edge in primary.Edges) { roundTrip.RemoveEdge(edge); } ISet <IPath <V, E> > backups = base.InternalSearch(roundTrip, src, dst, weigher, AllPaths).Paths; // Find first backup path that does not share any nodes with the primary. foreach (IPath <V, E> backup in backups) { if (IsDisjoint(primary, backup)) { result.Dpps.Add(new DisjointPathPair <V, E>(primary, backup)); break; } } } } for (int i = result.Dpps.Count - 1; i > 0; --i) { if (result.Dpps[i].Size <= 1) { result.Dpps.RemoveAt(i); } } result.BuildPaths(); return(result); }
public InternalWeigher(IEdgeWeigher <V, E> weigher, IDictionary <E, int> riskGrouping, bool[] subset) => (this.weigher, this.riskGrouping, subsetF) = (weigher, riskGrouping, subset);
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = 1) { CheckNotNull(weigher, "The edge weigher cannot be null."); CheckArgument(maxPaths != AllPaths, "KShortestPath cannot search all paths."); CheckArgument(maxPaths > 0, "The max number of paths must be greater than 0."); IGraph <V, E> originalGraph = CheckNotNull(graph, "The graph cannot be null."); var modifiedWeigher = new InnerEdgeWeigher(weigher); var result = new InnerOrderedResult(src, dst, maxPaths); var resultPaths = new List <IPath <V, E> >(maxPaths); var potentialPaths = new List <IPath <V, E> >(); var dijkstraSearch = new DijkstraGraphSearch <V, E>(); ISet <IPath <V, E> > dijkstraResults = dijkstraSearch.Search(originalGraph, src, dst, modifiedWeigher, 1).Paths; // Checks if the destination was reachable. if (dijkstraResults.Count == 0) { log.Warn("No path was found."); return(result); } // If it was reachable, add the first shortest path to the set of results. resultPaths.Add(dijkstraResults.First()); for (int k = 1; k < maxPaths; ++k) { for (int i = 0; i < resultPaths[k - 1].Edges.Count; ++i) { V spurNode = resultPaths[k - 1].Edges[i].Src; List <E> rootPathEdgeList = resultPaths[k - 1].Edges.Take(i).ToList(); foreach (IPath <V, E> path in resultPaths) { if (path.Edges.Count >= i && rootPathEdgeList.SequenceEqual(path.Edges.Take(i))) { modifiedWeigher.RemovedEdges.Add(path.Edges[i]); } } // Effectively remove all nodes from the source path. foreach (E edge in rootPathEdgeList) { foreach (E e in originalGraph.GetEdgesFrom(edge.Src)) { modifiedWeigher.RemovedEdges.Add(e); } foreach (E e in originalGraph.GetEdgesTo(edge.Src)) { modifiedWeigher.RemovedEdges.Add(e); } } dijkstraResults = dijkstraSearch.Search(originalGraph, spurNode, dst, modifiedWeigher, 1).Paths; if (dijkstraResults.Count > 0) { IPath <V, E> spurPath = dijkstraResults.First(); var totalPath = new List <E>(rootPathEdgeList); foreach (E edge in spurPath.Edges) { totalPath.Add(edge); } // The following line must use the original weigher, not the modified weigher, because the // modifed weigher will count -1 values used for modifying the graph and return an inaccurate cost. potentialPaths.Add(new DefaultPath <V, E>(totalPath, CalculatePathCost(weigher, totalPath))); } // Restore all removed paths and nodes. modifiedWeigher.RemovedEdges.Clear(); } if (potentialPaths.Count == 0) { break; } potentialPaths.Sort(new InnerPathComparer()); resultPaths.Add(potentialPaths[0]); potentialPaths.RemoveAt(0); } resultPaths.ForEach(p => result.PathSet.Add(p)); return(result); }
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = -1) { // Use the default result to remember cumulative costs and // parent edges to each respective vertex. var result = new DefaultResult(src, dst, maxPaths); // Cost to reach the source vertex is 0, of course. result.UpdateVertex(src, null, weigher.InitialWeight, false); if (graph.Edges.Count is 0) { result.BuildPaths(); return(result); } // Use the min priority queue to progressively find each // nearest vertex until we reach the desired destination, // if one was given, or until we reach all possible destinations. Heap <V> minQueue = new Heap <V>(graph.Vertices, new PathCostComparer(result)); while (minQueue.Count > 0) { // Get the nearest vertex. V nearest = minQueue.ExtractExtreme(); if (nearest.Equals(dst)) { break; } // Find its cost and use it to determine if the vertex is reachable. if (result.HasCost(nearest)) { IWeight cost = result.GetCost(nearest); // If the vertex is reachable, relax all its egress edges. foreach (E e in graph.GetEdgesFrom(nearest)) { result.RelaxEdge(e, cost, weigher, true); } } // Reprioritize the min queue. minQueue.Heapify(); } // Build a set of paths from the results and return it. result.BuildPaths(); return(result); }
public InnerEdgeWeigher(IEdgeWeigher <V, E> weigher) => innerEdgeWeigher = weigher;
public InnerEdgeWeigher(IEdgeWeigher <V, E> weigher) => this.weigher = weigher;
/// <summary> /// The abstract implementation of this algorithm's search function. /// </summary> /// <param name="graph">The graph to search.</param> /// <param name="src">The source vertex.</param> /// <param name="dst">The destination vertex.</param> /// <param name="weigher">The edge weigher.</param> /// <param name="maxPaths">The maximum number of paths to find.</param> /// <returns>A search result.</returns> protected abstract IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = AllPaths);
/// <inheritdoc/> protected override IResult <V, E> InternalSearch(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = -1) { // Prepare the search result. var result = new SpanningTreeResult(src, dst, maxPaths); // The source vertex has cost 0, of course. result.UpdateVertex(src, default, weigher.InitialWeight, true);
/// <inheritdoc/> public IResult <V, E> Search(IGraph <V, E> graph, V src, V dst, IEdgeWeigher <V, E> weigher, int maxPaths = AllPaths) { CheckArguments(graph, src, dst); return(InternalSearch(graph, src, dst, weigher ?? new DefaultEdgeWeigher <V, E>(), maxPaths)); }