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); }