def setUp(self): """ Build Graph with a few nodes but no edges yet """ self.graph = Graph() self.graph.add_nodes('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 setUp(self): self.graph = Graph() self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (6, 2)], node_from_edge=True, arg1=1.22, arg2=False)
class TestGraphAddNodeAttributes(UnittestPythonCompatibility): """ Test additional attribute storage for Graph add_node """ 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 """ self.attr.update({'_id': 1, self.graph.key_tag: 10}) self.assertDictEqual(self.graph.nodes[1], self.attr) def test_add_node_no_attribute(self): """ No attributes added should yield the default '_id' and 'key' """ self.attr = {} self.graph.add_node(10, **self.attr) def test_add_node_single_attribute(self): """ Add a single attribute """ self.attr = {'weight': 2.33} self.graph.add_node(10, **self.attr) def test_add_node_multiple_attribute(self): """ Add a multiple attributes """ self.attr = {'test': True, 'pv': 1.44} self.graph.add_node(10, **self.attr) def test_add_node_protected_attribute(self): """ The '_id' attribute is protected """ self.attr = {} self.graph.add_node(10, _id=5) def test_add_node_nested_attribute(self): """ Test adding nested attributed, e.a. dict in dict """ self.attr = {'func': len, 'nested': {'weight': 1.22, 'leaf': True}} self.graph.add_node(10, **self.attr)
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 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 read_pgf(pgf_file, graph=None, pickle_graph=False): """ Import graph from Graph Python Format file PGF format is the modules own file format consisting of a serialized graph data, nodes and edges dictionaries. Import either as plain text serialized dictionary or pickled graph object. The format is feature rich with good performance but is not portable. :param pgf_file: PGF data to parse :type pgf_file: File, string, stream or URL :param graph: Graph object to import to or Graph by default :type graph: :graphit:Graph :param pickle_graph: PGF format is a pickled graph :type pickle_graph: :py:bool :return: Graph instance :rtype: :graphit:Graph """ # Unpickle pickled PGF format if pickle_graph: pgf_file = open_anything(pgf_file, mode='rb') pgraph = pickle.load(pgf_file) # Transfer data from unpickled graph to graph if defined if graph: graph.origin.nodes, graph.origin.edges, graph.origin.adjacency, graph.origin.data = graph.storagedriver( pgraph.nodes, pgraph.edges, pgraph.data) return graph return pgraph pgf_file = open_anything(pgf_file) # Import graph from serialized Graph Python Format if graph is None: graph = Graph() elif not isinstance(graph, Graph): raise GraphitException('Unsupported graph type {0}'.format( type(graph))) pgf_eval = ast.literal_eval(pgf_file.read()) if not isinstance(pgf_eval, dict): raise GraphitException('Invalid PGF file format') missing_data = [d for d in pgf_file if d not in ('data', 'nodes', 'edges')] if missing_data: raise GraphitException( 'Invalid PGF file format, missing required attributes: {0}'.format( ','.join(missing_data))) graph.origin.nodes, graph.origin.edges, graph.origin.adjacency, graph.origin.data = graph.storagedriver( pgf_eval['nodes'], pgf_eval['edges'], pgf_eval['data']) return graph
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_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)
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 = [(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)] self.graph.add_edges(self.edges, node_from_edge=True)
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 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 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 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 setUp(self): """ Build empty graph to add a node to and test default state """ self.graph = Graph(auto_nid=False) # 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.assertFalse(self.graph.auto_nid)
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 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 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 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 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 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
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 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_gml(gml, graph=None): """ Read graph in GML format :param gml: GML graph data. :type gml: File, string, stream or URL :param graph: Graph object to import GML data in :type graph: :graphit:Graph :return: Graph object :rtype: :graphit:Graph """ # 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))) # Parse GML into nested structure of Record class instances gml_stream = StreamReader(open_anything(gml)) records = Record(gml_stream, name='root') gml_graphs = [g for g in records if g.name == 'graph'] if len(gml_graphs) > 1: logging.warning("GML file contains {0} 'graph' objects. Only parse first".format(len(gml_graphs))) gml_graph_record = gml_graphs[0] # GML node and edge labels are unique, turn off auto_nid graph.data['auto_nid'] = False # Set graph meta-data and attributes graph_attr = gml_graph_record.to_dict({}) graph.directed = True if 'directed' in graph_attr: directed = graph_attr.pop('directed') graph.directed = True if directed == 1 else False graph.data['directed'] = graph.directed graph.data.update(graph_attr) # Build graph from records build_nodes(graph, gml_graph_record) build_edges(graph, gml_graph_record) return graph
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 read_graph(graph_file, graph=None): """ Import graph from Graph Python Format file GPF format is the modules own file format consisting out of a serialized nodes and edges dictionary. The format is feature rich wth good performance but is not portable. :param graph_file: File path to read from :type graph_file: :py:str :param graph: Graph object to import to or Graph by default :type graph: Graph instance :return: Graph instance """ if not graph: graph = Graph() # Import graph from serialized Graph Python Format with open(graph_file) as f: code = compile(f.read(), "GPF_file", 'exec') exec(code) 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 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 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)