From f1fe0ba6a3d8710a272448f9b8036f53ea2c6e37 Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Tue, 25 Jun 2024 17:46:41 -0700 Subject: [PATCH 1/7] partial tests for graph library --- lib/utils/test/src/test_algorithms.cc | 71 +++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/lib/utils/test/src/test_algorithms.cc b/lib/utils/test/src/test_algorithms.cc index 0fb258bf15..8d4d0289fd 100644 --- a/lib/utils/test/src/test_algorithms.cc +++ b/lib/utils/test/src/test_algorithms.cc @@ -5,6 +5,7 @@ #include "utils/graph/construction.h" #include "utils/graph/hashmap_undirected_graph.h" #include "utils/graph/undirected.h" +#include "utils/graph/open_graphs.h" #include #include #include @@ -65,13 +66,14 @@ TEST_SUITE(FF_TEST_SUITE) { CHECK(get_predecessors(g, {n[1], n[2], n[3]}) == expected_result); SUBCASE("get_imm_dominators") { - std::unordered_map> result = get_imm_dominators(g); + std::unordered_map> result = + get_imm_dominators(g); - std::unordered_map> expected_result = { + std::unordered_map> expected_result = { {n[2], n[0]}, {n[1], n[0]}, {n[3], n[0]}, - {n[0], nullopt}, + {n[0], std::nullopt}, }; CHECK(result == expected_result); } @@ -106,6 +108,53 @@ TEST_SUITE(FF_TEST_SUITE) { } } + + + + + + + TEST_CASE("OpenMultiDiGraph") { + /* graph TD + .(( )) -->|e0| n0 + + n0 -->|e1| n1 + n0 -->|e2| n2 + n1 -->|e3| n3 + n2 -->|e4| n3 + + n3 -->|e5| ..(( )) + n2 -->|e6| ...(( )) + */ + + OpenMultiDiGraph g = OpenMultiDiGraph::create(); + std::vector n = add_nodes(g, 4); + NodePort p1 = g.add_node_port(); + NodePort p2 = g.add_node_port(); + NodePort p3 = g.add_node_port(); + NodePort p4 = g.add_node_port(); + std::vector p = {p1,p2,p3,p4}; + + InputMultiDiEdge e0{n[0], p[0], {1,1}}; + MultiDiEdge e1{n[1], p[1], n[0], p[0]}; + MultiDiEdge e2{n[2], p[2], n[0], p[0]}; + MultiDiEdge e3{n[3], p[3], n[1], p[1]}; + MultiDiEdge e4{n[3], p[3], n[2], p[2]}; + OutputMultiDiEdge e5{n[3], p[3], {3,3}}; + OutputMultiDiEdge e6{n[2], p[2], {2,2}}; + + std::vector e = {e0, e1, e2, e3, e4, e5, e6}; + + } + + + + + + + + + TEST_CASE("traversal") { DiGraph g = DiGraph::create(); std::vector const n = add_nodes(g, 5); @@ -139,12 +188,14 @@ TEST_SUITE(FF_TEST_SUITE) { } SUBCASE("nonlinear") { g.add_edge({n[1], n[3]}); - CHECK(is_acyclic(g) == true); // TODO, maybe a bug about the unchecked_dfs + CHECK(is_acyclic(g) == + true); // TODO, maybe a bug about the unchecked_dfs } SUBCASE("not connected") { g.remove_edge({n[2], n[3]}); - CHECK(get_dfs_ordering(g, {n[0]}) == std::vector{n[0], n[1], n[2]}); + CHECK(get_dfs_ordering(g, {n[0]}) == + std::vector{n[0], n[1], n[2]}); } } @@ -192,11 +243,11 @@ TEST_SUITE(FF_TEST_SUITE) { DiGraph g = DiGraph::create(); std::vector n = add_nodes(g, 6); std::vector edges = {{n[0], n[1]}, - {n[0], n[2]}, - {n[1], n[5]}, - {n[2], n[3]}, - {n[3], n[4]}, - {n[4], n[5]}}; + {n[0], n[2]}, + {n[1], n[5]}, + {n[2], n[3]}, + {n[3], n[4]}, + {n[4], n[5]}}; add_edges(g, edges); std::vector ordering = get_topological_ordering(g); auto CHECK_BEFORE = [&](int l, int r) { From f87afbad84c24eaf482b3931c1e4969cae937c40 Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Wed, 26 Jun 2024 14:28:07 -0700 Subject: [PATCH 2/7] Initial Tests for DiGraph and OpenMultiDiGraph --- lib/utils/test/src/test_algorithms.cc | 41 ++++--------- lib/utils/test/src/test_digraph.cc | 79 +++++++++++++++++++++++++ lib/utils/test/src/test_multidigraph.cc | 9 +-- 3 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 lib/utils/test/src/test_digraph.cc diff --git a/lib/utils/test/src/test_algorithms.cc b/lib/utils/test/src/test_algorithms.cc index 8d4d0289fd..b87005830e 100644 --- a/lib/utils/test/src/test_algorithms.cc +++ b/lib/utils/test/src/test_algorithms.cc @@ -4,8 +4,8 @@ #include "utils/graph/algorithms.h" #include "utils/graph/construction.h" #include "utils/graph/hashmap_undirected_graph.h" -#include "utils/graph/undirected.h" #include "utils/graph/open_graphs.h" +#include "utils/graph/undirected.h" #include #include #include @@ -108,12 +108,6 @@ TEST_SUITE(FF_TEST_SUITE) { } } - - - - - - TEST_CASE("OpenMultiDiGraph") { /* graph TD .(( )) -->|e0| n0 @@ -133,28 +127,19 @@ TEST_SUITE(FF_TEST_SUITE) { NodePort p2 = g.add_node_port(); NodePort p3 = g.add_node_port(); NodePort p4 = g.add_node_port(); - std::vector p = {p1,p2,p3,p4}; + std::vector p = {p1, p2, p3, p4}; - InputMultiDiEdge e0{n[0], p[0], {1,1}}; + InputMultiDiEdge e0{n[0], p[0], {1, 1}}; MultiDiEdge e1{n[1], p[1], n[0], p[0]}; MultiDiEdge e2{n[2], p[2], n[0], p[0]}; MultiDiEdge e3{n[3], p[3], n[1], p[1]}; MultiDiEdge e4{n[3], p[3], n[2], p[2]}; - OutputMultiDiEdge e5{n[3], p[3], {3,3}}; - OutputMultiDiEdge e6{n[2], p[2], {2,2}}; + OutputMultiDiEdge e5{n[3], p[3], {3, 3}}; + OutputMultiDiEdge e6{n[2], p[2], {2, 2}}; std::vector e = {e0, e1, e2, e3, e4, e5, e6}; - } - - - - - - - - TEST_CASE("traversal") { DiGraph g = DiGraph::create(); std::vector const n = add_nodes(g, 5); @@ -188,14 +173,12 @@ TEST_SUITE(FF_TEST_SUITE) { } SUBCASE("nonlinear") { g.add_edge({n[1], n[3]}); - CHECK(is_acyclic(g) == - true); // TODO, maybe a bug about the unchecked_dfs + CHECK(is_acyclic(g) == true); // TODO, maybe a bug about the unchecked_dfs } SUBCASE("not connected") { g.remove_edge({n[2], n[3]}); - CHECK(get_dfs_ordering(g, {n[0]}) == - std::vector{n[0], n[1], n[2]}); + CHECK(get_dfs_ordering(g, {n[0]}) == std::vector{n[0], n[1], n[2]}); } } @@ -243,11 +226,11 @@ TEST_SUITE(FF_TEST_SUITE) { DiGraph g = DiGraph::create(); std::vector n = add_nodes(g, 6); std::vector edges = {{n[0], n[1]}, - {n[0], n[2]}, - {n[1], n[5]}, - {n[2], n[3]}, - {n[3], n[4]}, - {n[4], n[5]}}; + {n[0], n[2]}, + {n[1], n[5]}, + {n[2], n[3]}, + {n[3], n[4]}, + {n[4], n[5]}}; add_edges(g, edges); std::vector ordering = get_topological_ordering(g); auto CHECK_BEFORE = [&](int l, int r) { diff --git a/lib/utils/test/src/test_digraph.cc b/lib/utils/test/src/test_digraph.cc new file mode 100644 index 0000000000..6ddd27b15d --- /dev/null +++ b/lib/utils/test/src/test_digraph.cc @@ -0,0 +1,79 @@ +#include "test/utils/doctest.h" +#include "utils/graph/adjacency_digraph.h" +#include "utils/graph/diedge.h" +#include "utils/graph/digraph_interfaces.h" + +using namespace FlexFlow; + +TEST_SUITE(FF_TEST_SUITE) { + TEST_CASE_TEMPLATE("DiGraph implementations", T, AdjacencyDiGraph) { + /* + graph TD + + n0 --> n1 + n0 --> n2 + n1 --> n2 + n2 --> n4 + n1 --> n3 + */ + + DiGraph g = DiGraph::create(); + std::vector n = repeat(5, [&] { return g.add_node(); }); + std::vector e = {{n[0], n[1]}, + {n[0], n[2]}, + {n[1], n[2]}, + {n[2], n[4]}, + {n[1], n[3]}}; + for (DirectedEdge const &edge : e) { + g.add_edge(edge); + } + + + CHECK(g.query_nodes(NodeQuery::all()) == + std::unordered_set{n[0], n[1], n[2], n[3], n[4]}); + + CHECK(g.query_nodes(NodeQuery{query_set{{n[0], n[2]}}}) == + std::unordered_set{n[0], n[2]}); + + std::unordered_set queried_edges = g.query_edges(DirectedEdgeQuery::all()); + std::unordered_set expected = {e[0], e[1], e[2], e[3], e[4]}; + CHECK(queried_edges == expected); + + queried_edges = g.query_edges(DirectedEdgeQuery{query_set{{n[0]}}, query_set{{n[1]}}}); + expected = std::unordered_set{e[0]}; + CHECK(queried_edges == expected); + + + SUBCASE("remove_node_unsafe") { + //assumes that, upon deleting a node, all outgoing edges are also deleted + g.remove_node_unsafe(n[0]); + + CHECK(g.query_nodes(NodeQuery::all()) == + std::unordered_set{n[1], n[2], n[3], n[4]}); + + CHECK(g.query_edges(DirectedEdgeQuery::all()) == + std::unordered_set{e[2], e[3], e[4]}); + + + g.remove_node_unsafe(n[1]); + + CHECK(g.query_nodes(NodeQuery::all()) == + std::unordered_set{n[2], n[3], n[4]}); + + CHECK(g.query_edges(DirectedEdgeQuery::all()) == + std::unordered_set{e[3]}); + } + + SUBCASE("remove_edge") { + g.remove_edge(e[0]); + + CHECK(g.query_edges(DirectedEdgeQuery::all()) == std::unordered_set{e[1], e[2], e[3], e[4]}); + CHECK(g.query_nodes(NodeQuery::all()) == std::unordered_set{n[0], n[1], n[2], n[3], n[4]}); + + g.remove_edge(e[1]); + g.remove_edge(e[3]); + CHECK(g.query_edges(DirectedEdgeQuery::all()) == std::unordered_set{e[2], e[4]}); + + } + } +} diff --git a/lib/utils/test/src/test_multidigraph.cc b/lib/utils/test/src/test_multidigraph.cc index 90e1bb2187..25d3bad94b 100644 --- a/lib/utils/test/src/test_multidigraph.cc +++ b/lib/utils/test/src/test_multidigraph.cc @@ -8,7 +8,6 @@ using namespace FlexFlow; TEST_SUITE(FF_TEST_SUITE) { TEST_CASE_TEMPLATE("MultiDiGraph implementations", T, AdjacencyMultiDiGraph) { MultiDiGraph g = MultiDiGraph::create(); - std::vector n = repeat(3, [&] { return g.add_node(); }); std::vector p = repeat(3, [&] { return g.add_node_port(); }); @@ -81,14 +80,8 @@ TEST_SUITE(FF_TEST_SUITE) { g.remove_edge(e[0]); CHECK(g.query_edges( - MultiDiEdgeQuery::all().with_src_nodes({n[0]}).with_dst_nodes( - {n[1]})) == std::unordered_set{}); - - CHECK(g.query_edges(MultiDiEdgeQuery::all().with_dst_nodes({n[2]})) == - std::unordered_set{e[1]}); + MultiDiEdgeQuery::all()) == std::unordered_set{}); - CHECK(g.query_edges(MultiDiEdgeQuery::all().with_src_idxs({p[2]})) == - std::unordered_set{e[2], e[3]}); } } } From 8b5179edc447aae731ff1463f50572470ee4acc2 Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Wed, 26 Jun 2024 14:31:25 -0700 Subject: [PATCH 3/7] formatting --- lib/utils/test/src/test_digraph.cc | 32 ++++++++++++------------- lib/utils/test/src/test_multidigraph.cc | 9 ++++++- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/utils/test/src/test_digraph.cc b/lib/utils/test/src/test_digraph.cc index 6ddd27b15d..72413daf32 100644 --- a/lib/utils/test/src/test_digraph.cc +++ b/lib/utils/test/src/test_digraph.cc @@ -11,7 +11,7 @@ TEST_SUITE(FF_TEST_SUITE) { graph TD n0 --> n1 - n0 --> n2 + n0 --> n2 n1 --> n2 n2 --> n4 n1 --> n3 @@ -19,33 +19,30 @@ TEST_SUITE(FF_TEST_SUITE) { DiGraph g = DiGraph::create(); std::vector n = repeat(5, [&] { return g.add_node(); }); - std::vector e = {{n[0], n[1]}, - {n[0], n[2]}, - {n[1], n[2]}, - {n[2], n[4]}, - {n[1], n[3]}}; + std::vector e = { + {n[0], n[1]}, {n[0], n[2]}, {n[1], n[2]}, {n[2], n[4]}, {n[1], n[3]}}; for (DirectedEdge const &edge : e) { g.add_edge(edge); } - CHECK(g.query_nodes(NodeQuery::all()) == std::unordered_set{n[0], n[1], n[2], n[3], n[4]}); CHECK(g.query_nodes(NodeQuery{query_set{{n[0], n[2]}}}) == std::unordered_set{n[0], n[2]}); - std::unordered_set queried_edges = g.query_edges(DirectedEdgeQuery::all()); + std::unordered_set queried_edges = + g.query_edges(DirectedEdgeQuery::all()); std::unordered_set expected = {e[0], e[1], e[2], e[3], e[4]}; CHECK(queried_edges == expected); - - queried_edges = g.query_edges(DirectedEdgeQuery{query_set{{n[0]}}, query_set{{n[1]}}}); + + queried_edges = g.query_edges( + DirectedEdgeQuery{query_set{{n[0]}}, query_set{{n[1]}}}); expected = std::unordered_set{e[0]}; CHECK(queried_edges == expected); - SUBCASE("remove_node_unsafe") { - //assumes that, upon deleting a node, all outgoing edges are also deleted + // assumes that, upon deleting a node, all outgoing edges are also deleted g.remove_node_unsafe(n[0]); CHECK(g.query_nodes(NodeQuery::all()) == @@ -54,7 +51,6 @@ TEST_SUITE(FF_TEST_SUITE) { CHECK(g.query_edges(DirectedEdgeQuery::all()) == std::unordered_set{e[2], e[3], e[4]}); - g.remove_node_unsafe(n[1]); CHECK(g.query_nodes(NodeQuery::all()) == @@ -67,13 +63,15 @@ TEST_SUITE(FF_TEST_SUITE) { SUBCASE("remove_edge") { g.remove_edge(e[0]); - CHECK(g.query_edges(DirectedEdgeQuery::all()) == std::unordered_set{e[1], e[2], e[3], e[4]}); - CHECK(g.query_nodes(NodeQuery::all()) == std::unordered_set{n[0], n[1], n[2], n[3], n[4]}); + CHECK(g.query_edges(DirectedEdgeQuery::all()) == + std::unordered_set{e[1], e[2], e[3], e[4]}); + CHECK(g.query_nodes(NodeQuery::all()) == + std::unordered_set{n[0], n[1], n[2], n[3], n[4]}); g.remove_edge(e[1]); g.remove_edge(e[3]); - CHECK(g.query_edges(DirectedEdgeQuery::all()) == std::unordered_set{e[2], e[4]}); - + CHECK(g.query_edges(DirectedEdgeQuery::all()) == + std::unordered_set{e[2], e[4]}); } } } diff --git a/lib/utils/test/src/test_multidigraph.cc b/lib/utils/test/src/test_multidigraph.cc index 25d3bad94b..90e1bb2187 100644 --- a/lib/utils/test/src/test_multidigraph.cc +++ b/lib/utils/test/src/test_multidigraph.cc @@ -8,6 +8,7 @@ using namespace FlexFlow; TEST_SUITE(FF_TEST_SUITE) { TEST_CASE_TEMPLATE("MultiDiGraph implementations", T, AdjacencyMultiDiGraph) { MultiDiGraph g = MultiDiGraph::create(); + std::vector n = repeat(3, [&] { return g.add_node(); }); std::vector p = repeat(3, [&] { return g.add_node_port(); }); @@ -80,8 +81,14 @@ TEST_SUITE(FF_TEST_SUITE) { g.remove_edge(e[0]); CHECK(g.query_edges( - MultiDiEdgeQuery::all()) == std::unordered_set{}); + MultiDiEdgeQuery::all().with_src_nodes({n[0]}).with_dst_nodes( + {n[1]})) == std::unordered_set{}); + + CHECK(g.query_edges(MultiDiEdgeQuery::all().with_dst_nodes({n[2]})) == + std::unordered_set{e[1]}); + CHECK(g.query_edges(MultiDiEdgeQuery::all().with_src_idxs({p[2]})) == + std::unordered_set{e[2], e[3]}); } } } From 5d3c54ab2d3589a7baad67d018661ad0889b54f3 Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Tue, 2 Jul 2024 17:45:47 -0700 Subject: [PATCH 4/7] initial commit for algorithms.h --- lib/utils/include/utils/graph/algorithms.h | 233 +++++++++++++-------- 1 file changed, 142 insertions(+), 91 deletions(-) diff --git a/lib/utils/include/utils/graph/algorithms.h b/lib/utils/include/utils/graph/algorithms.h index 87b42a90d2..02de7b09c5 100644 --- a/lib/utils/include/utils/graph/algorithms.h +++ b/lib/utils/include/utils/graph/algorithms.h @@ -1,3 +1,22 @@ +/** + * @file algorithms.h + * @brief General Use Algorithms for the Main Graph library API. + * + * Copyright 2024 CMU, Facebook, LANL, MIT, NVIDIA, and Stanford (alphabetical) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #ifndef _FLEXFLOW_UTILS_GRAPH_ALGORITHMS_H #define _FLEXFLOW_UTILS_GRAPH_ALGORITHMS_H @@ -19,134 +38,166 @@ namespace FlexFlow { -std::vector add_nodes(Graph &, int); -std::vector add_nodes(UndirectedGraph &, int); -std::vector add_nodes(DiGraph &, int); -std::vector add_nodes(MultiDiGraph &, int); +std::vector add_nodes(Graph &g, int num_nodes); +std::vector add_nodes(UndirectedGraph &g, int num_nodes); +std::vector add_nodes(DiGraph &g, int num_nodes); +std::vector add_nodes(MultiDiGraph &g, int num_nodes); std::vector add_nodes(OpenMultiDiGraph &g, int num_nodes); -std::vector add_node_ports(MultiDiGraph &, int); +std::vector add_node_ports(MultiDiGraph &g, int num_node_ports); -std::unordered_set get_nodes(GraphView const &); -std::unordered_set get_present_node_ports(MultiDiGraphView const &); +std::unordered_set get_nodes(GraphView const &g); +std::unordered_set get_present_node_ports(MultiDiGraphView const &g); -std::unordered_set get_nodes(OpenMultiDiEdge const &); +std::unordered_set get_nodes(OpenMultiDiEdge const &edge); -std::unordered_set query_nodes(GraphView const &, - std::unordered_set const &); +/** + * @brief Does this do anything? + **/ +std::unordered_set query_nodes(GraphView const &g, + std::unordered_set const &nodes); -void remove_node(MultiDiGraph &, Node const &); -void remove_node(DiGraph &, Node const &); -void remove_node(UndirectedGraph &, Node const &); +void remove_node(MultiDiGraph &g, Node const &node); +void remove_node(DiGraph &g, Node const &node); +void remove_node(UndirectedGraph &g, Node const &node); -void remove_node_if_unused(MultiDiGraph &, Node const &); -void remove_node_if_unused(DiGraph &, Node const &); -void remove_node_if_unused(UndirectedGraph &, Node const &); +void remove_node_if_unused(MultiDiGraph &g, Node const &node); +void remove_node_if_unused(DiGraph &g, Node const &node); +void remove_node_if_unused(UndirectedGraph &g, Node const &node); -void contract_node_inplace(MultiDiGraph &, Node const &from, Node const &into); -void contract_node_inplace(DiGraph &, Node const &from, Node const &into); -void contract_node_inplace(UndirectedGraph &, +/** + * @brief modifies the given graph in-place by merging the 2 nodes into a single + *one + **/ +void contract_node_inplace(MultiDiGraph &g, Node const &from, Node const &into); +void contract_node_inplace(DiGraph &g, Node const &from, Node const &into); +void contract_node_inplace(UndirectedGraph &g, Node const &from, Node const &into); -void contract_out_node_inplace(MultiDiGraph &, Node const &); -void contract_out_node_inplace(DiGraph &, Node const &); -void contract_out_node_inplace(UndirectedGraph &, Node const &); - -MultiDiGraphView contract_out_node(MultiDiGraphView const &, Node const &); -DiGraphView contract_out_node(DiGraphView const &, Node const &); -UndirectedGraphView contract_out_node(UndirectedGraphView const &, - Node const &); - -MultiDiGraphView - contract_node(MultiDiGraphView const &, Node const &from, Node const &into); +/** + * @brief identical to `contract_node_inplace`, but leaves the graph g unchanged + *and returns a new graph instead. + **/ +MultiDiGraphView contract_node(MultiDiGraphView const &g, + Node const &from, + Node const &into); DiGraphView - contract_node(DiGraphView const &, Node const &from, Node const &into); -UndirectedGraphView contract_node(UndirectedGraphView const &, + contract_node(DiGraphView const &g, Node const &from, Node const &into); +UndirectedGraphView contract_node(UndirectedGraphView const &g, Node const &from, Node const &into); -MultiDiGraphView apply_contraction(MultiDiGraphView const &, - std::unordered_map const &); -DiGraphView apply_contraction(DiGraphView const &, - std::unordered_map const &); -UndirectedGraphView apply_contraction(UndirectedGraphView const &, - std::unordered_map const &); - -std::size_t num_nodes(GraphView const &); -bool empty(GraphView const &); - -void add_edges(MultiDiGraph &, std::vector const &); -void add_edges(DiGraph &, std::vector const &); -void add_edges(UndirectedGraph &, std::vector const &); - -bool contains_node(GraphView const &, Node const &); - -bool contains_edge(MultiDiGraphView const &, MultiDiEdge const &); -bool contains_edge(DiGraphView const &, DirectedEdge const &); -bool contains_edge(UndirectedGraphView const &, UndirectedEdge const &); - -void remove_edges(MultiDiGraph &, std::unordered_set const &); -void remove_edges(DiGraph &, std::unordered_set const &); -void remove_edges(UndirectedGraph &, std::vector const &); - -std::unordered_set get_endpoints(UndirectedEdge const &); - -std::unordered_set get_edges(MultiDiGraphView const &); -std::unordered_set get_edges(DiGraphView const &); -std::unordered_set get_edges(UndirectedGraphView const &); +/** + * @brief modifies the given graph in-place by splitting the given node into 2 + *separate ones + **/ +void contract_out_node_inplace(MultiDiGraph &g, Node const &node); +void contract_out_node_inplace(DiGraph &g, Node const &node); +void contract_out_node_inplace(UndirectedGraph &g, Node const &node); + +/** + * @brief identical to `contract_out_node_inplace`, but leaves the graph g + *unchanged and returns a new graph instead. + **/ +MultiDiGraphView contract_out_node(MultiDiGraphView const &g, Node const &node); +DiGraphView contract_out_node(DiGraphView const &g, Node const &node); +UndirectedGraphView contract_out_node(UndirectedGraphView const &g, + Node const &node); + +/** + * @brief applies the contraction from node a into node b for all pairs of nodes + *(a,b) present in the map. + **/ +MultiDiGraphView apply_contraction(MultiDiGraphView const &g, + std::unordered_map const &nodes); +DiGraphView apply_contraction(DiGraphView const &g, + std::unordered_map const &nodes); +UndirectedGraphView + apply_contraction(UndirectedGraphView const &g, + std::unordered_map const &nodes); + +std::size_t num_nodes(GraphView const &g); +bool empty(GraphView const &g); + +void add_edges(MultiDiGraph &g, std::vector const &edges); +void add_edges(DiGraph &g, std::vector const &edges); +void add_edges(UndirectedGraph &g, std::vector const &edges); + +bool contains_node(GraphView const &gv, Node const &node); + +bool contains_edge(MultiDiGraphView const &gv, MultiDiEdge const &edge); +bool contains_edge(DiGraphView const &gv, DirectedEdge const &edge); +bool contains_edge(UndirectedGraphView const &gv, UndirectedEdge const &edge); + +void remove_edges(MultiDiGraph &g, + std::unordered_set const &edges); +void remove_edges(DiGraph &g, std::unordered_set const &edges); +void remove_edges(UndirectedGraph &g, std::vector const &edges); + +std::unordered_set get_endpoints(UndirectedEdge const &edge); + +std::unordered_set get_edges(MultiDiGraphView const &g); +std::unordered_set get_edges(DiGraphView const &g); +std::unordered_set get_edges(UndirectedGraphView const &g); std::unordered_set - get_edges(UpwardOpenMultiDiGraphView const &); + get_edges(UpwardOpenMultiDiGraphView const &g); std::unordered_set - get_edges(DownwardOpenMultiDiGraphView const &); -std::unordered_set get_edges(OpenMultiDiGraphView const &); + get_edges(DownwardOpenMultiDiGraphView const &g); +std::unordered_set get_edges(OpenMultiDiGraphView const &g); -std::unordered_set get_node_edges(UndirectedGraphView const &, - Node const &); +std::unordered_set get_node_edges(UndirectedGraphView const &g, + Node const &node); -std::unordered_set get_outputs(MultiDiGraphView const &); -std::unordered_set get_inputs(MultiDiGraphView const &); +std::unordered_set get_outputs(MultiDiGraphView const &g); +std::unordered_set get_inputs(MultiDiGraphView const &g); std::unordered_set - get_open_outputs(OpenMultiDiGraphView const &); + get_open_outputs(OpenMultiDiGraphView const &g); std::unordered_set - get_open_inputs(OpenMultiDiGraphView const &); + get_open_inputs(OpenMultiDiGraphView const &g); -std::unordered_set get_incoming_edges(MultiDiGraphView const &, - Node const &); -std::unordered_set get_incoming_edges(DiGraphView const &, - Node const &); +std::unordered_set get_incoming_edges(MultiDiGraphView const &g, + Node const &node); +std::unordered_set get_incoming_edges(DiGraphView const &g, + Node const &node); std::unordered_set - get_incoming_edges(UpwardOpenMultiDiGraphView const &, Node const &); + get_incoming_edges(UpwardOpenMultiDiGraphView const &g, Node const &node); std::unordered_set - get_incoming_edges(DownwardOpenMultiDiGraphView const &, Node const &); + get_incoming_edges(DownwardOpenMultiDiGraphView const &g, Node const &node); std::unordered_set - get_incoming_edges(OpenMultiDiGraphView const &, Node const &); + get_incoming_edges(OpenMultiDiGraphView const &g, Node const &node); -std::unordered_set get_incoming_edges(MultiDiGraphView const &, - std::unordered_set); +std::unordered_set + get_incoming_edges(MultiDiGraphView const &g, + std::unordered_set nodes); std::unordered_set - get_incoming_edges(DiGraphView const &, std::unordered_set const &); + get_incoming_edges(DiGraphView const &g, + std::unordered_set const &nodes); std::unordered_map> - get_incoming_edges_by_idx(MultiDiGraphView const &, Node const &); + get_incoming_edges_by_idx(MultiDiGraphView const &g, Node const &node); std::unordered_map> - get_outgoing_edges_by_idx(MultiDiGraphView const &, Node const &); + get_outgoing_edges_by_idx(MultiDiGraphView const &g, Node const &node); -std::unordered_set get_outgoing_edges(MultiDiGraphView const &, - Node const &); -std::unordered_set get_outgoing_edges(DiGraphView const &, - Node const &); +std::unordered_set get_outgoing_edges(MultiDiGraphView const &g, + Node const &node); +std::unordered_set get_outgoing_edges(DiGraphView const &g, + Node const &node); std::unordered_set - get_outgoing_edges(UpwardOpenMultiDiGraphView const &, Node const &); + get_outgoing_edges(UpwardOpenMultiDiGraphView const &g, Node const &node); std::unordered_set - get_outgoing_edges(DownwardOpenMultiDiGraphView const &, Node const &); + get_outgoing_edges(DownwardOpenMultiDiGraphView const &g, Node const &node); std::unordered_set - get_outgoing_edges(OpenMultiDiGraphView const &, Node const &); + get_outgoing_edges(OpenMultiDiGraphView const &g, Node const &node); std::unordered_set - get_outgoing_edges(MultiDiGraphView const &, - std::unordered_set const &); + get_outgoing_edges(MultiDiGraphView const &g, + std::unordered_set const &nodes); +std::unordered_set + get_outgoing_edges(DiGraphView const &g, + std::unordered_set const &nodes); + std::unordered_set get_outgoing_edges(DiGraphView const &, std::unordered_set const &); From 9573c8b606dbb851d5b3752a6d6157444c08b2ab Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Wed, 3 Jul 2024 13:55:03 -0700 Subject: [PATCH 5/7] Additional algorithms.h documentation --- lib/utils/include/utils/graph/algorithms.h | 85 +++++++++++++++++----- lib/utils/src/graph/algorithms.cc | 2 +- 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/lib/utils/include/utils/graph/algorithms.h b/lib/utils/include/utils/graph/algorithms.h index 02de7b09c5..f6804c8169 100644 --- a/lib/utils/include/utils/graph/algorithms.h +++ b/lib/utils/include/utils/graph/algorithms.h @@ -271,6 +271,11 @@ NodePort get_dst_idx(std::variant const &t) { std::unordered_set get_neighbors(UndirectedGraphView const &, Node const &); + +/** + * @brief returns all neighboring nodes to the given node. + * @details When fetching the neighbors, the graph is treated as undirected. So a,b are neighbors if edge (a,b) or (b,a) is present. +*/ std::unordered_set get_neighbors(DiGraphView const &, Node const &); std::unordered_set get_neighbors(MultiDiGraphView const &, Node const &); @@ -285,41 +290,81 @@ std::unordered_set get_closed_sinks(OpenMultiDiGraphView const &g); std::unordered_set get_open_sources(OpenMultiDiGraphView const &g); std::unordered_set get_open_sinks(OpenMultiDiGraphView const &g); -bool is_acyclic(MultiDiGraphView const &, std::unordered_set const &); -std::optional is_acyclic(DiGraphView const &); -std::optional is_acyclic(MultiDiGraphView const &); +bool is_acyclic(MultiDiGraphView const &g, std::unordered_set const &nodes); + +/** + * @brief If the graph has no nodes, std::nullopt is returned. +*/ +std::optional is_acyclic(DiGraphView const &g); +std::optional is_acyclic(MultiDiGraphView const &g); +/** + * @brief Computes the dominators for all nodes in a directed graph. + * @details A node "d" dominates a node "n" if every path from all sources to "n" must go through "d". Note that every node dominates itself. +*/ std::unordered_map> - get_dominators(DiGraphView const &); -std::unordered_set get_dominators(DiGraphView const &, Node const &); -std::unordered_set get_dominators(DiGraphView const &, - std::unordered_set const &); + get_dominators(DiGraphView const &g); + +/** + * @brief Computes the dominators for a specific node in a directed graph. + * @details A node "d" dominates a node "n" if every path from all sources to "n" must go through "d". Note that every node dominates itself. +*/ +std::unordered_set get_dominators(DiGraphView const &g, Node const &nodes); + +/** + * @brief Computes the intersection of dominators for a set of nodes in a directed graph. + * @details A node "d" dominates a node "n" if every path from all sources to "n" must go through "d". Note that every node dominates itself. +*/ +std::unordered_set get_dominators(DiGraphView const &g, + std::unordered_set const &nodes); +/** + * @brief Computes all post-dominators in a directed graph. + * @details A node "d" post-dominates a node "n" if every path from "n" to all sinks must go through "d". Note that every node post-dominates itself. +*/ std::unordered_map> - get_post_dominators(DiGraphView const &); + get_post_dominators(DiGraphView const &g); + +/** + * @brief Computes the immediate dominator for all nodes in a directed graph + * @details An immediate dominator is the unique node that strictly dominates "n" but does not strictly dominate any other node that strictly dominates "n". Every node, except the source node(s), has an immediate dominator. + * Thus, the sources will have a std::nullopt as the associated dominator. +*/ std::unordered_map> - get_imm_dominators(DiGraphView const &); + get_imm_dominators(DiGraphView const &g); + +/** + * @brief Computes the immediate post-dominator for all nodes in a directed graph + * @details An immediate post-dominator is the unique node that strictly post-dominates "n" but does not post-dominate any other post-dominator of "n". Every node, except the sink node(s), has an immediate dominator. + * Thus, the sinks will have a std::nullopt as the associated dominator. +*/ std::unordered_map> - get_imm_post_dominators(DiGraphView const &); -std::optional get_imm_post_dominator(DiGraphView const &, Node const &); -std::optional get_imm_post_dominator(MultiDiGraphView const &, - Node const &); -std::optional get_imm_post_dominator(DiGraphView const &, - std::unordered_set const &); + get_imm_post_dominators(DiGraphView const &g); + +/** + * @brief Computes the immediate post-dominator for the given node in a directed graph + * @details An immediate post-dominator is the unique node that strictly post-dominates "n" but does not post-dominate any other post-dominator of "n". Every node, except the sink node(s), has an immediate dominator. + * Thus, the sinks will have a std::nullopt as the associated dominator. +*/ +std::optional get_imm_post_dominator(DiGraphView const &g, Node const &node); +std::optional get_imm_post_dominator(MultiDiGraphView const &g, + Node const &node); +std::optional get_imm_post_dominator(DiGraphView const &g, + std::unordered_set const &nodes); std::vector - get_dfs_ordering(DiGraphView const &, + get_dfs_ordering(DiGraphView const &g, std::unordered_set const &starting_points); std::vector - get_unchecked_dfs_ordering(DiGraphView const &, + get_unchecked_dfs_ordering(DiGraphView const &g, std::unordered_set const &starting_points); std::vector - get_bfs_ordering(DiGraphView const &, + get_bfs_ordering(DiGraphView const &g, std::unordered_set const &starting_points); -std::vector get_topological_ordering(DiGraphView const &); +std::vector get_topological_ordering(DiGraphView const &g); // std::vector get_topological_ordering(MultiDiGraphView const &); // std::vector get_topological_ordering(OpenMultiDiGraphView const &); -std::vector get_unchecked_topological_ordering(DiGraphView const &); +std::vector get_unchecked_topological_ordering(DiGraphView const &g); std::vector get_edge_topological_ordering(DiGraphView const &); std::vector diff --git a/lib/utils/src/graph/algorithms.cc b/lib/utils/src/graph/algorithms.cc index 2223b120a7..6af6b57f17 100644 --- a/lib/utils/src/graph/algorithms.cc +++ b/lib/utils/src/graph/algorithms.cc @@ -616,7 +616,7 @@ std::unordered_map> return get_imm_dominators(flipped(g)); } -std::optional imm_post_dominator(DiGraphView const &g, Node const &n) { +std::optional get_imm_post_dominator(DiGraphView const &g, Node const &n) { return get_imm_post_dominators(g).at(n); } From 77ebdf23f80718aeb896003786053add05d73aed Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Wed, 3 Jul 2024 14:24:16 -0700 Subject: [PATCH 6/7] Documentation for algorithms.h --- lib/utils/include/utils/graph/algorithms.h | 108 +++++++++++---------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/lib/utils/include/utils/graph/algorithms.h b/lib/utils/include/utils/graph/algorithms.h index f6804c8169..3ca0d6bac7 100644 --- a/lib/utils/include/utils/graph/algorithms.h +++ b/lib/utils/include/utils/graph/algorithms.h @@ -199,22 +199,22 @@ std::unordered_set std::unordered_set const &nodes); std::unordered_set - get_outgoing_edges(DiGraphView const &, std::unordered_set const &); + get_outgoing_edges(DiGraphView const &g, std::unordered_set const &nodes); -std::unordered_set get_node_edges(UndirectedGraphView const &, - Node const &); +std::unordered_set get_node_edges(UndirectedGraphView const &g, + Node const &node); std::unordered_set - get_node_edges(UndirectedGraphView const &, - std::unordered_set const &); + get_node_edges(UndirectedGraphView const &g, + std::unordered_set const &nodes); -std::unordered_set get_predecessors(DiGraphView const &, Node const &); +std::unordered_set get_predecessors(DiGraphView const &g, Node const &node); std::unordered_map> - get_predecessors(DiGraphView const &, std::unordered_set const &); + get_predecessors(DiGraphView const &g, std::unordered_set const &nodes); -Node get_src_node(MultiDiEdge const &); -Node get_dst_node(MultiDiEdge const &); -Node get_dst_node(InputMultiDiEdge const &); -Node get_src_node(OutputMultiDiEdge const &); +Node get_src_node(MultiDiEdge const &edge); +Node get_dst_node(MultiDiEdge const &edge); +Node get_dst_node(InputMultiDiEdge const &edge); +Node get_src_node(OutputMultiDiEdge const &edge); struct GetSrcNodeFunctor { template @@ -240,10 +240,10 @@ Node get_dst_node(std::variant const &t) { return visit(GetDstNodeFunctor{}, t); } -NodePort get_src_idx(MultiDiEdge const &); -NodePort get_dst_idx(MultiDiEdge const &); -NodePort get_dst_idx(InputMultiDiEdge const &); -NodePort get_src_idx(OutputMultiDiEdge const &); +NodePort get_src_idx(MultiDiEdge const &edge); +NodePort get_dst_idx(MultiDiEdge const &edge); +NodePort get_dst_idx(InputMultiDiEdge const &edge); +NodePort get_src_idx(OutputMultiDiEdge const &edge); struct GetSrcIdxFunctor { template @@ -269,21 +269,21 @@ NodePort get_dst_idx(std::variant const &t) { return visit(GetDstIdxFunctor{}, t); } -std::unordered_set get_neighbors(UndirectedGraphView const &, - Node const &); +std::unordered_set get_neighbors(UndirectedGraphView const &g, + Node const &node); /** * @brief returns all neighboring nodes to the given node. * @details When fetching the neighbors, the graph is treated as undirected. So a,b are neighbors if edge (a,b) or (b,a) is present. */ -std::unordered_set get_neighbors(DiGraphView const &, Node const &); -std::unordered_set get_neighbors(MultiDiGraphView const &, Node const &); +std::unordered_set get_neighbors(DiGraphView const &g, Node const &node); +std::unordered_set get_neighbors(MultiDiGraphView const &g, Node const &node); // return the set of nodes without incoming edges -std::unordered_set get_sources(DiGraphView const &); +std::unordered_set get_sources(DiGraphView const &g); // return the set of nodes without outgoing edges -std::unordered_set get_sinks(DiGraphView const &); +std::unordered_set get_sinks(DiGraphView const &g); std::unordered_set get_closed_sources(OpenMultiDiGraphView const &g); std::unordered_set get_closed_sinks(OpenMultiDiGraphView const &g); @@ -366,40 +366,46 @@ std::vector get_topological_ordering(DiGraphView const &g); // std::vector get_topological_ordering(OpenMultiDiGraphView const &); std::vector get_unchecked_topological_ordering(DiGraphView const &g); -std::vector get_edge_topological_ordering(DiGraphView const &); +std::vector get_edge_topological_ordering(DiGraphView const &g); std::vector - get_edge_topological_ordering(MultiDiGraphView const &); + get_edge_topological_ordering(MultiDiGraphView const &g); std::unordered_set> - get_weakly_connected_components(MultiDiGraphView const &); + get_weakly_connected_components(MultiDiGraphView const &g); std::unordered_set> - get_weakly_connected_components(DiGraphView const &); + get_weakly_connected_components(DiGraphView const &g); std::unordered_set> - get_connected_components(UndirectedGraphView const &); + get_connected_components(UndirectedGraphView const &g); std::unordered_set - get_transitive_reduction_delta(DiGraphView const &); + get_transitive_reduction_delta(DiGraphView const &g); +// Describes a bi-partition of a given set of nodes using GraphSplit = std::pair, std::unordered_set>; std::pair split_edge(MultiDiEdge const &e); -MultiDiEdge unsplit_edge(OutputMultiDiEdge const &, InputMultiDiEdge const &); - -std::unordered_set get_cut_set(MultiDiGraphView const &, - GraphSplit const &); +MultiDiEdge unsplit_edge(OutputMultiDiEdge const &out_edge, InputMultiDiEdge const &in_edge); +/** + * @brief For a given graph split, returns the cut-set, which is the set of edges that have one endpoint in each subset of the GraphSplit +*/ +std::unordered_set get_cut_set(MultiDiGraphView const &g, + GraphSplit const &split); -std::unordered_set get_cut_set(MultiDiGraphView const &, - std::unordered_set const &); +/** + * @brief For a given set of nodes, returns the set of edges that have one endpoint in the set of nodes and the other endpoint outside of it. +*/ +std::unordered_set get_cut_set(MultiDiGraphView const &g, + std::unordered_set const &nodes); bidict> - get_edge_splits(MultiDiGraphView const &, GraphSplit const &); + get_edge_splits(MultiDiGraphView const &g, GraphSplit const &split); -UndirectedGraphView get_subgraph(UndirectedGraphView const &, - std::unordered_set const &); -DiGraphView get_subgraph(DiGraphView const &, std::unordered_set const &); -MultiDiGraphView get_subgraph(MultiDiGraphView const &, - std::unordered_set const &); +UndirectedGraphView get_subgraph(UndirectedGraphView const &g, + std::unordered_set const &nodes); +DiGraphView get_subgraph(DiGraphView const &g, std::unordered_set const &nodes); +MultiDiGraphView get_subgraph(MultiDiGraphView const &g, + std::unordered_set const &nodes); template OpenMultiDiGraphView get_subgraph(OpenMultiDiGraphView const &g, @@ -407,24 +413,26 @@ OpenMultiDiGraphView get_subgraph(OpenMultiDiGraphView const &g, return OpenMultiDiGraphView::create(g, nodes); } -std::unordered_map calculate_topo_rank(DiGraphView const &); -Node get_node_with_greatest_topo_rank(std::unordered_set const &, - DiGraphView const &); +std::unordered_map calculate_topo_rank(DiGraphView const &g); +Node get_node_with_greatest_topo_rank(std::unordered_set const &nodes, + DiGraphView const &g); MultiDiGraphView join(MultiDiGraphView const &lhs, MultiDiGraphView const &rhs); DiGraphView join(DiGraphView const &lhs, DiGraphView const &rhs); UndirectedGraphView join(UndirectedGraphView const &lhs, UndirectedGraphView const &rhs); +/** + * @brief Returns a digraph with all the edges flipped +*/ +DiGraphView flipped(DiGraphView const &g); -DiGraphView flipped(DiGraphView const &); - -DiGraphView with_added_edges(DiGraphView const &, - std::unordered_set const &); +DiGraphView with_added_edges(DiGraphView const &g, + std::unordered_set const &edges); -UndirectedGraphView as_undirected(DiGraphView const &); -MultiDiGraphView as_multidigraph(DiGraphView const &); -DiGraphView as_digraph(UndirectedGraphView const &); -OpenMultiDiGraphView as_openmultidigraph(MultiDiGraphView const &); +UndirectedGraphView as_undirected(DiGraphView const &g); +MultiDiGraphView as_multidigraph(DiGraphView const &g); +DiGraphView as_digraph(UndirectedGraphView const &g); +OpenMultiDiGraphView as_openmultidigraph(MultiDiGraphView const &g); void export_as_dot( DotFile &, From bcc55de0ec4eaea321c96bbe41014774b22626ea Mon Sep 17 00:00:00 2001 From: Pietro Max Marsella Date: Fri, 26 Jul 2024 12:01:55 -0700 Subject: [PATCH 7/7] minor change --- lib/utils/include/utils/graph/algorithms.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/include/utils/graph/algorithms.h b/lib/utils/include/utils/graph/algorithms.h index 3ca0d6bac7..4dc7aa7b7b 100644 --- a/lib/utils/include/utils/graph/algorithms.h +++ b/lib/utils/include/utils/graph/algorithms.h @@ -90,7 +90,7 @@ UndirectedGraphView contract_node(UndirectedGraphView const &g, /** * @brief modifies the given graph in-place by splitting the given node into 2 - *separate ones + *separate nodes. **/ void contract_out_node_inplace(MultiDiGraph &g, Node const &node); void contract_out_node_inplace(DiGraph &g, Node const &node);