Skip to content
Snippets Groups Projects
Commit 66c7de50 authored by p-hamann's avatar p-hamann
Browse files

Update edmonds karp implementation

parent 9c13b932
1 merge request!13Update edmonds karp implementation
#include <limits>
#include <queue>
#include <cassert>
#include <algorithm>
#include <iostream>
#include <gp-bnb/edmonds_karp.hpp>
// initialization of sources and sinks not elegant, but useful
edmonds_karp::edmonds_karp(const graph &g)
: g_(g) {
};
......@@ -15,12 +14,11 @@ void edmonds_karp::reset(std::vector<node_id>& sources, std::vector<node_id>& si
sinks_ = &sinks;
// Initial flow value
flow_value_ = 0;
flow_ = 0;
// Initialize vectors
flow_.assign(g_.num_edges(), 0);
sources_and_sinks_.assign(g_.num_nodes() + 1, 0);
resid_flow_.assign(g_.num_edges(), 0);
flow_edges_.assign(g_.num_edges(), false);
// Sources marked as 1, sinks marked as -1. All other nodes are 0.
for (unsigned int i = 0; i < sources_->size(); ++i) {
......@@ -33,13 +31,10 @@ void edmonds_karp::reset(std::vector<node_id>& sources, std::vector<node_id>& si
};
/* Breadth-first search in the graph from source nodes to sink nodes */
std::pair<int, node_id> edmonds_karp::bfs(std::vector<unsigned int> &pred) const {
node_id edmonds_karp::bfs(std::vector<std::pair<node_id, edge_id>> &pred) const {
pred.clear();
pred.assign(g_.num_nodes() + 1, -1); // undiscovered nodes are marked with -1
std::vector<int> gain(g_.num_nodes() + 1, 0);
bool sink_reached = false;
node_id sink;
pred.assign(g_.num_nodes() + 1, std::make_pair(0, 0)); // undiscovered nodes are marked with 0
// Push all source nodes to the queue
std::queue<node_id> q;
......@@ -47,13 +42,12 @@ std::pair<int, node_id> edmonds_karp::bfs(std::vector<unsigned int> &pred) const
for (unsigned int i = 0; i < sources_->size(); ++i) {
node_id s = sources_->operator[](i);
q.push(s);
pred[s] = s;
gain[s] = std::numeric_limits<int>::max();
pred[s] = std::make_pair(s, 0);
}
// Perform BFS from each source node and stop when the sink was reached
// or there was no path found from the source to the sink
while (!q.empty() && !sink_reached) {
while (!q.empty()) {
node_id u = q.front();
q.pop();
......@@ -62,91 +56,37 @@ std::pair<int, node_id> edmonds_karp::bfs(std::vector<unsigned int> &pred) const
// iterate through neighbors of u
for (node_id i = 0; i < adjacency.size(); ++i) {
node_id v = adjacency[i];
edge_id edge_id = edge_ids[i];
edge_id eid = edge_ids[i];
int edge_weight = 1; // unweighted graph
if (((u >= v && flow_[edge_id] < edge_weight)
|| ( u < v && resid_flow_[edge_id] < edge_weight))
&& pred[v] == (unsigned int) -1) { // only add those neighbors with rest capacity and which were not discovered yet
pred[v] = u;
gain[v] = std::min(gain[u], edge_weight - (u >= v ? flow_[edge_id] : resid_flow_[edge_id]));
if(!flow_edges_[eid-1] && pred[v].first == 0) {
pred[v] = std::make_pair(u, eid);
if (!sink_reached) {
q.push(v);
sink = v;
}
// Check if any of the sink nodes are reached
if (sources_and_sinks_[v] == -1)
sink_reached = true;
if (sources_and_sinks_[v] == -1) return v;
q.push(v);
}
}
if (sink_reached)
return std::make_pair(gain[sink], sink);
}
return std::make_pair(0.0, sink);
return 0;
};
void edmonds_karp::add_source(node_id s) {
sources_->push_back(s);
sources_and_sinks_[s] = 1;
run();
}
void edmonds_karp::add_sink(node_id t) {
sinks_->push_back(t);
sources_and_sinks_[t] = -1;
run();
}
/* Edmonds-Karp Algorithm for unweighted, undirected graphs
Step 0: Index graph edges
Step 1: Initialize flow and residual flow vectors with length |E|
--Loop:
Step 2: Perform BFS that returns max gain from source to sink in one path
Step 3: Add gain that was calculated during BFS to the flow value. Break from the loop if gain was 0
Step 4: Update flow and residual flow values for each node */
// Edmonds-Karp Algorithm for unweighted, undirected graphs
void edmonds_karp::run() {
while (true) {
std::vector<node_id> pred;
std::vector<std::pair<node_id, edge_id>> pred;
// BFS from source nodes to sink nodes
auto bfs_result = bfs(pred);
int gain = bfs_result.first;
node_id v = bfs_result.second;
if (gain == 0) break;
// Add gain that was calculated during BFS to the flow value
flow_value_ += gain;
// Update flow and residual flow values for each edge
// in the path from the sink node to the source node
bool source_reached = false;
while (!source_reached) {
node_id u = pred[v];
if (u >= v) {
unsigned int edge_id = g_.get_edge_id(v, u);
flow_[edge_id] += gain;
resid_flow_[edge_id] -= gain;
} else {
unsigned int edge_id = g_.get_edge_id(u, v);
flow_[edge_id] -= gain;
resid_flow_[edge_id] += gain;
}
node_id n = bfs(pred);
if (n == 0) break;
flow_++;
v = u;
// Check if any of the sink nodes are reached
if (sources_and_sinks_[v] == 1)
source_reached = true;
while (true) {
flow_edges_[pred[n].second-1] = true;
if (sources_and_sinks_[(n = pred[n].first)] == 1) break;
}
}
......
......@@ -2,43 +2,31 @@
#define EDMONDSKARP_H_
#include <vector>
#include <map>
#include <gp-bnb/graph.hpp>
class edmonds_karp {
private:
const graph &g_;
std::map<std::pair<node_id, node_id>, unsigned int> indexed_edges_;
std::vector<node_id>* sources_;
std::vector<node_id>* sinks_;
std::vector<int> sources_and_sinks_;
int flow_value_;
std::vector<int> flow_;
std::vector<int> resid_flow_;
unsigned int flow_;
std::vector<bool> flow_edges_;
/**
* Indexes edges of the graph
* Every edge in the graph is mapped to its unique id
*/
void index_edges();
/**
* Performs a breadth-first search on the graph from the source node to find an augmenting path to the sink node respecting the flow values
* @param residFlow The residual flow in the network.
* @param pred Used to store the path from the source to the sink.
* @return The gain in terms of flow and id of the sink node.
* @return The sink node.
*/
std::pair<int, node_id> bfs(std::vector<unsigned int> &pred) const;
node_id bfs(std::vector<std::pair<node_id ,edge_id>> &pred) const;
public:
/**
* Constructs an instance of the EdmondsKarp algorithm for the given graph, source and sink
* Constructs an instance of the EdmondsKarp algorithm for the given graph
* @param graph The graph.
* @param source The source node.
* @param sink The sink node.
*/
edmonds_karp(const graph &g);
......@@ -54,20 +42,13 @@ public:
*/
void run();
/**
* Dynamically add a source or a sink node and
* recalculate maximum flow value
*/
void add_source(node_id s);
void add_sink(node_id t);
/**
* Returns the value of the maximum flow from source to sink.
*
* @return The maximum flow value
*/
int get_max_flow() const {
return flow_value_;
return flow_;
};
};
......
......@@ -106,17 +106,14 @@ TEST_CASE("EdmondsKarpMaxFlow") {
REQUIRE(max_flow6 == 18);
// Testing add_source() function with delaunay_n10.graph
std::vector<node_id> sources7 = {1, 3, 8, 9};
std::vector<node_id> sinks7 = {50, 60};
std::vector<node_id> sources7 = {1, 3, 8, 9, 100};
std::vector<node_id> sinks7 = {50, 60, 88};
edmonds_karp ek7 = edmonds_karp(g3);
ek7.reset(sources7, sinks7);
ek7.run();
ek7.add_source(100);
ek7.add_sink(88);
int max_flow7 = ek7.get_max_flow();
REQUIRE(max_flow7 == 18);
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment