From d0c44bf20a26bd53f804210b6e667b97a9d4d2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Uhl=C3=ADk?= <jan@uhlik.me> Date: Thu, 29 Mar 2018 21:05:07 +0200 Subject: [PATCH] Add Ford-Fulkerson algorithm. --- .../src/maximum_flow/FordFulkerson.cpp | 224 ++++++++++++++++++ .../src/maximum_flow/FordFulkerson.hpp | 55 +++++ .../src/minimum_cut/FordFulkerson.cpp | 162 +++++++++++++ .../src/minimum_cut/FordFulkerson.hpp | 60 +++++ 4 files changed, 501 insertions(+) create mode 100644 alib2graph_algo/src/maximum_flow/FordFulkerson.cpp create mode 100644 alib2graph_algo/src/maximum_flow/FordFulkerson.hpp create mode 100644 alib2graph_algo/src/minimum_cut/FordFulkerson.cpp create mode 100644 alib2graph_algo/src/minimum_cut/FordFulkerson.hpp diff --git a/alib2graph_algo/src/maximum_flow/FordFulkerson.cpp b/alib2graph_algo/src/maximum_flow/FordFulkerson.cpp new file mode 100644 index 0000000000..d5adc4361c --- /dev/null +++ b/alib2graph_algo/src/maximum_flow/FordFulkerson.cpp @@ -0,0 +1,224 @@ +// FordFulkerson.cpp +// +// Created on: 29. 03. 2016 +// Author: Jan Broz +// Modified by: Jan Uhlik +// +// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved. +// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library + +#include "FordFulkerson.hpp" + +#include <queue> +#include <climits> +#include <registration/AlgoRegistration.hpp> + +namespace maximum_flow { + +enum State { FRESH, OPEN, CLOSED }; +struct Context { + Capacity capacity; + Flow flow; + ext::unordered_map<node::Node, node::Node> prev; + ext::unordered_map<node::Node, int> sgn; + ext::unordered_map<node::Node, int> maxFlowTo; +}; + +static Capacity getCapacity(const DirectedGraph &graph) { + Capacity capacity; + + for (const auto &edge : graph.getEdges()) { + capacity[edge.first][edge.second] = edge.capacity(); + } + + return capacity; +} + +static bool findPath_dir(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink, + Context &ctx) { + ext::unordered_map<node::Node, State> state; + std::queue<node::Node> open; + node::Node actual; + + for (const node::Node &node : graph.getNodes()) { + state[node] = FRESH; + ctx.maxFlowTo[node] = 0; + } + ctx.maxFlowTo[source] = INT_MAX; + ctx.prev[source] = source; + state[source] = OPEN; + open.push(source); + + do { + actual = open.front(); + open.pop(); + state[actual] = CLOSED; + for (const node::Node &succ : graph.successors(actual)) { + if (state[succ] == FRESH && ctx.flow[actual][succ] < ctx.capacity[actual][succ]) { + state[succ] = OPEN; + open.push(succ); + ctx.prev[succ] = actual; + ctx.sgn[succ] = 1; + ctx.maxFlowTo[succ] = std::min(ctx.maxFlowTo[actual], ctx.capacity[actual][succ] - ctx.flow[actual][succ]); + } + } + for (const node::Node &pred : graph.predecessors(actual)) { + if (state[pred] == FRESH && ctx.flow[pred][actual] > 0) { + state[pred] = OPEN; + open.push(pred); + ctx.prev[pred] = actual; + ctx.sgn[pred] = -1; + ctx.maxFlowTo[pred] = std::min(ctx.maxFlowTo[actual], ctx.flow[pred][actual]); + } + } + } while (!open.empty() && actual != sink); + + return actual == sink; +} + +static bool findPath_undir(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink, + Context &ctx) { + ext::unordered_map<node::Node, State> state; + std::queue<node::Node> open; + node::Node actual; + + for (const node::Node &node : graph.getNodes()) { + state[node] = FRESH; + ctx.maxFlowTo[node] = 0; + } + ctx.maxFlowTo[source] = INT_MAX; + ctx.prev[source] = source; + state[source] = OPEN; + open.push(source); + + do { + actual = open.front(); + open.pop(); + state[actual] = CLOSED; + for (const node::Node &succ : graph.successors(actual)) { + if (state[succ] == FRESH && ctx.flow[actual][succ] < ctx.capacity[actual][succ]) { + state[succ] = OPEN; + open.push(succ); + ctx.prev[succ] = actual; + ctx.maxFlowTo[succ] = std::min(ctx.maxFlowTo[actual], ctx.capacity[actual][succ] - ctx.flow[actual][succ]); + } + } + } while (!open.empty() && actual != sink); + + return actual == sink; +} + +static void updateFlow_dir(const node::Node &source, const node::Node &sink, Context &ctx) { +/* alternative for finding the increase of flow + int path_flow = INT_MAX; + for (v=t; v!=s; v=parent[v]) + { + u = parent[v]; + path_flow = min(path_flow, rGraph[u][v]); + } +*/ + int pathFlow = ctx.maxFlowTo[sink]; + node::Node actual = sink; + while (actual != source) { + node::Node prev = ctx.prev[actual]; + if (ctx.sgn[actual] > 0) + ctx.flow[prev][actual] += pathFlow; + else + ctx.flow[actual][prev] -= pathFlow; + actual = prev; + } +} + +static void updateFlow_undir(const node::Node &source, const node::Node &sink, Context &ctx) { + int pathFlow = ctx.maxFlowTo[sink]; + node::Node actual = sink; + while (actual != source) { + node::Node prev = ctx.prev[actual]; + ctx.flow[prev][actual] += pathFlow; + ctx.flow[actual][prev] -= pathFlow; + actual = prev; + } +} + +static Flow fordfulkerson_impl_dir(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + Context ctx; + + ctx.capacity = getCapacity(graph); + for (const auto &edge : graph.getEdges()) { + ctx.flow[edge.first][edge.second] = 0; + ctx.flow[edge.second][edge.first] = 0; + } + + while (findPath_dir(graph, source, sink, ctx)) + updateFlow_dir(source, sink, ctx); + + // assign negative flow for the reversed pairs of nodes ? + for (auto u : ctx.flow) + for (auto v : u.second) + if (ctx.flow[u.first][v.first] != 0) + ctx.flow[v.first][u.first] = -ctx.flow[u.first][v.first]; + + return ctx.flow; +} + +static Flow fordfulkerson_impl_undir(const UndirectedGraph &ugraph, node::Node source, node::Node sink) { + DirectedGraph graph; + + for (auto &node: ugraph.getNodes()) { + graph.addNode(node); + } + for (auto &edge: ugraph.getEdges()) { + graph.addEdge(edge.first, edge.second, edge.capacity()); + } + + Context ctx; + ctx.capacity = getCapacity(graph); + for (const auto &edge : graph.getEdges()) { + ctx.flow[edge.first][edge.second] = 0; + } + + while (findPath_undir(graph, source, sink, ctx)) + updateFlow_undir(source, sink, ctx); + + return ctx.flow; +} + +Flow FordFulkerson::findMaximumFlow(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + return fordfulkerson_impl_dir(graph, source, sink); +} + +Flow FordFulkerson::findMaximumFlow(const UndirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + return fordfulkerson_impl_undir(graph, source, sink); +} + +// --------------------------------------------------------------------------------------------------------------------- + +} // namespace maximum_flow + +// --------------------------------------------------------------------------------------------------------------------- + +namespace { +auto FordFulkersonUndirected = registration::AbstractRegister<maximum_flow::FordFulkerson, + maximum_flow::Flow, + const maximum_flow::UndirectedGraph &, + const node::Node &, + const node::Node &>( + maximum_flow::FordFulkerson::findMaximumFlow); + +auto FordFulkersonDirected = registration::AbstractRegister<maximum_flow::FordFulkerson, + maximum_flow::Flow, + const maximum_flow::DirectedGraph &, + const node::Node &, + const node::Node &>( + maximum_flow::FordFulkerson::findMaximumFlow); +} diff --git a/alib2graph_algo/src/maximum_flow/FordFulkerson.hpp b/alib2graph_algo/src/maximum_flow/FordFulkerson.hpp new file mode 100644 index 0000000000..e64fc181ae --- /dev/null +++ b/alib2graph_algo/src/maximum_flow/FordFulkerson.hpp @@ -0,0 +1,55 @@ +// FordFulkerson.hpp +// +// Created on: 29. 03. 2016 +// Author: Jan Broz +// Modified by: Jan Uhlik +// +// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved. +// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library + +#ifndef ALIB2_FORDFULKERSON_HPP +#define ALIB2_FORDFULKERSON_HPP + +// --------------------------------------------------------------------------------------------------------------------- + +#include <alib/unordered_map> + +#include <graph/GraphClasses.hpp> +#include <node/NodeClasses.hpp> +#include <edge/EdgeClasses.hpp> + +namespace maximum_flow { + +typedef ext::unordered_map<node::Node, ext::unordered_map<node::Node, int> > Capacity; +typedef ext::unordered_map<node::Node, ext::unordered_map<node::Node, int> > Flow; + +// Old implementation without templates +using UndirectedGraph = graph::UndirectedGraph<node::Node, edge::CapacityEdge<node::Node, int>>; +using DirectedGraph = graph::DirectedGraph<node::Node, edge::CapacityEdge<node::Node, int>>; + +// --------------------------------------------------------------------------------------------------------------------- + +class FordFulkerson { + public: + +// --------------------------------------------------------------------------------------------------------------------- + + static Flow findMaximumFlow(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink); + + static Flow findMaximumFlow(const UndirectedGraph &graph, + const node::Node &source, + const node::Node &sink); + +// --------------------------------------------------------------------------------------------------------------------- + +}; + +// ===================================================================================================================== + + +// --------------------------------------------------------------------------------------------------------------------- + +} // namespace maximum_flow +#endif //ALIB2_FORDFULKERSON_HPP diff --git a/alib2graph_algo/src/minimum_cut/FordFulkerson.cpp b/alib2graph_algo/src/minimum_cut/FordFulkerson.cpp new file mode 100644 index 0000000000..07675602d6 --- /dev/null +++ b/alib2graph_algo/src/minimum_cut/FordFulkerson.cpp @@ -0,0 +1,162 @@ +// FordFulkerson.cpp +// +// Created on: 29. 03. 2016 +// Author: Jan Broz +// Modified by: Jan Uhlik +// +// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved. +// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library + +#include "FordFulkerson.hpp" + +#include <unordered_map> // return value +#include <queue> // BFS +#include <alib/iostream> + +#include <maximum_flow/FordFulkerson.hpp> +#include <registration/AlgoRegistration.hpp> + +namespace minimum_cut { + +static maximum_flow::Capacity getCapacity(const DirectedGraph &graph) { + maximum_flow::Capacity capacity; + + for (const auto &edge : graph.getEdges()) { + capacity[edge.first][edge.second] = edge.capacity(); + } + + return capacity; +} + +static maximum_flow::Capacity getCapacity(const UndirectedGraph &graph) { + maximum_flow::Capacity capacity; + + for (const auto &edge : graph.getEdges()) { + capacity[edge.first][edge.second] = edge.capacity(); + capacity[edge.second][edge.first] = edge.capacity(); + } + + return capacity; +} + +static Cut fordfulkerson_impl_dir(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + Cut cut; + + maximum_flow::Capacity capacity = getCapacity(graph); + + maximum_flow::Flow flow = maximum_flow::FordFulkerson::findMaximumFlow(graph, source, sink); + + // mark nodes, which are reachable from source + ext::unordered_map<node::Node, int> state; + std::queue<node::Node> open; + ext::vector<std::pair<node::Node, node::Node>> candidates; + for (const node::Node &node : graph.getNodes()) + state[node] = 0; + state[source] = 1; + open.push(source); + while (!open.empty()) { + node::Node actual = open.front(); + open.pop(); + for (const node::Node &succ : graph.successors(actual)) { + if (state[succ] == 0 && flow[actual][succ] < capacity[actual][succ]) { + state[succ] = 1; + open.push(succ); + } else if (flow[actual][succ] == capacity[actual][succ]) { + candidates.push_back({actual, succ}); + } + } + for (const node::Node &pred : graph.predecessors(actual)) { + if (state[pred] == 0 && flow[pred][actual] > 0) { + state[pred] = 1; + open.push(pred); + } else if (flow[pred][actual] == 0) { + candidates.push_back({pred, actual}); + } + } + } + + // cut are those edges, which lead from nodes reachable from source to nodes unreachable from source + for (std::pair<node::Node, node::Node> edge : candidates) { + if ((state[edge.first] == 1 && state[edge.second] == 0) + || (state[edge.first] == 0 && state[edge.second] == 1)) + cut.insert(edge); + } + + return cut; +} + +static Cut fordfulkerson_impl_undir(const UndirectedGraph &ugraph, + const node::Node &source, + const node::Node &sink) { + Cut cut; + + maximum_flow::Capacity capacity = getCapacity(ugraph); + + maximum_flow::Flow flow = maximum_flow::FordFulkerson::findMaximumFlow(ugraph, source, sink); + + // mark nodes, which are reachable from source + ext::unordered_map<node::Node, int> state; + std::queue<node::Node> open; + ext::vector<std::pair<node::Node, node::Node>> candidates; + for (const node::Node &node : ugraph.getNodes()) + state[node] = 0; + state[source] = 1; + open.push(source); + while (!open.empty()) { + node::Node actual = open.front(); + open.pop(); + for (const node::Node &neigh : ugraph.successors(actual)) { + if (state[neigh] == 0 && flow[actual][neigh] < capacity[actual][neigh]) { + state[neigh] = 1; + open.push(neigh); + } else if (flow[actual][neigh] == capacity[actual][neigh]) { + candidates.push_back({actual, neigh}); + } + } + } + + // cut are those edges, which lead from nodes reachable from source to nodes unreachable from source + for (std::pair<node::Node, node::Node> edge : candidates) { + if ((state[edge.first] == 1 && state[edge.second] == 0) + || (state[edge.first] == 0 && state[edge.second] == 1)) + cut.insert(edge); + } + + return cut; +} + +Cut FordFulkerson::findMinimumCut(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + return fordfulkerson_impl_dir(graph, source, sink); +} + +Cut FordFulkerson::findMinimumCut(const UndirectedGraph &graph, + const node::Node &source, + const node::Node &sink) { + return fordfulkerson_impl_undir(graph, source, sink); +} + +//auto BasicAlgorithmUndirectedGraph = registration::OverloadRegister < FordFulkerson, UndirectedGraph, UndirectedGraph > ( FordFulkerson::findMinimumCut ); + +} // namespace minimumcut + +// --------------------------------------------------------------------------------------------------------------------- + +namespace { +auto FordFulkersonUndirected = registration::AbstractRegister<minimum_cut::FordFulkerson, + minimum_cut::Cut, + const minimum_cut::UndirectedGraph &, + const node::Node &, + const node::Node &>( + minimum_cut::FordFulkerson::findMinimumCut); + +auto FordFulkersonDirected = registration::AbstractRegister<minimum_cut::FordFulkerson, + minimum_cut::Cut, + const minimum_cut::DirectedGraph &, + const node::Node &, + const node::Node &>( + minimum_cut::FordFulkerson::findMinimumCut); +} diff --git a/alib2graph_algo/src/minimum_cut/FordFulkerson.hpp b/alib2graph_algo/src/minimum_cut/FordFulkerson.hpp new file mode 100644 index 0000000000..497cee1af8 --- /dev/null +++ b/alib2graph_algo/src/minimum_cut/FordFulkerson.hpp @@ -0,0 +1,60 @@ +// FordFulkerson.hpp +// +// Created on: 29. 03. 2016 +// Author: Jan Broz +// Modified by: Jan Uhlik +// +// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved. +// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library + +#ifndef ALIB2_FORDFULKERSONCUT_HPP +#define ALIB2_FORDFULKERSONCUT_HPP + +#include <unordered_set> + +#include <graph/GraphClasses.hpp> +#include <node/NodeClasses.hpp> +#include <edge/EdgeClasses.hpp> + +namespace std { +template<> +struct hash<std::pair<node::Node, node::Node> > { + std::size_t operator()(const std::pair<node::Node, node::Node> &p) const { + return std::hash<std::string>()(static_cast<std::string>(p.first) + static_cast<std::string>(p.second)); + } +}; +} + +// ===================================================================================================================== + +namespace minimum_cut { + +typedef std::unordered_set<std::pair<node::Node, node::Node> > Cut; + +// Old implementation without templates +using UndirectedGraph = graph::UndirectedGraph<node::Node, edge::CapacityEdge<node::Node, int>>; +using DirectedGraph = graph::DirectedGraph<node::Node, edge::CapacityEdge<node::Node, int>>; + +class FordFulkerson { + public: +// --------------------------------------------------------------------------------------------------------------------- + + static Cut findMinimumCut(const DirectedGraph &graph, + const node::Node &source, + const node::Node &sink); + + static Cut findMinimumCut(const UndirectedGraph &graph, + const node::Node &source, + const node::Node &sink); + +// --------------------------------------------------------------------------------------------------------------------- + +}; + +// ===================================================================================================================== + + +// --------------------------------------------------------------------------------------------------------------------- + +} // namespace minimum_cut +#endif //ALIB2_FORDFULKERSONCUT_HPP -- GitLab