Esempio n. 1
0
 def setUp(self):
     self.g1 = Graph()
     self.g1.add_nodes('a', 'b', 'c', 'd', 'e')
     self.g1.add_edge('a', 'b', weight=10)
     self.g1.add_edge('a', 'c', weight=3)
     self.g1.add_edge('b', 'c', weight=1)
     self.g1.add_edge('b', 'd', weight=2)
     self.g1.add_edge('c', 'b', weight=4)
     self.g1.add_edge('c', 'd', weight=8)
     self.g1.add_edge('c', 'e', weight=2)
     self.g1.add_edge('d', 'e', weight=7)
     self.g1.add_edge('e', 'd', weight=9)
Esempio n. 2
0
    def setUp(self):
        self.g1 = Undirected(Graph())
        self.g1.add_nodes('a', 'b', 'c')
        self.g1.add_edge('a', 'b')
        self.g1.add_edge('b', 'c')

        self.g_directed = Graph()
        self.g_directed.add_nodes(1, 2, 3, 4, 5)
        self.g_directed.add_edge(1, 2)
        self.g_directed.add_edge(1, 3)
        self.g_directed.add_edge(3, 2)
        self.g_directed.add_edge(2, 3)
        self.g_directed.add_edge(3, 4)
        self.g_directed.add_edge(4, 3)

        self.g2 = Undirected(self.g_directed)
Esempio n. 3
0
def topological_sort(graph: Graph, key: Callable[[Node], int] = None)\
        -> List[Node]:
    """
    Topologically sort this graph by repeatedly removing a node with no
    incoming edges and all of its outgoing edges and adding it to the order.
    Total runtime: O(|E|) + O(|V|) + O(|V|) = O(|V| + |E|).
    TODO: Check total runtime

    https://courses.cs.washington.edu/courses/cse326/03wi/lectures/RaoLect20.pdf

    :param graph: the graph to operate on
    :param key: a function of one argument used to extract a comparison key
        to determine which node to visit first (the "smallest" element)
    :return: a topological ordering of the given graph
    """
    # TODO: Raise exception if not DAG
    # TODO: Implement using DFS
    # TODO: Implement using priority queue

    # the number of incoming edges for each node: O(|E|)
    in_degrees = {v: len(graph.parents(v)) for v in graph.nodes()}
    # the nodes ready to be removed: O(|V|)
    ready = [v for v in graph.nodes() if in_degrees[v] == 0]
    # the topological ordering
    order = []

    def pop_min(l):
        if key:
            _, idx = min([(ready[i], i) for i in range(len(ready))],
                         key=key)
            return l.pop(idx)
        else:
            _, idx = min((ready[i], i) for i in range(len(ready)))
            return l.pop(idx)

    # dequeue and output: O(|V|)?
    while ready:
        u = pop_min(ready)
        order.append(u)

        for v in graph.neighbors(u):
            in_degrees[v] -= 1
            if in_degrees[v] == 0:
                ready.append(v)

    return order
