Пример #1
0
        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);
        }