Пример #1
0
def components(graph: Undirected) -> List[Set[Node]]:
    """
    Compute a tuple of sets of nodes that make up connected components in the
    given graph. Implemented as follows:

    1. Create a set containing the nodes of the graph: O(|V|).
    2. Create an empty list: O(1).
    3. Perform a breadth-first search from any node in the set: O(|V| + |E|).
    4. Remove the discovered nodes from the set and add them to the list as a
       new set: O(|V|) + O(|V|).
    5. Repeat until the set is empty: O(|V|).
    6. Return the list

    TODO: Calculate total runtime (3. and 5. are not necessarily multiplied)

    :param graph: the undirected graph to operate on
    :return: a tuple of connected components
    """
    nodes = graph.nodes()  # 1.
    comps = []  # 2.

    while nodes:  # 5.
        # 3.
        discovered: Set[Node] = set(
            DepthFirstIterator(graph, next(iter(nodes))))
        # 4.
        nodes.difference_update(discovered)
        comps.append(discovered)

    return comps  # 6.
Пример #2
0
    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)
Пример #3
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)
Пример #4
0
    def test_eq(self):
        g2 = Unweighted(Undirected())
        g3 = Undirected(Unweighted())

        g2.add_nodes(1, 2, 3)
        g2.add_edge(1, 3, weight=5)
        g2.add_edge(3, 2)

        g3.add_nodes(3, 2, 1)
        g3.add_edge(3, 2, weight=7)
        g3.add_edge(1, 3)

        self.assertEqual(g2, g3)
Пример #5
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)
Пример #6
0
    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)
Пример #7
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')
Пример #8
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'))
Пример #9
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)
Пример #10
0
 def test_constructor(self):
     self.assertEqual(Unweighted(Graph()), Unweighted())
     self.assertEqual(Unweighted(Undirected()), Undirected(Unweighted()))
Пример #11
0
    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)
Пример #12
0
    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)
Пример #13
0
    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)
Пример #14
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)