def test_add_nodes_from_graph(self): """ Test adding multiple nodes from another graph """ self.itr = [1, 2, 3] g = Graph() g.add_nodes(self.itr) self.graph.add_nodes(g.nodes)
def test_combinatorial_issuperset(self): """ Test graph 1 issuperset of graph 2 """ graph2 = Graph(auto_nid=False) graph2.add_nodes(range(7, 11)) graph2.add_edges([(7, 8), (8, 9), (9, 10)]) self.assertFalse(graph_issuperset(graph2, self.graph1)) self.assertTrue(graph_issuperset(self.graph1, graph2))
class TestGraphAddNodeExceptionWarning(UnittestPythonCompatibility): """ Test logged warnings and raised Exceptions by Graph add_node. Same as for add_nodes """ def setUp(self): """ Build empty graph to add a node to and test default state """ self.graph = Graph() def test_add_node_none(self): """ Unable to add 'None' node when auto_nid False """ # no problem when auto_nid self.graph.add_node() self.assertTrue(len(self.graph) == 1) self.graph.auto_nid = False self.assertRaises(GraphitException, self.graph.add_node, None) def test_add_node_hasable(self): """ When auto_nid equals False, the nid should be a hashable object :return: """ self.graph.auto_nid = False self.assertRaises(GraphitException, self.graph.add_node, [1, 2]) def test_add_node_duplicate(self): """ Duplication is no problem with auto_nid but without the previous node is updated with the attributes of the new one. A warning is logged. """ # With auto_nid self.graph.add_nodes([1, 1]) self.assertEqual(len(self.graph), 2) # Without auto_nid self.graph.auto_nid = False self.graph.add_nodes([3, 3]) self.assertEqual(len(self.graph), 3) self.assertItemsEqual(self.graph.keys(), [1, 1, 3]) self.assertItemsEqual(self.graph.keys('_id'), [1, 2, 3])
class TestGraphAddEdgeExceptionWarning(UnittestPythonCompatibility): """ Test logged warnings and raised Exceptions by Graph add_edge. Same as for add_nodes """ def setUp(self): """ Build empty graph to add a node to and test default state """ self.graph = Graph() def test_add_edge_node_not_exist(self): """ Test adding edges for nodes that do not exist """ self.assertRaises(GraphitException, self.graph.add_edge, 1, 2) def test_add_edge_exist(self): """ Test adding edges that already exist. A warning is logged but edge ID is returned """ self.graph.add_nodes([1, 2]) self.graph.add_edge(1, 2) eid = self.graph.add_edge(1, 2) self.assertTrue(eid, (1, 2)) def test_remove_edge_exist(self): """ Test removal of edges that do not exist should only log a warning """ self.graph.add_nodes([1, 2]) try: self.graph.remove_edge(1, 2) except GraphitException: self.fail('remove_edge raised GraphitException unexpectedly')
def read_adl(adl_file, graph=None): """ Construct a graph from a adjacency list (ADL) .. note:: the directionality of the graph is not defined explicitly in the adjacency list and thus depends on the graph.directional attribute that is False (undirectional) by default. :param adl_file: ADL graph data. :type adl_file: File, string, stream or URL :param graph: Graph object to import ADL data in :type graph: :graphit:Graph :return: Graph object :rtype: :graphit:Graph """ adl_file = open_anything(adl_file) # User defined or default Graph object if graph is None: graph = Graph() elif not isinstance(graph, Graph): raise GraphitException('Unsupported graph type {0}'.format(type(graph))) # ADL node labels are unique, turn off auto_nid graph.data['auto_nid'] = False for line in adl_file.readlines(): # Ignore comments (# ..) line = line.split('#')[0].strip() if line: nodes = line.split() graph.add_nodes(nodes) if len(nodes) > 1: graph.add_edges([(nodes[0], n) for n in nodes[1:]]) return graph
class TestGraphIteration(UnittestPythonCompatibility): """ Test methods for iteration over nodes and edges in a graph """ def setUp(self): """ Build default Graph with node and edge attributes """ self.graph = Graph() self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}), ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}), ('h', {'weight': 3.0})]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge') def test_iterators_isgenerator(self): """ Node and edge iterators return a generator """ self.assertTrue(isinstance(self.graph.iternodes(), types.GeneratorType)) self.assertTrue(isinstance(self.graph.iteredges(), types.GeneratorType)) def test_iterators_iternodes(self): """ Iternodes returns single node graphs based on sorted node ID which in case of auto_nid returns the nodes in the order they where added. """ for i, n in enumerate(self.graph.iternodes(), start=1): self.assertIsInstance(n, Graph) self.assertEqual(n.nid, i) # The Graph '__iter__' magic method points to iternodes for i, n in enumerate(self.graph, start=1): self.assertIsInstance(n, Graph) self.assertEqual(n.nid, i) def test_iterators_iternodes_reversed(self): """ Iterate over nodes in reversed order based on node ID """ self.assertListEqual([n.nid for n in self.graph.iternodes(reverse=True)], [5, 4, 3, 2, 1]) def test_iterators_iternodes_subgraph(self): """ Iternodes on a subgraph will only iterate over the nodes in the subgraph """ sub = self.graph.getnodes([1, 3, 4]) self.assertEqual(len(sub), 3) self.assertEqual([n.nid for n in sub.iternodes()], [1, 3, 4]) self.assertEqual([n.nid for n in sub], [1, 3, 4]) def test_iterators_iteredges(self): """ Iteredges returns single edge graphs based on sorted edge ID. """ edges = [] for e in self.graph.iteredges(): self.assertIsInstance(e, Graph) edges.append(e.nid) self.assertListEqual(edges, [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (3, 5), (4, 3), (4, 5), (5, 3), (5, 4)]) def test_iterators_iteredges_reversed(self): """ Iterate over edges in reversed order based on edge ID """ self.assertListEqual([e.nid for e in self.graph.iteredges(reverse=True)], [(5, 4), (5, 3), (4, 5), (4, 3), (3, 5), (3, 4), (3, 2), (2, 3), (2, 1), (1, 2)])
class TestGraphCopy(UnittestPythonCompatibility): """ Test Graph copy and deepcopy methods """ def setUp(self): """ Build default graph with nodes, edges and attributes """ self.graph = Graph() self.graph.add_nodes([('g', { 'weight': 1.0 }), ('r', { 'weight': 1.5 }), ('a', { 'weight': 2.0 }), ('p', { 'weight': 2.5 }), ('h', { 'weight': 3.0 })]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], isedge=True) def tearDown(self): """ Test copied state Testing equality in node, edge and adjacency data stores is based on the internal '_storage' object and not so much the storage object itself which is often just a wrapper. """ # Main Graph object is new self.assertTrue(id(self.copied) != id(self.graph)) if self.shallow: # Internal node and edge stores point to parent. self.assertEqual(id(self.copied.nodes._storage), id(self.graph.nodes._storage)) self.assertEqual(id(self.copied.edges._storage), id(self.graph.edges._storage)) # ORM and origin objects point to parent self.assertEqual(id(self.copied.orm), id(self.graph.orm)) self.assertEqual(id(self.copied.origin), id(self.graph.origin)) else: # Internal node and edge stores point to parent. self.assertNotEqual(id(self.copied.nodes._storage), id(self.graph.nodes._storage)) self.assertNotEqual(id(self.copied.edges._storage), id(self.graph.edges._storage)) # ORM and origin objects point to parent self.assertNotEqual(id(self.copied.orm), id(self.graph.orm)) self.assertNotEqual(id(self.copied.origin), id(self.graph.origin)) def test_graph_copy_shallow(self): """ Test making a shallow copy of a graph. This essentially copies the Graph object while linking tot the data store in the parent Graph """ self.shallow = True self.copied = self.graph.copy(deep=False) def test_graph_copy_deep(self): """ Test making a deep copy of a graph (default) copying everything """ self.shallow = False self.copied = self.graph.copy() def test_graph_buildin_copy_shallow(self): """ Test making a shallow copy of a graph using the 'copy' method of the copy class. This calls the Graph.copy method """ self.shallow = True self.copied = copy.copy(self.graph) def test_graph_buildin_copy_deep(self): """ Test making a deep copy of a graph using the 'deepcopy' method of the copy class. This calls the Graph.copy method """ self.shallow = False self.copied = copy.deepcopy(self.graph) def test_graph_buildin_copy_deep_view(self): """ Test copying subgraphs either with the set 'view' only or the full origin graph (full graph) """ # Regular copy self.shallow = False self.copied = copy.deepcopy(self.graph) # Build subgraph, same origin view = self.graph.getnodes([3, 4, 5]) self.assertEqual(id(view.origin), id(self.graph.origin)) # Deep copy with or without view, different origin copy_view = view.copy(deep=True, copy_view=False) copy_full = view.copy(deep=True, copy_view=True) self.assertNotEqual(id(copy_view.origin), id(self.graph.origin)) self.assertNotEqual(id(copy_full.origin), id(self.graph.origin)) # Subgraph 'view' should be identical to the original # regardless the copy mode self.assertEqual(copy_view.nodes.keys(), view.nodes.keys()) self.assertEqual(copy_view.edges.keys(), view.edges.keys()) self.assertEqual(copy_view.adjacency.keys(), view.adjacency.keys()) self.assertEqual(copy_full.nodes.keys(), view.nodes.keys()) self.assertEqual(copy_full.edges.keys(), view.edges.keys()) self.assertEqual(copy_full.adjacency.keys(), view.adjacency.keys()) # The view copy origin should either be identical to the view # (copy_view = True) or to the full graph (copy_view = False) self.assertEqual(list(copy_view.nodes._storage.keys()), list(view.nodes.keys())) self.assertEqual(list(copy_full.nodes._storage.keys()), list(view.origin.nodes.keys())) # The copy_full has its origin equals self and thus copy_full.origin.nodes # equals copy_full.nodes. However, the view is also set which means that # by default the full graph is not accessible without resetting it copy_full.nodes.reset_view() self.assertEqual(copy_full.nodes.keys(), self.graph.nodes.keys())
class TestGraphCombinatorialSetlike(UnittestPythonCompatibility): def setUp(self): """ Setup two graphs for combinatorial tests """ self.graph1 = Graph(auto_nid=False) self.graph1.add_nodes(range(1, 11)) self.graph1.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (4, 7), (6, 8), (7, 8), (8, 9), (9, 10)]) self.graph2 = Graph(auto_nid=False) self.graph2.add_nodes(range(6, 16)) self.graph2.add_edges([(6, 8), (7, 8), (8, 9), (9, 10), (10, 11), (10, 12), (12, 13), (11, 14), (13, 15), (14, 15)]) def test_combinatorial_intersection(self): """ Test intersection between two graphs """ intr = graph_intersection(self.graph1, self.graph2) self.assertItemsEqual(intr.nodes.keys(), range(6, 11)) self.assertItemsEqual(intr.edges.keys(), [(8, 9), (6, 8), (9, 8), (9, 10), (8, 7), (8, 6), (7, 8), (10, 9)]) self.assertFalse(intr.nodes.is_view) self.assertFalse(intr.edges.is_view) self.assertEqual(intr, intr.origin) def test_combinatorial_intersection_edgediff(self): """ Test intersection between two graphs with a different edge population """ self.graph2.remove_edge(8, 9) intr = graph_intersection(self.graph1, self.graph2) self.assertItemsEqual(intr.nodes.keys(), range(6, 11)) self.assertItemsEqual(intr.edges.keys(), [(6, 8), (9, 10), (8, 7), (8, 6), (7, 8), (10, 9)]) self.assertFalse(intr.nodes.is_view) self.assertFalse(intr.edges.is_view) self.assertEqual(intr, intr.origin) def test_combinatorial_difference(self): """ Test difference between two graphs """ diff = graph_difference(self.graph1, self.graph2) self.assertItemsEqual(diff.nodes.keys(), range(1, 6)) self.assertItemsEqual(diff.edges.keys(), [(1, 2), (3, 2), (2, 1), (2, 3), (4, 3), (5, 3), (3, 4), (3, 5)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) diff = graph_difference(self.graph2, self.graph1) self.assertItemsEqual(diff.nodes.keys(), range(11, 16)) self.assertItemsEqual(diff.edges.keys(), [(14, 11), (13, 12), (15, 13), (12, 13), (13, 15), (14, 15), (11, 14), (15, 14)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) def test_combinatorial_difference_edgediff(self): """ Test difference between two graphs using edge oriented difference. """ diff = graph_difference(self.graph1, self.graph2, edge_diff=True) self.assertItemsEqual(diff.nodes.keys(), range(1, 8)) self.assertItemsEqual(diff.edges.keys(), [(1, 2), (3, 2), (2, 1), (2, 3), (4, 3), (5, 3), (3, 4), (3, 5), (5, 6), (6, 5), (4, 7), (7, 4)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) diff = graph_difference(self.graph2, self.graph1, edge_diff=True) self.assertItemsEqual(diff.nodes.keys(), range(10, 16)) self.assertItemsEqual(diff.edges.keys(), [(14, 11), (13, 12), (15, 13), (12, 13), (13, 15), (14, 15), (11, 14), (15, 14), (10, 11), (11, 10), (10, 12), (12, 10)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) def test_combinatorial_symmetric_difference(self): """ Test symmetric difference between two graphs using edge oriented difference. Returns a new graph """ diff = graph_symmetric_difference(self.graph1, self.graph2, edge_diff=True) self.assertItemsEqual(diff.nodes.keys(), [1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15]) self.assertItemsEqual(diff.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3), (11, 14), (14, 11), (12, 13), (13, 12), (14, 15), (15, 14), (13, 15), (15, 13), (4, 7), (7, 4), (5, 6), (6, 5), (10, 11), (11, 10), (10, 12), (12, 10)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) def test_combinatorial_symmetric_difference_edgediff(self): """ Test symmetric difference between two graphs. Returns a new graph """ diff = graph_symmetric_difference(self.graph1, self.graph2) self.assertItemsEqual(diff.nodes.keys(), [1, 2, 3, 4, 5, 11, 12, 13, 14, 15]) self.assertItemsEqual(diff.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3), (11, 14), (14, 11), (12, 13), (13, 12), (14, 15), (15, 14), (13, 15), (15, 13)]) self.assertFalse(diff.nodes.is_view) self.assertFalse(diff.edges.is_view) self.assertEqual(diff, diff.origin) def test_combinatorial_union(self): """ Test union between two graphs. Returns a new graph """ union = graph_union(self.graph1, self.graph2) self.assertItemsEqual(union.nodes.keys(), range(1, 16)) self.assertItemsEqual(union.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3), (5, 6), (6, 5), (4, 7), (7, 4), (6, 8), (8, 6), (7, 8), (8, 7), (8, 9), (9, 8), (9, 10), (10, 9), (10, 11), (11, 10), (12, 10), (10, 12), (12, 13), (13, 12), (11, 14), (14, 11), (13, 15), (15, 13), (14, 15), (15, 14)]) self.assertFalse(union.nodes.is_view) self.assertFalse(union.edges.is_view) self.assertEqual(union, union.origin) def test_combinatorial_issubset(self): """ Test graph 1 issubset of graph 2 """ graph2 = Graph(auto_nid=False) graph2.add_nodes(range(7, 11)) graph2.add_edges([(7, 8), (8, 9), (9, 10)]) self.assertTrue(graph_issubset(graph2, self.graph1)) self.assertFalse(graph_issubset(self.graph1, graph2)) def test_combinatorial_issuperset(self): """ Test graph 1 issuperset of graph 2 """ graph2 = Graph(auto_nid=False) graph2.add_nodes(range(7, 11)) graph2.add_edges([(7, 8), (8, 9), (9, 10)]) self.assertFalse(graph_issuperset(graph2, self.graph1)) self.assertTrue(graph_issuperset(self.graph1, graph2))
class TestGraphAddNodesAutonid(UnittestPythonCompatibility): """ Test Graph the add_nodes method using different input with the Graph class set to default auto_nid = True """ def setUp(self): """ Build empty graph to add nodes to and test default state """ self.graph = Graph() # empty before addition self.assertTrue(len(self.graph) == 0) self.assertTrue(len(self.graph.nodes) == 0) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == 0) # auto_nid self.assertTrue(self.graph.auto_nid) self.assertEqual(self.graph._nodeid, 1) def tearDown(self): """ Test state after node addition """ length = len(self.itr) nids = range(1, length + 1) # auto_nid self.assertItemsEqual(list(self.graph.nodes), nids) self.assertEqual(self.graph._nodeid, length + 1) # filled after addition self.assertTrue(len(self.graph) == length) self.assertTrue(len(self.graph.nodes) == length) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == length) # node key self.assertItemsEqual(self.graph.keys(), self.itr) def test_add_nodes_from_list(self): """ Test adding multiple nodes from a list """ self.itr = [1, 2, 3] self.graph.add_nodes(self.itr) def test_add_nodes_from_tuple(self): """ Test adding multiple nodes from a tuple """ self.itr = [1, 2, 3] self.graph.add_nodes(tuple(self.itr)) def test_add_nodes_from_set(self): """ Test adding multiple nodes from a set """ self.itr = [1, 2, 3] self.graph.add_nodes(set(self.itr)) def test_add_nodes_from_dict(self): """ Test adding multiple nodes from a dict """ self.itr = [1, 2, 3] self.graph.add_nodes({1: 'one', 2: 'two', 3: 'three'}) def test_add_nodes_from_string(self): """ Test adding multiple nodes from a string """ self.itr = ['g', 'r', 'a', 'p', 'h'] self.graph.add_nodes(''.join(self.itr)) def test_add_nodes_from_range(self): """ Test adding multiple nodes from a range """ self.itr = range(5) self.graph.add_nodes(self.itr) def test_add_nodes_from_graph(self): """ Test adding multiple nodes from another graph """ self.itr = [1, 2, 3] g = Graph() g.add_nodes(self.itr) self.graph.add_nodes(g.nodes)
class TestGraphAddEdgesAttributes(UnittestPythonCompatibility): """ Test additional attribute storage for Graph add_edges. Add_edges is a wrapper around add_edge, here we only test attribute addition. """ def setUp(self): """ Build Graph with a few nodes but no edges yet """ self.graph = Graph() self.graph.add_nodes('graph') def test_add_edges_no_attribute(self): """ No attributes added should yield empty dict """ edges = [(1, 2), (2, 3), (3, 4), (3, 5)] self.graph.add_edges(edges) self.assertTrue(all([len(e) == 0 for e in self.graph.edges.values()])) def test_add_edges_single_global_attribute(self): """ Test adding a single global attribute to all edges """ edge_dict = {'weight': 2.33} edges = [(1, 2), (2, 3), (3, 4), (3, 5)] self.graph.add_edges(edges, **edge_dict) for attr in self.graph.edges.values(): self.assertDictEqual(attr, edge_dict) def test_add_edges_multiple_global_attribute(self): """ Test adding a multiple global attributes to all edges """ edge_dict = {'test': True, 'pv': 1.44} edges = [(1, 2), (2, 3), (3, 4), (3, 5)] self.graph.add_edges(edges, **edge_dict) for attr in self.graph.edges.values(): self.assertDictEqual(attr, edge_dict) def test_add_edges_global_attribute_directed(self): """ Test adding a single global attribute to directed edges """ edge_dict_one = {'test': True, 'pv': 1.44} edges_one = [(1, 2), (2, 3), (3, 4), (3, 5)] edge_dict_two = {'test': False, 'pv': 5.44} edges_two = [(2, 1), (3, 2), (4, 3), (5, 3)] self.graph.directed = True self.graph.add_edges(edges_one, **edge_dict_one) self.graph.add_edges(edges_two, **edge_dict_two) for edge in edges_one: self.assertDictEqual(self.graph.edges[edge], edge_dict_one) for edge in edges_two: self.assertDictEqual(self.graph.edges[edge], edge_dict_two) def test_add_edges_unique_attributes(self): """ Test add unique edge attributes included two tuple """ edges = [(1, 2, {'weight': 1.0}), (2, 3, {'weight': 1.5}), (3, 4, {'weight': 2.0}), (3, 5, {'weight': 2.5})] self.graph.add_edges(edges) for edge in edges: e = (edge[0], edge[1]) self.assertDictEqual(self.graph.edges[e], edge[2]) self.assertDictEqual(self.graph.edges[e[::-1]], edge[2]) def test_add_edges_global_unique_attributes(self): """ Test add unique edge attributes included two tuple and add a single global attribute to it """ edges = [(1, 2, {'weight': 1.0}), (2, 3, {'weight': 1.5}), (3, 4, {'weight': 2.0}), (3, 5, {'weight': 2.5})] self.graph.add_edges(edges, pv=True) for edge in edges: e = (edge[0], edge[1]) attr = edge[2] attr['pv'] = True self.assertDictEqual(self.graph.edges[e], attr) self.assertDictEqual(self.graph.edges[e[::-1]], attr)
class TestGraphIteration(UnittestPythonCompatibility): """ Test methods for iteration over nodes and edges in a graph """ def setUp(self): """ Build default Graph with node and edge attributes """ self.graph = Graph() self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}), ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}), ('h', {'weight': 3.0})]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge') def test_magic_method_eq(self): """ Test Graph equality __eq__ (==) test """ self.assertTrue(self.graph == self.graph) self.assertTrue(self.graph.getnodes([1, 3, 4]) == self.graph.getnodes([1, 3, 4])) self.assertTrue(self.graph == self.graph.copy(deep=True)) self.assertFalse(self.graph.getnodes([1, 2]) == self.graph.getnodes([2, 3])) self.assertFalse(self.graph.getedges([(1, 2), (2, 3)]) == self.graph.getnodes([2, 3])) def test_magic_method_add(self): """ Test Graph addition __add__ (+) support """ # Adding self to self does not change anything self.assertEqual(self.graph + self.graph, self.graph) # Adding sub graphs together to yield the full graph only works if # there is an overlap in the graphs connecting them together. Without # the overlap the connecting edges are lost sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([3, 4, 5]) self.assertEqual(sub1 + sub2, self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) self.assertNotEqual(sub1 + sub2, self.graph) # Sub graphs are still views on the origin combined = sub1 + sub2 self.assertTrue(combined.nodes.is_view) self.assertTrue(combined.edges.is_view) self.assertEqual(id(combined.origin), id(self.graph.origin)) # Adding graphs together that do not share a common origin sub1_copy = sub1.copy() combined = sub1_copy + sub2 self.assertFalse(combined.nodes.is_view) self.assertFalse(combined.edges.is_view) self.assertNotEqual(id(combined.origin), id(self.graph.origin)) def test_magic_method_iadd(self): """ Test Graph in place addition __iadd__ (+=) support """ # Adding sub graphs together to yield the full graph only works if # there is an overlap in the graphs connecting them together. Without # the overlap the connecting edges are lost sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([3, 4, 5]) sub1 += sub2 self.assertEqual(sub1, self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) sub1 += sub2 self.assertNotEqual(sub1, self.graph) # Sub graphs are still views on the origin self.assertTrue(sub1.nodes.is_view) self.assertTrue(sub1.edges.is_view) self.assertEqual(id(sub1.origin), id(self.graph.origin)) # Adding graphs together that do not share a common origin sub1_copy = sub1.copy() sub1_copy += sub2 self.assertFalse(sub1_copy.nodes.is_view) self.assertFalse(sub1_copy.edges.is_view) self.assertNotEqual(id(sub1_copy.origin), id(self.graph.origin)) def test_magic_method_contains(self): """ Test Graph contains __contains__ test """ # Equal graphs also contain each other self.assertTrue(self.graph in self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) self.assertTrue(sub1 in self.graph) self.assertFalse(sub2 in sub1) def test_magic_method_getitem(self): """ Test Graph dictionary style __getitem__ item lookup """ self.assertEqual(self.graph[2], self.graph.getnodes(2)) self.assertEqual(self.graph[(2, 3)], self.graph.getedges((2, 3))) # Support for slicing self.assertEqual(self.graph[2:], self.graph.getnodes([2, 3, 4, 5])) self.assertEqual(self.graph[2:4], self.graph.getnodes([2, 3])) self.assertEqual(self.graph[1:5:2], self.graph.getnodes([1, 3])) self.assertTrue(self.graph[1:-1].empty()) def test_magic_method_ge(self): """ Test Graph greater-then or equal __ge__ (>=) to support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(self.graph >= sub) self.assertFalse(sub >= self.graph) self.assertTrue(sub >= sub) def test_magic_method_gt(self): """ Test Graph greater-then __gt__ (>) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(self.graph > sub) self.assertFalse(sub > self.graph) def test_magic_method_len(self): """ Test Graph length __len__ support """ self.assertEqual(len(self.graph), 5) def test_magic_method_le(self): """ Test Graph less-then or equal __le__ (<=) to support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(sub <= self.graph) self.assertFalse(self.graph <= sub) self.assertTrue(sub <= sub) def test_magic_method_lt(self): """ Test Graph greater-then __lt__ (<) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(sub < self.graph) self.assertFalse(self.graph < sub) def test_magic_method_ne(self): """ Test Graph equality __ne__ (!=) test """ self.assertFalse(self.graph != self.graph) self.assertFalse(self.graph.getnodes([1, 3, 4]) != self.graph.getnodes([1, 3, 4])) self.assertFalse(self.graph != self.graph.copy(deep=True)) self.assertTrue(self.graph.getnodes([1, 2]) != self.graph.getnodes([2, 3])) self.assertTrue(self.graph.getedges([(1, 2), (2, 3)]) != self.graph.getnodes([2, 3])) def test_magic_method_sub(self): """ Test Graph subtract __sub__ (-) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertEqual(self.graph - sub, self.graph.getnodes([1,5])) self.assertTrue(len(sub - self.graph) == 0) def test_magic_method_isub(self): """ Test Graph inplace subtract __isub__ (-=) support """ cp = self.graph.copy() self.graph -= self.graph.getnodes([2, 3, 4]) self.assertEqual(self.graph, cp.getnodes([1,5]))
def read_p2g(p2g_file, graph=None): """ Read graph in P2G format :param p2g_file: P2G data to parse :type p2g_file: File, string, stream or URL :param graph: Graph object to import to or Graph by default :type graph: :graphit:Graph :return: Graph instance :rtype: :graphit:Graph """ p2g_file = open_anything(p2g_file) if graph is None: graph = Graph() elif not isinstance(graph, Graph): raise GraphitException('Unsupported graph type {0}'.format( type(graph))) # P2G graphs are directed graph.directed = True graph_name = None graph_layout = None curr_node = None nodes = {} for i, line in enumerate(p2g_file.readlines()): line = line.strip() if line: # Parse p2g graph name (first line) sline = line.split() if not graph_name: graph_name = line continue # Parse number of nodes and edges (second line) elif not graph_layout: try: graph_layout = map(int, sline) except ValueError: raise GraphitException( 'P2G import error: line {0} - {1}'.format(i, line)) continue # Parse nodes and edges if len(sline) == 1: nodes[line] = [] curr_node = line elif len(sline) == 2: try: nodes[curr_node] = map(int, sline) except ValueError: raise GraphitException( 'P2G import error: malformed edge on line {0} - {1}'. format(i, line)) else: raise GraphitException( 'P2G import error: line {0} - {1}'.format(i, line)) graph.data['name'] = graph_name # Add nodes mapped_nodes = graph.add_nodes(nodes.keys()) # Add edges for i, nid in enumerate(nodes.keys()): for e in nodes[nid]: if e < len(mapped_nodes): graph.add_edge(mapped_nodes[i], mapped_nodes[e]) else: raise GraphitException( 'P2G import error: edge node index {0} not in graph'. format(e)) if len(nodes) != graph_layout[0] or (len(graph.edges)) != graph_layout[1]: logging.warning( 'P2G import warning: declared number of nodes and edges {0}-{1} does not match {2}-{3}' .format(graph_layout[0], graph_layout[1], len(nodes), len(graph.edges))) return graph
class TestGraphRemoveNodes(UnittestPythonCompatibility): """ Test removal of single or multiple nodes """ def setUp(self): """ Build default graph with few nodes and edges """ self.graph = Graph() self.graph.add_nodes('graph', isnode=True) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], isedge=True) def tearDown(self): """ Test state after node remove """ for node in self.to_remove: self.assertTrue(node not in self.graph.nodes) # node not in edges self.assertTrue(all([node not in e for e in self.graph.edges])) # node not in adjacency self.assertTrue(node not in self.graph.adjacency) self.assertTrue( all([node not in a for a in self.graph.adjacency.values()])) # Nodes not in removed should still be there for node in {1, 2, 3, 4, 5}.difference(set(self.to_remove)): self.assertTrue(node in self.graph.nodes) self.assertTrue(node in self.graph.adjacency) def test_remove_node(self): """ Test removal of single node """ self.to_remove = [3] self.graph.remove_node(self.to_remove[0]) def test_remove_nodes(self): """ Test removal of multiple nodes """ self.to_remove = [1, 3, 4] self.graph.remove_nodes(self.to_remove) def test_graph_clear(self): """ Test clear method to removal all nodes and edges """ self.to_remove = [1, 2, 3, 4, 5] self.graph.clear() self.assertTrue(len(self.graph) == 0) self.assertTrue(len(self.graph.nodes) == 0) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == 0)
class TestGraphAddNodesAttributes(UnittestPythonCompatibility): """ Test additional attribute storage for Graph add_nodes """ def setUp(self): """ Build empty graph to add a node to and test default state """ self.graph = Graph() def tearDown(self): """ Test state after node addition """ match = [ set(self.attr.items()).issubset(set(n.items())) for n in self.graph.nodes.values() ] self.assertTrue(all(match)) def test_add_nodes_single_attribute(self): """ Test adding identical attribute for all nodes using add_nodes """ self.attr = {'weight': 2.33} self.graph.add_nodes('graph', **self.attr) def test_add_nodes_attribute_dict(self): """ Test adding multiple nodes with unique attributes using a tuple in add_nodes """ self.attr = {} nodes = [('g', { 'weight': 1.0 }), ('r', { 'weight': 1.5 }), ('a', { 'weight': 2.0 }), ('p', { 'weight': 2.5 }), ('h', { 'weight': 3.0, 'pv': True })] self.graph.add_nodes(nodes) match = [] for i in nodes: for node in self.graph.nodes.values(): if node[self.graph.key_tag] == i[0]: match.append(set(i[1].items()).issubset(set(node.items()))) self.assertTrue(all(match)) def test_add_nodes_attribute_dict2(self): """ Test adding multiple nodes with unique attributes using a tuple in add_nodes Also add one attribute that is identical everywhere """ self.attr = {} nodes = [('g', { 'weight': 1.0 }), ('r', { 'weight': 1.5 }), ('a', { 'weight': 2.0 }), ('p', { 'weight': 2.5 }), ('h', { 'weight': 3.0, 'pv': True })] self.graph.add_nodes(nodes, extra='yes') match = [] for i in nodes: i[1]['extra'] = 'yes' for node in self.graph.nodes.values(): if node[self.graph.key_tag] == i[0]: match.append(set(i[1].items()).issubset(set(node.items()))) self.assertTrue(all(match))
class TestGraphAddEdge(UnittestPythonCompatibility): """ Test Graph add_edge method with the Graph.auto_nid set to False mimicking the behaviour of many popular graph packages """ def setUp(self): """ Build Graph with a few nodes but no edges yet """ self.graph = Graph(auto_nid=False) self.graph.add_nodes('graph') # Only nodes no edges yet self.assertTrue(len(self.graph) == 5) self.assertTrue(len(self.graph.nodes) == 5) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == 5) self.assertTrue(all([len(a) == 0 for a in self.graph.adjacency.values()])) # auto_nid self.assertFalse(self.graph.data.auto_nid) def tearDown(self): """ Test state after edge addition """ # If undirected, add reversed edges if not self.graph.directed: self.edges.extend([e[::-1] for e in self.edges if e[::-1] not in self.edges]) for edge in self.edges: # Edge should be present self.assertTrue(edge in self.graph.edges) # Nodes connected should be present self.assertTrue(all([node in self.graph.nodes for node in edge])) # Adjacency setup self.assertTrue(edge[1] in self.graph.adjacency[edge[0]]) # If directional, reverse edge not in graph if self.graph.directed: rev_edge = edge[::-1] if rev_edge not in self.edges: self.assertTrue(rev_edge not in self.graph.edges) # filled after addition self.assertTrue(len(self.graph) == 5) self.assertTrue(len(self.graph.nodes) == 5) self.assertTrue(len(self.graph.edges) == len(self.edges)) self.assertTrue(len(self.graph.adjacency) == 5) def test_add_edge_undirectional(self): """ Test adding a single un-directional edge """ self.edges = [('g', 'r')] self.graph.add_edge(*self.edges[0]) def test_add_edges_undirectional(self): """ Test adding multiple un-directional edges """ self.edges = [('g', 'r'), ('r', 'a'), ('a', 'p'), ('a', 'h'), ('p', 'h')] self.graph.add_edges(self.edges) def test_add_edge_directional(self): """ Test adding a single directional edge """ self.edges = [('g', 'r')] self.graph.directed = False self.graph.add_edge(*self.edges[0]) def test_add_edges_directional(self): """ Test adding multiple directional edges """ self.edges = [('g', 'r'), ('r', 'a'), ('a', 'p'), ('a', 'h'), ('p', 'h')] self.graph.directed = False self.graph.add_edges(self.edges) def test_add_nodes_and_edges(self): """ Test adding edges and creating the nodes from the edges. Auto_nid is set to False automatically to force identical node/edge names. When node exists, no message. """ self.graph = Graph() self.edges = [('g', 'r'), ('r', 'a'), ('a', 'p'), ('a', 'h'), ('p', 'h')] self.graph.add_edges(self.edges, node_from_edge=True)
class TestGraphEdgeDirectionality(UnittestPythonCompatibility): """ Test adding edges in directional or un-directional way """ def setUp(self): """ Build Graph with a few nodes but no edges yet """ self.graph = Graph() self.graph.add_nodes([1, 2, 3]) # Only nodes no edges yet self.assertTrue(len(self.graph) == 3) self.assertTrue(len(self.graph.nodes) == 3) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == 3) self.assertTrue(all([len(a) == 0 for a in self.graph.adjacency.values()])) def tearDown(self): """ Test state after edge addition """ # If undirected, add reversed edges if not self.graph.directed: self.edges.extend([e[::-1] for e in self.edges if e[::-1] not in self.edges]) for edge in self.edges: # Edge should be present self.assertTrue(edge in self.graph.edges) # Nodes connected should be present self.assertTrue(all([node in self.graph.nodes for node in edge])) # Adjacency setup self.assertTrue(edge[1] in self.graph.adjacency[edge[0]]) # If directional, reverse edge not in graph if self.graph.directed: rev_edge = edge[::-1] if rev_edge not in self.edges: self.assertTrue(rev_edge not in self.graph.edges) # filled after addition self.assertTrue(len(self.graph) == 3) self.assertTrue(len(self.graph.nodes) == 3) self.assertTrue(len(self.graph.edges) == len(self.edges)) self.assertTrue(len(self.graph.adjacency) == 3) def test_add_edge_undirectional_graph(self): """ Test default add edge in undirected graph """ self.graph.directed = False self.edges = [(1, 2), (2, 3)] self.graph.add_edges(self.edges) def test_add_edge_directional_graph(self): """ Test default add edge in directed graph """ self.graph.directed = True self.edges = [(1, 2), (2, 3)] self.graph.add_edges(self.edges) def test_add_edge_mixed_graph(self): """ Add edges with local override in directionality yielding a mixed directional graph """ self.edges = [(1, 2), (2, 3), (3, 2)] self.graph.add_edge(1, 2, directed=True) self.graph.add_edge(2, 3) self.graph.directed = True
class TestGraphAlgorithms(UnittestPythonCompatibility): def setUp(self): edges = { (5, 4): { 'type': 'universal' }, (5, 6): { 'type': 'universal' }, (11, 9): { 'type': 'universal' }, (3, 2): { 'type': 'universal' }, (2, 1): { 'type': 'monotone' }, (9, 10): { 'type': 'universal' }, (2, 3): { 'type': 'universal' }, (9, 6): { 'type': 'universal' }, (6, 5): { 'type': 'universal' }, (1, 2): { 'type': 'monotone' }, ('object', 12): { 'type': 'universal' }, (6, 9): { 'type': 'universal' }, (6, 7): { 'type': 'universal' }, (12, 13): { 'type': 'monotone' }, (7, 8): {}, (7, 6): { 'type': 'universal' }, (13, 12): { 'type': 'monotone' }, (3, 8): { 'type': 'universal' }, (4, 5): { 'type': 'universal' }, (12, 'object'): { 'type': 'universal' }, (9, 11): { 'type': 'universal' }, (4, 3): { 'type': 'universal' }, (8, 3): { 'type': 'universal' }, (3, 4): { 'type': 'universal' }, (10, 9): { 'type': 'universal' } } self.graph = Graph(auto_nid=False) self.graph.directed = True self.gn = NetworkXGraph() self.gn.directed = True self.nx = networkx.DiGraph() weight = 0 for node in range(1, 14): self.graph.add_node(node, weight=weight) self.gn.add_node(node, weight=weight) self.nx.add_node(node, _id=node, key=node, weight=weight) weight += 1 self.graph.add_node('object') self.gn.add_node('object') self.nx.add_node('object', _id=node + 1, key='object') weight = 0 for eid in sorted(edges.keys(), key=lambda x: str(x[0])): self.graph.add_edge(*eid, weight=weight) self.gn.add_edge(*eid, weight=weight) self.nx.add_edge(*eid, weight=weight) weight += 0.05 def test_graph_shortest_path_method(self): """ Test Dijkstra shortest path method """ from networkx.algorithms.shortest_paths.generic import shortest_path from networkx.algorithms.traversal.depth_first_search import dfs_preorder_nodes print(shortest_path(self.nx, 8, 10)) print(list(dfs_preorder_nodes(self.nx, 8))) # In a mixed directed graph where 7 connects to 8 but not 8 to 7 self.assertEqual(dijkstra_shortest_path(self.graph, 8, 10), [8, 3, 4, 5, 6, 9, 10]) self.assertEqual(list(dfs_paths(self.graph, 8, 10)), [[8, 3, 4, 5, 6, 9, 10]]) self.assertEqual(list(dfs_paths(self.graph, 8, 10, method='bfs')), [[8, 3, 4, 5, 6, 9, 10]]) # Fully connect 7 and 8 self.graph.add_edge(8, 7, directed=True) self.assertEqual(dijkstra_shortest_path(self.graph, 8, 10), [8, 7, 6, 9, 10]) self.assertEqual(list(dfs_paths(self.graph, 8, 10)), [[8, 7, 6, 9, 10], [8, 3, 4, 5, 6, 9, 10]]) self.assertEqual(list(dfs_paths(self.graph, 8, 10, method='bfs')), [[8, 7, 6, 9, 10], [8, 3, 4, 5, 6, 9, 10]]) def test_graph_dfs_method(self): """ Test graph depth-first-search and breath-first-search """ # Connectivity information using Depth First Search / Breath first search self.assertListEqual(dfs(self.graph, 8), [8, 3, 4, 5, 6, 9, 11, 10, 7, 2, 1]) self.assertListEqual(dfs(self.graph, 8, method='bfs'), [8, 3, 2, 4, 1, 5, 6, 7, 9, 10, 11]) def test_graph_node_reachability_methods(self): """ Test graph algorithms """ # Test if node is reachable from other node (uses dfs internally) self.assertTrue(is_reachable(self.graph, 8, 10)) self.assertFalse(is_reachable(self.graph, 8, 12)) def test_graph_centrality_method(self): """ Test graph Brandes betweenness centrality measure """ # Return Brandes betweenness centrality self.assertDictEqual( brandes_betweenness_centrality(self.graph), { 1: 0.0, 2: 0.11538461538461538, 3: 0.26282051282051283, 4: 0.21474358974358973, 5: 0.22756410256410256, 6: 0.3205128205128205, 7: 0.0673076923076923, 8: 0.060897435897435896, 9: 0.21794871794871795, 10: 0.0, 11: 0.0, 12: 0.01282051282051282, 13: 0.0, u'object': 0.0 }) print(brandes_betweenness_centrality(self.graph, weight='weight')) print(brandes_betweenness_centrality(self.graph, normalized=False)) # Test against NetworkX if possible if self.nx is not None: from networkx.algorithms.centrality.betweenness import betweenness_centrality # Regular Brandes betweenness centrality nx_between = betweenness_centrality(self.nx) gn_between = brandes_betweenness_centrality(self.graph) self.assertDictEqual(gn_between, nx_between) # Weighted Brandes betweenness centrality nx_between = betweenness_centrality(self.nx, weight='weight') gn_between = brandes_betweenness_centrality(self.graph, weight='weight') self.assertDictEqual(gn_between, nx_between) # Normalized Brandes betweenness centrality nx_between = betweenness_centrality(self.nx, normalized=False) gn_between = brandes_betweenness_centrality(self.graph, normalized=False) self.assertDictEqual(gn_between, nx_between) def test_graph_nodes_are_interconnected(self): """ Test if all nodes directly connected with one another """ nodes = [1, 2, 3, 4, 5, 6] self.graph = Graph() self.graph.add_nodes(nodes) for edge in itertools.combinations(nodes, 2): self.graph.add_edge(*edge) self.graph.remove_edge(5, 6) self.assertTrue(nodes_are_interconnected(self.graph, [1, 2, 4])) self.assertFalse(nodes_are_interconnected(self.graph, [3, 5, 6])) def test_graph_degree(self): """ Test (weighted) degree method """ self.assertDictEqual(degree(self.graph, [1, 3, 12]), { 1: 1, 3: 3, 12: 2 }) # Directed graphs behave the same as undirected self.graph.directed = False self.assertDictEqual(degree(self.graph, [1, 3, 12]), { 1: 1, 3: 3, 12: 2 }) self.assertDictEqual(degree(self.graph, [1, 3, 12], weight='weight'), { 1: 0, 3: 1.3499999999999999, 12: 0.35000000000000003 }) # Loops counted twice self.graph.add_edge(12, 12) self.assertDictEqual(degree(self.graph, [1, 3, 12]), { 1: 1, 3: 3, 12: 4 }) self.assertDictEqual(degree(self.graph, [1, 3, 12], weight='weight'), { 1: 0, 3: 1.3499999999999999, 12: 2.3499999999999996 })
class TestGraphAddEdgeAttributes(UnittestPythonCompatibility): """ Test additional attribute storage for Graph add_edge """ def setUp(self): """ Build Graph with a few nodes but no edges yet """ self.graph = Graph() self.graph.add_nodes('graph') # Only nodes no edges yet self.assertTrue(len(self.graph) == 5) self.assertTrue(len(self.graph.nodes) == 5) self.assertTrue(len(self.graph.edges) == 0) self.assertTrue(len(self.graph.adjacency) == 5) self.assertTrue(all([len(a) == 0 for a in self.graph.adjacency.values()])) # auto_nid self.assertTrue(self.graph.data.auto_nid) def tearDown(self): """ Test state after edge attribute addition """ # If undirected, add reversed edges if not self.graph.directed: for edge, attr in list(self.edges.items()): self.edges[edge[::-1]] = attr for edge, attr in self.edges.items(): self.assertTrue(edge in self.graph.edges) self.assertDictEqual(self.graph.edges[edge], attr) def test_add_edge_no_attribute(self): """ No attributes added should yield empty dict """ self.edges = {(1, 2): {}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_single_attribute_undirected(self): """ Add a single attribute :return: """ self.edges = {(1, 2): {'weight': 2.33}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_multiple_attributes_undirected(self): """ Add a single attribute """ self.edges = {(1, 2): {'test': True, 'pv': 1.44}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_nested_attribute_undirected(self): """ Test adding nested attributed, e.a. dict in dict """ self.edges = {(1, 2): {'func': len, 'nested': {'weight': 1.22, 'leaf': True}}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_single_attribute_directed(self): """ Add a single attribute, directed :return: """ self.graph.directed = True self.edges = {(1, 2): {'weight': 2.33}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_multiple_attributes_directed(self): """ Add a single attribute, directed """ self.graph.directed = True self.edges = {(1, 2): {'test': True, 'pv': 1.44}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_nested_attribute_directed(self): """ Test adding nested attributed, e.a. dict in dict, directed """ self.graph.directed = True self.edges = {(1, 2): {'func': len, 'nested': {'weight': 1.22, 'leaf': True}}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, **attr) def test_add_edge_single_attribute_mixed(self): """ Add a single attribute, override undirected graph """ self.edges = {(1, 2): {'weight': 2.33}} for edge, attr in self.edges.items(): self.graph.add_edge(*edge, directed=True, **attr) self.graph.directed = True
class TestGraphNodeAttribute(UnittestPythonCompatibility): """ Test methods to get and set node attributes using a node storage driver. `DictStorage` is the default driver tested here. """ def setUp(self): """ Build default Graph with node and edge attributes """ self.graph = Graph() self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}), ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}), ('h', {'weight': 3.0})]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge') def test_graph_node_attr_storeget(self): """ Test getting node attributes directly from the `nodes` storage """ self.assertEqual(self.graph.nodes[1]['weight'], 1.0) self.assertEqual(self.graph.nodes[3]['value'], 'ap') def test_graph_node_attr_storeset(self): """ Test setting node attributes directly from the `nodes` storage """ self.graph.nodes[1]['weight'] = 5.0 self.graph.nodes[3]['value'] = 'dd' self.assertEqual(self.graph.nodes[1]['weight'], 5.0) self.assertEqual(self.graph.nodes[3]['value'], 'dd') def test_graph_node_attr_key_tag(self): """ Test get attributes based on `key_tag` """ self.assertEqual(self.graph.nodes[1][self.graph.data.key_tag], 'g') self.assertEqual(self.graph.nodes[3][self.graph.data.key_tag], 'a') self.assertEqual(self.graph.get(1), 'g') # uses default node data tag def test_graph_node_attr_value_tag(self): """ Test get attributes based on `value_tag` """ self.assertEqual(self.graph.nodes[1][self.graph.data.value_tag], 'gr') self.assertEqual(self.graph.nodes[3][self.graph.data.value_tag], 'ap') def test_graph_node_attr_dict(self): """ Test if the returned full attribute dictionary is of expected format """ self.assertDictEqual(self.graph.nodes[1], {'_id': 1, 'key': 'g', 'weight': 1.0, 'value': 'gr'}) self.assertDictEqual(self.graph.nodes[3], {'_id': 3, 'key': 'a', 'weight': 2.0, 'value': 'ap'}) def test_graph_node_attr_exception(self): """ Test `nodes` exception if node not present """ self.assertRaises(GraphitException, self.graph.__getitem__, 10) self.assertIsNone(self.graph.nodes.get(10)) def test_graph_node_attr_graphget(self): """ Test access node attributes by nid using the (sub)graph 'get' method """ self.assertEqual(self.graph.get(4), 'p') self.assertEqual(self.graph.get(4, 'weight'), 2.5) # Key does not exist self.assertIsNone(self.graph.get(4, key='no_key')) # Key does not exist return defaultkey self.assertEqual(self.graph.get(4, key='no_key', defaultattr='weight'), 2.5) def test_graph_node_attr_singlenode_get(self): """ Test getting node attribute values directly using the single node Graph API which has the required methods (node_tools) added to it. """ node = self.graph.getnodes(5) self.assertEqual(node['key'], 'h') self.assertEqual(node.key, 'h') self.assertEqual(node.get('key'), 'h') self.assertEqual(node['weight'], 3.0) self.assertEqual(node.weight, 3.0) self.assertEqual(node.get('weight'), 3.0) def test_graph_node_attr_singlenode_set(self): """ Test setting node attribute values directly using the single node Graph API which has the required methods (node_tools) added to it. """ node = self.graph.getnodes(5) node.weight = 5.0 node['key'] = 'z' node.set('value', True) self.assertEqual(node.nodes[5]['weight'], 5.0) self.assertEqual(node.nodes[5]['key'], 'z') self.assertEqual(node.nodes[5]['value'], True) def test_graph_node_attr_singlenode_exception(self): """ Test exceptions in direct access to node attributes in a single graph class """ node = self.graph.getnodes(5) self.assertEqual(node.get(), None) # Default get returns value, not set self.assertRaises(KeyError, node.__getitem__, 'no_key') self.assertRaises(AttributeError, node.__getattr__, 'no_key') def test_graph_nodes_dict_keys(self): """ Test graph dict-like 'keys' support. """ self.assertListEqual(self.graph.keys(), ['g', 'r', 'a', 'p', 'h']) self.assertListEqual(self.graph.keys('weight'), [1.0, 1.5, 2.0, 2.5, 3.0]) def test_graph_nodes_dict_values(self): """ Test graph dict-like 'values' support. """ self.assertListEqual(self.graph.values(), ['gr', 'ra', 'ap', 'ph', None]) self.assertItemsEqual(self.graph.values('no_value'), [None, None, None, None, None]) def test_graph_nodes_dict_items(self): """ Test graph dict-like 'items' support. """ self.assertItemsEqual(self.graph.items(), [('g', 'gr'), ('r', 'ra'), ('a', 'ap'), ('p', 'ph'), ('h', None)]) self.assertItemsEqual(self.graph.items(valuestring='_id'), [('g', 1), ('r', 2), ('a', 3), ('p', 4), ('h', 5)]) self.assertItemsEqual(self.graph.items(keystring='_id', valuestring='weight'), [(1, 1.0), (2, 1.5), (3, 2.0), (4, 2.5), (5, 3.0)])
class TestGraphEdgeAttribute(UnittestPythonCompatibility): """ Test methods to get and set edge attributes using an edge storage driver. `DictStorage` is the default driver tested here which happens to be the same as for nodes. """ def setUp(self): """ Build default Graph with node and edge attributes """ self.graph = Graph() self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}), ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}), ('h', {'weight': 3.0})]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge') def test_graph_edge_attr_storeget(self): """ Test getting edge attributes directly from the `edges` storage """ self.assertEqual(self.graph.edges[(1, 2)]['value'], True) self.assertEqual(self.graph.edges[(1, 2)]['weight'], 43.2) def test_graph_edge_attr_storeset(self): """ Test setting node attributes directly from the `edges` storage """ self.graph.edges[(1, 2)]['weight'] = 5.0 self.graph.edges[(2, 3)]['value'] = 'dd' self.assertEqual(self.graph.edges[(1, 2)]['weight'], 5.0) self.assertEqual(self.graph.edges[(2, 3)]['value'], 'dd') def test_graph_edge_attr_key_tag(self): """ Test get attributes based on `key_tag` """ self.assertEqual(self.graph.edges[(1, 2)][self.graph.data.key_tag], 'edge') self.assertEqual(self.graph.get((1, 2)), 'edge') # uses default node data tag def test_graph_edge_attr_value_tag(self): """ Test get attributes based on `value_tag` """ self.assertEqual(self.graph.edges[(4, 5)][self.graph.data.value_tag], True) def test_graph_edge_attr_dict(self): """ Test if the returned full attribute dictionary is of expected format """ self.assertDictEqual(self.graph.edges[(4, 5)], {'value': True, 'weight': 43.2, 'key': 'edge'}) def test_graph_edge_attr_exception(self): """ Test `edges` exception if edge not present """ self.assertRaises(GraphitException, self.graph.__getitem__, (5, 6)) self.assertIsNone(self.graph.nodes.get((5, 6))) def test_graph_edge_attr_graphget(self): """ Test access edge attributes by nid using the (sub)graph 'get' method """ self.assertEqual(self.graph.get((1, 2)), 'edge') self.assertEqual(self.graph.get((1, 2), 'weight'), 43.2) # Key does not exist self.assertIsNone(self.graph.get((1, 2), key='no_key')) # Key does not exist return defaultkey self.assertEqual(self.graph.get((1, 2), key='no_key', defaultattr='weight'), 43.2) def test_graph_edge_attr_singleedge_set(self): """ Test setting edge attribute values directly using the single edge Graph API which has the required methods (edge_tools) added to it. """ edge = self.graph.getedges((1, 2), directed=True) edge.weight = 4.5 edge['key'] = 'edge_set' edge.set('value', False) self.assertEqual(edge.edges[(1, 2)]['weight'], 4.5) self.assertEqual(edge.edges[(1, 2)]['key'], 'edge_set') self.assertEqual(edge.edges[(1, 2)]['value'], False) def test_graph_edge_attr_singleedge_exception(self): """ Test exceptions in direct access to edge attributes in a single graph class """ edge = self.graph.getedges((1, 2), directed=True) self.assertEqual(edge.get(), True) self.assertRaises(KeyError, edge.__getitem__, 'no_key') self.assertRaises(AttributeError, edge.__getattr__, 'no_key') def test_graph_edge_attr_undirectional(self): """ Undirectional edge has one attribute store """ # True for DictStorage but may be different for other drivers self.assertEqual(id(self.graph.edges[(1, 2)]), id(self.graph.edges[(2, 1)])) self.assertDictEqual(self.graph.edges[(1, 2)], self.graph.edges[(2, 1)]) self.graph.edges[(1, 2)]['key'] = 'edge_modified' self.assertTrue(self.graph.edges[(2, 1)]['key'] == 'edge_modified') def test_graph_edge_attr_directional(self): """ Directional edge has two separated attribute stores """ self.graph.add_edge(2, 5, directed=True, attr='to') self.graph.add_edge(5, 2, directed=True, attr='from') # True for DictStorage but may be different for other drivers self.assertNotEqual(id(self.graph.edges[(2, 5)]), id(self.graph.edges[(5, 2)])) self.assertNotEqual(self.graph.edges[(2, 5)], self.graph.edges[(5, 2)]) self.graph.edges[(5, 2)]['attr'] = 'return' self.assertTrue(self.graph.edges[(2, 5)]['attr'] == 'to') self.assertTrue(self.graph.edges[(5, 2)]['attr'] == 'return')
class TestGraphAddNodeConnected(UnittestPythonCompatibility): """ Test add_connect method for direct addition and edge connection of a new node to an existing node using a single node object """ currpath = os.path.dirname(__file__) def setUp(self): """ Build empty graph to add a node to and test default state """ self.graph = Graph(auto_nid=False) # Add two nodes self.graph.add_nodes(('one', 'two')) self.graph.add_edge('one', 'two') # Two nodes and one edge self.assertTrue(len(self.graph) == 2) self.assertTrue(len(self.graph.nodes) == 2) self.assertTrue(len(self.graph.edges) == 2) self.assertTrue(len(self.graph.adjacency) == 2) # auto_nid self.assertFalse(self.graph.data.auto_nid) def tearDown(self): """ Test state after node addition """ nids = sorted(self.graph.nodes) # The nid should equal the node self.assertTrue(self.node in nids) # The _id is still set self.assertEqual(self.graph.nodes[self.node]['_id'], 3) self.assertEqual(self.graph.data.nodeid, 4) # filled after addition self.assertTrue(len(self.graph) == 3) self.assertTrue(len(self.graph.nodes) == 3) self.assertTrue(len(self.graph.edges) == 4) self.assertTrue(len(self.graph.adjacency) == 3) # no adjacency self.assertTrue(len(self.graph.adjacency[nids[0]]) == 1) def test_add_connect_string(self): """ Test add connect a string node """ self.node = 'three' node = self.graph.getnodes('two') node.add_connect(self.node) self.assertTrue(('two', 'three') in self.graph.edges) self.assertTrue(('three', 'two') in self.graph.edges) def test_add_connect_attr(self): """ Test add connect a node with node and edge attributes """ self.node = 'three' node = self.graph.getnodes('two') node.add_connect(self.node, node_kwargs={ 'arg': True, 'n': 1.22 }, edge_kwargs={ 'arg': True, 'e': 5.44 }) # Node should contain keyword arguments self.assertTrue(self.graph.nodes[self.node]['arg']) self.assertTrue(self.graph.nodes[self.node]['n'] == 1.22) # Edges should contain keyword arguments edge = self.graph.getedges(('two', self.node)) self.assertTrue(all(e.get('arg', False) for e in edge.edges.values())) self.assertTrue(all(e.get('e') == 5.44 for e in edge.edges.values()))