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))
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
def test_graph_directional_to_undirectional(self): """ Test convert a directional to undirectional graph. Returns a deep copy. """ # Already undirectional ug = graph_directional_to_undirectional(self.graph) self.assertFalse(ug.directed) self.assertEqual(ug, self.graph) # Make a directed graph dg = Graph(directed=True) dg.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (6, 2)], node_from_edge=True, arg1=1.22, arg2=False) dg.add_edges([(3, 2), (2, 6)], arg1=2.66) ug2 = graph_directional_to_undirectional(dg) self.assertFalse(ug2.directed) self.assertEqual(ug2, self.graph) # Attribute overwrite. Different dict update behaviour between PY 2/3 if MAJOR_PY_VERSION == 2: self.assertEqual(ug2.edges[(2, 3)]['arg1'], 2.66) self.assertEqual(ug2.edges[(3, 2)]['arg1'], 2.66) else: self.assertEqual(ug2.edges[(2, 3)]['arg1'], 1.22) self.assertEqual(ug2.edges[(3, 2)]['arg1'], 1.22) undirectional_checked = [] for edge in ug2.edges: undirectional_checked.append( id(ug2.edges[edge]) == id(ug2.edges[(edge[1], edge[0])])) self.assertTrue(all(undirectional_checked))
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 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 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 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 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]))
class TestGraphMixed(UnittestPythonCompatibility): """ Test graph with mixed directed and undirected edges 1 - 2 - 3 - 6 | / | 4 - 5 """ def setUp(self): """ Build mixed directed and undirected Graph """ self.graph = Graph(directed=True) self.graph.add_edges([(1, 2), (2, 3), (3, 2), (3, 6), (3, 5), (5, 4), (4, 5), (4, 2)], node_from_edge=True, arg1=1.22, arg2=False) self.graph.add_edge(3, 4, directed=False, node_from_edge=True, arg1=1.22, arg2=False) for i, edge in enumerate(self.graph.iteredges()): edge['nd'] = i def test_graph_is_mixed(self): self.assertTrue(self.graph.directed) self.assertEqual(graph_directionality(self.graph), 'mixed') self.assertEqual( sum([1 for edge in self.graph.iteredges() if edge.is_directed]), 4) self.assertEqual( sum([1 for edge in self.graph.iteredges() if not edge.is_directed]), 6) self.assertTrue(len(self.graph.edges) == 10) def test_graph_contains(self): """ Test a mix of directed and undirected (pairs) edges """ directed = [edge.is_directed for edge in self.graph.iteredges()] self.assertEqual( directed, [True, False, False, False, True, True, True, False, False, False]) def test_graph_directional_to_undirectional(self): """ Test conversion of a directional to undirectional graph """ undirectional = graph_directional_to_undirectional(self.graph) # directed attribute changed to True self.assertFalse(undirectional.directed) self.assertNotEqual(undirectional, self.graph) # data reference pointers determine directionality self.assertEqual(graph_directionality(undirectional), 'undirectional') self.assertEqual( graph_directionality(undirectional, has_data_reference=False), 'undirectional') # all edges exist in pairs resulting duplicated values values = undirectional.edges(data='nd').values() self.assertEqual(len(values), len(set(values)) * 2)
class TestGraphUndirectional(UnittestPythonCompatibility): """ Test graph with undirected edges """ def setUp(self): """ Build undirected Graph """ self.graph = Graph() self.graph.add_edges([(1, 2), (2, 3), (2, 4), (4, 5), (4, 3), (3, 5), (3, 6)], node_from_edge=True, arg1=1.22, arg2=False) def test_graph_is_undirected(self): self.assertFalse(self.graph.directed) self.assertEqual(graph_directionality(self.graph), 'undirectional') self.assertTrue( all([not edge.is_directed for edge in self.graph.iteredges()])) self.assertTrue(len(self.graph.edges) == 14) # 2 * 7 def test_graph_contains(self): """ Test if pair of directed edges is contained in undirected edge """ for edge in self.graph.edges: self.assertTrue(edge in self.graph.edges) self.assertTrue(tuple(reversed(edge)) in self.graph.edges) def test_graph_adjacency(self): """ Node adjacency in a undirected graph reflects the pairs of directed edges that exists between nodes. This is also seen in the adjacency 'link count' and 'degree' metrics for nodes. """ # Number of edges equals the link count of the full node adjacency self.assertEqual(len(self.graph.edges), self.graph.adjacency.link_count()) # Undirected degree is bidirectional. It equals the number of # connected edges to a node degree = self.graph.adjacency.degree() for node in self.graph: self.assertEqual(degree[node.nid], len(node.connected_edges())) def test_graph_degree(self): """ Total degree equals sum of equal inwards and outwards degree """ degree = self.graph.adjacency.degree() indegree = self.graph.adjacency.degree(method='indegree') outdegree = self.graph.adjacency.degree(method='outdegree') self.assertDictEqual(indegree, outdegree) self.assertEqual(sum(indegree.values()) * 2, sum(degree.values())) def test_graph_edge_removal_undirected(self): """ Undirected edge removal removes pair of directed edges """ self.graph.remove_edge(2, 3) self.assertFalse((2, 3) in self.graph.edges) self.assertFalse((3, 2) in self.graph.edges) self.assertEqual(len(self.graph.edges), 12) def test_graph_edge_removal_directed(self): """ Directed removal in a undirected graph is supported """ self.graph.remove_edge(2, 3, directed=True) self.assertFalse((2, 3) in self.graph.edges) self.assertTrue((3, 2) in self.graph.edges) self.assertEqual(len(self.graph.edges), 13) # globally still marked as undirected but of mixed type self.assertFalse(self.graph.directed) self.assertEqual(graph_directionality(self.graph), 'mixed') def test_graph_edge_removal_directed_data_reference(self): """ Test resolving data references after directed edge removal in a undirected graph to prevent orphan pointers """ # Before removal values are referenced self.assertDictEqual(self.graph.edges[(2, 3)], self.graph.edges[(2, 3)]) self.assertEqual(id(self.graph.edges[(2, 3)]), id(self.graph.edges[(2, 3)])) # Remove parent edge copies data to referencing edge self.graph.remove_edge(2, 3, directed=True) self.assertTrue( self.graph.edges._data_pointer_key not in self.graph.edges[(3, 2)]) self.assertDictEqual(self.graph.edges[(3, 2)], { 'arg1': 1.22, 'arg2': False }) # Remove referencing edge does not change anything self.graph.remove_edge(4, 2, directed=True) self.assertTrue( self.graph.edges._data_pointer_key not in self.graph.edges[(2, 4)]) self.assertDictEqual(self.graph.edges[(2, 4)], { 'arg1': 1.22, 'arg2': False }) def test_graph_undirectional_to_directional(self): """ Test conversion of a undirectional to directional graph Conversion essentially breaks all linked edge pairs by removing the data reference pointer. """ directional = graph_undirectional_to_directional(self.graph) # directed attribute changed to True self.assertTrue(directional.directed) self.assertNotEqual(id(directional), id(self.graph)) # directional graph still contains same nodes and edges self.assertEqual(directional, self.graph) # data reference pointers determine directionality self.assertEqual(graph_directionality(directional), 'directional') self.assertEqual( graph_directionality(directional, has_data_reference=False), 'undirectional') # directional edge pair no longer point to same value directional_checked = [] for edge in directional.edges: directional_checked.append( id(directional.edges[edge]) != id(directional.edges[( edge[1], edge[0])])) self.assertTrue(all(directional_checked)) def test_graph_undirected_linked_values(self): """ Test setting and getting linked edge data """ self.graph.edges[(2, 3)]['test'] = True # In storage backend only one edge of the pair has the data self.assertTrue('test' in self.graph.edges._storage[(2, 3)]) self.assertFalse('test' in self.graph.edges._storage[(3, 2)]) # transparent getting and setting of linked data self.assertTrue(self.graph.edges[(2, 3)]['test']) self.assertTrue(self.graph.edges[(3, 2)]['test']) self.graph.edges[(3, 2)]['test'] = False self.assertFalse(self.graph.edges[(2, 3)]['test']) self.assertFalse(self.graph.edges[(3, 2)]['test'])
class TestGraphDirectional(UnittestPythonCompatibility): """ Test graph with directed edges 1 - 2 - 3 - 6 | / | 4 - 5 """ def setUp(self): """ Build directed Graph """ self.graph = Graph(directed=True) self.graph.add_edges([(1, 2), (2, 3), (3, 6), (3, 5), (5, 4), (4, 3), (4, 2)], node_from_edge=True, arg1=1.22, arg2=False) self.graph.edges[(3, 6)]['arg1'] = 2.44 self.graph.edges[(5, 4)]['arg3'] = 'test' def test_graph_is_directed(self): self.assertTrue(self.graph.directed) self.assertEqual(graph_directionality(self.graph), 'directional') self.assertTrue( all([edge.is_directed for edge in self.graph.iteredges()])) self.assertTrue(len(self.graph.edges) == 7) def test_graph_contains(self): """ Only forward edge present """ for edge in self.graph.edges: self.assertTrue(edge in self.graph.edges) self.assertFalse(tuple(reversed(edge)) in self.graph.edges) def test_graph_adjacency(self): """ Node adjacency in a directed graph reflects the presence of only a forward edge connecting nodes. This is also seen in the adjacency 'link count' and 'degree' metrics for nodes. """ # Number of edges equals the link count of the full node adjacency self.assertEqual(len(self.graph.edges), self.graph.adjacency.link_count()) # Directed degree is unidirectional. It equals the number of # connected edges to a node degree = self.graph.adjacency.degree() for node in self.graph: self.assertEqual(degree[node.nid], len(node.connected_edges())) def test_graph_degree(self): """ The total degree equals the sum of inwards and outwards degrees but the latter two are not equals """ degree = self.graph.adjacency.degree() indegree = self.graph.adjacency.degree(method='indegree') outdegree = self.graph.adjacency.degree(method='outdegree') self.assertEqual(sum(degree.values()), sum(indegree.values()) + sum(outdegree.values())) self.assertNotEqual(indegree, outdegree) def test_graph_directional_to_undirectional(self): """ Test conversion of a directional to undirectional graph """ undirectional = graph_directional_to_undirectional(self.graph) # directed attribute changed to True self.assertFalse(undirectional.directed) self.assertNotEqual(undirectional, self.graph) # data reference pointers determine directionality self.assertEqual(graph_directionality(undirectional), 'undirectional') self.assertEqual( graph_directionality(undirectional, has_data_reference=False), 'undirectional') # directional edge pair point to same value undirectional_checked = [] for edge in undirectional.edges: undirectional_checked.append( id(undirectional.edges[edge]) == id(undirectional.edges[( edge[1], edge[0])])) self.assertTrue(all(undirectional_checked)) # edge argument equality self.assertDictEqual(undirectional.edges[(3, 6)], { 'arg1': 2.44, 'arg2': False }) self.assertDictEqual(undirectional.edges[(6, 3)], { 'arg1': 2.44, 'arg2': False }) self.assertDictEqual(undirectional.edges[(5, 4)], { 'arg1': 1.22, 'arg2': False, 'arg3': 'test' }) self.assertDictEqual(undirectional.edges[(4, 5)], { 'arg1': 1.22, 'arg2': False, 'arg3': 'test' })
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 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 TestGraphRemoveEdges(UnittestPythonCompatibility): """ Test removal of edges in directed and undirected way """ def setUp(self): """ Build Graph with nodes and edges """ self.graph = Graph() self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], node_from_edge=True) self.assertTrue(len(self.graph) == 5) self.assertTrue(len(self.graph.nodes) == 5) self.assertTrue(len(self.graph.edges) == 10) self.assertTrue(len(self.graph.adjacency) == 5) def tearDown(self): """ Test state after edge removal """ if self.edges: # 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 removed self.assertTrue(edge not in self.graph.edges) # Nodes connected should still be there self.assertTrue(all([node in self.graph.nodes for node in edge])) # Adjacency should be corrected self.assertTrue(edge[1] not in self.graph.adjacency[edge[0]]) # If directional, reverse edge still in graph if self.graph.directed: rev_edge = edge[::-1] if rev_edge not in self.edges: self.assertTrue(rev_edge 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) == 10 - len(self.edges)) self.assertTrue(len(self.graph.adjacency) == 5) def test_remove_edge_single_undirected(self): """ Test removal of single undirected edge """ self.edges = [(1, 2)] self.graph.remove_edge(*self.edges[0]) def test_remove_edge_single_directed(self): """ Test removal of single directed edge """ self.graph.directed = True self.edges = [(1, 2)] self.graph.remove_edge(*self.edges[0]) def test_remove_edge_multiple_undirected(self): """ Test removal of multiple undirected edges """ self.edges = [(1, 2), (2, 3), (4, 5)] self.graph.remove_edges(self.edges) def test_remove_edge_multiple_directed(self): """ Test removal of multiple directed edges """ self.graph.directed = True self.edges = [(1, 2), (2, 3), (4, 5)] self.graph.remove_edges(self.edges) def test_remove_edge_single_mixed(self): """ Test removal of a single directed edge in a global undirected graph using local override of directionality """ self.edges = [(1, 2)] self.graph.remove_edge(*self.edges[0], directed=True) self.graph.directed = True def test_remove_edge_multiple_mixed(self): """ Test removal of multiple directed edges in a global undirected graph using local override of directionality """ self.edges = [(1, 2), (2, 3), (4, 5)] self.graph.remove_edges(self.edges, directed=True) self.graph.directed = True def test_graph_clear(self): """ Test clear method to removal all nodes and edges """ self.edges = [] 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 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 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)