private void TestShortestPathsAlgorithm(IWeightedGraph <int, double> graph, IVertex <int> source, IVertex <int> target, double pathCosts, ShortestPathAlgorithm algorithm, double precision = 0.1) { IWeightedGraph <int, double> shortestPath = null; switch (algorithm) { case ShortestPathAlgorithm.Dijkstra: shortestPath = Dijkstra.FindShortestPath(graph, source, target, 0.0, double.MaxValue, (x, y) => x + y); break; case ShortestPathAlgorithm.BellmanFordMoore: shortestPath = BellmanFordMoore.FindShortestPath(graph, source, target, 0.0, double.MaxValue, (x, y) => x + y); break; default: throw new NotSupportedException($"Testing shortest path for the {algorithm} algorithm is currently not supported."); } AssertDoublesNearlyEqual(pathCosts, shortestPath.GetAllEdges().Sum(e => e.Weight), precision); }
public static IWeightedGraph <TV, TW> FindCostMinimalFlow <TV, TW>(IWeightedGraph <TV, TW> graph, TV superSourceValue, TV superTargetValue, Func <TW, TW, TW> combineValues, Func <TW, TW, TW> substractValues, Func <TW, TW> negateValue, TW zeroValue, TW maxValue) where TV : IEquatable <TV> where TW : IComparable { // Build graph with super nodes IWeightedGraph <TV, TW> graphWithSuperNodes = BuildGraphWithSuperNodes(graph, superSourceValue, superTargetValue, negateValue); // Get max flow in graph with super nodes IWeightedGraph <TV, TW> maxFlow = EdmondsKarp.FindMaxFlow(graphWithSuperNodes, graphWithSuperNodes.GetFirstMatchingVertex(v => v.Value.Equals(superSourceValue)), graphWithSuperNodes.GetFirstMatchingVertex(v => v.Value.Equals(superTargetValue)), combineValues, substractValues, zeroValue); TW maxFlowValue = FlowValue(maxFlow, superSourceValue, combineValues, zeroValue); // Check for existance of a b-flow IEnumerable <IVertex <TV> > sources = graph.GetAllMatchingVerteces(v => v.GetAttribute <double>(Constants.BALANCE) > 0); IEnumerable <IVertex <TV> > targets = graph.GetAllMatchingVerteces(v => v.GetAttribute <double>(Constants.BALANCE) < 0); TW sourcesBalance = zeroValue; TW targetsBalance = zeroValue; foreach (IVertex <TV> source in sources) { sourcesBalance = combineValues(sourcesBalance, source.GetAttribute <TW>(Constants.BALANCE)); } foreach (IVertex <TV> target in targets) { targetsBalance = combineValues(targetsBalance, target.GetAttribute <TW>(Constants.BALANCE)); } if (maxFlowValue.Equals(sourcesBalance) && maxFlowValue.Equals(negateValue(targetsBalance))) { // Copy flow values from graph with super nodes to original graph foreach (IWeightedEdge <TV, TW> edge in graphWithSuperNodes.GetAllEdges()) { if (edge is IWeightedDirectedEdge <TV, TW> directedEdge) { if (!directedEdge.OriginVertex.Value.Equals(superSourceValue) && !directedEdge.TargetVertex.Value.Equals(superTargetValue)) { graph.GetEdgeBetweenVerteces(directedEdge.OriginVertex.Value, directedEdge.TargetVertex.Value) .SetAttribute(Constants.FLOW, edge.GetAttribute <TW>(Constants.FLOW)); } } else { throw new GraphNotDirectedException(); } } bool modifyingCycleLeft; do { // Build residual graph IWeightedGraph <TV, TW> residualGraph = BuildResidualGraph(graph, substractValues, negateValue, zeroValue); // Prepare graph for BellmanFordMoore IWeightedGraph <TV, TW> bfmGraph = BuildGraphForBellmanFordMoore(residualGraph, superSourceValue, zeroValue); // Attempt to find modifying cycle IVertex <TV> bfmSuperSource = bfmGraph.GetFirstMatchingVertex(v => v.Value.Equals(superSourceValue)); if (modifyingCycleLeft = BellmanFordMoore.TryFindNegativeCycle(bfmGraph, bfmSuperSource, zeroValue, maxValue, combineValues, out IEnumerable <IWeightedDirectedEdge <TV, TW> > cycle)) { // Get minimum capacity of the cycle in the residual graph List <IWeightedDirectedEdge <TV, TW> > rgCycle = new List <IWeightedDirectedEdge <TV, TW> >(); foreach (IWeightedDirectedEdge <TV, TW> edge in cycle) { rgCycle.Add((IWeightedDirectedEdge <TV, TW>)residualGraph.GetEdgeBetweenVerteces(edge.OriginVertex.Value, edge.TargetVertex.Value)); } TW minCycleCapacity = rgCycle.Min(e => e.Weight); // Modify b-flow along cycle foreach (IWeightedDirectedEdge <TV, TW> edge in rgCycle) { if (edge.GetAttribute <EdgeDirection>(Constants.DIRECTION) == EdgeDirection.Forward) { IWeightedDirectedEdge <TV, TW> graphEdge = (IWeightedDirectedEdge <TV, TW>)graph.GetEdgeBetweenVerteces(edge.OriginVertex.Value, edge.TargetVertex.Value); graphEdge.SetAttribute(Constants.FLOW, combineValues(graphEdge.GetAttribute <TW>(Constants.FLOW), minCycleCapacity)); } else { IWeightedDirectedEdge <TV, TW> graphEdge = (IWeightedDirectedEdge <TV, TW>)graph.GetEdgeBetweenVerteces(edge.TargetVertex.Value, edge.OriginVertex.Value); graphEdge.SetAttribute(Constants.FLOW, substractValues(graphEdge.GetAttribute <TW>(Constants.FLOW), minCycleCapacity)); } } } }while (modifyingCycleLeft); // Return same graph return(graph); } else { throw new NoBFlowException(); } }
public static IWeightedGraph <TV, TW> FindCostMinimalFlow <TV, TW>(IWeightedGraph <TV, TW> graph, TV superSourceValue, TV superTargetValue, Func <TW, TW, TW> combineValues, Func <TW, TW, TW> substractValues, Func <TW, TW> negateValue, TW zeroValue, TW maxValue) where TV : IEquatable <TV> where TW : IComparable { // Add initial flow values to all graph edges: 0 or maximum edge capacity for edges with negative costs foreach (IWeightedEdge <TV, TW> edge in graph.GetAllEdges()) { if (edge.GetAttribute <TW>(Constants.COSTS).CompareTo(zeroValue) < 0) { edge.SetAttribute(Constants.FLOW, edge.Weight); } else { edge.SetAttribute(Constants.FLOW, zeroValue); } } // Set pseudo-balances and track pseudo-nodes List <IVertex <TV> > pseudoSources = new List <IVertex <TV> >(); List <IVertex <TV> > pseudoTargets = new List <IVertex <TV> >(); foreach (IVertex <TV> vertex in graph.GetAllVerteces()) { // Compute pseudo balances UpdatePseudoBalance(graph, vertex, combineValues, substractValues, zeroValue); // Track pseudo-sources and pseudo-targets if (vertex.GetAttribute <TW>(Constants.BALANCE).CompareTo(vertex.GetAttribute <TW>(Constants.PSEUDO_BALANCE)) > 0) { pseudoSources.Add(vertex); } else if (vertex.GetAttribute <TW>(Constants.BALANCE).CompareTo(vertex.GetAttribute <TW>(Constants.PSEUDO_BALANCE)) < 0) { pseudoTargets.Add(vertex); } } while (pseudoSources.Any() && pseudoTargets.Any()) { // Build residual graph IWeightedGraph <TV, TW> residualGraph = CycleCanceling.BuildResidualGraph(graph, substractValues, negateValue, zeroValue); // Select pseudo-source IVertex <TV> pseudoSource = pseudoSources.First(); // Attempt to find a pseudo-target reachable from the pseudo-source in the residual graph IVertex <TV> pseudoTarget = null; IWeightedGraph <TV, TW> pathToTarget = null; foreach (IVertex <TV> currentPseudoTarget in pseudoTargets) { try { IWeightedGraph <TV, TW> bfmGraph = BuildGraphForBellmanFordMoore(residualGraph); IVertex <TV> bfmSource = bfmGraph.GetFirstMatchingVertex(v => v.Value.Equals(pseudoSource.Value)); IVertex <TV> bfmTarget = bfmGraph.GetFirstMatchingVertex(v => v.Value.Equals(currentPseudoTarget.Value)); IWeightedGraph <TV, TW> pathToCurrentTarget = BellmanFordMoore.FindShortestPath(bfmGraph, bfmSource, bfmTarget, zeroValue, maxValue, combineValues); if (pathToCurrentTarget != null) { pseudoTarget = currentPseudoTarget; pathToTarget = pathToCurrentTarget; break; } } catch (Exception) { /* silent */ } } // Abort if no pair of reachable pseudo-nodes was found if (pseudoTarget == null) { break; } // Determine max possible augmenting flow value TW minPathCapacity = maxValue; foreach (IWeightedEdge <TV, TW> edge in pathToTarget.GetAllEdges()) { if (edge is IWeightedDirectedEdge <TV, TW> directedEdge) { IWeightedEdge <TV, TW> residualEdge = residualGraph.GetEdgeBetweenVerteces(directedEdge.OriginVertex.Value, directedEdge.TargetVertex.Value); minPathCapacity = MinValue(new TW[] { minPathCapacity, residualEdge.Weight }); } else { throw new GraphNotDirectedException(); } } TW sourceRestBalance = substractValues(pseudoSource.GetAttribute <TW>(Constants.BALANCE), pseudoSource.GetAttribute <TW>(Constants.PSEUDO_BALANCE)); TW targetRestBalance = substractValues(pseudoTarget.GetAttribute <TW>(Constants.PSEUDO_BALANCE), pseudoTarget.GetAttribute <TW>(Constants.BALANCE)); TW augmentingFlow = MinValue(new TW[] { minPathCapacity, sourceRestBalance, targetRestBalance }); // Update b-flow in original graph foreach (IWeightedEdge <TV, TW> edge in pathToTarget.GetAllEdges()) { if (edge is IWeightedDirectedEdge <TV, TW> directedEdge) { IWeightedEdge <TV, TW> residualEdge = residualGraph.GetEdgeBetweenVerteces(directedEdge.OriginVertex.Value, directedEdge.TargetVertex.Value); if (residualEdge.GetAttribute <EdgeDirection>(Constants.DIRECTION) == EdgeDirection.Forward) { IWeightedEdge <TV, TW> graphEdge = graph.GetEdgeBetweenVerteces(directedEdge.OriginVertex.Value, directedEdge.TargetVertex.Value); graphEdge.SetAttribute(Constants.FLOW, combineValues(graphEdge.GetAttribute <TW>(Constants.FLOW), augmentingFlow)); } else { IWeightedEdge <TV, TW> graphEdge = graph.GetEdgeBetweenVerteces(directedEdge.TargetVertex.Value, directedEdge.OriginVertex.Value); graphEdge.SetAttribute(Constants.FLOW, substractValues(graphEdge.GetAttribute <TW>(Constants.FLOW), augmentingFlow)); } } else { throw new GraphNotDirectedException(); } } // Remove pseudo-nodes (from tracking) if they will have their balance satisfied if (augmentingFlow.Equals(sourceRestBalance)) { pseudoSources.Remove(pseudoSource); } if (augmentingFlow.Equals(targetRestBalance)) { pseudoTargets.Remove(pseudoTarget); } // Update pseudo-balances of used pseudo-source and pseudo-target UpdatePseudoBalance(graph, pseudoSource, combineValues, substractValues, zeroValue); UpdatePseudoBalance(graph, pseudoTarget, combineValues, substractValues, zeroValue); } // Check balances if (!graph.GetAllVerteces().All(v => v.GetAttribute <TW>(Constants.BALANCE).Equals(v.GetAttribute <TW>(Constants.PSEUDO_BALANCE)))) { throw new NoBFlowException(); } // If we reach this, a valid b-flow was found! return(graph); }