From 6e284a1410de1c463cd72ea969140c1d4e5cd041 Mon Sep 17 00:00:00 2001 From: p-hamann <p.hamann@dareit.de> Date: Mon, 20 May 2019 12:12:27 +0200 Subject: [PATCH] Add IBFS implementation --- bnb/ibfs_subtree.cpp | 82 +++++++++++++++++++++++++ bnb/incremental_bfs.cpp | 98 ++++++++++++++++++++++++++++++ include/gp-bnb/ibfs_subtree.hpp | 79 ++++++++++++++++++++++++ include/gp-bnb/incremental_bfs.hpp | 46 ++++++++++++++ meson.build | 2 + 5 files changed, 307 insertions(+) create mode 100644 bnb/ibfs_subtree.cpp create mode 100644 bnb/incremental_bfs.cpp create mode 100644 include/gp-bnb/ibfs_subtree.hpp create mode 100644 include/gp-bnb/incremental_bfs.hpp diff --git a/bnb/ibfs_subtree.cpp b/bnb/ibfs_subtree.cpp new file mode 100644 index 0000000..854da4b --- /dev/null +++ b/bnb/ibfs_subtree.cpp @@ -0,0 +1,82 @@ +#include <gp-bnb/ibfs_subtree.hpp> + +ibfs_subtree::ibfs_subtree(std::vector<node_id> roots, int id, const graph& g) : id_(id), g_(g), labels_(std::vector<std::unordered_set<node_id>> {std::unordered_set<node_id> (roots.begin(), roots.end()), std::unordered_set<node_id> () }) { +}; + +void ibfs_subtree::increment_current_max() { + labels_.push_back(std::unordered_set<node_id> ()); + current_max_++; +}; + +void ibfs_subtree::add_node(label l, node_id node, node_id pred) { + labels_[l].insert(node); + succ_[pred].insert(node); + pred_[node] = pred; +}; + +std::vector<node_id> ibfs_subtree::reduce_path(node_id leaf) { + last_flow_.clear(); + last_resid_orphans_.clear(); + + std::vector<node_id> new_front_nodes; + + node_id current = leaf; + // deletes the nodes of the augmentation path and adopts orphans + for (label i = current_max_; i > 0; --i) { + last_flow_.push_back(current); + labels_[i].erase(current); + + // searches and adopts orphans iteratively + std::vector<std::unordered_set<node_id>> orphans; + orphans.push_back(succ_[current]); + for (label j = i + 1; j < current_max_; ++j) { + std::unordered_set<node_id> next_back; + for (node_id n : orphans.back()) { + for (node_id m : succ_[n]) { + labels_[j].erase(m); + pred_.erase(j); + next_back.insert(m); + } + succ_.erase(n); + } + orphans.push_back(next_back); + } + for (label j = 0; j < orphans.size(); ++j) { + for (node_id n : orphans[j]) { + label l = adopt(n, i+j); + if (l == 0) { + last_resid_orphans_.push_back(n); + } else if (l == current_max_) { + new_front_nodes.push_back(n); + } + } + } + + node_id pred = pred_[current]; + succ_[pred].erase(current); + + pred_.erase(current); + succ_.erase(current); + + current = pred; + } + return new_front_nodes; +}; + +// tries to insert orphans with minimum label +// if they could have been inserted with label <= min, +// they would have happened because of BFS +label ibfs_subtree::adopt(node_id orphan, label min) { + for (label i = min; i < current_max_; ++i) { + for (node_id a : labels_[i]) { + for (node_id n : g_.get_adjacency(a)) { + if (n == orphan) { + add_node(i+1, orphan, a); + return i+1; + } + } + } + } + return 0; +}; + diff --git a/bnb/incremental_bfs.cpp b/bnb/incremental_bfs.cpp new file mode 100644 index 0000000..dd2aa5a --- /dev/null +++ b/bnb/incremental_bfs.cpp @@ -0,0 +1,98 @@ +#include <cassert> +#include <unordered_set> +#include <queue> + +#include <gp-bnb/incremental_bfs.hpp> + +incremental_bfs::incremental_bfs(const graph& g, std::vector<node_id> sources, std::vector<node_id> sinks) + : g_(g), sources_(sources), sinks_(sinks), s_(ibfs_subtree(sources, subtree::s, g)), t_(ibfs_subtree(sinks, subtree::t, g)) { + node_assignments_ = std::vector<subtree>(g.num_nodes(), none); + for (node_id node : sources) { + node_assignments_[node-1] = s; + } + for (node_id node : sinks) { + node_assignments_[node-1] = t; + } +}; + +void incremental_bfs::run() { + // increments the flow value to the number of pairwise neighbors of sources and sinks + for (node_id node : s_.get_front()) { + for (node_id neighbor : g_.get_adjacency(node)) { + if (node_assignments_[neighbor-1] == t) { + flow_++; + } + } + } + + // grows the subtrees alternating until one subtree cannot grow anymore + for (int i = 0; ; ++i) { + ibfs_subtree& current = (i%2 == 0) ? s_ : t_; + if (!grow(current)) { + break; + } + } +}; + +bool incremental_bfs::grow(ibfs_subtree& st) { + // creates a queue of candidate nodes for extending + std::queue<node_id> q; + for (node_id n : st.get_front()) { + q.push(n); + } + + while (!q.empty()) { + node_id node = q.front(); + q.pop(); + // searches for neighbors of candidate nodes + for (node_id neighbor : g_.get_adjacency(node)) { + // performs augmentation ... + if (-st.get_id() == node_assignments_[neighbor-1]) { + if (st.get_id() == subtree::s) { + for (node_id n : s_.reduce_path(node)) { + q.push(n); + } + t_.reduce_path(neighbor); + + } else { + for (node_id n : t_.reduce_path(node)) { + q.push(n); + } + s_.reduce_path(neighbor); + + } + + for (node_id n : s_.get_last_flow()) { + node_assignments_[n-1] = flow; + } + for (node_id n : s_.get_last_resid_orphans()) { + node_assignments_[n-1] = none; + } + for (node_id n : t_.get_last_flow()) { + node_assignments_[n-1] = flow; + } + for (node_id n : t_.get_last_resid_orphans()) { + node_assignments_[n-1] = none; + } + + flow_++; + // ... or extends st + } else if (node_assignments_[neighbor-1] == none) { + node_assignments_[neighbor-1] = (st.get_id() == subtree::s) ? s : t; + st.add_node(st.get_current_max()+1, neighbor, node); + } + } + + } + + // increments the label's upper bound of st + st.increment_current_max(); + + // determines if the tree is not grown + if (st.get_front().empty()) { + return false; + } + return true; +}; + + diff --git a/include/gp-bnb/ibfs_subtree.hpp b/include/gp-bnb/ibfs_subtree.hpp new file mode 100644 index 0000000..6664a44 --- /dev/null +++ b/include/gp-bnb/ibfs_subtree.hpp @@ -0,0 +1,79 @@ +#ifndef IBFSSUBTREE_HPP_ +#define IBFSSUBTREE_HPP_ + +#include <map> +#include <unordered_set> +#include <vector> +#include "graph.hpp" + +using label = unsigned int; +using node_id = unsigned int; + +class ibfs_subtree { +public: + + /** @brief Initializes a subtree for incremental bfs + * @param List of roots (sources or sinks) + */ + ibfs_subtree(std::vector<node_id> roots, int id, const graph& g); + + /** @brief Deletes the path of the new flow of this subtree + * Part of the augmentation of this subtree + * @param Leaf with edge to the leaf of the other tree along the augmenting path + * @return List of inserted orphans with label current_max_ + */ + std::vector<node_id> reduce_path(node_id leaf); + + /** @brief Increments the upper bound of the label of this subtree + */ + void increment_current_max(); + + /** @brief Adds a node to the subtree with label l as a child of pred + * @param Label of the node to be added + * @param Node to be added + * @param Predecessor of the node to be added + */ + void add_node(label l, node_id node, node_id pred); + + /** @brief Provides access to the nodes with the highest label + * @return Hash-Set of nodes with highest label + */ + std::unordered_set<node_id>& get_front() { + return labels_[current_max_]; + }; + + + label get_current_max() { + return current_max_; + }; + + int get_id() const { + return id_; + }; + + const std::vector<node_id>& get_last_resid_orphans() const { + return last_resid_orphans_; + }; + + const std::vector<node_id>& get_last_flow() const { + return last_flow_; + }; + + +private: + int id_; + const graph& g_; + + std::vector<std::unordered_set<node_id>> labels_; + std::map<node_id,node_id> pred_; + std::map<node_id,std::unordered_set<node_id>> succ_; + + label current_max_ = 0; + + std::vector<node_id> last_flow_; + std::vector<node_id> last_resid_orphans_; + + label adopt(node_id orphan, label min); +}; + +#endif /* IBFSSUBTREE_HPP_ */ diff --git a/include/gp-bnb/incremental_bfs.hpp b/include/gp-bnb/incremental_bfs.hpp new file mode 100644 index 0000000..f2be707 --- /dev/null +++ b/include/gp-bnb/incremental_bfs.hpp @@ -0,0 +1,46 @@ +#ifndef INCREMENTALBFS_H_ +#define INCREMENTALBFS_H_ + +#include <vector> +#include "ibfs_subtree.hpp" +#include "graph.hpp" + + +class incremental_bfs { +public: + /** @brief Initializes an incremental BFS instance + * @param Graph to be partitioned + * @param List of sources for S-T-cut + * @param List of sinks for S-T-cut + */ + incremental_bfs(const graph& g, std::vector<node_id> sources, std::vector<node_id> sinks); + + /** @brief Executes the IBFS algorithm + */ + void run(); + + /** @brief Returns the flow value after execution, which corresponds to the minimal S-T-cut + */ + int get_max_flow() const { + return flow_; + }; + +private: + enum subtree { s = -1, t = 1, none, flow }; + + const graph& g_; + + std::vector<node_id> sources_; + std::vector<node_id> sinks_; + + ibfs_subtree s_; + ibfs_subtree t_; + + std::vector<subtree> node_assignments_; + + unsigned int flow_ = 0; + + bool grow(ibfs_subtree& st); +}; + +#endif /* INCREMENTALBFS_H_ */ diff --git a/meson.build b/meson.build index b39d3c1..f4edb2a 100755 --- a/meson.build +++ b/meson.build @@ -13,6 +13,8 @@ executable('gp-bnb', 'bnb/partition.cpp', 'bnb/metis_reader.cpp', 'bnb/edmonds_karp.cpp', + 'bnb/incremental_bfs.cpp', + 'bnb/ibfs_subtree.cpp', 'bnb/bnb.cpp', include_directories: inc) -- GitLab