diff --git a/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h b/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h new file mode 100644 index 0000000..af3b3a4 --- /dev/null +++ b/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +using namespace std; + +namespace MaximumFlowGoldbergGenericPushRelabel +{ + class Graph + { + private: + int _noOfVertices; + int _source; + int _sink; + int _maximumFlow; + vector> _adjMatrix; + vector> _residualGraph; + vector _excessFlow; + vector _height; + vector _visited; + queue _nodeQueue; + void InitializePreflow(); + bool CheckOverFlow(); + bool Push(int nodeU); + void Relabel(int nodeU); + public: + void CreateGraph(int noOfVertices); + void PushDirectedEdge(int valueU, int valueV, int capacity); + int FindMaximumFlowGoldbergGenericPushRelabel(); + }; +} \ No newline at end of file diff --git a/Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h b/Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h new file mode 100644 index 0000000..c05d819 --- /dev/null +++ b/Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +using namespace std; + +namespace MaximumFlowRelabelToFront +{ + class Graph + { + private: + int _noOfVertices; + int _source; + int _sink; + int _maximumFlow; + vector> _adjMatrix; + vector> _residualGraph; + vector _excessFlow; + vector _height; + vector _visited; + list _nodeList; + void InitializePreflow(); + void Discharge(int nodeU); + void Push(int nodeU, int nodeV); + void Relabel(int nodeU); + public: + void CreateGraph(int noOfVertices); + void PushDirectedEdge(int valueU, int valueV, int capacity); + int FindMaximumFlowRelabelToFront(); + }; +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.cc b/SourceCodes/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.cc new file mode 100644 index 0000000..55fdfbc --- /dev/null +++ b/SourceCodes/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.cc @@ -0,0 +1,172 @@ +#include "../Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h" +#include +using namespace std; + +namespace MaximumFlowGoldbergGenericPushRelabel +{ + // Graph Private Member Methods + + // Initializes Pre-Flow in the given Flow Network + void Graph::InitializePreflow() + { + // The height of source is set to highest possible height value + this->_height[this->_source] = this->_noOfVertices; + + // Iterating over all the vertices + for (int i = 0; i < this->_noOfVertices; i++) + { + // For the all the edges (source, v) + if (this->_residualGraph[this->_source][i] > 0) + { + // v.excessFlow = capacity(source, v) + this->_excessFlow[i] = this->_residualGraph[this->_source][i]; + + // source.excessFlow = source.excessFlow - capacity(source, v) + this->_excessFlow[this->_source] = this->_excessFlow[this->_source] - this->_residualGraph[this->_source][i]; + + // Adjusting the flow and reverse flow along source->v and v->source respectively + this->_residualGraph[i][this->_source] = this->_residualGraph[this->_source][i]; + this->_residualGraph[this->_source][i] = 0; + } + } + } + + // Checks if there is any vertex which has excess flow + bool Graph::CheckOverFlow() + { + // Iterating over all of the vertices + for (int i = 0; i < this->_noOfVertices; i++) + { + // Checks if the current vertex is not any special vertex like source, sink + // and also if there is excess flow in the current vertex and it is already not present in the queue + if (i!=this->_source && i!=this->_sink && this->_excessFlow[i] > 0 && this->_visited[i] == false) + { + // Insert the current vertex into the queue + this->_nodeQueue.push(i); + + // Mark it as visited, so until it leaves the queue it is not inserted again even if all the excess flow is neutralized + this->_visited[i] = true; + } + } + + // Checks if there is no vertex having excess flow then returns false + if (this->_nodeQueue.empty()) + { + return false; + } + + return true; + } + + // Pushes the flow from nodeU to its neighbour vertices + bool Graph::Push(int nodeU) + { + int nodeV = -1; + int minimumFlow = INT_MAX; + + // Iterating over all the vertices + for (int i = 0; i < this->_noOfVertices; i++) + { + // For G'.Adj[nodeU] select the vertex if edge (nodeU, i) is non-saturated and height[nodeU] == height[i] + 1 + if (this->_residualGraph[nodeU][i] > 0 && this->_height[nodeU] == this->_height[i] + 1) + { + nodeV = i; + break; + } + } + + // Checks if any neighbour vertex found having non-saturated edge + if (nodeV != -1) + { + // Calculate the flow amount to be added along the edge and excess flow subtracted from nodeU + minimumFlow = min(this->_residualGraph[nodeU][nodeV], this->_excessFlow[nodeU]); + + // Adjust the flow and the reverse flow along (nodeU, nodeV) + this->_residualGraph[nodeU][nodeV] = this->_residualGraph[nodeU][nodeV] - minimumFlow; + this->_residualGraph[nodeV][nodeU] = this->_residualGraph[nodeV][nodeU] + minimumFlow; + + // Adjust the excess flows in nodeU and nodeV + this->_excessFlow[nodeU] = this->_excessFlow[nodeU] - minimumFlow; + this->_excessFlow[nodeV] = this->_excessFlow[nodeV] + minimumFlow; + + // Return that the Push operation is successful + return true; + } + + // Return that the Push operation is not successful + return false; + } + + // Relabels height of vertex nodeU when there are outgoing non-saturated edges available + void Graph::Relabel(int nodeU) + { + int minimumHeight = INT_MAX; + + // Iterating over all the vertices + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + // For G'.Adj[nodeU] select for which nodeV, height[nodeU] <= height[nodeV] + if (this->_residualGraph[nodeU][nodeV] > 0 && this->_height[nodeU] <= this->_height[nodeV]) + { + // Get the minimum height among all these G'.Adj[nodeU] + minimumHeight = min(minimumHeight, this->_height[nodeV]); + } + } + + // Set height[nodeU] + this->_height[nodeU] = minimumHeight + 1; + } + + + // Graph Public Member Methods + void Graph::CreateGraph(int noOfVertices) + { + this->_noOfVertices = noOfVertices; + this->_source = 0; + this->_sink = this->_noOfVertices - 1; + this->_maximumFlow = 0; + this->_adjMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, 0)); + this->_excessFlow = vector(this->_noOfVertices, 0); + this->_height = vector(this->_noOfVertices, 0); + this->_visited = vector(this->_noOfVertices, false); + } + + void Graph::PushDirectedEdge(int valueU, int valueV, int capacity) + { + this->_adjMatrix[valueU][valueV] = capacity; + } + + int Graph::FindMaximumFlowGoldbergGenericPushRelabel() + { + this->_residualGraph = this->_adjMatrix; + + // Initialize Pre-flow + this->InitializePreflow(); + + // Checks if there is some vertices which have excess flow + while (this->CheckOverFlow()) + { + // Get the vertex + int nodeU = this->_nodeQueue.front(); + + // Checks if the Push operation is successful + if (this->Push(nodeU)) + { + // Then remove the vertex from queue and set visited[nodeU] = true + // so that on next CheckOverFlow() method call this vertex can be discovered if it still has some excess flow + this->_nodeQueue.pop(); + this->_visited[nodeU] = false; + } + + // If the Push operation is not successful + else + { + // Then Relabel nodeU without removing it from the queue + this->Relabel(nodeU); + } + } + + // Return the excess flow in the sink vertex which is actually the maximum flow along the given flow network + return this->_excessFlow[this->_sink]; + } +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc new file mode 100644 index 0000000..0f5c20c --- /dev/null +++ b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc @@ -0,0 +1,169 @@ +#include "../Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h" +#include +#include +using namespace std; + +namespace MaximumFlowRelabelToFront +{ + // Graph Private Member Methods + + // Initializes Pre-Flow in the given Flow Network + void Graph::InitializePreflow() + { + // The height of source is set to highest possible height value + this->_height[this->_source] = this->_noOfVertices; + + // Iterating over all the vertices + for (int i = 0; i < this->_noOfVertices; i++) + { + // For the all the edges (source, v) + if (this->_residualGraph[this->_source][i] > 0) + { + // v.excessFlow = capacity(source, v) + this->_excessFlow[i] = this->_residualGraph[this->_source][i]; + + // source.excessFlow = source.excessFlow - capacity(source, v) + this->_excessFlow[this->_source] = this->_excessFlow[this->_source] - this->_residualGraph[this->_source][i]; + + // Adjusting the flow and reverse flow along source->v and v->source respectively + this->_residualGraph[i][this->_source] = this->_residualGraph[this->_source][i]; + this->_residualGraph[this->_source][i] = 0; + } + } + } + + // Discharges the excess flow from nodeU + void Graph::Discharge(int nodeU) + { + // Check if excess flow of nodeU is > 0 + while (this->_excessFlow[nodeU] > 0) + { + // Falg to check if any amount of excess flow is pushed to any neighbour vertex + bool hasPushed = false; + + // Iterating over all of the vertices + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + // For G'.Adj[nodeU] check if edge (nodeU, nodeV) is admissible + if (this->_residualGraph[nodeU][nodeV] > 0 && this->_height[nodeU] == 1 + this->_height[nodeV]) + { + // Push excess flow along the admissible edge (nodeU, nodeV) + this->Push(nodeU, nodeV); + // Set the hasPushed flag to true + hasPushed = true; + // Check if there is no excess flow left in nodeU then no need to check any more admissible edge going from nodeU + if (this->_excessFlow[nodeU] == 0) + { + // Then break from iterating over G'.Adj[nodeU] + break; + } + } + } + + // Check if Push operation is not done yet + if (!hasPushed) + { + // Then it indicates that all the outgoing edges from nodeU are inadmissible + // so perform the Relabel operation on nodeU + this->Relabel(nodeU); + } + } + } + + // Pushes the flow from nodeU to its neighbour vertices + void Graph::Push(int nodeU, int nodeV) + { + // Calculate the flow amount to be added along the edge and excess flow subtracted from nodeU + int minimumFlow = min(this->_residualGraph[nodeU][nodeV], this->_excessFlow[nodeU]); + + // Adjust the flow and the reverse flow along (nodeU, nodeV) + this->_residualGraph[nodeU][nodeV] = this->_residualGraph[nodeU][nodeV] - minimumFlow; + this->_residualGraph[nodeV][nodeU] = this->_residualGraph[nodeV][nodeU] + minimumFlow; + + // Adjust the excess flows in nodeU and nodeV + this->_excessFlow[nodeU] = this->_excessFlow[nodeU] - minimumFlow; + this->_excessFlow[nodeV] = this->_excessFlow[nodeV] + minimumFlow; + } + + // Relabels height of vertex nodeU when there are outgoing non-saturated edges available + void Graph::Relabel(int nodeU) + { + int minimumHeight = INT_MAX; + + // Iterating over all the vertices + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + // For G'.Adj[nodeU] select for which nodeV, height[nodeU] <= height[nodeV] + if (this->_residualGraph[nodeU][nodeV] > 0 && this->_height[nodeU] <= this->_height[nodeV]) + { + // Get the minimum height among all these G'.Adj[nodeU] + minimumHeight = min(minimumHeight, this->_height[nodeV]); + } + } + + // Set height[nodeU] + this->_height[nodeU] = minimumHeight + 1; + } + + + // Graph Public Member Methods + void Graph::CreateGraph(int noOfVertices) + { + this->_noOfVertices = noOfVertices; + this->_source = 0; + this->_sink = this->_noOfVertices - 1; + this->_maximumFlow = 0; + this->_adjMatrix = vector>(this->_noOfVertices, vector(this->_noOfVertices, 0)); + this->_excessFlow = vector(this->_noOfVertices, 0); + this->_height = vector(this->_noOfVertices, 0); + this->_visited = vector(this->_noOfVertices, false); + } + + void Graph::PushDirectedEdge(int valueU, int valueV, int capacity) + { + this->_adjMatrix[valueU][valueV] = capacity; + } + + int Graph::FindMaximumFlowRelabelToFront() + { + this->_residualGraph = this->_adjMatrix; + + // Initialize Pre-flow + this->InitializePreflow(); + + // Make the list L = G.V - {source, sink} + for (int i = 0; i < this->_noOfVertices; i++) + { + if (i != this->_source && i != this->_sink) + { + this->_nodeList.push_back(i); + } + } + + // Set current vertex = L.head + list::iterator nodeUiterator = this->_nodeList.begin(); + + // Iterate over all of the elements in the list L + while (nodeUiterator != this->_nodeList.end()) + { + // Get the height of current vertex + int oldHeight = this->_height[*nodeUiterator]; + + // Discharge the excess flow of current vertex + this->Discharge(*nodeUiterator); + + // Check if the height of current vertex increases which means the current vertex got relabeled + if (this->_height[*nodeUiterator] > oldHeight) + { + // Then move current vertex to the front of the list L + this->_nodeList.splice(this->_nodeList.begin(), this->_nodeList, nodeUiterator); + } + + // Go to the next vertex of current vertex in L + nodeUiterator++; + } + + // Return the excess flow in the sink vertex which is actually the maximum flow along the given flow network + return this->_excessFlow[this->_sink]; + } +} \ No newline at end of file diff --git a/SourceCodes/0003_Graph/CMakeLists.txt b/SourceCodes/0003_Graph/CMakeLists.txt index 0e12aa0..0b9177d 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -17,6 +17,8 @@ set(0003GRAPH_SOURCES 0015_MaximumFlowFordFulkerson.cc 0016_MaximumFlowEdmondsKarp.cc 0017_MaximumBipartiteMatching.cc + 0018_MaximumFlowGoldbergGenericPushRelabel.cc + 0019_MaximumFlowRelabelToFront.cc ) diff --git a/Tests/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabelTest.cc b/Tests/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabelTest.cc new file mode 100644 index 0000000..6c398be --- /dev/null +++ b/Tests/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabelTest.cc @@ -0,0 +1,35 @@ +#include +#include "../Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" + +namespace MaximumFlowGoldbergGenericPushRelabel +{ + UnitTestHelper unitTestHelper; + + TEST(MaximumFlowGoldbergGenericPushRelabel, GraphWithNoParallelEdges) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 23; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(2, 1, 4); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(3, 2, 9); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowGoldbergGenericPushRelabel(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/0019_MaximumFlowRelabelToFrontTest.cc b/Tests/0003_Graph/0019_MaximumFlowRelabelToFrontTest.cc new file mode 100644 index 0000000..0d40be0 --- /dev/null +++ b/Tests/0003_Graph/0019_MaximumFlowRelabelToFrontTest.cc @@ -0,0 +1,36 @@ +#include +#include "../Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h" +#include "../0000_CommonUtilities/UnitTestHelper.h" +using namespace std; + +namespace MaximumFlowRelabelToFront +{ + UnitTestHelper unitTestHelper; + + TEST(MaximumFlowRelabelToFront, SimpleGraph) + { + // Arrange + Graph graph; + int noOfVertices = 6; + int expectedMaximumFlow = 23; + + + // Act + graph.CreateGraph(noOfVertices); + + graph.PushDirectedEdge(0, 1, 16); + graph.PushDirectedEdge(0, 2, 13); + graph.PushDirectedEdge(1, 3, 12); + graph.PushDirectedEdge(2, 1, 4); + graph.PushDirectedEdge(2, 4, 14); + graph.PushDirectedEdge(3, 2, 9); + graph.PushDirectedEdge(3, 5, 20); + graph.PushDirectedEdge(4, 3, 7); + graph.PushDirectedEdge(4, 5, 4); + + int actualMaximumFlow = graph.FindMaximumFlowRelabelToFront(); + + // Assert + ASSERT_EQ(expectedMaximumFlow, actualMaximumFlow); + } +} \ No newline at end of file diff --git a/Tests/0003_Graph/CMakeLists.txt b/Tests/0003_Graph/CMakeLists.txt index cd89765..64c53d1 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -29,6 +29,8 @@ add_executable( 0015_MaximumFlowFordFulkersonTest.cc 0016_MaximumFlowEdmondsKarpTest.cc 0017_MaximumBipartiteMatchingTest.cc + 0018_MaximumFlowGoldbergGenericPushRelabelTest.cc + 0019_MaximumFlowRelabelToFrontTest.cc ) target_link_libraries(