Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

36 ford fulkerson algorithm for maximum flow problem #199

Merged
merged 4 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 27 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,14 @@ If you are interested, please contact us at [email protected] or contribute to
- [Floyd Warshall](#floyd-warshall)
- [Kruskal Algorithm](#kruskal-algorithm)
- [Borůvka's Algorithm](#borůvkas-algorithm)
- [Graph Slicing based on connectivity](#graph-slicing-based-on-connectivity)
- [Ford-Fulkerson Algorithm](#ford-fulkerson-algorithm)
- [Partition Algorithm Explanation](#partition-algorithm-explanation)
- [Vertex-Cut](#vertex-cut)
- [Edge Balanced Vertex-Cut](#edge-balanced-vertex-cut)
- [Greedy Vertex-Cut](#greedy-vertex-cut)
- [HDRF](#hdrf)
- [EBV](#ebv)
- [Graph Slicing based on connectivity](#graph-slicing-based-on-connectivity)
- [How to contribute](#how-to-contribute)
- [Site](#site)
- [Contact](#contact)
Expand Down Expand Up @@ -422,6 +423,31 @@ The algorithm begins by finding the minimum-weight edge incident to each vertex

Borůvka's algorithm can be shown to take O(log V) iterations of the outer loop until it terminates, and therefore to run in time O(E log V), where E is the number of edges, and V is the number of vertices in G (assuming E ≥ V).

### Graph Slicing based on connectivity

Mathematical definition of the problem:
Let G be the set of nodes in a graph and n be a given node in that set.
Let C be the non-strict subset of G containing both n and all nodes reachable
from n, and let C' be its complement. There's a third set M, which is the
non-strict subset of C containing all nodes that are reachable from any node in C'.
The problem consists of finding all nodes that belong to C but not to M.

Currently implemented Algorithm:

- Use DFS to find all nodes reachable from n. These are elements of set C.
- Initialize C' to be complement of C (i.e. all nodes - nodes that are in C)
- For all nodes in C', apply DFS and get the list of reachable nodes. This is set M.
- Finally removes nodes from C that belong to M. This is our solution.

Application:

This algorithm is used in garbage collection systems to decide which other objects need to be released, given that one object is about to be released.

### Ford-Fulkerson Algorithm

[Ford-Fulkerson Algorithm](https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm) is a greedy algorithm for finding a maximum flow in a flow network.
The idea behind the algorithm is as follows: as long as there is a path from the source (start node) to the sink (end node), with available capacity on all edges in the path, we send flow along one of the paths. Then we find another path, and so on. A path with available capacity is called an augmenting path.

## Partition Algorithm Explanation

### Vertex-Cut
Expand Down Expand Up @@ -466,26 +492,6 @@ Eva(u,v)(i) =I(u ∈ keep[i]) + I(v ∈ keep[i]) +α * \frac{ecount[i]}{(|E|/p)}

The lowest value is taken as partition Id.

### Graph Slicing based on connectivity

Mathematical definition of the problem:
Let G be the set of nodes in a graph and n be a given node in that set.
Let C be the non-strict subset of G containing both n and all nodes reachable
from n, and let C' be its complement. There's a third set M, which is the
non-strict subset of C containing all nodes that are reachable from any node in C'.
The problem consists of finding all nodes that belong to C but not to M.

Currently implemented Algorithm:

- Use DFS to find all nodes reachable from n. These are elements of set C.
- Initialize C' to be complement of C (i.e. all nodes - nodes that are in C)
- For all nodes in C', apply DFS and get the list of reachable nodes. This is set M.
- Finally removes nodes from C that belong to M. This is our solution.

Application:

This algorithm is used in garbage collection systems to decide which other objects need to be released, given that one object is about to be released.

## How to contribute

[![GitHub contributors](https://img.shields.io/github/contributors/ZigRazor/CXXGraph.svg)](https://GitHub.com/ZigRazor/CXXGraph/graphs/contributors/)
Expand Down
82 changes: 82 additions & 0 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,16 @@ namespace CXXGRAPH
*/
virtual const DialResult dial(const Node<T> &source, int maxWeight) const;

/**
* @brief Function runs the Ford-Fulkerson algorithm for some source node and
* target node in the graph and returns the maximum flow of the graph
*
* @param source source vertex
* @param target target vertex
* @return double Max-Flow value or -1 in case of error
*/
virtual double fordFulkersonMaxFlow(const Node<T> &source, const Node<T> &target) const;

/**
* \brief
* This function write the graph in an output file
Expand Down Expand Up @@ -2046,6 +2056,78 @@ namespace CXXGRAPH
return result;
}

template <typename T>
double Graph<T>::fordFulkersonMaxFlow(const Node<T> &source, const Node<T> &target) const
{
if (!isDirectedGraph())
{
return -1;
}
double maxFlow = 0;
std::unordered_map<const Node<T> *, const Node<T> *> parent;
std::map<const Node<T> *, std::map<const Node<T> *, double>> weightMap;
// build weight map
auto edgeSet = this->getEdgeSet();
for (const auto &edge : edgeSet)
{
// The Edge are all Directed at this point because is checked at the start
if (edge->isWeighted().value_or(false))
{
const DirectedWeightedEdge<T> *dw_edge = dynamic_cast<const DirectedWeightedEdge<T> *>(edge);
weightMap[edge->getNodePair().first][edge->getNodePair().second] = dw_edge->getWeight();
}
else
{
weightMap[edge->getNodePair().first][edge->getNodePair().second] = 0; // No Weighted Edge are assumed to be 0 weigthed
}
}

auto bfs_helper = [this, &source, &target, &parent, &weightMap]() -> bool
{
std::unordered_map<const Node<T> *, bool> visited;
std::queue<const Node<T> *> queue;
queue.push(&source);
visited[&source] = true;
parent[&source] = nullptr;
while (!queue.empty())
{
auto u = queue.front();
queue.pop();
for (auto &v : weightMap[u])
{
if (!visited[v.first] && v.second > 0)
{
queue.push(v.first);
visited[v.first] = true;
parent[v.first] = u;
}
}
}

return (visited[&target]);
};
// Updating the residual values of edges
while (bfs_helper())
{
double pathFlow = std::numeric_limits<double>::max();
for (auto v = &target; v != &source; v = parent[v])
{
auto u = parent[v];
pathFlow = std::min(pathFlow, weightMap[u][v]);
}
for (auto v = &target; v != &source; v = parent[v])
{
auto u = parent[v];
weightMap[u][v] -= pathFlow;
weightMap[v][u] += pathFlow;
}
// Adding the path flows
maxFlow += pathFlow;
}

return maxFlow;
}

template <typename T>
const std::vector<Node<T>> Graph<T>::graph_slicing(const Node<T> &start) const
{
Expand Down
130 changes: 130 additions & 0 deletions test/FordFulkersonTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include "gtest/gtest.h"
#include "CXXGraph.hpp"

TEST(FordFulkersonTest, test_1)
{
CXXGRAPH::Node<int> node0("0", 0);
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node0, node2, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node1, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node1, node3, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge5(5, node2, node4, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge6(6, node4, node3, 7);
CXXGRAPH::DirectedWeightedEdge<int> edge7(7, node4, node5, 4);
CXXGRAPH::DirectedWeightedEdge<int> edge8(8, node3, node5, 19);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);

CXXGRAPH::Graph<int> graph(edgeSet);

ASSERT_EQ(graph.fordFulkersonMaxFlow(node0, node5), 23);
}

TEST(FordFulkersonTest, test_2)
{
CXXGRAPH::Node<int> node0("0", 0);
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);

CXXGRAPH::UndirectedWeightedEdge<int> edge1(1, node0, node1, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node0, node2, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node1, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node1, node3, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge5(5, node2, node4, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge6(6, node4, node3, 7);
CXXGRAPH::DirectedWeightedEdge<int> edge7(7, node4, node5, 4);
CXXGRAPH::DirectedWeightedEdge<int> edge8(8, node3, node5, 19);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);

CXXGRAPH::Graph<int> graph(edgeSet);

ASSERT_EQ(graph.fordFulkersonMaxFlow(node0, node5), -1);
}

TEST(FordFulkersonTest, test_3)
{
CXXGRAPH::Node<int> node0("0", 0);
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node0, node2, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node1, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node1, node3, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge5(5, node2, node4, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge6(6, node4, node3, 7);
CXXGRAPH::DirectedWeightedEdge<int> edge7(7, node4, node5, 4);
CXXGRAPH::DirectedWeightedEdge<int> edge8(8, node3, node5, 4);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);

CXXGRAPH::Graph<int> graph(edgeSet);

ASSERT_EQ(graph.fordFulkersonMaxFlow(node0, node5), 8);
}

TEST(FordFulkersonTest, test_4)
{
CXXGRAPH::Node<int> node0("0", 0);
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node0, node2, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node1, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node1, node3, 12);
CXXGRAPH::DirectedWeightedEdge<int> edge5(5, node2, node4, 11);
CXXGRAPH::DirectedWeightedEdge<int> edge6(6, node4, node3, 7);
CXXGRAPH::DirectedEdge<int> edge7(7, node4, node5);
CXXGRAPH::DirectedWeightedEdge<int> edge8(8, node3, node5, 4);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);

CXXGRAPH::Graph<int> graph(edgeSet);

ASSERT_EQ(graph.fordFulkersonMaxFlow(node0, node5), 4);
}