From bed5241a6b41c7b649c069fb4ea1ffaa0b2b7a2b Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Fri, 25 Oct 2019 16:50:15 -0700 Subject: [PATCH 1/7] Implement weak partial ordering (WPO) --- include/WeakPartialOrdering.h | 655 ++++++++++++++++++++++++ test/WeakPartialOrderingTest.cpp | 846 +++++++++++++++++++++++++++++++ 2 files changed, 1501 insertions(+) create mode 100644 include/WeakPartialOrdering.h create mode 100644 test/WeakPartialOrderingTest.cpp diff --git a/include/WeakPartialOrdering.h b/include/WeakPartialOrdering.h new file mode 100644 index 0000000..d493f6b --- /dev/null +++ b/include/WeakPartialOrdering.h @@ -0,0 +1,655 @@ +/* + * Implementation of a weak partial ordering (WPO) of a rooted directed graph, + * as described in the paper: + * + * Sung Kook Kim, Arnaud J. Venet, and Aditya V. Thakur. + * Deterministic Parallel Fixpoint Computation. To appear in POPL 2020 + * + * Preprint: https://arxiv.org/abs/1909.05951 + * + * Authors: Sung Kook Kim, Aditya V. Thakur. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Exceptions.h" + +namespace sparta { + +// Forward declarations +template +class WpoNode; + +template +class WeakPartialOrdering; + +namespace wpo_impl { + +template +class WpoBuilder; + +} // end namespace wpo_impl + +/* + * A node of a weak partial ordering. + * It is either head, plain, or exit. + */ +template +class WpoNode final { + public: + enum class Type { Plain, Head, Exit }; + + private: + // Uses index of an array to id Wpo nodes. + using WpoIdx = uint32_t; + + private: + NodeId m_node; + Type m_type; + + // Successors of scheduling constraints. + std::set m_successors; + // Predecessors of scheduling constraints. + std::set m_predecessors; + // Size of maximal SCC with this as its header. + uint32_t m_size; + // Number of outer predecessors w.r.t. the component (for exits only). + std::unordered_map m_num_outer_preds; + // Whether this is a widening point. + // Only the heads of the innermost components require widening. + // See Section 2, Note 1 in the following paper: + // Halbwachs N., Henry J. (2012) When the Decreasing Sequence Fails. + // In: Miné A., Schmidt D. (eds) Static Analysis. SAS 2012. + // Lecture Notes in Computer Science, vol 7460. Springer, Berlin, Heidelberg + bool m_widen; + + public: + WpoNode(const NodeId& node, Type type, uint32_t size, bool widen) + : m_node(node), m_type(type), m_size(size), m_widen(widen) {} + + public: + // Return the NodeId for this node + const NodeId& get_node() const { return m_node; } + + // Check the type of this node + bool is_plain() const { return m_type == Type::Plain; } + bool is_head() const { return m_type == Type::Head; } + bool is_exit() const { return m_type == Type::Exit; } + + // Get successors. + const std::set& get_successors() const { return m_successors; } + + // Get predecessors. + const std::set& get_predecessors() const { return m_predecessors; } + + // Get number of predecessors. + uint32_t get_num_preds() const { return m_predecessors.size(); } + + // Get number of outer predecessors w.r.t. the component (for exits only). + const std::unordered_map& get_num_outer_preds() const { + RUNTIME_CHECK(m_type == Type::Exit, undefined_operation()); + return m_num_outer_preds; + } + + // Get size of the SCC. + uint32_t get_size() const { return m_size; } + + // Check whether this is a widening point. + bool is_widening_point() const { return m_widen; } + + private: + // Add successor. + void add_successor(WpoIdx idx) { m_successors.insert(idx); } + + // Add predecessor. + void add_predecessor(WpoIdx idx) { m_predecessors.insert(idx); } + + // Check if the given node is a successor. + bool is_successor(WpoIdx idx) { + return m_successors.find(idx) != m_successors.end(); + } + + // Increment the number of outer predecessors. + void set_num_outer_preds(WpoIdx idx, uint32_t num) { + RUNTIME_CHECK(m_type == Type::Exit, internal_error()); + m_num_outer_preds[idx] = num; + } + + public: + WpoNode(WpoNode&&) = default; + WpoNode(const WpoNode&) = delete; + WpoNode& operator=(const WpoNode&) = delete; + WpoNode& operator=(WpoNode&&) = delete; + + template + friend class wpo_impl::WpoBuilder; +}; // end class WpoNode + +/* + * Implementation of the decomposition of a rooted directed graph into a weak + * partial ordering (WPO). A technical paper to appear in POPL 2020: + * + * Sung Kook Kim, Arnaud J. Venet, and Aditya V. Thakur. + * Deterministic Parallel Fixpoint Computation. To appear in POPL 2020 + * + * Preprint: https://arxiv.org/abs/1909.05951 + * + * Weak partial ordering generalizes weak topological ordering (WTO) and allows + * concurrent fixpoint iteration algorithms. + */ +template > +class WeakPartialOrdering final { + private: + using WpoNodeT = WpoNode; + using Type = typename WpoNodeT::Type; + using WpoIdx = uint32_t; + + private: + // WPO nodes. + std::vector m_nodes; + // Top level nodes. Nodes that are outside of any component. + // This is NOT used in the concurrent fixpoint iteration. + std::vector m_toplevel; + // post DFN of the nodes. + std::unordered_map m_post_dfn; + // This is used when generating a WTO from a WPO. + // See algorithm ConstructWTO^{BU} in Section 7 of the POPL 2020 paper. + bool m_lifted; + + // Get the post DFN of a node. + uint32_t get_post_dfn(NodeId n) const { + auto it = m_post_dfn.find(n); + if (it != m_post_dfn.end()) { + return it->second; + } + return 0; + } + + public: + // Construct a WPO for the given CFG. + WeakPartialOrdering( + const NodeId& root, + std::function(const NodeId&)> successors, + bool lift) + : m_lifted(lift) { + if (successors(root).empty()) { + m_nodes.emplace_back(root, Type::Plain, /*size=*/1, /*widen=*/false); + m_toplevel.push_back(0); + m_post_dfn[root] = 1; + return; + } + wpo_impl::WpoBuilder builder( + successors, m_nodes, m_toplevel, m_post_dfn, lift); + builder.build(root); + } + + // Total number of nodes in this wpo. + uint32_t size() const { return m_nodes.size(); } + + // Entry node of this wpo. + WpoIdx get_entry() { return m_nodes.size() - 1; } + + // Successors of the node. + const std::set& get_successors(WpoIdx idx) const { + return m_nodes[idx].get_successors(); + } + + // Predecessors of the node. + const std::set& get_predecessors(WpoIdx idx) const { + return m_nodes[idx].get_predecessors(); + } + + // Number of predecessors of the node. + uint32_t get_num_preds(WpoIdx idx) const { + return m_nodes[idx].get_num_preds(); + } + + // Get number of outer preds for the exit's component. + const std::unordered_map& get_num_outer_preds( + WpoIdx exit) const { + return m_nodes[exit].get_num_outer_preds(); + } + + // Head of the exit node. + WpoIdx get_head_of_exit(WpoIdx exit) const { return exit + 1; } + + // Exit of the head node. + WpoIdx get_exit_of_head(WpoIdx head) const { return head - 1; } + + // NodeId for the node. + const NodeId& get_node(WpoIdx idx) const { return m_nodes[idx].get_node(); } + + // Type queries for node. + bool is_plain(WpoIdx idx) const { return m_nodes[idx].is_plain(); } + bool is_head(WpoIdx idx) const { return m_nodes[idx].is_head(); } + bool is_exit(WpoIdx idx) const { return m_nodes[idx].is_exit(); } + + // Check whether a node is a widening point. + bool is_widening_point(WpoIdx idx) const { + return m_nodes[idx].is_widening_point(); + } + + // Check whether a predecessor is outside of the component of the head. + // This can be used to detect whether an edge is a back edge: + // is_backedge(head, pred) + // := !is_from_outside(head, pred) /\ is_predecessor(head, pred) + // This is used in interleaved widening and narrowing. + bool is_from_outside(NodeId head, NodeId pred) { + return get_post_dfn(head) < get_post_dfn(pred); + } + + WeakPartialOrdering(const WeakPartialOrdering& other) = delete; + WeakPartialOrdering(WeakPartialOrdering&& other) = delete; + WeakPartialOrdering& operator=(const WeakPartialOrdering& other) = delete; + WeakPartialOrdering& operator=(WeakPartialOrdering&& other) = delete; +}; // end class WeakPartialOrdering + +namespace wpo_impl { + +template +class WpoBuilder final { + private: + using WpoNodeT = WpoNode; + using WpoT = WeakPartialOrdering; + using Type = typename WpoNodeT::Type; + using WpoIdx = uint32_t; + + public: + WpoBuilder(std::function(const NodeId&)> successors, + std::vector& wpo_space, + std::vector& toplevel, + std::unordered_map& post_dfn, + bool lift) + : m_successors(successors), + m_wpo_space(wpo_space), + m_toplevel(toplevel), + m_post_dfn(post_dfn), + m_next_dfn(1), + m_next_post_dfn(1), + m_next_idx(0), + m_lift(lift) {} + + void build(const NodeId& root) { + construct_auxilary(root); + construct_wpo(); + // Inner preds to outer preds. + for (auto& m : m_num_inner_preds) { + auto& exitNode = node_of(m.first); + for (auto& p : m.second) { + auto& toNode = node_of(p.first); + auto num_preds = toNode.get_num_preds(); + exitNode.set_num_outer_preds(index_of(p.first), num_preds - p.second); + } + } + } + + private: + // Construct auxilary data-structures. + // Performs DFS iteratively to classify the edges and find lowest common + // ancestors of cross/forward edges. + // Nodes are identified by their DFNs in the builder. + void construct_auxilary(const NodeId& root) { + // It uses disjoint-sets data structure. + typedef std::unordered_map rank_t; + typedef std::unordered_map parent_t; + rank_t rank_map; + parent_t parent_map; + typedef boost::associative_property_map r_pmap_t; + r_pmap_t r_pmap(rank_map); + typedef boost::associative_property_map p_pmap_t; + p_pmap_t p_pmap(parent_map); + boost::disjoint_sets dsets(r_pmap, p_pmap); + + std::unordered_map ancestor; + std::stack> stack; + std::unordered_map black; + + stack.push(std::make_tuple(root, false, 0)); + while (!stack.empty()) { + // Iterative DFS. + auto& stack_top = stack.top(); + auto vertex_ref = std::get<0>(stack_top); + auto finished = std::get<1>(stack_top); + auto pred = std::get<2>(stack_top); + stack.pop(); + + if (finished) { + // DFS is done with this vertex. + // Set the post DFN. + m_post_dfn[vertex_ref] = m_next_post_dfn++; + + auto vertex = get_dfn(vertex_ref); + // Mark visited. + black[vertex] = true; + + dsets.union_set(vertex, pred); + ancestor[dsets.find_set(pred)] = pred; + } else { + if (get_dfn(vertex_ref) != + 0 /* means that the vertex is already discovered. */) { + // A forward edge. + // Forward edges can be ignored, as they are redundant. + continue; + } + // New vertex is discovered. + auto vertex = m_next_dfn++; + push_ref(vertex_ref); + set_dfn(vertex_ref, vertex); + dsets.make_set(vertex); + ancestor[vertex] = vertex; + + // This will be popped after all its successors are finished. + stack.push(std::make_tuple(vertex_ref, true, pred)); + + auto successors = m_successors(vertex_ref); + // Successors vector is reversed to match the order with WTO. + for (auto rit = successors.rbegin(); rit != successors.rend(); ++rit) { + auto succ = get_dfn(*rit); + if (succ == 0 /* 0 means that vertex is undiscovered. */) { + // Newly discovered vertex. Search continues. + stack.push(std::make_tuple(*rit, false, vertex)); + } else if (black[succ]) { + // A cross edge. + auto lca = ancestor[dsets.find_set(succ)]; + m_cross_fwds[lca].emplace_back(vertex, succ); + } else { + // A back edge. + m_back_preds[succ].push_back(vertex); + } + } + if (pred != 0) { + // A tree edge. + m_non_back_preds[vertex].push_back(pred); + } + } + } + } + + void construct_wpo() { + std::vector rank(get_next_dfn()); + std::vector parent(get_next_dfn()); + // A partition of vertices. Each subset is known to be strongly connected. + boost::disjoint_sets dsets(&rank[0], &parent[0]); + // Maps representative of a set to the vertex with minimum DFN. + std::vector rep(get_next_dfn()); + // Maps a head to its exit. + std::vector exit(get_next_dfn()); + // Maps a vertex to original non-back edges that now target the vertex. + std::vector>> origin( + get_next_dfn()); + // Maps a head to its size of components. + std::vector size(get_next_dfn()); + // Index of WpoNode in wpo space. + m_d2i.resize(2 * get_next_dfn()); + // DFN that will be assigned to the next exit. + uint32_t dfn = get_next_dfn(); + + // Initialization. + for (uint32_t v = 1; v < get_next_dfn(); v++) { + dsets.make_set(v); + rep[v] = exit[v] = v; + for (auto u : m_non_back_preds[v]) { + origin[v].emplace_back(u, v); + } + } + // In reverse DFS order, build WPOs for SCCs bottom-up. + for (uint32_t h = get_next_dfn() - 1; h > 0; h--) { + // Restore cross/fwd edges which has h as the LCA. + auto it = m_cross_fwds.find(h); + if (it != m_cross_fwds.end()) { + for (auto& edge : it->second) { + // edge: u -> v + auto& u = edge.first; + auto& v = edge.second; + auto rep_v = rep[dsets.find_set(v)]; + m_non_back_preds[rep_v].push_back(u); + origin[rep_v].emplace_back(u, v); + } + } + + // Find nested SCCs. + bool is_SCC = false; + std::unordered_set backpreds_h; + for (auto v : m_back_preds[h]) { + if (v != h) { + backpreds_h.insert(rep[dsets.find_set(v)]); + } else { + // Self-loop. + is_SCC = true; + } + } + if (!backpreds_h.empty()) { + is_SCC = true; + } + // Invariant: h \notin backpreds_h. + std::unordered_set nested_SCCs_h(backpreds_h); + std::vector worklist_h(backpreds_h.begin(), backpreds_h.end()); + while (!worklist_h.empty()) { + auto v = worklist_h.back(); + worklist_h.pop_back(); + for (auto p : m_non_back_preds[v]) { + auto rep_p = rep[dsets.find_set(p)]; + auto it = nested_SCCs_h.find(rep_p); + if (it == nested_SCCs_h.end() && rep_p != h) { + nested_SCCs_h.insert(rep_p); + worklist_h.push_back(rep_p); + } + } + } + // Invariant: h \notin nested_SCCs_h. + + // h is a Trivial SCC. + if (!is_SCC) { + size[h] = 1; + add_node(h, get_ref(h), Type::Plain, /*size=*/1, /*widen=*/false); + // Invariant: wpo_space = ...::h + continue; + } + + // Compute the size of the component C_h. + // Size of this component is initialized to 2: the head and the exit. + uint32_t size_h = 2; + for (auto v : nested_SCCs_h) { + size_h += size[v]; + } + size[h] = size_h; + // Invariant: size_h = size[h] = number of nodes in the component C_h. + + // Add new exit x_h. + auto x_h = dfn++; + add_node(x_h, get_ref(h), Type::Exit, size_h, false); + bool widen = true; + for (auto v : nested_SCCs_h) { + if (node_of(v).is_widening_point()) { + // Only the heads of the innermost components should be the widening + // points. + widen = false; + break; + } + } + add_node(h, get_ref(h), Type::Head, size_h, widen); + // Invariant: wpo_space = ...::x_h::h + if (backpreds_h.empty()) { + // Add scheduling constraints from h to x_h + add_successor(/*from=*/h, + /*to=*/x_h, + /*exit=*/x_h, + /*outer_pred?=*/false); + } else { + for (auto p : backpreds_h) { + add_successor(/*from=*/exit[p], + /*to=*/x_h, + /*exit=*/x_h, + /*outer_pred?=*/false); + } + } + // Invariant: Scheduling contraints to x_h are all constructed. + + // Add scheduling constraints between the WPOs for nested SCCs. + for (auto v : nested_SCCs_h) { + for (auto& edge : origin[v]) { + auto& u = edge.first; + auto& vv = edge.second; + auto& x_u = exit[rep[dsets.find_set(u)]]; + auto& x_v = exit[v]; + // Invariant: u -> vv, u \notin C_v, vv \in C_v, u \in C_h, v \in C_h. + if (m_lift) { + add_successor(/*from=*/x_u, + /*to=*/v, + /*exit=*/x_v, + /*outer_pred?=*/x_v != v); + // Invariant: x_u \in get_predecessors(v). + } else { + add_successor(/*from=*/x_u, + /*to=*/vv, + /*exit=*/x_v, + /*outer_pred?=*/x_v != v); + // Invariant: x_u \in get_predecessors(vv). + } + } + } + // Invariant: WPO for SCC with h as its header is constructed. + + // Update the partition by merging. + for (auto v : nested_SCCs_h) { + dsets.union_set(v, h); + rep[dsets.find_set(v)] = h; + } + + // Set exit of h to x_h. + exit[h] = x_h; + // Invariant: exit[h] = h if C_h is trivial SCC, x_h otherwise. + } + + // Add scheduling constraints between the WPOs for maximal SCCs. + for (uint32_t v = 1; v < get_next_dfn(); v++) { + if (rep[dsets.find_set(v)] == v) { + add_toplevel(v); + + for (auto& edge : origin[v]) { + auto& u = edge.first; + auto& vv = edge.second; + auto& x_u = exit[rep[dsets.find_set(u)]]; + auto& x_v = exit[v]; + // Invariant: u -> vv, u \notin C_v, vv \in C_v, u \in C_h, v \in C_h. + if (m_lift) { + add_successor(/*from=*/x_u, + /*to=*/v, + /*exit=*/x_v, + /*outer_pred?=*/x_v != v); + // Invariant: x_u \in get_predecessors(v). + } else { + add_successor(/*from=*/x_u, + /*to=*/vv, + /*exit=*/x_v, + /*outer_pred?=*/x_v != v); + // Invariant: x_u \in get_predecessors(vv). + } + } + } + } + // Invariant: WPO for the CFG is constructed. + } + + uint32_t get_dfn(NodeId n) { + auto it = m_dfn.find(n); + if (it != m_dfn.end()) { + return it->second; + } + return 0; + } + + void set_dfn(NodeId n, uint32_t num) { m_dfn[n] = num; } + + const NodeId& get_ref(uint32_t num) const { return m_ref.at(num - 1); } + + void push_ref(NodeId n) { m_ref.push_back(n); } + + uint32_t get_next_dfn() const { + // Includes exits. + // 0 represents invalid node. + return m_next_dfn; + } + + void add_node( + uint32_t dfn, NodeId ref, Type type, uint32_t size, bool widen) { + m_d2i[dfn] = m_next_idx++; + m_wpo_space.emplace_back(ref, type, size, widen); + } + + WpoNodeT& node_of(uint32_t dfn) { return m_wpo_space[index_of(dfn)]; } + + WpoIdx index_of(uint32_t dfn) { return m_d2i[dfn]; } + + void add_successor(uint32_t from, + uint32_t to, + uint32_t exit, + bool outer_pred) { + auto fromIdx = index_of(from); + auto toIdx = index_of(to); + auto& fromNode = node_of(from); + auto& toNode = node_of(to); + if (!fromNode.is_successor(toIdx)) { + if (outer_pred) { + auto& num_inner_preds_x = m_num_inner_preds[exit]; + if (num_inner_preds_x.find(to) == num_inner_preds_x.end()) { + num_inner_preds_x[to] = toNode.get_num_preds(); + } + } + fromNode.add_successor(toIdx); + toNode.add_predecessor(fromIdx); + } + } + + void add_toplevel(uint32_t what) { m_toplevel.push_back(index_of(what)); } + + std::function(const NodeId&)> m_successors; + // A reference to Wpo space (array of Wpo nodes). + std::vector& m_wpo_space; + // A reference to Wpo space that contains only the top level nodes. + std::vector& m_toplevel; + // A map from NodeId to DFN. + std::unordered_map m_dfn; + // A map from NodeId to post DFN. + std::unordered_map& m_post_dfn; + // A map from DFN to NodeId. + std::vector m_ref; + // A map from DFN to DFNs of its backedge predecessors. + std::unordered_map> m_back_preds; + // A map from DFN to DFNs of its non-backedge predecessors. + std::unordered_map> m_non_back_preds; + // A map from DFN to cross/forward edges (DFN is the lowest common ancestor). + std::unordered_map>> + m_cross_fwds; + // A map from DFN (exit) to a map from DFN to its number of inner preds. + std::unordered_map> + m_num_inner_preds; + // Next DFN to assign. + uint32_t m_next_dfn; + // Next post DFN to assign. + uint32_t m_next_post_dfn; + // Next WpoIdx to assign. + uint32_t m_next_idx; + // Maps DFN to WpoIdx. + std::vector m_d2i; + // 'Lift' the scheduling constraints when adding them. + bool m_lift; +}; // end class wpo_builder + +} // end namespace wpo_impl + +} // end namespace sparta diff --git a/test/WeakPartialOrderingTest.cpp b/test/WeakPartialOrderingTest.cpp new file mode 100644 index 0000000..e718328 --- /dev/null +++ b/test/WeakPartialOrderingTest.cpp @@ -0,0 +1,846 @@ +#include "WeakPartialOrdering.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace sparta; + +using WpoIdx = uint32_t; + +class SimpleGraph2 final { + public: + SimpleGraph2() {} + + void add_edge(const std::string& source, const std::string& target) { + m_edges[source].insert(target); + } + + std::vector successors(const std::string& node) { + auto& succs = m_edges[node]; + return std::vector(succs.begin(), succs.end()); + } + + private: + std::unordered_map> m_edges; + friend std::ostream& operator<<(std::ostream&, const SimpleGraph2&); +}; + +/* + * Print the graph in the DOT graph description language. You can use Graphviz + * or a similar program to render the output. + */ +std::ostream& operator<<(std::ostream& o, const SimpleGraph2& g) { + o << "digraph {\n"; + for (auto& edge : g.m_edges) { + for (auto& succ : edge.second) { + o << edge.first << " -> " << succ << "\n"; + } + } + o << "}\n"; + return o; +} + +struct Answer { + std::string node; + bool plain; + bool head; + bool exit; + uint32_t num_succs; + uint32_t num_preds; + uint32_t num_outer_preds; + bool widen; +}; + +/* + * This graph and the corresponding weak partial ordering are described + * on page 4 of Bourdoncle's paper: + * F. Bourdoncle. Efficient chaotic iteration strategies with widenings. + * In Formal Methods in Programming and Their Applications, pp 128-141. + * The graph is given as follows: + * + * +-----------------------+ + * | +-----+ | + * | | | | + * V V | | + * 1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 + * | | ^ ^ + * | | | | + * | +-----------------+ | + * +-----------------------------------+ + * + * Bourdoncle's algorithm computes the following weak partial ordering: + * + * 1 2 (3 4 (5 6) 7) 8 + */ +TEST(WeakPartialOrderingTest, exampleFromWtoPaper) { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "5"); + g.add_edge("5", "6"); + g.add_edge("6", "7"); + g.add_edge("7", "8"); + g.add_edge("2", "8"); + g.add_edge("4", "7"); + g.add_edge("6", "5"); + g.add_edge("7", "3"); + // "1 2 (3 4 (5 6) 7) 8" + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_FALSE(wpo.is_from_outside("5", "6")); + EXPECT_FALSE(wpo.is_from_outside("3", "7")); + EXPECT_TRUE(wpo.is_from_outside("3", "2")); + EXPECT_FALSE(wpo.is_from_outside("3", "4")); + + EXPECT_EQ(10, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + // Notice that forward edges are not considered. + Answer lst[] = { + {"1", true, false, false, 1, 0, 0, false}, + {"2", true, false, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, false}, + {"4", true, false, false, 1, 1, 0, false}, + {"5", false, true, false, 1, 1, 0, true}, + {"6", true, false, false, 1, 1, 0, false}, + {"5", false, false, true, 1, 1, 1, false}, + {"7", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 1, false}, + {"8", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 2 (3 4 (5 6) 7) 8"); +} + +/* + * Check that we correctly handle the edge cases where we have a single-node + * SCC as the last element of the top-level list of components, or as the last + * subcomponent in a component + */ +TEST(WeakPartialOrderingTest, SingletonSccAtEnd) { + { + // +--+ + // v | + // +---+ +------+ + // | 1 | --> | 2 | + // +---+ +------+ + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "2"); + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "1 (2)" + + EXPECT_FALSE(wpo.is_from_outside("2", "2")); + EXPECT_TRUE(wpo.is_from_outside("2", "1")); + EXPECT_TRUE(wpo.is_from_outside("2", "1")); + + EXPECT_EQ(3, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 1, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, true}, + {"2", false, false, true, 0, 1, 1, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2)"); + } + + { + // +--+ + // v | + // +---+ +------+ +---+ + // | 1 | <-- | 2 | --> | 3 | + // +---+ +------+ +---+ + // | ^ + // +---------+ + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "2"); + g.add_edge("2", "1"); + g.add_edge("2", "3"); + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "(1 (2)) 3" + + EXPECT_FALSE(wpo.is_from_outside("2", "2")); + EXPECT_FALSE(wpo.is_from_outside("1", "2")); + EXPECT_TRUE(wpo.is_from_outside("2", "1")); + + EXPECT_EQ(5, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", false, true, false, 1, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, true}, + {"2", false, false, true, 1, 1, 1, false}, + {"1", false, false, true, 1, 1, 0, false}, + {"3", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "(1 (2)) 3"); + } +} + +/* + * Check that we correctly handle the edge cases where we have a multi-node + * SCC as the last element of the top-level list of components, or as the last + * subcomponent in a component + */ +TEST(WeakPartialOrderingTest, SccAtEnd) { + { + // +---------+ + // v | + // +---+ +---+ +---+ + // | 1 | --> | 2 | --> | 3 | + // +---+ +---+ +---+ + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "2"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "1 (2 3)" + + EXPECT_FALSE(wpo.is_from_outside("2", "3")); + EXPECT_TRUE(wpo.is_from_outside("2", "1")); + + EXPECT_EQ(4, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 1, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, true}, + {"3", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 0, 1, 1, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2 3)"); + } + + { + // +-------------------+ + // | v + // +---+ +---+ +---+ +---+ + // | 2 | <-- | 1 | <-- | 3 | --> | 4 | + // +---+ +---+ +---+ +---+ + // ^ | + // +-------------------+ + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "2"); + g.add_edge("3", "1"); + g.add_edge("3", "4"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "(1 (2 3)) 4" + + EXPECT_FALSE(wpo.is_from_outside("1", "3")); + EXPECT_FALSE(wpo.is_from_outside("2", "3")); + EXPECT_TRUE(wpo.is_from_outside("2", "1")); + + EXPECT_EQ(6, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", false, true, false, 1, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, true}, + {"3", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 1, 1, 1, false}, + {"1", false, false, true, 1, 1, 0, false}, + {"4", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "(1 (2 3)) 4"); + } +} + +TEST(WeakPartialOrderingTest, SingleNode) { + { + // +---+ + // | 1 | + // +---+ + SimpleGraph2 g; + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "1" + + EXPECT_EQ(1, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 0, 0, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1"); + } +} + +TEST(WeakPartialOrderingTest, exampleFromWpoPaper) { + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "3"); + g.add_edge("3", "5"); + g.add_edge("5", "2"); + g.add_edge("2", "6"); + g.add_edge("6", "5"); + g.add_edge("6", "7"); + g.add_edge("7", "8"); + g.add_edge("8", "6"); + g.add_edge("6", "9"); + g.add_edge("9", "8"); + g.add_edge("2", "10"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + // "1 (2 (3 4) (6 7 9 8) 5) 10" + + EXPECT_EQ(13, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 1, 0, 0, false}, + {"2", false, true, false, 2, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, true}, + {"4", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 1, false}, + {"6", false, true, false, 2, 1, 0, true}, + {"7", true, false, false, 1, 1, 0, false}, + {"9", true, false, false, 1, 1, 0, false}, + {"8", true, false, false, 1, 2, 0, false}, + {"6", false, false, true, 1, 1, 1, false}, + {"5", true, false, false, 1, 2, 0, false}, + {"2", false, false, true, 1, 1, 1, false}, + {"10", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2 (3 4) (6 7 9 8) 5) 10"); + } +} + +TEST(WeakPartialOrderingTest, exampleFromWpoPaperIrreducible) { + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "2"); + g.add_edge("3", "4"); + g.add_edge("4", "3"); + g.add_edge("2", "5"); + g.add_edge("5", "4"); + g.add_edge("1", "6"); + g.add_edge("6", "4"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_FALSE(wpo.is_from_outside("3", "4")); + EXPECT_FALSE(wpo.is_from_outside("2", "3")); + EXPECT_TRUE(wpo.is_from_outside("2", "6")); + EXPECT_TRUE(wpo.is_from_outside("3", "6")); + EXPECT_TRUE(wpo.is_from_outside("3", "5")); + EXPECT_FALSE(wpo.is_from_outside("2", "5")); + + EXPECT_EQ(8, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 2, 0, 0, false}, + {"2", false, true, false, 2, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, true}, + {"5", true, false, false, 1, 1, 0, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"4", true, false, false, 1, 3, 0, false}, + {"3", false, false, true, 1, 1, 2, false}, + {"2", false, false, true, 0, 1, 2, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2 (3 5 6 4))"); + } + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "2"); + g.add_edge("3", "4"); + g.add_edge("4", "3"); + g.add_edge("2", "5"); + g.add_edge("5", "4"); + g.add_edge("1", "6"); + g.add_edge("6", "4"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, true); + // "1 6 (2 5 (3 4))" + + EXPECT_FALSE(wpo.is_from_outside("3", "4")); + EXPECT_FALSE(wpo.is_from_outside("2", "3")); + EXPECT_TRUE(wpo.is_from_outside("2", "6")); + EXPECT_TRUE(wpo.is_from_outside("3", "6")); + EXPECT_TRUE(wpo.is_from_outside("3", "5")); + EXPECT_FALSE(wpo.is_from_outside("2", "5")); + + EXPECT_EQ(8, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds + Answer lst[] = { + {"1", true, false, false, 2, 0, 0, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"2", false, true, false, 2, 2, 0, false}, + {"5", true, false, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 2, 0, true}, + {"4", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 1, false}, + {"2", false, false, true, 0, 1, 1, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 6 (2 5 (3 4))"); + } +} From be6c8c38bffbc89c89bbbcd160ad59e269560045 Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Sun, 27 Oct 2019 19:16:02 -0700 Subject: [PATCH 2/7] add comment describing the lifted in the test case --- test/WeakPartialOrderingTest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/WeakPartialOrderingTest.cpp b/test/WeakPartialOrderingTest.cpp index e718328..b4d4990 100644 --- a/test/WeakPartialOrderingTest.cpp +++ b/test/WeakPartialOrderingTest.cpp @@ -669,6 +669,13 @@ TEST(WeakPartialOrderingTest, exampleFromWpoPaper) { } } +/* + * This example illustrates the effect of setting the 'lifted' in the WPO + * construction. While the resulting structures are both valid WPOs, only the + * WPO with 'lifted' set during its construction has the same WTO as + * Bourdoncle's when linearized. However, 'lifted' adds unnecessary orders + * between WPO nodes, so it must be simply used when creating a WTO from a WPO. + */ TEST(WeakPartialOrderingTest, exampleFromWpoPaperIrreducible) { { SimpleGraph2 g; From 0994f9df3d52779c91de6e9eed0ddad336683e1f Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Mon, 16 Dec 2019 18:50:48 -0800 Subject: [PATCH 3/7] fix num_outer_preds related issue --- include/WeakPartialOrdering.h | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/include/WeakPartialOrdering.h b/include/WeakPartialOrdering.h index d493f6b..751416c 100644 --- a/include/WeakPartialOrdering.h +++ b/include/WeakPartialOrdering.h @@ -124,9 +124,9 @@ class WpoNode final { } // Increment the number of outer predecessors. - void set_num_outer_preds(WpoIdx idx, uint32_t num) { + void inc_num_outer_preds(WpoIdx idx) { RUNTIME_CHECK(m_type == Type::Exit, internal_error()); - m_num_outer_preds[idx] = num; + m_num_outer_preds[idx]++; } public: @@ -286,14 +286,17 @@ class WpoBuilder final { void build(const NodeId& root) { construct_auxilary(root); construct_wpo(); - // Inner preds to outer preds. - for (auto& m : m_num_inner_preds) { - auto& exitNode = node_of(m.first); - for (auto& p : m.second) { - auto& toNode = node_of(p.first); - auto num_preds = toNode.get_num_preds(); - exitNode.set_num_outer_preds(index_of(p.first), num_preds - p.second); + for (auto& p : m_for_outer_preds) { + auto& v = p.first; + auto& x_max = p.second; + auto h = m_wpo_space[v].is_head() ? v : m_parent[v]; + auto x = h - 1; + while (x != x_max) { + m_wpo_space[x].inc_num_outer_preds(v); + h = m_parent[h]; + x = h - 1; } + m_wpo_space[x].inc_num_outer_preds(v); } } @@ -528,6 +531,7 @@ class WpoBuilder final { for (auto v : nested_SCCs_h) { dsets.union_set(v, h); rep[dsets.find_set(v)] = h; + m_parent[index_of(v)] = index_of(h); } // Set exit of h to x_h. @@ -539,6 +543,7 @@ class WpoBuilder final { for (uint32_t v = 1; v < get_next_dfn(); v++) { if (rep[dsets.find_set(v)] == v) { add_toplevel(v); + m_parent[index_of(v)] = index_of(v); for (auto& edge : origin[v]) { auto& u = edge.first; @@ -605,10 +610,7 @@ class WpoBuilder final { auto& toNode = node_of(to); if (!fromNode.is_successor(toIdx)) { if (outer_pred) { - auto& num_inner_preds_x = m_num_inner_preds[exit]; - if (num_inner_preds_x.find(to) == num_inner_preds_x.end()) { - num_inner_preds_x[to] = toNode.get_num_preds(); - } + m_for_outer_preds.push_back(std::make_pair(toIdx, index_of(exit))); } fromNode.add_successor(toIdx); toNode.add_predecessor(fromIdx); @@ -635,9 +637,11 @@ class WpoBuilder final { // A map from DFN to cross/forward edges (DFN is the lowest common ancestor). std::unordered_map>> m_cross_fwds; - // A map from DFN (exit) to a map from DFN to its number of inner preds. - std::unordered_map> - m_num_inner_preds; + // Increase _num_outer_preds[x][pair.first] for Cx that satisfies + // pair.first \in Cx \subseteq C_{pair.second}. + std::vector> m_for_outer_preds; + // A map from node (DFN) to its parent. + std::unordered_map m_parent; // Next DFN to assign. uint32_t m_next_dfn; // Next post DFN to assign. From dc426609bbfbe68370c4d12482e438d941f2521b Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Mon, 16 Dec 2019 19:32:18 -0800 Subject: [PATCH 4/7] fix widening point related issue --- include/WeakPartialOrdering.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/WeakPartialOrdering.h b/include/WeakPartialOrdering.h index 751416c..1d79563 100644 --- a/include/WeakPartialOrdering.h +++ b/include/WeakPartialOrdering.h @@ -477,7 +477,7 @@ class WpoBuilder final { add_node(x_h, get_ref(h), Type::Exit, size_h, false); bool widen = true; for (auto v : nested_SCCs_h) { - if (node_of(v).is_widening_point()) { + if (node_of(v).is_head()) { // Only the heads of the innermost components should be the widening // points. widen = false; From c791ecda5643dde427dced7fbe6d74f7bdcc5fdc Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Mon, 16 Dec 2019 19:49:25 -0800 Subject: [PATCH 5/7] add more WPO test cases --- test/WeakPartialOrderingTest.cpp | 439 +++++++++++++++++++++++++++++++ 1 file changed, 439 insertions(+) diff --git a/test/WeakPartialOrderingTest.cpp b/test/WeakPartialOrderingTest.cpp index b4d4990..656d1d3 100644 --- a/test/WeakPartialOrderingTest.cpp +++ b/test/WeakPartialOrderingTest.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -851,3 +852,441 @@ TEST(WeakPartialOrderingTest, exampleFromWpoPaperIrreducible) { EXPECT_EQ(wto.str(), "1 6 (2 5 (3 4))"); } } + +TEST(WeakPartialOrderingTest, handlingOuterPreds) { + { + SimpleGraph2 g; + g.add_edge("1", "12"); + g.add_edge("1", "16"); + g.add_edge("1", "18"); + g.add_edge("1", "26"); + g.add_edge("12", "45"); + g.add_edge("12", "75"); + g.add_edge("12", "46"); + g.add_edge("16", "74"); + g.add_edge("16", "75"); + g.add_edge("18", "92"); + g.add_edge("26", "93"); + g.add_edge("45", "46"); + g.add_edge("46", "47"); + g.add_edge("47", "73"); + g.add_edge("73", "74"); + g.add_edge("73", "75"); + g.add_edge("73", "73"); + g.add_edge("74", "46"); + g.add_edge("75", "45"); + g.add_edge("92", "93"); + g.add_edge("93", "45"); + g.add_edge("93", "46"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_EQ(16, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds, widen + Answer lst[] = { + {"1", true, false, false, 4, 0, 0, false}, + {"12", true, false, false, 1, 1, 0, false}, + {"16", true, false, false, 2, 1, 0, false}, + {"18", true, false, false, 1, 1, 0, false}, + {"92", true, false, false, 1, 1, 0, false}, + {"26", true, false, false, 1, 1, 0, false}, + {"93", true, false, false, 2, 2, 0, false}, + {"45", false, true, false, 1, 2, 0, false}, + {"46", false, true, false, 1, 2, 0, false}, + {"47", true, false, false, 1, 1, 0, false}, + {"73", false, true, false, 1, 1, 0, true}, + {"73", false, false, true, 1, 1, 1, false}, + {"74", true, false, false, 1, 2, 0, false}, + {"46", false, false, true, 1, 1, 2, false}, + {"75", true, false, false, 1, 2, 0, false}, + {"45", false, false, true, 0, 1, 4, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 12 16 18 92 26 93 (45 (46 47 (73) 74) 75)"); + } + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "5"); + g.add_edge("5", "6"); + g.add_edge("6", "7"); + g.add_edge("6", "2"); + g.add_edge("5", "3"); + g.add_edge("1", "8"); + g.add_edge("8", "4"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_EQ(10, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds, widen + Answer lst[] = { + {"1", true, false, false, 2, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, true}, + {"8", true, false, false, 1, 1, 0, false}, + {"4", true, false, false, 1, 2, 0, false}, + {"5", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 2, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 1, 1, 2, false}, + {"7", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2 (3 8 4 5) 6) 7"); + } + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "5"); + g.add_edge("5", "6"); + g.add_edge("6", "7"); + g.add_edge("6", "2"); + g.add_edge("5", "3"); + g.add_edge("1", "8"); + g.add_edge("8", "3"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_EQ(10, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds, widen + Answer lst[] = { + {"1", true, false, false, 2, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, false}, + {"8", true, false, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 2, 0, true}, + {"4", true, false, false, 1, 1, 0, false}, + {"5", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 1, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 1, 1, 2, false}, + {"7", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "1 (2 8 (3 4 5) 6) 7"); + } + { + SimpleGraph2 g; + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "5"); + g.add_edge("5", "6"); + g.add_edge("6", "7"); + g.add_edge("6", "2"); + g.add_edge("5", "3"); + g.add_edge("1", "8"); + g.add_edge("8", "4"); + g.add_edge("7", "1"); + + WeakPartialOrdering wpo( + "1", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_EQ(11, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds, widen + Answer lst[] = { + {"1", false, true, false, 2, 0, 0, false}, + {"2", false, true, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, true}, + {"8", true, false, false, 1, 1, 0, false}, + {"4", true, false, false, 1, 2, 0, false}, + {"5", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 2, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 1, 1, 2, false}, + {"7", true, false, false, 1, 1, 0, false}, + {"1", false, false, true, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { + wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "(1 (2 (3 8 4 5) 6) 7)"); + } + { + SimpleGraph2 g; + g.add_edge("0", "1"); + g.add_edge("1", "2"); + g.add_edge("2", "3"); + g.add_edge("3", "4"); + g.add_edge("4", "5"); + g.add_edge("5", "6"); + g.add_edge("6", "7"); + g.add_edge("6", "2"); + g.add_edge("5", "3"); + g.add_edge("1", "8"); + g.add_edge("8", "4"); + g.add_edge("7", "1"); + g.add_edge("7", "9"); + + WeakPartialOrdering wpo( + "0", [&g](const std::string& n) { return g.successors(n); }, false); + + EXPECT_EQ(13, wpo.size()); + + // node, plain, head, exit, num_succs, num_preds, num_outer_preds, widen + Answer lst[] = { + {"0", true, false, false, 1, 0, 0, false}, + {"1", false, true, false, 2, 1, 0, false}, + {"2", false, true, false, 1, 1, 0, false}, + {"3", false, true, false, 1, 1, 0, true}, + {"8", true, false, false, 1, 1, 0, false}, + {"4", true, false, false, 1, 2, 0, false}, + {"5", true, false, false, 1, 1, 0, false}, + {"3", false, false, true, 1, 1, 2, false}, + {"6", true, false, false, 1, 1, 0, false}, + {"2", false, false, true, 1, 1, 2, false}, + {"7", true, false, false, 1, 1, 0, false}, + {"1", false, false, true, 1, 1, 1, false}, + {"9", true, false, false, 0, 1, 0, false}, + }; + + std::unordered_map count(wpo.size()); + std::stack wl; + wl.push(wpo.get_entry()); + int i = 0; + std::ostringstream wto; + bool first = true; + while (!wl.empty()) { + auto v = wl.top(); + wl.pop(); + for (auto w : wpo.get_successors(v)) { + count[w]++; + if (count[w] == wpo.get_num_preds(w)) { + wl.push(w); + } + } + + auto& answer = lst[i++]; + EXPECT_EQ(answer.node, wpo.get_node(v)) << wpo.get_node(v); + EXPECT_EQ(answer.plain, wpo.is_plain(v)) << wpo.get_node(v); + EXPECT_EQ(answer.head, wpo.is_head(v)) << wpo.get_node(v); + EXPECT_EQ(answer.exit, wpo.is_exit(v)) << wpo.get_node(v); + EXPECT_EQ(answer.num_succs, wpo.get_successors(v).size()) + << wpo.get_node(v); + EXPECT_EQ(answer.num_preds, wpo.get_num_preds(v)) << wpo.get_node(v); + EXPECT_EQ(answer.widen, wpo.is_widening_point(v)) << wpo.get_node(v); + if (wpo.is_head(v)) { + EXPECT_EQ(wpo.get_node(wpo.get_exit_of_head(v)), wpo.get_node(v)) + << wpo.get_node(v); + if (!first) { + wto << ' '; + } + wto << '(' << wpo.get_node(v); + } else if (wpo.is_exit(v)) { + EXPECT_EQ(answer.num_outer_preds, wpo.get_num_outer_preds(v).size()) + << wpo.get_node(v); + EXPECT_EQ(wpo.get_node(wpo.get_head_of_exit(v)), wpo.get_node(v)) + << wpo.get_node(v); + wto << ')'; + } else { + if (!first) { wto << ' '; + } + wto << wpo.get_node(v); + } + if (first) { + first = false; + } + } + EXPECT_EQ(wto.str(), "0 (1 (2 (3 8 4 5) 6) 7) 9"); + } +} From 7b9b9862adeabe68231aea59976e57d22789b1f5 Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Tue, 17 Dec 2019 15:19:29 -0800 Subject: [PATCH 6/7] clang-format for WPO test --- test/WeakPartialOrderingTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/WeakPartialOrderingTest.cpp b/test/WeakPartialOrderingTest.cpp index 613c524..85e288a 100644 --- a/test/WeakPartialOrderingTest.cpp +++ b/test/WeakPartialOrderingTest.cpp @@ -1284,7 +1284,8 @@ TEST(WeakPartialOrderingTest, handlingOuterPreds) { << wpo.get_node(v); wto << ')'; } else { - if (!first) { wto << ' '; + if (!first) { + wto << ' '; } wto << wpo.get_node(v); } From 59063d57b8aa284f4af9b35fe2a07d826ad7afd6 Mon Sep 17 00:00:00 2001 From: Sung-Kook Kim Date: Tue, 17 Dec 2019 15:31:01 -0800 Subject: [PATCH 7/7] update comments in WPO.h --- include/WeakPartialOrdering.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/WeakPartialOrdering.h b/include/WeakPartialOrdering.h index 5862647..f4ccd48 100644 --- a/include/WeakPartialOrdering.h +++ b/include/WeakPartialOrdering.h @@ -293,10 +293,12 @@ class WpoBuilder final { void build(const NodeId& root) { construct_auxilary(root); construct_wpo(); + // Compute num_outer_preds. for (auto& p : m_for_outer_preds) { auto& v = p.first; auto& x_max = p.second; auto h = m_wpo_space[v].is_head() ? v : m_parent[v]; + // index of exit == index of head - 1. auto x = h - 1; while (x != x_max) { m_wpo_space[x].inc_num_outer_preds(v); @@ -644,10 +646,11 @@ class WpoBuilder final { // A map from DFN to cross/forward edges (DFN is the lowest common ancestor). std::unordered_map>> m_cross_fwds; - // Increase _num_outer_preds[x][pair.first] for Cx that satisfies - // pair.first \in Cx \subseteq C_{pair.second}. + // Increase m_num_outer_preds[x][pair.first] for component C_x that satisfies + // pair.first \in C_x \subseteq C_{pair.second}. std::vector> m_for_outer_preds; - // A map from node (DFN) to its parent. + // A map from node to the head of minimal component that contains it as + // non-header. std::unordered_map m_parent; // Next DFN to assign. uint32_t m_next_dfn;