/// <summary>
        /// Wyznacza maksymalny przepływ o minimalnym koszcie
        /// </summary>
        /// <param name="g">Graf przepustowości</param>
        /// <param name="c">Graf kosztów</param>
        /// <param name="source">Wierzchołek źródłowy</param>
        /// <param name="target">Wierzchołek docelowy</param>
        /// <param name="parallel">Informacja czy algorytm ma korzystać z równoległej wersji algorytmu Forda-Bellmana</param>
        /// <param name="mf">Metoda wyznaczania wstępnego maksymalnego przepływu (bez uwzględniania kosztów)</param>
        /// <param name="af">Metoda powiększania przepływu</param>
        /// <param name="matrixToAVL">Czy optymalizować sposób reprezentacji grafu rezydualnego</param>
        /// <returns>
        /// Krotka (value, cost, flow) składająca się z
        /// wartości maksymalnego przepływu, jego kosztu i grafu opisującego ten przepływ
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Można wybrać metodę wyznaczania wstępnego maksymalnego przepływu (parametr mf).<para/>
        /// Domyślna wartość mf (null) oznacza konstrukcję wstępnego przepływu z wykorzystaniem sztucznej krawędzi.<para/>
        /// Można wybrać metodę powiększania przepływu (parametr pf).<para/>
        /// Znaczenie tego parametru zależy od wybranej metody wyznaczania wstępnego maksymalnego przepływu
        /// (parametr mf), dla niektórych wartości parametru mf
        /// (np. <see cref="MaxFlowGraphExtender.FordFulkersonDinicMaxFlow"/>)
        /// jawne wskazanie metody powiększania przepływu jest konieczne
        /// (pozostawienie domyślnej wartości parametru pf (null)
        /// spowoduje zgłoszenie wyjątku <see cref="ArgumentException"/>).<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego lub grafu
        /// z ujemnymi przepustowościami krawędzi zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy grafy przepustowości i kosztów mają różną strukturę lub parametry
        /// source i target są równe metoda również zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy w grafie przepustowości istnieją krawędzie w obu kierunkach
        /// pomiędzy parą wierzchołków metoda również zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="MinCostFlowGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double value, double cost, Graph flow) MinCostFlow(this Graph g, Graph c, int source, int target, bool parallel = false, MaxFlow mf = null, AugmentFlow af = null, bool matrixToAVL = true)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }
            if (source == target)
            {
                throw new ArgumentException("Source and target must be different");
            }
            if (g.VerticesCount != c.VerticesCount)
            {
                throw new ArgumentException("Inconsistent capacity and cost graphs");
            }

            var fordBellmanShortestPaths = parallel ? ShortestPathsGraphExtender.FordBellmanShortestPathsParallel : new FordBellmanShortestPaths(ShortestPathsGraphExtender.FordBellmanShortestPaths);

            var gOut = new HashSet <int>();
            var cOut = new HashSet <int>();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                gOut.Clear();
                cOut.Clear();
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.Weight < 0.0)
                    {
                        throw new ArgumentException("Negative capacity edges are not allowed");
                    }
                    if (!g.GetEdgeWeight(edge.To, edge.From).IsNaN())
                    {
                        throw new ArgumentException(
                                  "Edges in both directions between pair of vertices are not allowed");
                    }
                    gOut.Add(edge.To);
                }

                foreach (var edge in c.OutEdges(i))
                {
                    cOut.Add(edge.To);
                }

                if (!gOut.SetEquals(cOut))
                {
                    throw new ArgumentException("Inconsistent capacity and cost graphs");
                }
            }

            var   tempCost = double.NaN;
            var   tempFlow = double.NaN;
            var   maxFlow  = double.NaN;
            Graph flow;

            if (mf != null)
            {
                (maxFlow, flow) = mf(g, source, target, af, matrixToAVL);
            }
            else
            {
                if (!(tempFlow = g.GetEdgeWeight(source, target)).IsNaN())
                {
                    g.DelEdge(source, target);
                }
                if (!(tempCost = c.GetEdgeWeight(source, target)).IsNaN())
                {
                    c.DelEdge(source, target);
                }
                flow = g.IsolatedVerticesGraph();
                var maxPossibleFlow = g.OutEdges(source).Sum(e => e.Weight);
                g.AddEdge(source, target, maxPossibleFlow + 1.0);
                flow.AddEdge(source, target, maxPossibleFlow + 1.0);
                var maxPossibleCost = 0.0;
                for (var i = 0; i < c.VerticesCount; i++)
                {
                    foreach (var edge4 in c.OutEdges(i))
                    {
                        maxPossibleCost += Math.Abs(edge4.Weight);
                        flow.AddEdge(edge4.From, edge4.To, 0.0);
                    }
                }
                c.AddEdge(source, target, maxPossibleCost + 1.0);
            }

            var residualFlow = flow.IsolatedVerticesGraph();
            var residualCost = flow.IsolatedVerticesGraph();

            for (var i = 0; i < flow.VerticesCount; i++)
            {
                foreach (var edge in flow.OutEdges(i))
                {
                    var something = Math.Min(g.GetEdgeWeight(edge.From, edge.To), double.MaxValue) - edge.Weight;
                    if (something > 0.0)
                    {
                        residualFlow.AddEdge(edge.From, edge.To, something);
                        residualCost.AddEdge(edge.From, edge.To, c.GetEdgeWeight(edge.From, edge.To));
                    }

                    if (edge.Weight > 0.0)
                    {
                        residualFlow.AddEdge(edge.To, edge.From, edge.Weight);
                        residualCost.AddEdge(edge.To, edge.From, -c.GetEdgeWeight(edge.From, edge.To));
                    }
                }
            }

            var foundFlow = 0.0;

            while (!fordBellmanShortestPaths(residualCost, target, out var pi))
            {
                var cycle        = residualCost.FindNegativeCostCycle(pi).cycle;
                var cycleMaxFlow = double.PositiveInfinity;
                var flag         = false;
                foreach (var edge in cycle)
                {
                    if (edge.From == target && edge.To == source)
                    {
                        flag = true;
                    }
                    var weight = residualFlow.GetEdgeWeight(edge.From, edge.To);
                    if (cycleMaxFlow > weight)
                    {
                        cycleMaxFlow = weight;
                    }
                }
                if (flag)
                {
                    foundFlow += cycleMaxFlow;
                }
                foreach (var edge in cycle)
                {
                    if (flag = (flow.GetEdgeWeight(edge.To, edge.From) > 0.0))
                    {
                        var weight = flow.ModifyEdgeWeight(edge.To, edge.From, -cycleMaxFlow);
                        if (weight < 0.0)
                        {
                            flow.ModifyEdgeWeight(edge.To, edge.From, -weight);
                            flow.ModifyEdgeWeight(edge.From, edge.To, -weight);
                        }
                    }
                    else
                    {
                        flow.ModifyEdgeWeight(edge.From, edge.To, cycleMaxFlow);
                    }

                    if (residualFlow.ModifyEdgeWeight(edge.From, edge.To, -cycleMaxFlow) == 0.0)
                    {
                        residualFlow.DelEdge(edge);
                        residualCost.DelEdge(edge);
                    }

                    if (residualFlow.ModifyEdgeWeight(edge.To, edge.From, cycleMaxFlow).IsNaN())
                    {
                        residualFlow.AddEdge(edge.To, edge.From, cycleMaxFlow);
                        var weight = (flag ? c.GetEdgeWeight(edge.To, edge.From) : c.GetEdgeWeight(edge.From, edge.To));
                        residualCost.AddEdge(edge.To, edge.From, flag ? weight : (-weight));
                    }
                }
            }
            if (mf == null)
            {
                g.DelEdge(source, target);
                c.DelEdge(source, target);
                flow.DelEdge(source, target);
                if (!tempFlow.IsNaN())
                {
                    foundFlow += tempFlow;
                    g.AddEdge(source, target, tempFlow);
                    c.AddEdge(source, target, tempCost);
                    flow.AddEdge(source, target, tempFlow);
                }
                maxFlow = foundFlow;
            }
            var cost = 0.0;

            for (var k = 0; k < flow.VerticesCount; k++)
            {
                cost += flow.OutEdges(k).Sum(e => e.Weight * c.GetEdgeWeight(e.From, e.To));
            }
            return(maxFlow, cost, flow);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Wyznacza maksymalny przepływ metodą FordaFulkersona lub metodą Dinica
        /// </summary>
        /// <param name="g">Badany graf (sieć przepływowa)</param>
        /// <param name="source">Wierzchołek źródłowy</param>
        /// <param name="target">Wierzchołek docelowy</param>
        /// <param name="af">Metoda powiększania przepływu</param>
        /// <param name="matrixToHashTable">Czy optymalizować sposób reprezentacji grafu rezydualnego</param>
        /// <returns>
        /// Krotka (value, flow) składająca się z wartości maksymalnego przepływu
        /// i grafu opisującego ten przepływ
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        /// Jeśli jako parametr af zostanie wybrane wyszukiwanie ścieżki powiększającej to metoda realizuje
        /// algorytm Forda-Fulkersona, jeśli jako parametr af zostanie wybrane wyszukiwanie przepływu blokującego
        /// to metoda realizuje algorytm Dinica.<para/>
        ///  Można oczywiście zdefiniować własną metodę powiększania przepływu zgodną
        /// z typem delegacyjnym <see cref="AugmentFlow"/>.<para/>
        /// Jeśli parametr matrixToHashTable ma wartość true oraz graf g jest typu <see cref="AdjacencyMatrixGraph"/>
        /// (czyli macierz sąsiedztwa) i ma nie więcej niż 10% krawędzi, to wykorzystywany przez algorytm graf
        /// rezydualny jest typu <see cref="AdjacencyListsGraph{HashTableAdjacencyList}"/>.
        /// W przeciwnym przypadku graf rezydualny jest takiego samego typu jak graf g.<para/>
        /// Natomiast jeśli graf g jest typu <see cref="AdjacencyMatrixGraph"/> to inne grafy robocze
        /// (ścieżka powiększająca, przepływ blokujący, graf warstwowy) są typu
        /// <see cref="AdjacencyListsGraph{HashTableAdjacencyList}"/> niezależnie
        /// od wartości parametru matrixToHashTable.<para/>
        /// Wynikowy przepływ maksymalny flow zawsze jest takiego samego typu jak graf g
        /// (niezależnie od wartości parametru matrixToHashTable.<para/>
        /// Jeśli po danej krawędzi nie płynie żaden przepływ,
        /// to nadal jest ona w wynikowym grafie flow (oczywiście z wagą 0).<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego lub grafu
        /// z ujemnymi wagami krawędzi zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Gdy parametry source i target są równe metoda również zgłasza wyjątek <see cref="ArgumentException"/>.
        /// </remarks>
        /// <seealso cref="MaxFlowGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double value, Graph flow) FordFulkersonDinicMaxFlow(this Graph g, int source, int target, AugmentFlow af, bool matrixToHashTable = true)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }
            if (source == target)
            {
                throw new ArgumentException("Source and target must be different");
            }
            if (af == null)
            {
                throw new ArgumentException("Flow increase method not specified");
            }

            var flow = g.IsolatedVerticesGraph();

            var residual = matrixToHashTable
                ? g is AdjacencyMatrixGraph && 10 * g.EdgesCount < g.VerticesCount * g.VerticesCount
                    ? new AdjacencyListsGraph <HashTableAdjacencyList>(true, g.VerticesCount)
                    : g.IsolatedVerticesGraph()
                : g.IsolatedVerticesGraph();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    if (edge.Weight < 0.0)
                    {
                        throw new ArgumentException("Negative capacity weights are not allowed");
                    }
                    flow.AddEdge(edge.From, edge.To, 0.0);
                    if (edge.Weight > 0.0)
                    {
                        residual.AddEdge(edge.From, edge.To, Math.Min(edge.Weight, double.MaxValue));
                    }
                }
            }

            var value = 0.0;

            while (true)
            {
                var(augmentingValue, augmentingFlow) = af(residual, source, target);
                if (augmentingValue == 0.0)
                {
                    break;
                }
                value += augmentingValue;
                for (var i = 0; i < augmentingFlow.VerticesCount; i++)
                {
                    foreach (var edge in augmentingFlow.OutEdges(i))
                    {
                        var weight = flow.GetEdgeWeight(edge.To, edge.From);
                        if (weight > 0.0)
                        {
                            weight = flow.ModifyEdgeWeight(edge.To, edge.From, -edge.Weight);
                            if (weight < 0.0)
                            {
                                flow.ModifyEdgeWeight(edge.To, edge.From, -weight);
                                flow.ModifyEdgeWeight(edge.From, edge.To, -weight);
                            }
                        }
                        else
                        {
                            flow.ModifyEdgeWeight(edge.From, edge.To, edge.Weight);
                        }
                        if (residual.ModifyEdgeWeight(edge.From, edge.To, -edge.Weight) == 0.0)
                        {
                            residual.DelEdge(edge);
                        }
                        if (residual.ModifyEdgeWeight(edge.To, edge.From, edge.Weight).IsNaN())
                        {
                            residual.AddEdge(edge.To, edge.From, edge.Weight);
                        }
                    }
                }
            }
            return(value, flow);
        }