diff --git a/src/edmonds.cpp b/src/edmonds.cpp index cea9dd6..5cc37b3 100644 --- a/src/edmonds.cpp +++ b/src/edmonds.cpp @@ -1,5 +1,6 @@ #include "edmonds.h" #include +#include using namespace ED; @@ -82,6 +83,7 @@ std::vector path_to_forest_root(Graph const & graph, NodeId id) return retval; } +__attribute__((noinline)) NodeId find_outer_vertex(Graph const & graph) { for(NodeId id = 0; id < graph.num_nodes(); ++id) @@ -94,12 +96,35 @@ NodeId find_outer_vertex(Graph const & graph) return invalid_node_id; } +void collect_exposed_vertices(Graph & graph, std::stack & container) +{ + std::stack().swap(container); + for(NodeId id = 0; id < graph.num_nodes(); id++) + { + if (graph.matched_neighbor(id) == id) + { + container.push(id); + graph.node(id).scanned = true; + } + } +} + void maximum_matching_from_initial_matching(Graph & graph) { graph.reset_forest(); - NodeId id; - while((id = find_outer_vertex(graph)) != invalid_node_id) + // Over the course of the algorithm, this will maintain all outer vertices + // that have not been scanned yet. + // Note that at the beginning, this is exactly the exposed edges. + // Throughout the algorithm, we push new ids whenever there are new outer vertices. + // Also note that we separately mark each node whether it has been collected to this stack. + // When this stack runs out, then we know that all vertices marked 'scanned' have already been processed, + // but also all vertices not marked 'scanned' are not outer vertices, so we can in fact terminate. + std::stack outer_unvisited_nodes; + collect_exposed_vertices(graph, outer_unvisited_nodes); + while(not outer_unvisited_nodes.empty()) { + NodeId const id = outer_unvisited_nodes.top(); + outer_unvisited_nodes.pop(); for(NodeId neighbor_id : graph.node(id).neighbors()) { //check_integrity(graph); @@ -109,6 +134,8 @@ void maximum_matching_from_initial_matching(Graph & graph) //std::cout << "Grow forest" << std::endl; // Grow Forest graph.node(neighbor_id).phi = id; + assert(graph.matched_neighbor(neighbor_id) != neighbor_id); + outer_unvisited_nodes.push(graph.matched_neighbor(neighbor_id)); } else if (graph.is_outer(neighbor_id) and graph.rho(id) != graph.rho(neighbor_id)) { @@ -136,6 +163,7 @@ void maximum_matching_from_initial_matching(Graph & graph) // Note that since this is tail-recursion, this will not generate // new stack frames in OPT mode graph.reset_forest(); + collect_exposed_vertices(graph, outer_unvisited_nodes); break; } else @@ -158,6 +186,8 @@ void maximum_matching_from_initial_matching(Graph & graph) }; // found first vertex fixed by rho NodeId blossom_root_id = x_path[distance_from_x]; + + // Update φ along the paths to encode the ear decomposition for(size_t i = 1; i <= distance_from_x; i += 2) { if (graph.rho(graph.phi(x_path[i])) != blossom_root_id) @@ -172,6 +202,8 @@ void maximum_matching_from_initial_matching(Graph & graph) graph.node(graph.phi(y_path[i])).phi = y_path[i]; } } + + // Link x and y if (graph.rho(x_path.front()) != blossom_root_id) { graph.node(x_path.front()).phi = y_path.front(); @@ -185,14 +217,26 @@ void maximum_matching_from_initial_matching(Graph & graph) // ρ(v) in the paths from x or y to r // We update ρ(v) first for the paths themselves, and then 'contract' ρ // by updating ρ(v) to r for all vertices where ρ(ρ(v)) = r + // Also, while walking along the paths, we can add all vertices (which are now outer) + // to the stack. for(size_t i = 0; i <= distance_from_x; ++i) { graph.node(x_path[i]).rho = blossom_root_id; + if(not graph.node(x_path[i]).scanned) + { + outer_unvisited_nodes.push(x_path[i]); + } } for(size_t i = 0; i <= distance_from_y; ++i) { graph.node(y_path[i]).rho = blossom_root_id; + if(not graph.node(y_path[i]).scanned) + { + outer_unvisited_nodes.push(y_path[i]); + } } + + // Iterating over whole graph. for(NodeId node_id = 0; node_id < graph.num_nodes(); ++node_id) { if (graph.rho(graph.rho(node_id)) == blossom_root_id) diff --git a/src/graph.cpp b/src/graph.cpp index 2f94593..f0c640f 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -176,24 +176,6 @@ std::ostream & operator<<(std::ostream & output, Graph const & graph) return output; } -bool Graph::is_outer(NodeId const id) const { - return matched_neighbor(id) == id or \ - phi(matched_neighbor(id)) != matched_neighbor(id); -} - -bool Graph::is_inner(NodeId const id) const -{ - return phi(id) != id and \ - phi(matched_neighbor(id)) == matched_neighbor(id); -} - -bool Graph::is_out_of_forest(const ED::NodeId id) const -{ - return matched_neighbor(id) != id and \ - phi(id) == id and \ - phi(matched_neighbor(id)) == matched_neighbor(id); -} - void Graph::reset_forest() { NodeId cur_id = 0; diff --git a/src/graph.hpp b/src/graph.hpp index 2a7387f..85fc18a 100644 --- a/src/graph.hpp +++ b/src/graph.hpp @@ -260,6 +260,28 @@ NodeId Graph::rho(const NodeId id) const return _nodes[id].rho; } +inline +bool Graph::is_outer(NodeId const id) const { + return matched_neighbor(id) == id or \ +phi(matched_neighbor(id)) != matched_neighbor(id); +} + +inline +bool Graph::is_inner(NodeId const id) const +{ + return phi(id) != id and \ +phi(matched_neighbor(id)) == matched_neighbor(id); +} + +inline +bool Graph::is_out_of_forest(const ED::NodeId id) const +{ + return matched_neighbor(id) != id and \ +phi(id) == id and \ +phi(matched_neighbor(id)) == matched_neighbor(id); +} + + } // namespace ED #endif /* GRAPH_HPP */