Skip to content

Commit

Permalink
36 ford fulkerson algorithm for maximum flow problem (#199)
Browse files Browse the repository at this point in the history
* Introduced Ford-Fulkerson Algorithm for Maximum Flow Problem

* Introduced Tests for FordFulkerson Algorithm

* Reformatted Files

* Introduced Ford Fulkerson Algorithm on README
  • Loading branch information
ZigRazor authored Mar 19, 2022
1 parent edae327 commit 982a3d3
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 21 deletions.
48 changes: 27 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,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 @@ -424,6 +425,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 @@ -468,26 +494,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);
}

0 comments on commit 982a3d3

Please sign in to comment.