Esempio n. 4
0
    def setUp(self):
        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c')
        self.g1.add_nodes('x', 'y')
        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u')
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Undirected(Graph())
        self.g2.add_nodes('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 's')
        self.g2.add_edge('a', 'b')
        self.g2.add_edge('a', 's')
        self.g2.add_edge('s', 'c')
        self.g2.add_edge('s', 'g')
        self.g2.add_edge('c', 'd')
        self.g2.add_edge('c', 'e')
        self.g2.add_edge('c', 'f')
        self.g2.add_edge('f', 'g')
        self.g2.add_edge('g', 'h')
        self.g2.add_edge('h', 'e')
Esempio n. 5
0
def post_order(graph: Graph, v: Node) -> List[Node]:
    """
    Compute the post-order of the given graph using a depth-first search from v.
    Total runtime: O(|V| + |E|) time due to DFS.

    :param graph: the graph to operate on
    :param v: the node to search from
    :return: a list of nodes in the order they were done being processed
    :raises ValueError: if v is not a defined node in graph
    """
    if v not in graph.nodes():
        raise ValueError(f'node {v} is not defined')

    order = [node for node in DepthFirstIterator(graph, v)]
    order.reverse()
    return order
Esempio n. 6
0
    def setUp(self):
        self.g_empty = Graph()

        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c', 'x', 'y')

        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u')
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Graph()
        self.g2.add_nodes('a', 'b', 'c', 'd')

        self.g2.add_edge('a', 'b')
        self.g2.add_edge('a', 'd')
        self.g2.add_edge('b', 'd')
        self.g2.add_edge('c', 'd')

        self.g3 = Undirected(Graph())
        self.g3.add_nodes('a', 'b', 'c')
        self.g3.add_nodes('x', 'y', 'z')

        self.g3.add_edge('a', 'b')
        self.g3.add_edge('a', 'c')
        self.g3.add_edge('c', 'b')

        self.g3.add_edge('x', 'y')
        self.g3.add_edge('y', 'z')

        self.g4 = Graph()
        self.g4.add_nodes('a', 'b', 'c', 'd', 'e')
        self.g4.add_edge('a', 'b', weight=10)
        self.g4.add_edge('a', 'c', weight=3)
        self.g4.add_edge('b', 'c', weight=1)
        self.g4.add_edge('b', 'd', weight=2)
        self.g4.add_edge('c', 'b', weight=4)
        self.g4.add_edge('c', 'd', weight=8)
        self.g4.add_edge('c', 'e', weight=2)
        self.g4.add_edge('d', 'e', weight=7)
        self.g4.add_edge('e', 'd', weight=9)
Esempio n. 7
0
    def test_copy_constructor_mutable_node(self):
        class A:
            def __init__(self, v):
                self.v = v

        obj_node = A(['mutable', 'list'])
        self.g1.add_node(obj_node)
        self.g1.add_edge('y', obj_node)

        g1_copy = Graph(self.g1)

        self.assertEqual(self.g1, g1_copy)
        self.assertFalse(self.g1 is g1_copy)

        # ensure that obj_node is updated everywhere (deep copy will not work)
        obj_node.v.append('new')

        self.assertEqual(self.g1, g1_copy)
        self.assertFalse(self.g1 is g1_copy)
Esempio n. 8
0
    def setUp(self):
        self.g_empty = Graph()

        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c', 'x', 'y')
        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u', weight=10)
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Graph()
        self.g2.add_nodes('a', 'b', 'c', 'd')
        self.g2.add_edge('a', 'b', weight=5)
        self.g2.add_edge('a', 'd', weight=-2)
        self.g2.add_edge('b', 'd')
        self.g2.add_edge('c', 'd')
Esempio n. 9
0
class TestGraphAlgorithms(unittest.TestCase):
    """
    Tests for graph algorithms.
    """
    def setUp(self):
        self.g_empty = Graph()

        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c', 'x', 'y')

        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u')
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Graph()
        self.g2.add_nodes('a', 'b', 'c', 'd')

        self.g2.add_edge('a', 'b')
        self.g2.add_edge('a', 'd')
        self.g2.add_edge('b', 'd')
        self.g2.add_edge('c', 'd')

        self.g3 = Undirected(Graph())
        self.g3.add_nodes('a', 'b', 'c')
        self.g3.add_nodes('x', 'y', 'z')

        self.g3.add_edge('a', 'b')
        self.g3.add_edge('a', 'c')
        self.g3.add_edge('c', 'b')

        self.g3.add_edge('x', 'y')
        self.g3.add_edge('y', 'z')

        self.g4 = Graph()
        self.g4.add_nodes('a', 'b', 'c', 'd', 'e')
        self.g4.add_edge('a', 'b', weight=10)
        self.g4.add_edge('a', 'c', weight=3)
        self.g4.add_edge('b', 'c', weight=1)
        self.g4.add_edge('b', 'd', weight=2)
        self.g4.add_edge('c', 'b', weight=4)
        self.g4.add_edge('c', 'd', weight=8)
        self.g4.add_edge('c', 'e', weight=2)
        self.g4.add_edge('d', 'e', weight=7)
        self.g4.add_edge('e', 'd', weight=9)

    def test_post_order(self):
        self.assertRaises(ValueError, post_order, self.g1, 'fake')

        acceptable_orders = [['a', 'b', 'c', 'u'], ['a', 'c', 'b', 'u'],
                             ['b', 'c', 'a', 'u'], ['b', 'a', 'c', 'u'],
                             ['c', 'a', 'b', 'u'], ['c', 'b', 'a', 'u']]
        actual = post_order(self.g1, 'u')
        self.assertTrue(actual in acceptable_orders)
        self.assertEqual(['y', 'x'], post_order(self.g1, 'x'))

    def test_topological_sort(self):
        self.assertEqual([], topological_sort(self.g_empty))

        g2_order = topological_sort(self.g2)
        for u, v in self.g2.edges():
            self.assertTrue(g2_order.index(u) < g2_order.index(v))

        # TODO: Test key

    def test_count_components(self):
        self.assertTrue(
            tuple(components(self.g3)) in itertools.permutations(
                [{'a', 'b', 'c'}, {'x', 'y', 'z'}]))
        # TODO: More tests

    def test_shortest_path(self):
        self.assertEqual(['a', 'c', 'b', 'd'],
                         shortest_path(self.g4, 'a', 'd'))

    def test_distance(self):
        self.assertEqual(9, distance(self.g4, 'a', 'd'))
Esempio n. 10
0
class TestBreadthFirstIterator(unittest.TestCase):
    """
    Tests for BreadthFirstIterator.
    """
    def setUp(self):
        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c')
        self.g1.add_nodes('x', 'y')
        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u')
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Undirected(Graph())
        self.g2.add_nodes('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 's')
        self.g2.add_edge('a', 'b')
        self.g2.add_edge('a', 's')
        self.g2.add_edge('s', 'c')
        self.g2.add_edge('s', 'g')
        self.g2.add_edge('c', 'd')
        self.g2.add_edge('c', 'e')
        self.g2.add_edge('c', 'f')
        self.g2.add_edge('f', 'g')
        self.g2.add_edge('g', 'h')
        self.g2.add_edge('h', 'e')

    def test_iterator(self):
        g1_u = list(BreadthFirstIterator(self.g1, 'u'))
        g1_x = list(BreadthFirstIterator(self.g1, 'x'))

        self.assertEqual(['u', 'a', 'c', 'b'], g1_u)
        self.assertEqual(['x', 'y'], g1_x)

        g2_a = list(BreadthFirstIterator(self.g2, 'a'))

        self.assertEqual(['a', 'b', 's', 'c', 'g', 'd', 'e', 'f', 'h'], g2_a)

    def test_key(self):
        # reverse alphabetical order
        g1_u = list(
            BreadthFirstIterator(self.g1, 'u', key=lambda x: -1 * ord(x)))
        g2_a = list(
            BreadthFirstIterator(self.g2, 'a', key=lambda x: -1 * ord(x)))

        self.assertEqual(['u', 'c', 'a', 'b'], g1_u)
        self.assertEqual(['a', 's', 'b', 'g', 'c', 'h', 'f', 'e', 'd'], g2_a)
Esempio n. 11
0
class TestDijkstraIterator(unittest.TestCase):
    def setUp(self):
        self.g1 = Graph()
        self.g1.add_nodes('a', 'b', 'c', 'd', 'e')
        self.g1.add_edge('a', 'b', weight=10)
        self.g1.add_edge('a', 'c', weight=3)
        self.g1.add_edge('b', 'c', weight=1)
        self.g1.add_edge('b', 'd', weight=2)
        self.g1.add_edge('c', 'b', weight=4)
        self.g1.add_edge('c', 'd', weight=8)
        self.g1.add_edge('c', 'e', weight=2)
        self.g1.add_edge('d', 'e', weight=7)
        self.g1.add_edge('e', 'd', weight=9)

    def test_iterator(self):
        g1_a = list(DijkstraIterator(self.g1, 'a'))

        self.assertEqual([('a', 0), ('c', 3), ('e', 5), ('b', 7), ('d', 9)],
                         g1_a)
Esempio n. 12
0
class TestGraph(unittest.TestCase):
    """
    Tests for Graph (directed).
    """
    def setUp(self):
        self.g_empty = Graph()

        self.g1 = Graph()
        self.g1.add_nodes('u', 'a', 'b', 'c', 'x', 'y')
        self.g1.add_edge('u', 'a')
        self.g1.add_edge('a', 'u', weight=10)
        self.g1.add_edge('u', 'c')
        self.g1.add_edge('c', 'a')
        self.g1.add_edge('c', 'b')
        self.g1.add_edge('b', 'u')
        self.g1.add_edge('x', 'y')

        self.g2 = Graph()
        self.g2.add_nodes('a', 'b', 'c', 'd')
        self.g2.add_edge('a', 'b', weight=5)
        self.g2.add_edge('a', 'd', weight=-2)
        self.g2.add_edge('b', 'd')
        self.g2.add_edge('c', 'd')

    def test_copy_constructor(self):
        g1_copy = Graph(self.g1)

        self.assertEqual(self.g1.nodes(), g1_copy.nodes())
        self.assertEqual(self.g1.edges(), g1_copy.edges())

        # changes made to g1_copy should not affect self.g1
        g1_copy.add_node('new1')
        g1_copy.add_edge('a', 'new1')

        self.assertTrue('new1' in g1_copy.nodes())
        self.assertTrue('new1' in g1_copy.neighbors('a'))
        self.assertTrue('new1' not in self.g1.nodes())
        self.assertTrue('new1' not in self.g1.neighbors('a'))

        # changes made to self.g1 should not affect g1_copy
        self.g1.add_node('new2')
        self.g1.add_edge('b', 'new2')

        self.assertTrue('new2' in self.g1.nodes())
        self.assertTrue('new2' in self.g1.neighbors('b'))
        self.assertTrue('new2' not in g1_copy.nodes())
        self.assertTrue('new2' not in g1_copy.neighbors('b'))

    def test_copy_constructor_mutable_node(self):
        class A:
            def __init__(self, v):
                self.v = v

        obj_node = A(['mutable', 'list'])
        self.g1.add_node(obj_node)
        self.g1.add_edge('y', obj_node)

        g1_copy = Graph(self.g1)

        self.assertEqual(self.g1, g1_copy)
        self.assertFalse(self.g1 is g1_copy)

        # ensure that obj_node is updated everywhere (deep copy will not work)
        obj_node.v.append('new')

        self.assertEqual(self.g1, g1_copy)
        self.assertFalse(self.g1 is g1_copy)

    def test_weight_undefined_edge(self):
        self.assertRaises(ValueError, self.g2.weight, 'd', 'b')

    def test_weight_undefined_node(self):
        self.assertRaises(ValueError, self.g2.weight, 'd', 'x')
        self.assertRaises(ValueError, self.g2.weight, 'x', 'a')
        self.assertRaises(ValueError, self.g2.weight, 'x', 'y')

    def test_weight(self):
        self.assertEqual(1, self.g1.weight('u', 'a'))
        self.assertEqual(10, self.g1.weight('a', 'u'))
        self.assertEqual(5, self.g2.weight('a', 'b'))
        self.assertEqual(-2, self.g2.weight('a', 'd'))
        self.assertEqual(1, self.g2.weight('b', 'd'))
        self.assertEqual(1, self.g2.weight('c', 'd'))

    def test_parents_undefined_node(self):
        self.assertRaises(ValueError, self.g1.parents, 'z')

    def test_parents(self):
        self.assertEqual({'a', 'b'}, self.g1.parents('u'))
        self.assertEqual({'u', 'c'}, self.g1.parents('a'))
        self.assertEqual({'c'}, self.g1.parents('b'))
        self.assertEqual({'u'}, self.g1.parents('c'))
        self.assertEqual(set(), self.g1.parents('x'))
        self.assertEqual({'x'}, self.g1.parents('y'))

    def test_neighbors_undefined_node(self):
        self.assertRaises(ValueError, self.g1.neighbors, 'z')

    def test_neighbors(self):
        self.assertEqual({'a', 'c'}, self.g1.neighbors('u'))
        self.assertEqual({'u'}, self.g1.neighbors('a'))
        self.assertEqual({'u'}, self.g1.neighbors('b'))
        self.assertEqual({'a', 'b'}, self.g1.neighbors('c'))
        self.assertEqual({'y'}, self.g1.neighbors('x'))
        self.assertEqual(set(), self.g1.neighbors('y'))

    def tst_add_node_defined_node(self):
        self.assertRaises(ValueError, self.g1.add_node, 'u')
        self.assertRaises(ValueError, self.g1.add_node, 'x')

        self.g_empty.add_node(True)

        self.assertRaises(ValueError, self.g_empty.add_node, 1)  # True == 1

    def test_add_node(self):
        self.g_empty.add_node(5)
        self.g_empty.add_node('hello')
        self.g_empty.add_node(True)

        self.assertEqual({5, 'hello', True}, self.g_empty.nodes())

    def test_add_nodes_defined_nodes(self):
        self.assertRaises(ValueError, self.g2.add_nodes, 'f', 'c', 'g')

        # no nodes should be added if add_nodes raises an exception
        self.assertEqual({'a', 'b', 'c', 'd'}, self.g2.nodes())

    def test_add_nodes(self):
        onebyone = Graph()
        onebyone.add_node('a')
        onebyone.add_node('b')
        onebyone.add_node('c')

        allatonce = Graph()
        allatonce.add_nodes('a', 'b', 'c')

        self.assertEqual(onebyone.nodes(), allatonce.nodes())

    # def test_add_edge_

    def test_add_edge(self):
        self.assertRaises(ValueError, self.g_empty.add_edge, 'fake1', 'fake2')

        self.g_empty.add_node(1)
        self.g_empty.add_node(2)
        self.g_empty.add_edge(1, 2)

        self.assertEqual(self.g_empty._a_in, {1: set(), 2: {1}})
        self.assertEqual(self.g_empty._a_out, {1: {2}, 2: set()})

        self.assertRaises(ValueError, self.g_empty.add_edge, 1, 2)

    def test_add_edge_undefined_node(self):
        self.assertRaises(ValueError, self.g_empty.add_edge, 'hello', 'world')

    def test_remove_node(self):
        self.g1.remove_node('x')

        self.assertEqual({'u', 'a', 'b', 'c', 'y'}, self.g1.nodes())
        self.assertRaises(ValueError, self.g1.remove_node, 'z')

    def test_remove_edge(self):
        before = self.g1.edges()
        self.g1.remove_edge('a', 'u')
        after = self.g1.edges()

        # reverse edge ('u', 'a') still remains
        self.assertEqual(after, before.difference({('a', 'u')}))
        self.assertRaises(ValueError, self.g1.remove_edge, 'u', 'x')
Esempio n. 13
0
    def test_copy_constructor(self):
        g1_copy = Graph(self.g1)

        self.assertEqual(self.g1.nodes(), g1_copy.nodes())
        self.assertEqual(self.g1.edges(), g1_copy.edges())

        # changes made to g1_copy should not affect self.g1
        g1_copy.add_node('new1')
        g1_copy.add_edge('a', 'new1')

        self.assertTrue('new1' in g1_copy.nodes())
        self.assertTrue('new1' in g1_copy.neighbors('a'))
        self.assertTrue('new1' not in self.g1.nodes())
        self.assertTrue('new1' not in self.g1.neighbors('a'))

        # changes made to self.g1 should not affect g1_copy
        self.g1.add_node('new2')
        self.g1.add_edge('b', 'new2')

        self.assertTrue('new2' in self.g1.nodes())
        self.assertTrue('new2' in self.g1.neighbors('b'))
        self.assertTrue('new2' not in g1_copy.nodes())
        self.assertTrue('new2' not in g1_copy.neighbors('b'))
Esempio n. 14
0
 def test_constructor(self):
     self.assertEqual(Unweighted(Graph()), Unweighted())
     self.assertEqual(Unweighted(Undirected()), Undirected(Unweighted()))
Esempio n. 15
0
class TestUndirectedGraph(unittest.TestCase):
    """
    Tests for Undirected.
    """
    def setUp(self):
        self.g1 = Undirected(Graph())
        self.g1.add_nodes('a', 'b', 'c')
        self.g1.add_edge('a', 'b')
        self.g1.add_edge('b', 'c')

        self.g_directed = Graph()
        self.g_directed.add_nodes(1, 2, 3, 4, 5)
        self.g_directed.add_edge(1, 2)
        self.g_directed.add_edge(1, 3)
        self.g_directed.add_edge(3, 2)
        self.g_directed.add_edge(2, 3)
        self.g_directed.add_edge(3, 4)
        self.g_directed.add_edge(4, 3)

        self.g2 = Undirected(self.g_directed)

    def test_constructor(self):
        self.assertEqual(Undirected(Graph()), Undirected())

    def test_double_decorator(self):
        double_g_directed = Undirected(Undirected(self.g_directed))
        double_g2 = Undirected(self.g2)

        self.assertEqual(self.g2, double_g_directed)
        self.assertEqual(self.g2, double_g2)

    def test_from_weighted_directed(self):
        # adding another reverse edge should be fine and result in same graph
        self.g_directed.add_edge(2, 1)
        g3 = Undirected(self.g_directed)

        self.assertEqual(self.g2, g3)

        # adding different weight reverse edge should create ambiguous case
        self.g_directed.add_edge(3, 1, weight=12)

        self.assertRaises(ValueError, Undirected, self.g_directed)

    def test_parents(self):
        self.assertEqual({'b'}, self.g1.parents('a'))
        self.assertEqual({'a', 'c'}, self.g1.parents('b'))
        self.assertEqual({'b'}, self.g1.parents('c'))

    def test_neighbors(self):
        self.assertEqual({'b'}, self.g1.neighbors('a'))
        self.assertEqual({'a', 'c'}, self.g1.neighbors('b'))
        self.assertEqual({'b'}, self.g1.neighbors('c'))

    def test_add_edge_defined_edge(self):
        self.assertRaises(ValueError, self.g1.add_edge, 'b', 'a')

    def test_remove_edge(self):
        before = self.g2.edges()
        self.g2.remove_edge(2, 3)
        after = self.g2.edges()

        # both edges existed in directed graph, both are removed
        self.assertEqual(after, before.difference({(2, 3), (3, 2)}))
        self.assertRaises(ValueError, self.g2.remove_edge, 1, 5)

    def test_eq_directed_edges(self):
        # it is implied that these directed edges exists already in self.g2
        self.g_directed.add_edge(2, 1)
        self.g_directed.add_edge(3, 1)
        g2_test1 = Undirected(self.g_directed)

        self.assertEqual(self.g2, g2_test1)

    def test_eq_new_edge(self):
        # connecting previously disconnected nodes should make them different
        self.g_directed.add_edge(4, 5)
        g2_test2 = Undirected(self.g_directed)

        self.assertNotEqual(self.g2, g2_test2)

    def test_eq_new_node(self):
        # a new node should make them different
        self.g_directed.add_node(6)
        g2_test3 = Undirected(self.g_directed)

        self.assertNotEqual(self.g2, g2_test3)
Esempio n. 16
0
    def test_add_nodes(self):
        onebyone = Graph()
        onebyone.add_node('a')
        onebyone.add_node('b')
        onebyone.add_node('c')

        allatonce = Graph()
        allatonce.add_nodes('a', 'b', 'c')

        self.assertEqual(onebyone.nodes(), allatonce.nodes())