summaryrefslogtreecommitdiffstats
path: root/ipapython/graph.py
blob: e27249466889720247258870a99c688be4690a45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#
# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
#


class Graph():
    """
    Simple oriented graph structure

    G = (V, E) where G is graph, V set of vertices and E list of edges.
    E = (tail, head) where tail and head are vertices
    """

    def __init__(self):
        self.vertices = set()
        self.edges = []
        self._adj = dict()

    def add_vertex(self, vertex):
        self.vertices.add(vertex)
        self._adj[vertex] = []

    def add_edge(self, tail, head):
        if tail not in self.vertices:
            raise ValueError("tail is not a vertex")
        if head not in self.vertices:
            raise ValueError("head is not a vertex")
        self.edges.append((tail, head))
        self._adj[tail].append(head)

    def remove_edge(self, tail, head):
        try:
            self.edges.remove((tail, head))
        except KeyError:
            raise ValueError(
                "graph does not contain edge: (%s, %s)" % (tail, head))
        self._adj[tail].remove(head)

    def remove_vertex(self, vertex):
        try:
            self.vertices.remove(vertex)
        except KeyError:
            raise ValueError("graph does not contain vertex: %s" % vertex)

        # delete _adjacencies
        del self._adj[vertex]
        for key, _adj in self._adj.iteritems():
            _adj[:] = [v for v in _adj if v != vertex]

        # delete edges
        edges = [e for e in self.edges if e[0] != vertex and e[1] != vertex]
        self.edges[:] = edges

    def get_tails(self, head):
        """
        Get list of vertices where a vertex is on the right side of an edge
        """
        return [e[0] for e in self.edges if e[1] == head]

    def get_heads(self, tail):
        """
        Get list of vertices where a vertex is on the left side of an edge
        """
        return [e[1] for e in self.edges if e[0] == tail]

    def bfs(self, start=None):
        """
        Breadth-first search traversal of the graph from `start` vertex.
        Return a set of all visited vertices
        """
        if not start:
            start = list(self.vertices)[0]
        visited = set()
        queue = [start]
        while queue:
            vertex = queue.pop(0)
            if vertex not in visited:
                visited.add(vertex)
                queue.extend(set(self._adj.get(vertex, [])) - visited)
        return visited