Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply performance optimization and bug fixes #8

Merged
merged 1 commit into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 87 additions & 52 deletions src/main/cpp/ds/tree/IntRootedForest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
//================================================================================
// Macros
//================================================================================

// validation: on
// #define VALIDATE(s) s

// validation: off
#define VALIDATE(s)

#define FOR_EACH_CHILD(c, p) for (auto c = nodes_[(p)].first_child; (c) != NOT_AVAILABLE; (c) = nodes_[(c)].right)

namespace ds {
Expand Down Expand Up @@ -109,19 +116,19 @@ class IntRootedForest {
//================================================================================
// array subscript operator for writing
Node& operator[](std::size_t index) {
if (!is_valid(index)) throw std::invalid_argument("invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("invalid index"));
return nodes_[index];
}

// array subscript operator for reading
Node const& operator[](std::size_t index) const {
if (!is_valid(index)) throw std::invalid_argument("invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("invalid index"));
return nodes_[index];
}

// get nodes in the one-level lower
std::vector<int> get_children(int index) const {
if (!is_valid(index)) throw std::invalid_argument("get_children: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("get_children: invalid index"));

std::vector<int> ret;
FOR_EACH_CHILD(c, index) ret.push_back(c);
Expand All @@ -141,7 +148,7 @@ class IntRootedForest {
* leaving: traverse from node to parent
*/
std::vector<std::pair<int, int>> dfs_preorder_edges(int index) const {
if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_edges: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_edges: invalid index"));

std::vector<std::pair<int, int>> ret, stack;
stack.push_back({index, false});
Expand All @@ -162,7 +169,7 @@ class IntRootedForest {
}

std::vector<std::pair<int, int>> dfs_reverse_preorder_edges(int index) const {
if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_edges: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_edges: invalid index"));

std::vector<std::pair<int, int>> ret, stack;
stack.push_back({index, false});
Expand All @@ -183,7 +190,7 @@ class IntRootedForest {
}

std::vector<int> bfs_nodes(int index) const {
if (!is_valid(index)) throw std::invalid_argument("bfs_nodes: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("bfs_nodes: invalid index"));

std::vector<int> ret;
std::queue<int> q;
Expand All @@ -199,7 +206,7 @@ class IntRootedForest {
}

std::vector<int> dfs_preorder_nodes(int index) const {
if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_nodes: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("dfs_preorder_nodes: invalid index"));

std::vector<int> ret, stack;
stack.push_back(index);
Expand All @@ -216,7 +223,7 @@ class IntRootedForest {
}

std::vector<int> dfs_reverse_preorder_nodes(int index) const {
if (!is_valid(index)) throw std::invalid_argument("dfs_reverse_preorder_nodes: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("dfs_reverse_preorder_nodes: invalid index"));

std::vector<int> ret, stack;
stack.push_back(index);
Expand All @@ -238,7 +245,7 @@ class IntRootedForest {
* @return std::vector<int>
*/
std::vector<int> get_leaves(int index) const {
if (!is_valid(index)) throw std::invalid_argument("get_leaves: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("get_leaves: invalid index"));

std::vector<int> ret;
for (auto x : dfs_reverse_preorder_nodes(index)) {
Expand All @@ -248,15 +255,15 @@ class IntRootedForest {
}

std::vector<int> get_ancestors(int index) const {
if (!is_valid(index)) throw std::invalid_argument("get_ancestors: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("get_ancestors: invalid index"));

std::vector<int> ret;
for (auto p = nodes_[index].parent; p != NOT_AVAILABLE; p = nodes_[p].parent) ret.push_back(p);
return ret;
}

int get_root(int index) const {
if (!is_valid(index)) throw std::invalid_argument("get_root: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("get_root: invalid index"));

int ret = NOT_AVAILABLE;
for (auto p = index; p != NOT_AVAILABLE; p = nodes_[p].parent) ret = p;
Expand Down Expand Up @@ -311,9 +318,9 @@ class IntRootedForest {
}

void remove(int index) {
if (!is_valid(index)) throw std::invalid_argument("remove: invalid index");
VALIDATE(if (!is_valid(index)) throw std::invalid_argument("remove: invalid index"));
detach(index);
if (!nodes_[index].is_leaf()) throw std::invalid_argument("remove: must be a leaf");
VALIDATE(if (!nodes_[index].is_leaf()) throw std::invalid_argument("remove: must be a leaf"));

--num_live_nodes_;
nodes_[index].alive = false;
Expand All @@ -325,12 +332,16 @@ class IntRootedForest {
//================================================================================
private:
void add_child(int parent, int child) {
if (!is_valid(parent)) throw std::invalid_argument("add_child: parent invalid index");
if (!is_valid(child)) throw std::invalid_argument("add_child: child invalid index");
VALIDATE({
if (!is_valid(parent)) throw std::invalid_argument("add_child: parent invalid index");
if (!is_valid(child)) throw std::invalid_argument("add_child: child invalid index");
});
auto& p = nodes_[parent];
auto& c = nodes_[child];

if (!c.is_root()) throw std::invalid_argument("add_child: child must be a root");
VALIDATE({
if (!c.is_root()) throw std::invalid_argument("add_child: child must be a root");
});

if (p.has_child()) {
nodes_[p.first_child].left = child;
Expand All @@ -344,7 +355,9 @@ class IntRootedForest {

public:
void detach(int index) {
if (!is_valid(index)) throw std::invalid_argument("detach: invalid index");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("detach: invalid index");
});

auto& node = nodes_[index];
if (node.parent != NOT_AVAILABLE) nodes_[node.parent].num_children--;
Expand All @@ -358,10 +371,11 @@ class IntRootedForest {
};

void swap(int a, int b) {
if (!is_valid(a)) throw std::invalid_argument("swap: a invalid index");
if (!is_valid(b)) throw std::invalid_argument("swap: b invalid index");
if (get_root(a) == get_root(b)) throw std::invalid_argument("swap: a and b must belong to different trees");

VALIDATE({
if (!is_valid(a)) throw std::invalid_argument("swap: a invalid index");
if (!is_valid(b)) throw std::invalid_argument("swap: b invalid index");
if (get_root(a) == get_root(b)) throw std::invalid_argument("swap: a and b must belong to different trees")
});
// if (a == b) return; // never happens

auto& na = nodes_[a];
Expand All @@ -386,20 +400,25 @@ class IntRootedForest {
* @param replace_by not to replace
*/
void replace(int index, int replace_by) {
if (!is_valid(index)) throw std::invalid_argument("replace: invalid index");
if (!is_valid(replace_by)) throw std::invalid_argument("replace: replace_by invalid index");
if (index == replace_by) throw std::invalid_argument("replace: replace_by must differ from index");
if (util::contains(get_ancestors(index), replace_by))
throw std::invalid_argument("replace: replace_by cannot be an ancestor of index");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("replace: invalid index");
if (!is_valid(replace_by)) throw std::invalid_argument("replace: replace_by invalid index");
if (index == replace_by) throw std::invalid_argument("replace: replace_by must differ from index");
if (util::contains(get_ancestors(index), replace_by)) {
throw std::invalid_argument("replace: replace_by cannot be an ancestor of index");
}
});

detach(replace_by);
swap(index, replace_by);
}

void move_to(int index, int new_parent) {
if (!is_valid(index)) throw std::invalid_argument("move_to: invalid index");
if (!is_valid(new_parent)) throw std::invalid_argument("move_to: new_parent invalid index");
if (index == new_parent) throw std::invalid_argument("move_to: index and new_parent cannot be the same");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("move_to: invalid index");
if (!is_valid(new_parent)) throw std::invalid_argument("move_to: new_parent invalid index");
if (index == new_parent) throw std::invalid_argument("move_to: index and new_parent cannot be the same");
});

detach(index);
add_child(new_parent, index);
Expand All @@ -410,12 +429,15 @@ class IntRootedForest {
* @param node node that is not a root
*/
void move_to_before(int index, int target) {
if (!is_valid(index)) throw std::invalid_argument("move_to_after: invalid index");
if (!is_valid(target)) throw std::invalid_argument("move_to_after: target invalid index");
if (nodes_[target].is_root()) throw std::invalid_argument("move_to_before: target must not be a root");
if (index == target) throw std::invalid_argument("move_to_after: index and target cannot be the same");
if (util::contains(get_ancestors(target), index))
throw std::invalid_argument("replace: index cannot be an ancestor of target");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("move_to_after: invalid index");
if (!is_valid(target)) throw std::invalid_argument("move_to_after: target invalid index");
if (nodes_[target].is_root()) throw std::invalid_argument("move_to_before: target must not be a root");
if (index == target) throw std::invalid_argument("move_to_after: index and target cannot be the same");
if (util::contains(get_ancestors(target), index)) {
throw std::invalid_argument("replace: index cannot be an ancestor of target");
}
});

detach(index);

Expand All @@ -437,12 +459,15 @@ class IntRootedForest {
* @param node node that is not a root
*/
void move_to_after(int index, int target) {
if (!is_valid(index)) throw std::invalid_argument("move_to_after: invalid index");
if (!is_valid(target)) throw std::invalid_argument("move_to_after: target invalid index");
if (nodes_[target].is_root()) throw std::invalid_argument("move_to_after: target must not be a root");
if (index == target) throw std::invalid_argument("move_to_after: index and target cannot be the same");
if (util::contains(get_ancestors(target), index))
throw std::invalid_argument("move_to_after: index cannot be an ancestor of target");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("move_to_after: invalid index");
if (!is_valid(target)) throw std::invalid_argument("move_to_after: target invalid index");
if (nodes_[target].is_root()) throw std::invalid_argument("move_to_after: target must not be a root");
if (index == target) throw std::invalid_argument("move_to_after: index and target cannot be the same");
if (util::contains(get_ancestors(target), index)) {
throw std::invalid_argument("move_to_after: index cannot be an ancestor of target");
}
});

detach(index);

Expand All @@ -462,7 +487,9 @@ class IntRootedForest {
* @brief Moves this node to the first among all its siblings.
*/
void make_first_child(int index) {
if (!is_valid(index)) throw std::invalid_argument("make_first_child: invalid index");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("make_first_child: invalid index");
});

if (nodes_[index].is_root() || nodes_[index].is_first_child()) return; // do nothing

Expand All @@ -474,10 +501,13 @@ class IntRootedForest {
* @param node node whose children are to be added to this node's children
*/
void add_children_from(int index, int target) {
if (!is_valid(index)) throw std::invalid_argument("add_children_from: invalid index");
if (!is_valid(target)) throw std::invalid_argument("add_children_from: target invalid index");
if (util::contains(get_ancestors(index), target))
throw std::invalid_argument("add_children_from: target cannot be an ancestor of index");
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("add_children_from: invalid index");
if (!is_valid(target)) throw std::invalid_argument("add_children_from: target invalid index");
if (util::contains(get_ancestors(index), target)) {
throw std::invalid_argument("add_children_from: target cannot be an ancestor of index");
}
});

if (index == target) return; // do nothing

Expand All @@ -504,12 +534,17 @@ class IntRootedForest {
* This original node will be detached form its tree but not removed.
*/
void replace_by_children(int index) {
if (!is_valid(index)) throw std::invalid_argument("replace_by_children: invalid index");
auto& node = nodes_[index];
if (node.is_root()) throw std::invalid_argument("replace_by_children:this must not be a root");

auto ch = get_children(index);
for (auto c : ch) move_to_before(c, index);
VALIDATE({
if (!is_valid(index)) throw std::invalid_argument("replace_by_children: invalid index");
auto& node = nodes_[index];
if (node.is_root()) throw std::invalid_argument("replace_by_children:this must not be a root");
});

for (auto c = nodes_[index].first_child; c != NOT_AVAILABLE;) {
auto nxt = nodes_[c].right;
move_to_before(c, index);
c = nxt;
}
detach(index);
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/cpp/modular-bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ int main(int argc, char* argv[]) {
auto graph = readwrite::read_edge_list(std::cin);

// run algorithm
#if PROFILE_ON
util::Profiler prof;
auto result = modular::modular_decomposition_time(graph, true, &prof);
prof.print();
#else
auto result = modular::modular_decomposition_time(graph, true);
#endif

// output result
printf("%d\n", result.first.modular_width());
Expand Down
4 changes: 2 additions & 2 deletions src/main/cpp/modular/MDTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ std::ostream &operator<<(std::ostream &os, MDNode const &node) { return os << no

MDTree modular_decomposition(ds::graph::Graph const &graph, bool sorted) { return MDTree(graph, sorted); }

std::pair<MDTree, double> modular_decomposition_time(ds::graph::Graph const &graph, bool sorted) {
std::pair<MDTree, double> modular_decomposition_time(ds::graph::Graph const &graph, bool sorted, util::Profiler *prof) {
auto time_start = std::chrono::system_clock::now();
auto ret = MDTree(graph, false);
auto ret = MDTree(graph, false, prof);
auto time_finish = std::chrono::system_clock::now();
auto elapsed = time_finish - time_start;
auto elapsed_sec = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count() * 1e-9;
Expand Down
9 changes: 6 additions & 3 deletions src/main/cpp/modular/MDTree.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "util/profiler.hpp"

#include "compute/MDSolver.hpp"

namespace modular {
Expand Down Expand Up @@ -47,8 +49,8 @@ class MDTree {

public:
MDTree() : root_(-1){};
MDTree(ds::graph::Graph const &graph, bool sorted = false) {
auto result = compute::MDSolver::compute(graph);
MDTree(ds::graph::Graph const &graph, bool sorted = false, util::Profiler *prof = nullptr) {
auto result = compute::MDSolver::compute(graph, prof);
if (result.second >= 0) {
*this = MDTree(result.first, result.second);
if (sorted) this->sort();
Expand Down Expand Up @@ -160,5 +162,6 @@ class MDTree {

MDTree modular_decomposition(ds::graph::Graph const &graph, bool sorted = false);

std::pair<MDTree, double> modular_decomposition_time(ds::graph::Graph const &graph, bool sorted = false);
std::pair<MDTree, double> modular_decomposition_time(ds::graph::Graph const &graph, bool sorted = false,
util::Profiler *prof = nullptr);
} // namespace modular
Loading