From 82436f1f5cd65aad26b71267f63d576ac326785d Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Wed, 9 Apr 2025 06:34:42 +0530 Subject: [PATCH 1/2] feature-test: 0019 max flow relabel to front --- ...18_MaximumFlowGoldbergGenericPushRelabel.h | 1 - .../0019_MaximumFlowRelabelToFront.h | 31 ++++ ...8_MaximumFlowGoldbergGenericPushRelabel.cc | 1 - .../0019_MaximumFlowRelabelToFront.cc | 150 ++++++++++++++++++ SourceCodes/0003_Graph/CMakeLists.txt | 1 + .../0019_MaximumFlowRelabelToFrontTest.cc | 36 +++++ Tests/0003_Graph/CMakeLists.txt | 1 + 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 Headers/0003_Graph/0019_MaximumFlowRelabelToFront.h create mode 100644 SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc create mode 100644 Tests/0003_Graph/0019_MaximumFlowRelabelToFrontTest.cc diff --git a/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h b/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h index 270d87e..af3b3a4 100644 --- a/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h +++ b/Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include using namespace std; 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 index 85d6419..55fdfbc 100644 --- a/SourceCodes/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.cc +++ b/SourceCodes/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.cc @@ -1,5 +1,4 @@ #include "../Headers/0003_Graph/0018_MaximumFlowGoldbergGenericPushRelabel.h" -#include #include using namespace std; diff --git a/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc new file mode 100644 index 0000000..2f5156e --- /dev/null +++ b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc @@ -0,0 +1,150 @@ +#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; + } + } + } + + void Graph::Discharge(int nodeU) + { + while (this->_excessFlow[nodeU] > 0) + { + bool hasPushed = false; + for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++) + { + if (this->_residualGraph[nodeU][nodeV] > 0 && this->_height[nodeU] == 1 + this->_height[nodeV]) + { + this->Push(nodeU, nodeV); + hasPushed = true; + if (this->_excessFlow[nodeU] == 0) + { + break; + } + } + } + + if (!hasPushed) + { + 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(); + + for (int i = 0; i < this->_noOfVertices; i++) + { + if (i != this->_source && i != this->_sink) + { + this->_nodeList.push_back(i); + } + } + + list::iterator nodeUiterator = this->_nodeList.begin(); + + while (nodeUiterator != this->_nodeList.end()) + { + int oldHeight = this->_height[*nodeUiterator]; + this->Discharge(*nodeUiterator); + if (this->_height[*nodeUiterator] > oldHeight) + { + this->_nodeList.splice(this->_nodeList.begin(), this->_nodeList, nodeUiterator); + nodeUiterator++; + } + else + { + 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 c66a359..0b9177d 100644 --- a/SourceCodes/0003_Graph/CMakeLists.txt +++ b/SourceCodes/0003_Graph/CMakeLists.txt @@ -18,6 +18,7 @@ set(0003GRAPH_SOURCES 0016_MaximumFlowEdmondsKarp.cc 0017_MaximumBipartiteMatching.cc 0018_MaximumFlowGoldbergGenericPushRelabel.cc + 0019_MaximumFlowRelabelToFront.cc ) 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 f26d3a4..64c53d1 100644 --- a/Tests/0003_Graph/CMakeLists.txt +++ b/Tests/0003_Graph/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable( 0016_MaximumFlowEdmondsKarpTest.cc 0017_MaximumBipartiteMatchingTest.cc 0018_MaximumFlowGoldbergGenericPushRelabelTest.cc + 0019_MaximumFlowRelabelToFrontTest.cc ) target_link_libraries( From 225d1ca3266fe9749426d7471a46ca9f80835669 Mon Sep 17 00:00:00 2001 From: Debashis Nandi Date: Thu, 10 Apr 2025 02:32:15 +0530 Subject: [PATCH 2/2] feature-docs: 0019 optimization, comments add --- .../0019_MaximumFlowRelabelToFront.cc | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc index 2f5156e..0f5c20c 100644 --- a/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc +++ b/SourceCodes/0003_Graph/0019_MaximumFlowRelabelToFront.cc @@ -32,26 +32,39 @@ namespace MaximumFlowRelabelToFront } } + // 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); } } @@ -70,7 +83,6 @@ namespace MaximumFlowRelabelToFront // 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 @@ -119,6 +131,7 @@ namespace MaximumFlowRelabelToFront // 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) @@ -127,21 +140,27 @@ namespace MaximumFlowRelabelToFront } } + // 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); - nodeUiterator++; - } - else - { - 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