From a6e537bf25edbc6f3f55a46c50a5be7df4914b0a Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sat, 20 Apr 2019 16:05:47 -0700 Subject: [PATCH 01/14] copied graph generators to infra --- infra/graph_generators.py | 79 ++++++++++++++++++++++++++++++++++ infra/graph_generators_test.py | 68 +++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 infra/graph_generators.py create mode 100644 infra/graph_generators_test.py diff --git a/infra/graph_generators.py b/infra/graph_generators.py new file mode 100644 index 0000000..4c12dd3 --- /dev/null +++ b/infra/graph_generators.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Mar 17 13:00:33 2019 + +@author: Zargham +""" +import networkx as nx + +def lineGraphGen(N, bidir=False,nodeTypeName='vanilla',edgeTypeName='vanilla'): + + line = nx.path_graph(N, create_using=nx.MultiDiGraph) + if not(bidir): + G = line + else: + edges = line.edges + G = nx.MultiDiGraph() + for e in edges: + G.add_edge(e[0],e[1]) + G.add_edge(e[1],e[0]) + + nx.set_node_attributes(G,nodeTypeName, 'type') + nx.set_edge_attributes(G,edgeTypeName, 'type') + + return G + +def starGraphGen(N, kind='sink',nodeTypeName='vanilla',edgeTypeName='vanilla'): + + star = nx.star_graph(N) + G = nx.MultiDiGraph() + + for e in star.edges: + if (kind == 'source') or (kind == 'bidir'): + G.add_edge(e[0],e[1]) + if (kind == 'sink') or (kind == 'bidir'): + G.add_edge(e[1],e[0]) + + nx.set_node_attributes(G,nodeTypeName, 'type') + nx.set_edge_attributes(G,edgeTypeName, 'type') + + return G + +def circleGraphGen(N, bidir=False,nodeTypeName='vanilla',edgeTypeName='vanilla' ): + + circle = nx.cycle_graph(N, create_using=nx.MultiDiGraph) + if not(bidir): + G = circle + else: + edges = circle.edges + G = nx.MultiDiGraph() + for e in edges: + G.add_edge(e[0],e[1]) + G.add_edge(e[1],e[0]) + + nx.set_node_attributes(G,nodeTypeName, 'type') + nx.set_edge_attributes(G,edgeTypeName, 'type') + + return G + +def treeGraphGen(r,h, kind='sink',nodeTypeName='vanilla',edgeTypeName='vanilla'): + + tree = nx.balanced_tree(r,h, create_using=nx.MultiDiGraph) + + if kind=='source': + G = tree + elif kind =='sink': + G = nx.MultiDiGraph() + for e in tree.edges: + G.add_edge(e[1],e[0]) + elif kind == 'bidir': + G = nx.MultiDiGraph() + for e in tree.edges: + G.add_edge(e[1],e[0]) + G.add_edge(e[0],e[1]) + + nx.set_node_attributes(G,nodeTypeName, 'type') + nx.set_edge_attributes(G,edgeTypeName, 'type') + + return G \ No newline at end of file diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py new file mode 100644 index 0000000..2b5f824 --- /dev/null +++ b/infra/graph_generators_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Mar 20 17:17:54 2019 + +@author: Zargham +""" +import networkx as nx +import unittest +from graph_generators import lineGraphGen, starGraphGen, treeGraphGen + +class GraphGenerators_LineGraph(unittest.TestCase): + def test_lineGraphGen_sanity(self): + g = lineGraphGen(4) + self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3)]) + + def test_lineGraphGen_bidir(self): + g = lineGraphGen(4, bidir=True) + # (node, neighbor) + self.assertEqual(list(g.edges()), [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)]) + +class GraphGenerators_StarGraph(unittest.TestCase): + +# how to validate that 'kind' is either 'source', 'sink', or 'bidir' ?? + + def test_starGraph_sink(self): + g = starGraphGen(4) + self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0), (4, 0)]) + + def test_starGraph_source(self): + g = starGraphGen(4, kind='source') + self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4)]) + + def test_starGraph_bidir(self): + g = starGraphGen(4, kind='bidir') + self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (2, 0), (3, 0), (4, 0)]) + +class GraphGenerators_TreeGraph(unittest.TestCase): + #note: our default is diff from networkx's default in this case (sink vs source) + def test_treeGraph_source_sink(self): + g = treeGraphGen(2,2) + self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)]) + + def test_treeGraph_source(self): + g = treeGraphGen(2,2, kind='source') + self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]) + + def test_treeGraph_bidir(self): + g = treeGraphGen(2,2, kind='bidir') + self.assertEqual(list(g.edges()), [ + (1, 0), (1, 3), (1, 4), (0, 1), (0, 2), (2, 0), + (2, 5), (2, 6), (3, 1), (4, 1), (5, 2), (6, 2) + ]) + +# this is the way I might test that the types assigned in Z's functions are correct +# I think it would be a good idea to abstract out that logic into a helper function +# so that the code isn't duplicated in every generator + +class GraphGenerators_SetType(unittest.TestCase): + def test_lineGraphGen_setType(self): + g = lineGraphGen(4, nodeTypeName='foo',edgeTypeName='bar') + + # each node and edge should have the correct 'type' value + for k, v in nx.get_node_attributes(g, 'type').items(): + self.assertEqual(v, 'foo') + + for k, v in nx.get_edge_attributes(g, 'type').items(): + self.assertEqual(v, 'bar') \ No newline at end of file From 5a8013af8d50850576fd808bd72ffeb6acb5ca6c Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sat, 20 Apr 2019 16:17:46 -0700 Subject: [PATCH 02/14] applied black to graph generators --- infra/graph_generators.py | 90 ++++++++++++++++++---------------- infra/graph_generators_test.py | 73 ++++++++++++++++++--------- 2 files changed, 97 insertions(+), 66 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 4c12dd3..d71f2e1 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -7,73 +7,77 @@ """ import networkx as nx -def lineGraphGen(N, bidir=False,nodeTypeName='vanilla',edgeTypeName='vanilla'): + +def lineGraphGen(N, bidir=False, nodeTypeName="vanilla", edgeTypeName="vanilla"): line = nx.path_graph(N, create_using=nx.MultiDiGraph) - if not(bidir): + if not (bidir): G = line - else: + else: edges = line.edges G = nx.MultiDiGraph() for e in edges: - G.add_edge(e[0],e[1]) - G.add_edge(e[1],e[0]) - - nx.set_node_attributes(G,nodeTypeName, 'type') - nx.set_edge_attributes(G,edgeTypeName, 'type') - + G.add_edge(e[0], e[1]) + G.add_edge(e[1], e[0]) + + nx.set_node_attributes(G, nodeTypeName, "type") + nx.set_edge_attributes(G, edgeTypeName, "type") + return G -def starGraphGen(N, kind='sink',nodeTypeName='vanilla',edgeTypeName='vanilla'): - + +def starGraphGen(N, kind="sink", nodeTypeName="vanilla", edgeTypeName="vanilla"): + star = nx.star_graph(N) G = nx.MultiDiGraph() for e in star.edges: - if (kind == 'source') or (kind == 'bidir'): - G.add_edge(e[0],e[1]) - if (kind == 'sink') or (kind == 'bidir'): - G.add_edge(e[1],e[0]) - - nx.set_node_attributes(G,nodeTypeName, 'type') - nx.set_edge_attributes(G,edgeTypeName, 'type') - + if (kind == "source") or (kind == "bidir"): + G.add_edge(e[0], e[1]) + if (kind == "sink") or (kind == "bidir"): + G.add_edge(e[1], e[0]) + + nx.set_node_attributes(G, nodeTypeName, "type") + nx.set_edge_attributes(G, edgeTypeName, "type") + return G -def circleGraphGen(N, bidir=False,nodeTypeName='vanilla',edgeTypeName='vanilla' ): - + +def circleGraphGen(N, bidir=False, nodeTypeName="vanilla", edgeTypeName="vanilla"): + circle = nx.cycle_graph(N, create_using=nx.MultiDiGraph) - if not(bidir): + if not (bidir): G = circle - else: + else: edges = circle.edges G = nx.MultiDiGraph() for e in edges: - G.add_edge(e[0],e[1]) - G.add_edge(e[1],e[0]) - - nx.set_node_attributes(G,nodeTypeName, 'type') - nx.set_edge_attributes(G,edgeTypeName, 'type') - + G.add_edge(e[0], e[1]) + G.add_edge(e[1], e[0]) + + nx.set_node_attributes(G, nodeTypeName, "type") + nx.set_edge_attributes(G, edgeTypeName, "type") + return G -def treeGraphGen(r,h, kind='sink',nodeTypeName='vanilla',edgeTypeName='vanilla'): - - tree = nx.balanced_tree(r,h, create_using=nx.MultiDiGraph) - - if kind=='source': + +def treeGraphGen(r, h, kind="sink", nodeTypeName="vanilla", edgeTypeName="vanilla"): + + tree = nx.balanced_tree(r, h, create_using=nx.MultiDiGraph) + + if kind == "source": G = tree - elif kind =='sink': + elif kind == "sink": G = nx.MultiDiGraph() for e in tree.edges: - G.add_edge(e[1],e[0]) - elif kind == 'bidir': + G.add_edge(e[1], e[0]) + elif kind == "bidir": G = nx.MultiDiGraph() for e in tree.edges: - G.add_edge(e[1],e[0]) - G.add_edge(e[0],e[1]) + G.add_edge(e[1], e[0]) + G.add_edge(e[0], e[1]) - nx.set_node_attributes(G,nodeTypeName, 'type') - nx.set_edge_attributes(G,edgeTypeName, 'type') - - return G \ No newline at end of file + nx.set_node_attributes(G, nodeTypeName, "type") + nx.set_edge_attributes(G, edgeTypeName, "type") + + return G diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index 2b5f824..82876cf 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -9,6 +9,7 @@ import unittest from graph_generators import lineGraphGen, starGraphGen, treeGraphGen + class GraphGenerators_LineGraph(unittest.TestCase): def test_lineGraphGen_sanity(self): g = lineGraphGen(4) @@ -17,52 +18,78 @@ def test_lineGraphGen_sanity(self): def test_lineGraphGen_bidir(self): g = lineGraphGen(4, bidir=True) # (node, neighbor) - self.assertEqual(list(g.edges()), [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)]) + self.assertEqual( + list(g.edges()), [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)] + ) + class GraphGenerators_StarGraph(unittest.TestCase): -# how to validate that 'kind' is either 'source', 'sink', or 'bidir' ?? + # how to validate that 'kind' is either 'source', 'sink', or 'bidir' ?? def test_starGraph_sink(self): g = starGraphGen(4) self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0), (4, 0)]) def test_starGraph_source(self): - g = starGraphGen(4, kind='source') + g = starGraphGen(4, kind="source") self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4)]) def test_starGraph_bidir(self): - g = starGraphGen(4, kind='bidir') - self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (2, 0), (3, 0), (4, 0)]) + g = starGraphGen(4, kind="bidir") + self.assertEqual( + list(g.edges()), + [(0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (2, 0), (3, 0), (4, 0)], + ) + class GraphGenerators_TreeGraph(unittest.TestCase): - #note: our default is diff from networkx's default in this case (sink vs source) + # note: our default is diff from networkx's default in this case (sink vs source) def test_treeGraph_source_sink(self): - g = treeGraphGen(2,2) - self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)]) + g = treeGraphGen(2, 2) + self.assertEqual( + list(g.edges()), [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)] + ) def test_treeGraph_source(self): - g = treeGraphGen(2,2, kind='source') - self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]) + g = treeGraphGen(2, 2, kind="source") + self.assertEqual( + list(g.edges()), [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)] + ) def test_treeGraph_bidir(self): - g = treeGraphGen(2,2, kind='bidir') - self.assertEqual(list(g.edges()), [ - (1, 0), (1, 3), (1, 4), (0, 1), (0, 2), (2, 0), - (2, 5), (2, 6), (3, 1), (4, 1), (5, 2), (6, 2) - ]) + g = treeGraphGen(2, 2, kind="bidir") + self.assertEqual( + list(g.edges()), + [ + (1, 0), + (1, 3), + (1, 4), + (0, 1), + (0, 2), + (2, 0), + (2, 5), + (2, 6), + (3, 1), + (4, 1), + (5, 2), + (6, 2), + ], + ) + + +# this is the way I might test that the types assigned in Z's functions are correct +# I think it would be a good idea to abstract out that logic into a helper function +# so that the code isn't duplicated in every generator -# this is the way I might test that the types assigned in Z's functions are correct -# I think it would be a good idea to abstract out that logic into a helper function -# so that the code isn't duplicated in every generator class GraphGenerators_SetType(unittest.TestCase): def test_lineGraphGen_setType(self): - g = lineGraphGen(4, nodeTypeName='foo',edgeTypeName='bar') + g = lineGraphGen(4, nodeTypeName="foo", edgeTypeName="bar") # each node and edge should have the correct 'type' value - for k, v in nx.get_node_attributes(g, 'type').items(): - self.assertEqual(v, 'foo') + for k, v in nx.get_node_attributes(g, "type").items(): + self.assertEqual(v, "foo") - for k, v in nx.get_edge_attributes(g, 'type').items(): - self.assertEqual(v, 'bar') \ No newline at end of file + for k, v in nx.get_edge_attributes(g, "type").items(): + self.assertEqual(v, "bar") From 7f6a018af300723bd6498af996f103f0108b6a15 Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sat, 20 Apr 2019 16:44:48 -0700 Subject: [PATCH 03/14] matching variable convensions --- infra/graph_generators.py | 87 ++++++++++++++++------------------ infra/graph_generators_test.py | 8 ++-- 2 files changed, 45 insertions(+), 50 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index d71f2e1..4b7bd9d 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -1,83 +1,80 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Mar 17 13:00:33 2019 - -@author: Zargham -""" +''' +This Module provides functions for generating simple networks +''' import networkx as nx -def lineGraphGen(N, bidir=False, nodeTypeName="vanilla", edgeTypeName="vanilla"): +#add function to make bidir - line = nx.path_graph(N, create_using=nx.MultiDiGraph) - if not (bidir): - G = line - else: - edges = line.edges - G = nx.MultiDiGraph() +#add function to reverse direction + +def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): + + graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) + if bidir: + edges = graph.copy().edges for e in edges: - G.add_edge(e[0], e[1]) - G.add_edge(e[1], e[0]) + graph.add_edge(e[1], e[0]) - nx.set_node_attributes(G, nodeTypeName, "type") - nx.set_edge_attributes(G, edgeTypeName, "type") + nx.set_node_attributes(graph, node_type_name, "type") + nx.set_edge_attributes(graph, edge_type_name, "type") - return G + return graph -def starGraphGen(N, kind="sink", nodeTypeName="vanilla", edgeTypeName="vanilla"): +def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - star = nx.star_graph(N) - G = nx.MultiDiGraph() + star = nx.star_graph(num_nodes) + graph = nx.MultiDiGraph() for e in star.edges: if (kind == "source") or (kind == "bidir"): - G.add_edge(e[0], e[1]) + graph.add_edge(e[0], e[1]) if (kind == "sink") or (kind == "bidir"): - G.add_edge(e[1], e[0]) + graph.add_edge(e[1], e[0]) - nx.set_node_attributes(G, nodeTypeName, "type") - nx.set_edge_attributes(G, edgeTypeName, "type") + nx.set_node_attributes(graph, node_type_name, "type") + nx.set_edge_attributes(graph, edge_type_name, "type") - return G + return graph -def circleGraphGen(N, bidir=False, nodeTypeName="vanilla", edgeTypeName="vanilla"): +def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): - circle = nx.cycle_graph(N, create_using=nx.MultiDiGraph) + circle = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) if not (bidir): - G = circle + graph = circle else: edges = circle.edges - G = nx.MultiDiGraph() + graph = nx.MultiDiGraph() for e in edges: - G.add_edge(e[0], e[1]) - G.add_edge(e[1], e[0]) + graph.add_edge(e[0], e[1]) + graph.add_edge(e[1], e[0]) - nx.set_node_attributes(G, nodeTypeName, "type") - nx.set_edge_attributes(G, edgeTypeName, "type") + nx.set_node_attributes(graph, node_type_name, "type") + nx.set_edge_attributes(graph, edge_type_name, "type") - return G + return graph -def treeGraphGen(r, h, kind="sink", nodeTypeName="vanilla", edgeTypeName="vanilla"): +def tree_graph_gen(r, h, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): tree = nx.balanced_tree(r, h, create_using=nx.MultiDiGraph) if kind == "source": - G = tree + graph = tree elif kind == "sink": - G = nx.MultiDiGraph() + graph = nx.MultiDiGraph() for e in tree.edges: - G.add_edge(e[1], e[0]) + graph.add_edge(e[1], e[0]) elif kind == "bidir": - G = nx.MultiDiGraph() + graph = nx.MultiDiGraph() for e in tree.edges: - G.add_edge(e[1], e[0]) - G.add_edge(e[0], e[1]) + graph.add_edge(e[1], e[0]) + graph.add_edge(e[0], e[1]) - nx.set_node_attributes(G, nodeTypeName, "type") - nx.set_edge_attributes(G, edgeTypeName, "type") + nx.set_node_attributes(graph, node_type_name, "type") + nx.set_edge_attributes(graph, edge_type_name, "type") - return G + return graph diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index 82876cf..1005616 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Wed Mar 20 17:17:54 2019 +''' +This Module provides unit tests for functions for generating simple networks +''' -@author: Zargham -""" import networkx as nx import unittest from graph_generators import lineGraphGen, starGraphGen, treeGraphGen From aac3efb9dd7fa2653b7714a70edb9f7014c2c19c Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sun, 21 Apr 2019 18:42:19 -0700 Subject: [PATCH 04/14] factored out helper functions --- infra/graph_generators.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 4b7bd9d..3909ab0 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -5,17 +5,33 @@ import networkx as nx -#add function to make bidir +#this function adds an edge with opposite direction for each edge present +def make_bidir(graph): + edges = graph.copy().edges + for e in edges: + graph.add_edge(e[1], e[0]) + + return graph + + +#this function adds an edge with opposite direction for each edge present +#and removed the original edge +def reverse_dir(graph): + edges = graph.copy().edges + for e in edges: + graph.add_edge(e[1], e[0]) + graph.remove_edge(e[0], e[1]) + + return graph + -#add function to reverse direction +#todo add error handling def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: - edges = graph.copy().edges - for e in edges: - graph.add_edge(e[1], e[0]) + graph = make_bidir(graph) nx.set_node_attributes(graph, node_type_name, "type") nx.set_edge_attributes(graph, edge_type_name, "type") From 7cf686f5f43ff926e359a15b2fddfac0b1d1f76b Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sun, 21 Apr 2019 19:39:28 -0700 Subject: [PATCH 05/14] refactored functions with helpers --- infra/graph_generators.py | 48 +++++++++++++++------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 3909ab0..4466199 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -41,14 +41,14 @@ def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_n def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - star = nx.star_graph(num_nodes) - graph = nx.MultiDiGraph() + + graph = nx.MultiDiGraph(nx.star_graph(num_nodes-1)) - for e in star.edges: - if (kind == "source") or (kind == "bidir"): - graph.add_edge(e[0], e[1]) - if (kind == "sink") or (kind == "bidir"): - graph.add_edge(e[1], e[0]) + for n in range(1,num_nodes): + if kind == "sink": + graph.remove_edge(0,n) + elif kind == "source": + graph.remove_edge(n,0) nx.set_node_attributes(graph, node_type_name, "type") nx.set_edge_attributes(graph, edge_type_name, "type") @@ -58,37 +58,25 @@ def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_n def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): - circle = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) - if not (bidir): - graph = circle - else: - edges = circle.edges - graph = nx.MultiDiGraph() - for e in edges: - graph.add_edge(e[0], e[1]) - graph.add_edge(e[1], e[0]) - + graph = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) + + if bidir: + graph = make_bidir(graph) + nx.set_node_attributes(graph, node_type_name, "type") nx.set_edge_attributes(graph, edge_type_name, "type") return graph -def tree_graph_gen(r, h, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - - tree = nx.balanced_tree(r, h, create_using=nx.MultiDiGraph) +def tree_graph_gen(rate, height, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - if kind == "source": - graph = tree - elif kind == "sink": - graph = nx.MultiDiGraph() - for e in tree.edges: - graph.add_edge(e[1], e[0]) + graph = nx.balanced_tree(rate, height, create_using=nx.MultiDiGraph) + + if kind == "sink": + graph = reverse_dir(graph) elif kind == "bidir": - graph = nx.MultiDiGraph() - for e in tree.edges: - graph.add_edge(e[1], e[0]) - graph.add_edge(e[0], e[1]) + graph = make_bidir(graph) nx.set_node_attributes(graph, node_type_name, "type") nx.set_edge_attributes(graph, edge_type_name, "type") From 3be7d78a6b736ae897b81a1ff08b32130f4c577f Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sun, 21 Apr 2019 19:59:27 -0700 Subject: [PATCH 06/14] refactored node and edge types into helper --- infra/graph_generators.py | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 4466199..377c966 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -5,8 +5,8 @@ import networkx as nx -#this function adds an edge with opposite direction for each edge present def make_bidir(graph): + """this function adds an edge with opposite direction for each edge present""" edges = graph.copy().edges for e in edges: graph.add_edge(e[1], e[0]) @@ -17,6 +17,10 @@ def make_bidir(graph): #this function adds an edge with opposite direction for each edge present #and removed the original edge def reverse_dir(graph): + """ + this function adds an edge with opposite direction for each edge present + and removed the original edge + """ edges = graph.copy().edges for e in edges: graph.add_edge(e[1], e[0]) @@ -24,24 +28,37 @@ def reverse_dir(graph): return graph +def set_types(graph, edge_types="vanilla", node_types="vanilla"): + """ maps edge types and node types onto graph""" + if type(node_types)== dict: + for n in graph.nodes: + graph.nodes[n]['type']=node_types[n] + elif node_types == str: + nx.set_node_attributes(graph, node_types, "type") + + if type(edge_types)== dict: + for e in graph.edges: + graph.edges[e]['type']=edge_types[e] + elif edge_types == str: + nx.set_edge_attributes(graph, edge_types, "type") + + return graph #todo add error handling def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): - + """returns a line graph of length num_nodes""" graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: graph = make_bidir(graph) - nx.set_node_attributes(graph, node_type_name, "type") - nx.set_edge_attributes(graph, edge_type_name, "type") - + graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) + return graph def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - - + """returns a star graph of length num_nodes""" graph = nx.MultiDiGraph(nx.star_graph(num_nodes-1)) for n in range(1,num_nodes): @@ -50,27 +67,25 @@ def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_n elif kind == "source": graph.remove_edge(n,0) - nx.set_node_attributes(graph, node_type_name, "type") - nx.set_edge_attributes(graph, edge_type_name, "type") - + graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) + return graph def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): - + """returns a cycle graph of length num_nodes""" graph = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: graph = make_bidir(graph) - nx.set_node_attributes(graph, node_type_name, "type") - nx.set_edge_attributes(graph, edge_type_name, "type") + graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) return graph def tree_graph_gen(rate, height, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): - + """returns a tree graph of depth height and splitting rate rate""" graph = nx.balanced_tree(rate, height, create_using=nx.MultiDiGraph) if kind == "sink": @@ -78,7 +93,6 @@ def tree_graph_gen(rate, height, kind="sink", node_type_name="vanilla", edge_typ elif kind == "bidir": graph = make_bidir(graph) - nx.set_node_attributes(graph, node_type_name, "type") - nx.set_edge_attributes(graph, edge_type_name, "type") + graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) return graph From d6cf6793aa68dcec2cedda057ba2da2a5f94c09d Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sun, 21 Apr 2019 20:43:35 -0700 Subject: [PATCH 07/14] added error handling for inputs --- infra/graph_generators.py | 55 ++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 377c966..f7540c6 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -28,26 +28,43 @@ def reverse_dir(graph): return graph -def set_types(graph, edge_types="vanilla", node_types="vanilla"): +def set_types(graph, node_types="vanilla", edge_types="vanilla"): """ maps edge types and node types onto graph""" if type(node_types)== dict: for n in graph.nodes: - graph.nodes[n]['type']=node_types[n] - elif node_types == str: + try: + graph.nodes[n]['type']=node_types[n] + except: + raise ValueError('if node_types is dictionary, must be keyed to the nodes of the graph, '+str(node_types)+' provided') + elif type(node_types) == str: nx.set_node_attributes(graph, node_types, "type") + else: + raise ValueError('node_types must be string or dictionary keyed to the nodes of the graph, '+str(node_types)+' provided') + if type(edge_types)== dict: for e in graph.edges: - graph.edges[e]['type']=edge_types[e] - elif edge_types == str: + try: + graph.edges[e]['type']=edge_types[e] + except: + raise ValueError('edges_types must be string or dictionary keyed to the nodes of the graph, '+str(edge_types)+' provided') + elif type(edge_types) == str: nx.set_edge_attributes(graph, edge_types, "type") + else: + raise ValueError('if edges_types is dictionary, must keyed to the edges of the graph, '+str(edge_types)+' provided') return graph -#todo add error handling - def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): """returns a line graph of length num_nodes""" + + if not(type(num_nodes)==int)or(num_nodes<1): + raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') + + if not(type(bidir)==bool): + raise ValueError('bidir must be boolean, '+str(bidir)+' provided') + + graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: graph = make_bidir(graph) @@ -59,6 +76,13 @@ def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_n def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): """returns a star graph of length num_nodes""" + + if not(type(num_nodes)==int)or(num_nodes<1): + raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') + + if not(kind in ["sink", "source", "bidir"]): + raise ValueError('kind must be "sink", "source" or "bidir", '+str(kind)+' provided') + graph = nx.MultiDiGraph(nx.star_graph(num_nodes-1)) for n in range(1,num_nodes): @@ -74,6 +98,13 @@ def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_n def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): """returns a cycle graph of length num_nodes""" + + if not(type(num_nodes)==int)or(num_nodes<1): + raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') + + if not(type(bidir)==bool): + raise ValueError('bidir must be boolean, '+str(bidir)+' provided') + graph = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: @@ -86,6 +117,16 @@ def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type def tree_graph_gen(rate, height, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): """returns a tree graph of depth height and splitting rate rate""" + + if not(type(rate)==int)or(rate<1): + raise ValueError('rate must be positive integer, '+str(rate)+' provided') + + if not(type(height)==int)or(height<1): + raise ValueError('height must be positive integer, '+str(height)+' provided') + + if not(kind in ["sink", "source", "bidir"]): + raise ValueError('kind must be "sink", "source" or "bidir", '+str(kind)+' provided') + graph = nx.balanced_tree(rate, height, create_using=nx.MultiDiGraph) if kind == "sink": From e05f8b50f80eaa52245151d93121d5094d26359a Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Sun, 21 Apr 2019 20:48:08 -0700 Subject: [PATCH 08/14] ran black to format post edits --- infra/graph_generators.py | 168 +++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 66 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index f7540c6..98d0d17 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -''' +""" This Module provides functions for generating simple networks -''' +""" import networkx as nx @@ -10,12 +10,12 @@ def make_bidir(graph): edges = graph.copy().edges for e in edges: graph.add_edge(e[1], e[0]) - + return graph - -#this function adds an edge with opposite direction for each edge present -#and removed the original edge + +# this function adds an edge with opposite direction for each edge present +# and removed the original edge def reverse_dir(graph): """ this function adds an edge with opposite direction for each edge present @@ -25,110 +25,146 @@ def reverse_dir(graph): for e in edges: graph.add_edge(e[1], e[0]) graph.remove_edge(e[0], e[1]) - + return graph + def set_types(graph, node_types="vanilla", edge_types="vanilla"): """ maps edge types and node types onto graph""" - if type(node_types)== dict: + if type(node_types) == dict: for n in graph.nodes: - try: - graph.nodes[n]['type']=node_types[n] + try: + graph.nodes[n]["type"] = node_types[n] except: - raise ValueError('if node_types is dictionary, must be keyed to the nodes of the graph, '+str(node_types)+' provided') + raise ValueError( + "if node_types is dictionary, must be keyed to the nodes of the graph, " + + str(node_types) + + " provided" + ) elif type(node_types) == str: - nx.set_node_attributes(graph, node_types, "type") + nx.set_node_attributes(graph, node_types, "type") else: - raise ValueError('node_types must be string or dictionary keyed to the nodes of the graph, '+str(node_types)+' provided') - - - if type(edge_types)== dict: + raise ValueError( + "node_types must be string or dictionary keyed to the nodes of the graph, " + + str(node_types) + + " provided" + ) + + if type(edge_types) == dict: for e in graph.edges: try: - graph.edges[e]['type']=edge_types[e] + graph.edges[e]["type"] = edge_types[e] except: - raise ValueError('edges_types must be string or dictionary keyed to the nodes of the graph, '+str(edge_types)+' provided') + raise ValueError( + "edges_types must be string or dictionary keyed to the nodes of the graph, " + + str(edge_types) + + " provided" + ) elif type(edge_types) == str: nx.set_edge_attributes(graph, edge_types, "type") else: - raise ValueError('if edges_types is dictionary, must keyed to the edges of the graph, '+str(edge_types)+' provided') - + raise ValueError( + "if edges_types is dictionary, must keyed to the edges of the graph, " + + str(edge_types) + + " provided" + ) + return graph -def line_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): + +def line_graph_gen( + num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla" +): """returns a line graph of length num_nodes""" - - if not(type(num_nodes)==int)or(num_nodes<1): - raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') - - if not(type(bidir)==bool): - raise ValueError('bidir must be boolean, '+str(bidir)+' provided') - - + + if not (type(num_nodes) == int) or (num_nodes < 1): + raise ValueError( + "num_nodes must be positive integer, " + str(num_nodes) + " provided" + ) + + if not (type(bidir) == bool): + raise ValueError("bidir must be boolean, " + str(bidir) + " provided") + graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: graph = make_bidir(graph) graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) - + return graph -def star_graph_gen(num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): +def star_graph_gen( + num_nodes, kind="sink", node_type_name="vanilla", edge_type_name="vanilla" +): """returns a star graph of length num_nodes""" - - if not(type(num_nodes)==int)or(num_nodes<1): - raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') - - if not(kind in ["sink", "source", "bidir"]): - raise ValueError('kind must be "sink", "source" or "bidir", '+str(kind)+' provided') - - graph = nx.MultiDiGraph(nx.star_graph(num_nodes-1)) - - for n in range(1,num_nodes): + + if not (type(num_nodes) == int) or (num_nodes < 1): + raise ValueError( + "num_nodes must be positive integer, " + str(num_nodes) + " provided" + ) + + if not (kind in ["sink", "source", "bidir"]): + raise ValueError( + 'kind must be "sink", "source" or "bidir", ' + str(kind) + " provided" + ) + + graph = nx.MultiDiGraph(nx.star_graph(num_nodes - 1)) + + for n in range(1, num_nodes): if kind == "sink": - graph.remove_edge(0,n) + graph.remove_edge(0, n) elif kind == "source": - graph.remove_edge(n,0) + graph.remove_edge(n, 0) graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) - + return graph -def circle_graph_gen(num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla"): +def circle_graph_gen( + num_nodes, bidir=False, node_type_name="vanilla", edge_type_name="vanilla" +): """returns a cycle graph of length num_nodes""" - - if not(type(num_nodes)==int)or(num_nodes<1): - raise ValueError('num_nodes must be positive integer, '+str(num_nodes)+' provided') - - if not(type(bidir)==bool): - raise ValueError('bidir must be boolean, '+str(bidir)+' provided') - + + if not (type(num_nodes) == int) or (num_nodes < 1): + raise ValueError( + "num_nodes must be positive integer, " + str(num_nodes) + " provided" + ) + + if not (type(bidir) == bool): + raise ValueError("bidir must be boolean, " + str(bidir) + " provided") + graph = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) - + if bidir: graph = make_bidir(graph) - + graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) return graph -def tree_graph_gen(rate, height, kind="sink", node_type_name="vanilla", edge_type_name="vanilla"): +def tree_graph_gen( + rate, height, kind="sink", node_type_name="vanilla", edge_type_name="vanilla" +): """returns a tree graph of depth height and splitting rate rate""" - - if not(type(rate)==int)or(rate<1): - raise ValueError('rate must be positive integer, '+str(rate)+' provided') - - if not(type(height)==int)or(height<1): - raise ValueError('height must be positive integer, '+str(height)+' provided') - - if not(kind in ["sink", "source", "bidir"]): - raise ValueError('kind must be "sink", "source" or "bidir", '+str(kind)+' provided') - + + if not (type(rate) == int) or (rate < 1): + raise ValueError("rate must be positive integer, " + str(rate) + " provided") + + if not (type(height) == int) or (height < 1): + raise ValueError( + "height must be positive integer, " + str(height) + " provided" + ) + + if not (kind in ["sink", "source", "bidir"]): + raise ValueError( + 'kind must be "sink", "source" or "bidir", ' + str(kind) + " provided" + ) + graph = nx.balanced_tree(rate, height, create_using=nx.MultiDiGraph) - + if kind == "sink": graph = reverse_dir(graph) elif kind == "bidir": From 2d2488282576724eb811bd4ca4d0e87b10e0ff34 Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Mon, 22 Apr 2019 08:06:39 -0700 Subject: [PATCH 09/14] value error use 'in' --- infra/graph_generators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index 98d0d17..bc98990 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -104,7 +104,7 @@ def star_graph_gen( "num_nodes must be positive integer, " + str(num_nodes) + " provided" ) - if not (kind in ["sink", "source", "bidir"]): + if kind not in ("source", "sink", "bidir"): raise ValueError( 'kind must be "sink", "source" or "bidir", ' + str(kind) + " provided" ) @@ -158,7 +158,7 @@ def tree_graph_gen( "height must be positive integer, " + str(height) + " provided" ) - if not (kind in ["sink", "source", "bidir"]): + if kind not in ("source", "sink", "bidir"): raise ValueError( 'kind must be "sink", "source" or "bidir", ' + str(kind) + " provided" ) From cd31f9ec9f4152bfd2b58bbc0fe76b279023f2fe Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Mon, 22 Apr 2019 19:40:33 -0700 Subject: [PATCH 10/14] updated method names and added tests; tests fail --- infra/graph_generators_test.py | 84 +++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index 1005616..2dfa284 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -5,58 +5,58 @@ import networkx as nx import unittest -from graph_generators import lineGraphGen, starGraphGen, treeGraphGen +from graph_generators import line_graph_gen, star_graph_gen, tree_graph_gen, make_bidir, reverse_dir, set_types -class GraphGenerators_LineGraph(unittest.TestCase): - def test_lineGraphGen_sanity(self): - g = lineGraphGen(4) +class Graph_Generators_Line_Graph(unittest.TestCase): + def test_line_graph_gen_sanity(self): + g = line_graph_gen(4) self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3)]) - def test_lineGraphGen_bidir(self): - g = lineGraphGen(4, bidir=True) + def test_line_graph_gen_bidir(self): + g = line_graph_gen(4, bidir=True) # (node, neighbor) self.assertEqual( list(g.edges()), [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)] ) -class GraphGenerators_StarGraph(unittest.TestCase): +class Graph_Generators_Star_Graph(unittest.TestCase): # how to validate that 'kind' is either 'source', 'sink', or 'bidir' ?? - def test_starGraph_sink(self): - g = starGraphGen(4) + def test_star_graph_sink(self): + g = star_graph_gen(4) self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0), (4, 0)]) - def test_starGraph_source(self): - g = starGraphGen(4, kind="source") + def test_star_graph_source(self): + g = star_graph_gen(4, kind="source") self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4)]) - def test_starGraph_bidir(self): - g = starGraphGen(4, kind="bidir") + def test_star_graph_bidir(self): + g = star_graph_gen(4, kind="bidir") self.assertEqual( list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (2, 0), (3, 0), (4, 0)], ) -class GraphGenerators_TreeGraph(unittest.TestCase): +class Graph_Generators_Tree_Graph(unittest.TestCase): # note: our default is diff from networkx's default in this case (sink vs source) - def test_treeGraph_source_sink(self): - g = treeGraphGen(2, 2) + def test_tree_graph_source_sink(self): + g = tree_graph_gen(2, 2) self.assertEqual( list(g.edges()), [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)] ) def test_treeGraph_source(self): - g = treeGraphGen(2, 2, kind="source") + g = tree_graph_gen(2, 2, kind="source") self.assertEqual( list(g.edges()), [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)] ) - def test_treeGraph_bidir(self): - g = treeGraphGen(2, 2, kind="bidir") + def test_tree_graph_bidir(self): + g = tree_graph_gen(2, 2, kind="bidir") self.assertEqual( list(g.edges()), [ @@ -76,14 +76,13 @@ def test_treeGraph_bidir(self): ) -# this is the way I might test that the types assigned in Z's functions are correct -# I think it would be a good idea to abstract out that logic into a helper function -# so that the code isn't duplicated in every generator - -class GraphGenerators_SetType(unittest.TestCase): - def test_lineGraphGen_setType(self): - g = lineGraphGen(4, nodeTypeName="foo", edgeTypeName="bar") +class Graph_Generators_Set_Types(unittest.TestCase): + def test_set_types_str(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + g = set_types(g, node_types="foo", edge_types="bar") + # each node and edge should have the correct 'type' value for k, v in nx.get_node_attributes(g, "type").items(): @@ -91,3 +90,36 @@ def test_lineGraphGen_setType(self): for k, v in nx.get_edge_attributes(g, "type").items(): self.assertEqual(v, "bar") + + def test_set_types_dict(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + node_dict = {0:"foo0", 1:"foo1", 2:"foo2"} + edge_dict = {(0,1,0):"bar0", (1,2,0):"bar1"} + + + g = set_types(g, node_types=node_dict, edge_types=edge_dict) + + + # each node and edge should have the correct 'type' value + for k, v in nx.get_node_attributes(g, "type").items(): + self.assertEqual(v, node_dict[k]) + + for k, v in nx.get_edge_attributes(g, "type").items(): + self.assertEqual(v, edge_dict[k]) + +class Graph_Generators_Reverse_Dir(unittest.TestCase): + def test_reverse_dir(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + g = reverse_dir(g) + + self.assertEqual(list(g.edges()), [(1, 0), (2, 1), (3, 2)]) + +class Graph_Generators_Make_Bidir(unittest.TestCase): + def test_make_bidir(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + g = make_bidir(g) + + self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3),(1, 0), (2, 1), (3, 2)]) From d4b715a278feace5758ffd04eb0067c073237254 Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Mon, 22 Apr 2019 19:56:32 -0700 Subject: [PATCH 11/14] fixed the set types method --- infra/graph_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index bc98990..ff26e6c 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -69,7 +69,7 @@ def set_types(graph, node_types="vanilla", edge_types="vanilla"): + " provided" ) - return graph + return graph def line_graph_gen( From 3d6c01a87f7c9b0a3d059b28061361c9424cbba1 Mon Sep 17 00:00:00 2001 From: Michael Zargham Date: Mon, 22 Apr 2019 20:46:54 -0700 Subject: [PATCH 12/14] added more tests; tests passed --- infra/graph_generators_test.py | 115 ++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index 2dfa284..855f2d7 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -1,11 +1,19 @@ #!/usr/bin/env python3 -''' +""" This Module provides unit tests for functions for generating simple networks -''' +""" import networkx as nx import unittest -from graph_generators import line_graph_gen, star_graph_gen, tree_graph_gen, make_bidir, reverse_dir, set_types +from graph_generators import ( + line_graph_gen, + circle_graph_gen, + star_graph_gen, + tree_graph_gen, + make_bidir, + reverse_dir, + set_types, +) class Graph_Generators_Line_Graph(unittest.TestCase): @@ -17,9 +25,31 @@ def test_line_graph_gen_bidir(self): g = line_graph_gen(4, bidir=True) # (node, neighbor) self.assertEqual( - list(g.edges()), [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)] + list(g.edges()), [(0, 1), (1, 2), (1, 0), (2, 3), (2, 1), (3, 2)] ) + def test_line_graph_gen_bogus_bidir(self): + with self.assertRaises(ValueError): + line_graph_gen(4, bidir="troll") + + +class Graph_Generators_Circle_Graph(unittest.TestCase): + def test_circle_graph_gen_sanity(self): + g = circle_graph_gen(4) + self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3), (3, 0)]) + + def test_circle_graph_gen_bidir(self): + g = circle_graph_gen(4, bidir=True) + # (node, neighbor) + self.assertEqual( + list(g.edges()), + [(0, 1), (0, 3), (1, 2), (1, 0), (2, 3), (2, 1), (3, 0), (3, 2)], + ) + + def test_circle_graph_gen_bogus_bidir(self): + with self.assertRaises(ValueError): + circle_graph_gen(4, bidir="troll") + class Graph_Generators_Star_Graph(unittest.TestCase): @@ -27,19 +57,22 @@ class Graph_Generators_Star_Graph(unittest.TestCase): def test_star_graph_sink(self): g = star_graph_gen(4) - self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0), (4, 0)]) + self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0)]) def test_star_graph_source(self): g = star_graph_gen(4, kind="source") - self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3), (0, 4)]) + self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3)]) def test_star_graph_bidir(self): g = star_graph_gen(4, kind="bidir") self.assertEqual( - list(g.edges()), - [(0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (2, 0), (3, 0), (4, 0)], + list(g.edges()), [(0, 1), (0, 2), (0, 3), (1, 0), (2, 0), (3, 0)] ) + def test_star_graph_gen_bogus(self): + with self.assertRaises(ValueError): + star_graph_gen(4, kind="troll") + class Graph_Generators_Tree_Graph(unittest.TestCase): # note: our default is diff from networkx's default in this case (sink vs source) @@ -60,14 +93,14 @@ def test_tree_graph_bidir(self): self.assertEqual( list(g.edges()), [ - (1, 0), - (1, 3), - (1, 4), (0, 1), (0, 2), - (2, 0), + (1, 3), + (1, 4), + (1, 0), (2, 5), (2, 6), + (2, 0), (3, 1), (4, 1), (5, 2), @@ -75,14 +108,16 @@ def test_tree_graph_bidir(self): ], ) + def test_tree_graph_gen_bogus(self): + with self.assertRaises(ValueError): + tree_graph_gen(2, 2, kind="troll") class Graph_Generators_Set_Types(unittest.TestCase): def test_set_types_str(self): - g = nx.path_graph(3, create_using=nx.MultiDiGraph) - + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + g = set_types(g, node_types="foo", edge_types="bar") - # each node and edge should have the correct 'type' value for k, v in nx.get_node_attributes(g, "type").items(): @@ -90,16 +125,14 @@ def test_set_types_str(self): for k, v in nx.get_edge_attributes(g, "type").items(): self.assertEqual(v, "bar") - + def test_set_types_dict(self): - g = nx.path_graph(3, create_using=nx.MultiDiGraph) - - node_dict = {0:"foo0", 1:"foo1", 2:"foo2"} - edge_dict = {(0,1,0):"bar0", (1,2,0):"bar1"} - - + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + node_dict = {0: "foo0", 1: "foo1", 2: "foo2"} + edge_dict = {(0, 1, 0): "bar0", (1, 2, 0): "bar1"} + g = set_types(g, node_types=node_dict, edge_types=edge_dict) - # each node and edge should have the correct 'type' value for k, v in nx.get_node_attributes(g, "type").items(): @@ -108,18 +141,38 @@ def test_set_types_dict(self): for k, v in nx.get_edge_attributes(g, "type").items(): self.assertEqual(v, edge_dict[k]) + def test_set_types_bogus_node_type(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + node_input = 0 + edge_dict = {(0, 1, 0): "bar0", (1, 2, 0): "bar1"} + + with self.assertRaises(ValueError): + set_types(g, node_types=node_input, edge_types=edge_dict) + + def test_set_types_bogus_edge_type(self): + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + + node_dict = {0: "foo0", 1: "foo1", 2: "foo2"} + edge_input = 0 + + with self.assertRaises(ValueError): + set_types(g, node_types=node_dict, edge_types=edge_input) + + class Graph_Generators_Reverse_Dir(unittest.TestCase): def test_reverse_dir(self): - g = nx.path_graph(3, create_using=nx.MultiDiGraph) - + g = nx.path_graph(4, create_using=nx.MultiDiGraph) + g = reverse_dir(g) - + self.assertEqual(list(g.edges()), [(1, 0), (2, 1), (3, 2)]) - + + class Graph_Generators_Make_Bidir(unittest.TestCase): def test_make_bidir(self): - g = nx.path_graph(3, create_using=nx.MultiDiGraph) - + g = nx.path_graph(3, create_using=nx.MultiDiGraph) + g = make_bidir(g) - - self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3),(1, 0), (2, 1), (3, 2)]) + + self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (1, 0), (2, 1)]) From bbdd9731021f242428bca47c95ece1c95842830e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Sun, 5 May 2019 23:54:01 +0300 Subject: [PATCH 13/14] Add test runner hook to graph_generators_test Added an ifmain block that runs the unit tests. This enables running the tests via `python3 infra/graph_generators_test.py`. --- infra/graph_generators_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index 855f2d7..a6b885c 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -176,3 +176,7 @@ def test_make_bidir(self): g = make_bidir(g) self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (1, 0), (2, 1)]) + + +if __name__ == "__main__": + unittest.main() From 22aed7425cee0132368fd0f6c477ad21a2de2793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Sun, 5 May 2019 23:42:26 +0300 Subject: [PATCH 14/14] Refactor unit tests and helper methods - Refactors the helper functions for bidirectionalizing and reversing graphs, so that they do not mutate the input graphs but rather return new graphs. (Mutating function arguments is a bad smell in general, and should be avoided when not necessary.) - Create a new helper base test class with assertions for graph isomorphism and that a graph is isomorphic to a provided iterator of edges. - Write more extensive tests for the bidirectional and reverse functions. Test plan: `python3 infra/graph_generators_test.py` --- infra/graph_generators.py | 49 ++++++----- infra/graph_generators_test.py | 150 ++++++++++++++++++++++----------- 2 files changed, 131 insertions(+), 68 deletions(-) diff --git a/infra/graph_generators.py b/infra/graph_generators.py index ff26e6c..bec0153 100644 --- a/infra/graph_generators.py +++ b/infra/graph_generators.py @@ -5,28 +5,35 @@ import networkx as nx -def make_bidir(graph): - """this function adds an edge with opposite direction for each edge present""" - edges = graph.copy().edges - for e in edges: - graph.add_edge(e[1], e[0]) - - return graph +def bidirectional(graph): + """ + Returns a bidirectional copy of the input graph. + For every edge in the input graph, the bidirectional graph + has a corresponding reversed edge. -# this function adds an edge with opposite direction for each edge present -# and removed the original edge -def reverse_dir(graph): + Does not mutate the input graph. """ - this function adds an edge with opposite direction for each edge present - and removed the original edge + output = nx.MultiDiGraph() + for e in graph.edges: + output.add_edge(e[0], e[1]) + output.add_edge(e[1], e[0]) + return output + + +def reverse(graph): """ - edges = graph.copy().edges - for e in edges: - graph.add_edge(e[1], e[0]) - graph.remove_edge(e[0], e[1]) + Returns a reversed direction copy of the input graph. - return graph + For every edge in the input graph, there will be a reversed edge in the + resultant graph. + + Does not mutate the input graph. + """ + output = nx.MultiDiGraph() + for e in graph.edges: + output.add_edge(e[1], e[0]) + return output def set_types(graph, node_types="vanilla", edge_types="vanilla"): @@ -87,7 +94,7 @@ def line_graph_gen( graph = nx.path_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: - graph = make_bidir(graph) + graph = bidirectional(graph) graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) @@ -138,7 +145,7 @@ def circle_graph_gen( graph = nx.cycle_graph(num_nodes, create_using=nx.MultiDiGraph) if bidir: - graph = make_bidir(graph) + graph = bidirectional(graph) graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) @@ -166,9 +173,9 @@ def tree_graph_gen( graph = nx.balanced_tree(rate, height, create_using=nx.MultiDiGraph) if kind == "sink": - graph = reverse_dir(graph) + graph = reverse(graph) elif kind == "bidir": - graph = make_bidir(graph) + graph = bidirectional(graph) graph = set_types(graph, edge_types=node_type_name, node_types=edge_type_name) diff --git a/infra/graph_generators_test.py b/infra/graph_generators_test.py index a6b885c..f1d6183 100644 --- a/infra/graph_generators_test.py +++ b/infra/graph_generators_test.py @@ -10,40 +10,120 @@ circle_graph_gen, star_graph_gen, tree_graph_gen, - make_bidir, - reverse_dir, + bidirectional, + reverse, set_types, ) -class Graph_Generators_Line_Graph(unittest.TestCase): +class GraphTest(unittest.TestCase): + def assertIsopmorphic(self, g1, g2): + self.assertTrue(nx.is_isomorphic(g1, g2)) + + def assertIsomorphicEdges(self, g1, edges): + g2 = nx.MultiDiGraph() + g2.add_edges_from(edges) + self.assertIsopmorphic(g1, g2) + + +class BidirectionalTest(GraphTest): + def test_empty_graph(self): + g = nx.MultiDiGraph() + bidir = bidirectional(g) + self.assertIsopmorphic(g, bidir) + + def test_simple_graph(self): + g = nx.MultiDiGraph() + g.add_edge(1, 2) + bidir_expected = nx.MultiDiGraph() + bidir_expected.add_edge(1, 2) + bidir_expected.add_edge(2, 1) + bidir_actual = bidirectional(g) + self.assertIsopmorphic(bidir_expected, bidir_actual) + + def test_bidirectional_graph(self): + g = nx.MultiDiGraph() + g.add_edge(1, 2) + g.add_edge(2, 1) + # Since it's a multidigraph, every edge gets doubled + expected_bidir = nx.MultiDiGraph() + expected_bidir.add_edges_from([(1, 2), (2, 1), (2, 1), (1, 2)]) + self.assertIsopmorphic(expected_bidir, bidirectional(g)) + + def test_non_mutating(self): + g = nx.MultiDiGraph() + g.add_edge(1, 2) + g.add_edge(1, 3) + g_copy = g.copy() + bidirectional(g) + self.assertIsopmorphic(g, g_copy) + + def test_path(self): + g = bidirectional(nx.path_graph(3, create_using=nx.MultiDiGraph)) + self.assertIsomorphicEdges(g, [(0, 1), (1, 0), (1, 2), (2, 1)]) + + +class ReversedTest(GraphTest): + def test_empty_graph(self): + g = nx.MultiDiGraph() + r = reverse(g) + self.assertIsopmorphic(g, r) + + def test_simple_graph(self): + g = nx.MultiDiGraph() + # Putting in 3 nodes so the reversed graph and the input aren't isomorphic + g.add_edge(1, 2) + g.add_edge(1, 3) + reversed_expected = nx.MultiDiGraph() + reversed_expected.add_edge(2, 1) + reversed_expected.add_edge(3, 1) + reversed_actual = reverse(g) + self.assertIsopmorphic(reversed_expected, reversed_actual) + + def test_path(self): + g = nx.path_graph(4, create_using=nx.MultiDiGraph) + g = reverse(g) + self.assertIsomorphicEdges(g, [(1, 0), (2, 1), (3, 2)]) + + def test_bidirectional_graph(self): + g = nx.MultiDiGraph() + g.add_edge(1, 2) + g.add_edge(2, 1) + self.assertIsopmorphic(g, reverse(g)) + + def test_non_mutating(self): + g = nx.MultiDiGraph() + g.add_edge(1, 2) + g.add_edge(1, 3) + g_copy = g.copy() + reverse(g) + self.assertIsopmorphic(g, g_copy) + + +class Graph_Generators_Line_Graph(GraphTest): def test_line_graph_gen_sanity(self): g = line_graph_gen(4) - self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3)]) + self.assertIsomorphicEdges(g, [(0, 1), (1, 2), (2, 3)]) def test_line_graph_gen_bidir(self): g = line_graph_gen(4, bidir=True) - # (node, neighbor) - self.assertEqual( - list(g.edges()), [(0, 1), (1, 2), (1, 0), (2, 3), (2, 1), (3, 2)] - ) + self.assertIsomorphicEdges(g, [(0, 1), (1, 2), (1, 0), (2, 3), (2, 1), (3, 2)]) def test_line_graph_gen_bogus_bidir(self): with self.assertRaises(ValueError): line_graph_gen(4, bidir="troll") -class Graph_Generators_Circle_Graph(unittest.TestCase): +class Graph_Generators_Circle_Graph(GraphTest): def test_circle_graph_gen_sanity(self): g = circle_graph_gen(4) - self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (2, 3), (3, 0)]) + self.assertIsomorphicEdges(g, [(0, 1), (1, 2), (2, 3), (3, 0)]) def test_circle_graph_gen_bidir(self): g = circle_graph_gen(4, bidir=True) # (node, neighbor) - self.assertEqual( - list(g.edges()), - [(0, 1), (0, 3), (1, 2), (1, 0), (2, 3), (2, 1), (3, 0), (3, 2)], + self.assertIsomorphicEdges( + g, [(0, 1), (0, 3), (1, 2), (1, 0), (2, 3), (2, 1), (3, 0), (3, 2)] ) def test_circle_graph_gen_bogus_bidir(self): @@ -51,47 +131,41 @@ def test_circle_graph_gen_bogus_bidir(self): circle_graph_gen(4, bidir="troll") -class Graph_Generators_Star_Graph(unittest.TestCase): +class Graph_Generators_Star_Graph(GraphTest): # how to validate that 'kind' is either 'source', 'sink', or 'bidir' ?? def test_star_graph_sink(self): g = star_graph_gen(4) - self.assertEqual(list(g.edges()), [(1, 0), (2, 0), (3, 0)]) + self.assertIsomorphicEdges(g, [(1, 0), (2, 0), (3, 0)]) def test_star_graph_source(self): g = star_graph_gen(4, kind="source") - self.assertEqual(list(g.edges()), [(0, 1), (0, 2), (0, 3)]) + self.assertIsomorphicEdges(g, [(0, 1), (0, 2), (0, 3)]) def test_star_graph_bidir(self): g = star_graph_gen(4, kind="bidir") - self.assertEqual( - list(g.edges()), [(0, 1), (0, 2), (0, 3), (1, 0), (2, 0), (3, 0)] - ) + self.assertIsomorphicEdges(g, [(0, 1), (0, 2), (0, 3), (1, 0), (2, 0), (3, 0)]) def test_star_graph_gen_bogus(self): with self.assertRaises(ValueError): star_graph_gen(4, kind="troll") -class Graph_Generators_Tree_Graph(unittest.TestCase): +class Graph_Generators_Tree_Graph(GraphTest): # note: our default is diff from networkx's default in this case (sink vs source) def test_tree_graph_source_sink(self): g = tree_graph_gen(2, 2) - self.assertEqual( - list(g.edges()), [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)] - ) + self.assertIsomorphicEdges(g, [(1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 2)]) def test_treeGraph_source(self): g = tree_graph_gen(2, 2, kind="source") - self.assertEqual( - list(g.edges()), [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)] - ) + self.assertIsomorphicEdges(g, ((0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6))) def test_tree_graph_bidir(self): g = tree_graph_gen(2, 2, kind="bidir") - self.assertEqual( - list(g.edges()), + self.assertIsomorphicEdges( + g, [ (0, 1), (0, 2), @@ -113,7 +187,7 @@ def test_tree_graph_gen_bogus(self): tree_graph_gen(2, 2, kind="troll") -class Graph_Generators_Set_Types(unittest.TestCase): +class Graph_Generators_Set_Types(GraphTest): def test_set_types_str(self): g = nx.path_graph(3, create_using=nx.MultiDiGraph) @@ -160,23 +234,5 @@ def test_set_types_bogus_edge_type(self): set_types(g, node_types=node_dict, edge_types=edge_input) -class Graph_Generators_Reverse_Dir(unittest.TestCase): - def test_reverse_dir(self): - g = nx.path_graph(4, create_using=nx.MultiDiGraph) - - g = reverse_dir(g) - - self.assertEqual(list(g.edges()), [(1, 0), (2, 1), (3, 2)]) - - -class Graph_Generators_Make_Bidir(unittest.TestCase): - def test_make_bidir(self): - g = nx.path_graph(3, create_using=nx.MultiDiGraph) - - g = make_bidir(g) - - self.assertEqual(list(g.edges()), [(0, 1), (1, 2), (1, 0), (2, 1)]) - - if __name__ == "__main__": unittest.main()