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