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])
Example #4
0
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')
Example #5
0
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
Example #6
0
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)])
Example #7
0
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)
Example #10
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)
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]))
Example #12
0
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))
Example #15
0
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)
Example #16
0
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
        })
Example #18
0
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()))