Index: tools/telemetry/third_party/altgraph/altgraph/Graph.py |
diff --git a/tools/telemetry/third_party/altgraph/altgraph/Graph.py b/tools/telemetry/third_party/altgraph/altgraph/Graph.py |
deleted file mode 100644 |
index 491e5c22872c129a28b3f89baac0fe1d7d677fb0..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/third_party/altgraph/altgraph/Graph.py |
+++ /dev/null |
@@ -1,677 +0,0 @@ |
-""" |
-altgraph.Graph - Base Graph class |
-================================= |
- |
-.. |
- #--Version 2.1 |
- #--Bob Ippolito October, 2004 |
- |
- #--Version 2.0 |
- #--Istvan Albert June, 2004 |
- |
- #--Version 1.0 |
- #--Nathan Denny, May 27, 1999 |
-""" |
- |
-from altgraph import GraphError |
-from collections import deque |
- |
-class Graph(object): |
- """ |
- The Graph class represents a directed graph with *N* nodes and *E* edges. |
- |
- Naming conventions: |
- |
- - the prefixes such as *out*, *inc* and *all* will refer to methods |
- that operate on the outgoing, incoming or all edges of that node. |
- |
- For example: :py:meth:`inc_degree` will refer to the degree of the node |
- computed over the incoming edges (the number of neighbours linking to |
- the node). |
- |
- - the prefixes such as *forw* and *back* will refer to the |
- orientation of the edges used in the method with respect to the node. |
- |
- For example: :py:meth:`forw_bfs` will start at the node then use the outgoing |
- edges to traverse the graph (goes forward). |
- """ |
- |
- def __init__(self, edges=None): |
- """ |
- Initialization |
- """ |
- |
- self.next_edge = 0 |
- self.nodes, self.edges = {}, {} |
- self.hidden_edges, self.hidden_nodes = {}, {} |
- |
- if edges is not None: |
- for item in edges: |
- if len(item) == 2: |
- head, tail = item |
- self.add_edge(head, tail) |
- elif len(item) == 3: |
- head, tail, data = item |
- self.add_edge(head, tail, data) |
- else: |
- raise GraphError("Cannot create edge from %s"%(item,)) |
- |
- |
- def __repr__(self): |
- return '<Graph: %d nodes, %d edges>' % ( |
- self.number_of_nodes(), self.number_of_edges()) |
- |
- def add_node(self, node, node_data=None): |
- """ |
- Adds a new node to the graph. Arbitrary data can be attached to the |
- node via the node_data parameter. Adding the same node twice will be |
- silently ignored. |
- |
- The node must be a hashable value. |
- """ |
- # |
- # the nodes will contain tuples that will store incoming edges, |
- # outgoing edges and data |
- # |
- # index 0 -> incoming edges |
- # index 1 -> outgoing edges |
- |
- if node in self.hidden_nodes: |
- # Node is present, but hidden |
- return |
- |
- if node not in self.nodes: |
- self.nodes[node] = ([], [], node_data) |
- |
- def add_edge(self, head_id, tail_id, edge_data=1, create_nodes=True): |
- """ |
- Adds a directed edge going from head_id to tail_id. |
- Arbitrary data can be attached to the edge via edge_data. |
- It may create the nodes if adding edges between nonexisting ones. |
- |
- :param head_id: head node |
- :param tail_id: tail node |
- :param edge_data: (optional) data attached to the edge |
- :param create_nodes: (optional) creates the head_id or tail_id node in case they did not exist |
- """ |
- # shorcut |
- edge = self.next_edge |
- |
- # add nodes if on automatic node creation |
- if create_nodes: |
- self.add_node(head_id) |
- self.add_node(tail_id) |
- |
- # update the corresponding incoming and outgoing lists in the nodes |
- # index 0 -> incoming edges |
- # index 1 -> outgoing edges |
- |
- try: |
- self.nodes[tail_id][0].append(edge) |
- self.nodes[head_id][1].append(edge) |
- except KeyError: |
- raise GraphError('Invalid nodes %s -> %s' % (head_id, tail_id)) |
- |
- # store edge information |
- self.edges[edge] = (head_id, tail_id, edge_data) |
- |
- |
- self.next_edge += 1 |
- |
- def hide_edge(self, edge): |
- """ |
- Hides an edge from the graph. The edge may be unhidden at some later |
- time. |
- """ |
- try: |
- head_id, tail_id, edge_data = self.hidden_edges[edge] = self.edges[edge] |
- self.nodes[tail_id][0].remove(edge) |
- self.nodes[head_id][1].remove(edge) |
- del self.edges[edge] |
- except KeyError: |
- raise GraphError('Invalid edge %s' % edge) |
- |
- def hide_node(self, node): |
- """ |
- Hides a node from the graph. The incoming and outgoing edges of the |
- node will also be hidden. The node may be unhidden at some later time. |
- """ |
- try: |
- all_edges = self.all_edges(node) |
- self.hidden_nodes[node] = (self.nodes[node], all_edges) |
- for edge in all_edges: |
- self.hide_edge(edge) |
- del self.nodes[node] |
- except KeyError: |
- raise GraphError('Invalid node %s' % node) |
- |
- def restore_node(self, node): |
- """ |
- Restores a previously hidden node back into the graph and restores |
- all of its incoming and outgoing edges. |
- """ |
- try: |
- self.nodes[node], all_edges = self.hidden_nodes[node] |
- for edge in all_edges: |
- self.restore_edge(edge) |
- del self.hidden_nodes[node] |
- except KeyError: |
- raise GraphError('Invalid node %s' % node) |
- |
- def restore_edge(self, edge): |
- """ |
- Restores a previously hidden edge back into the graph. |
- """ |
- try: |
- head_id, tail_id, data = self.hidden_edges[edge] |
- self.nodes[tail_id][0].append(edge) |
- self.nodes[head_id][1].append(edge) |
- self.edges[edge] = head_id, tail_id, data |
- del self.hidden_edges[edge] |
- except KeyError: |
- raise GraphError('Invalid edge %s' % edge) |
- |
- def restore_all_edges(self): |
- """ |
- Restores all hidden edges. |
- """ |
- for edge in list(self.hidden_edges.keys()): |
- try: |
- self.restore_edge(edge) |
- except GraphError: |
- pass |
- |
- def restore_all_nodes(self): |
- """ |
- Restores all hidden nodes. |
- """ |
- for node in list(self.hidden_nodes.keys()): |
- self.restore_node(node) |
- |
- def __contains__(self, node): |
- """ |
- Test whether a node is in the graph |
- """ |
- return node in self.nodes |
- |
- def edge_by_id(self, edge): |
- """ |
- Returns the edge that connects the head_id and tail_id nodes |
- """ |
- try: |
- head, tail, data = self.edges[edge] |
- except KeyError: |
- head, tail = None, None |
- raise GraphError('Invalid edge %s' % edge) |
- |
- return (head, tail) |
- |
- def edge_by_node(self, head, tail): |
- """ |
- Returns the edge that connects the head_id and tail_id nodes |
- """ |
- for edge in self.out_edges(head): |
- if self.tail(edge) == tail: |
- return edge |
- return None |
- |
- def number_of_nodes(self): |
- """ |
- Returns the number of nodes |
- """ |
- return len(self.nodes) |
- |
- def number_of_edges(self): |
- """ |
- Returns the number of edges |
- """ |
- return len(self.edges) |
- |
- def __iter__(self): |
- """ |
- Iterates over all nodes in the graph |
- """ |
- return iter(self.nodes) |
- |
- def node_list(self): |
- """ |
- Return a list of the node ids for all visible nodes in the graph. |
- """ |
- return list(self.nodes.keys()) |
- |
- def edge_list(self): |
- """ |
- Returns an iterator for all visible nodes in the graph. |
- """ |
- return list(self.edges.keys()) |
- |
- def number_of_hidden_edges(self): |
- """ |
- Returns the number of hidden edges |
- """ |
- return len(self.hidden_edges) |
- |
- def number_of_hidden_nodes(self): |
- """ |
- Returns the number of hidden nodes |
- """ |
- return len(self.hidden_nodes) |
- |
- def hidden_node_list(self): |
- """ |
- Returns the list with the hidden nodes |
- """ |
- return list(self.hidden_nodes.keys()) |
- |
- def hidden_edge_list(self): |
- """ |
- Returns a list with the hidden edges |
- """ |
- return list(self.hidden_edges.keys()) |
- |
- def describe_node(self, node): |
- """ |
- return node, node data, outgoing edges, incoming edges for node |
- """ |
- incoming, outgoing, data = self.nodes[node] |
- return node, data, outgoing, incoming |
- |
- def describe_edge(self, edge): |
- """ |
- return edge, edge data, head, tail for edge |
- """ |
- head, tail, data = self.edges[edge] |
- return edge, data, head, tail |
- |
- def node_data(self, node): |
- """ |
- Returns the data associated with a node |
- """ |
- return self.nodes[node][2] |
- |
- def edge_data(self, edge): |
- """ |
- Returns the data associated with an edge |
- """ |
- return self.edges[edge][2] |
- |
- def update_edge_data(self, edge, edge_data): |
- """ |
- Replace the edge data for a specific edge |
- """ |
- self.edges[edge] = self.edges[edge][0:2] + (edge_data,) |
- |
- def head(self, edge): |
- """ |
- Returns the node of the head of the edge. |
- """ |
- return self.edges[edge][0] |
- |
- def tail(self, edge): |
- """ |
- Returns node of the tail of the edge. |
- """ |
- return self.edges[edge][1] |
- |
- def out_nbrs(self, node): |
- """ |
- List of nodes connected by outgoing edges |
- """ |
- l = [self.tail(n) for n in self.out_edges(node)] |
- return l |
- |
- def inc_nbrs(self, node): |
- """ |
- List of nodes connected by incoming edges |
- """ |
- l = [self.head(n) for n in self.inc_edges(node)] |
- return l |
- |
- def all_nbrs(self, node): |
- """ |
- List of nodes connected by incoming and outgoing edges |
- """ |
- l = dict.fromkeys( self.inc_nbrs(node) + self.out_nbrs(node) ) |
- return list(l) |
- |
- def out_edges(self, node): |
- """ |
- Returns a list of the outgoing edges |
- """ |
- try: |
- return list(self.nodes[node][1]) |
- except KeyError: |
- raise GraphError('Invalid node %s' % node) |
- |
- return None |
- |
- def inc_edges(self, node): |
- """ |
- Returns a list of the incoming edges |
- """ |
- try: |
- return list(self.nodes[node][0]) |
- except KeyError: |
- raise GraphError('Invalid node %s' % node) |
- |
- return None |
- |
- def all_edges(self, node): |
- """ |
- Returns a list of incoming and outging edges. |
- """ |
- return set(self.inc_edges(node) + self.out_edges(node)) |
- |
- def out_degree(self, node): |
- """ |
- Returns the number of outgoing edges |
- """ |
- return len(self.out_edges(node)) |
- |
- def inc_degree(self, node): |
- """ |
- Returns the number of incoming edges |
- """ |
- return len(self.inc_edges(node)) |
- |
- def all_degree(self, node): |
- """ |
- The total degree of a node |
- """ |
- return self.inc_degree(node) + self.out_degree(node) |
- |
- def _topo_sort(self, forward=True): |
- """ |
- Topological sort. |
- |
- Returns a list of nodes where the successors (based on outgoing and |
- incoming edges selected by the forward parameter) of any given node |
- appear in the sequence after that node. |
- """ |
- topo_list = [] |
- queue = deque() |
- indeg = {} |
- |
- # select the operation that will be performed |
- if forward: |
- get_edges = self.out_edges |
- get_degree = self.inc_degree |
- get_next = self.tail |
- else: |
- get_edges = self.inc_edges |
- get_degree = self.out_degree |
- get_next = self.head |
- |
- for node in self.node_list(): |
- degree = get_degree(node) |
- if degree: |
- indeg[node] = degree |
- else: |
- queue.append(node) |
- |
- while queue: |
- curr_node = queue.popleft() |
- topo_list.append(curr_node) |
- for edge in get_edges(curr_node): |
- tail_id = get_next(edge) |
- if tail_id in indeg: |
- indeg[tail_id] -= 1 |
- if indeg[tail_id] == 0: |
- queue.append(tail_id) |
- |
- if len(topo_list) == len(self.node_list()): |
- valid = True |
- else: |
- # the graph has cycles, invalid topological sort |
- valid = False |
- |
- return (valid, topo_list) |
- |
- def forw_topo_sort(self): |
- """ |
- Topological sort. |
- |
- Returns a list of nodes where the successors (based on outgoing edges) |
- of any given node appear in the sequence after that node. |
- """ |
- return self._topo_sort(forward=True) |
- |
- def back_topo_sort(self): |
- """ |
- Reverse topological sort. |
- |
- Returns a list of nodes where the successors (based on incoming edges) |
- of any given node appear in the sequence after that node. |
- """ |
- return self._topo_sort(forward=False) |
- |
- def _bfs_subgraph(self, start_id, forward=True): |
- """ |
- Private method creates a subgraph in a bfs order. |
- |
- The forward parameter specifies whether it is a forward or backward |
- traversal. |
- """ |
- if forward: |
- get_bfs = self.forw_bfs |
- get_nbrs = self.out_nbrs |
- else: |
- get_bfs = self.back_bfs |
- get_nbrs = self.inc_nbrs |
- |
- g = Graph() |
- bfs_list = get_bfs(start_id) |
- for node in bfs_list: |
- g.add_node(node) |
- |
- for node in bfs_list: |
- for nbr_id in get_nbrs(node): |
- g.add_edge(node, nbr_id) |
- |
- return g |
- |
- def forw_bfs_subgraph(self, start_id): |
- """ |
- Creates and returns a subgraph consisting of the breadth first |
- reachable nodes based on their outgoing edges. |
- """ |
- return self._bfs_subgraph(start_id, forward=True) |
- |
- def back_bfs_subgraph(self, start_id): |
- """ |
- Creates and returns a subgraph consisting of the breadth first |
- reachable nodes based on the incoming edges. |
- """ |
- return self._bfs_subgraph(start_id, forward=False) |
- |
- def iterdfs(self, start, end=None, forward=True): |
- """ |
- Collecting nodes in some depth first traversal. |
- |
- The forward parameter specifies whether it is a forward or backward |
- traversal. |
- """ |
- visited, stack = set([start]), deque([start]) |
- |
- if forward: |
- get_edges = self.out_edges |
- get_next = self.tail |
- else: |
- get_edges = self.inc_edges |
- get_next = self.head |
- |
- while stack: |
- curr_node = stack.pop() |
- yield curr_node |
- if curr_node == end: |
- break |
- for edge in sorted(get_edges(curr_node)): |
- tail = get_next(edge) |
- if tail not in visited: |
- visited.add(tail) |
- stack.append(tail) |
- |
- def iterdata(self, start, end=None, forward=True, condition=None): |
- """ |
- Perform a depth-first walk of the graph (as ``iterdfs``) |
- and yield the item data of every node where condition matches. The |
- condition callback is only called when node_data is not None. |
- """ |
- |
- visited, stack = set([start]), deque([start]) |
- |
- if forward: |
- get_edges = self.out_edges |
- get_next = self.tail |
- else: |
- get_edges = self.inc_edges |
- get_next = self.head |
- |
- get_data = self.node_data |
- |
- while stack: |
- curr_node = stack.pop() |
- curr_data = get_data(curr_node) |
- if curr_data is not None: |
- if condition is not None and not condition(curr_data): |
- continue |
- yield curr_data |
- if curr_node == end: |
- break |
- for edge in get_edges(curr_node): |
- tail = get_next(edge) |
- if tail not in visited: |
- visited.add(tail) |
- stack.append(tail) |
- |
- def _iterbfs(self, start, end=None, forward=True): |
- """ |
- The forward parameter specifies whether it is a forward or backward |
- traversal. Returns a list of tuples where the first value is the hop |
- value the second value is the node id. |
- """ |
- queue, visited = deque([(start, 0)]), set([start]) |
- |
- # the direction of the bfs depends on the edges that are sampled |
- if forward: |
- get_edges = self.out_edges |
- get_next = self.tail |
- else: |
- get_edges = self.inc_edges |
- get_next = self.head |
- |
- while queue: |
- curr_node, curr_step = queue.popleft() |
- yield (curr_node, curr_step) |
- if curr_node == end: |
- break |
- for edge in get_edges(curr_node): |
- tail = get_next(edge) |
- if tail not in visited: |
- visited.add(tail) |
- queue.append((tail, curr_step + 1)) |
- |
- |
- def forw_bfs(self, start, end=None): |
- """ |
- Returns a list of nodes in some forward BFS order. |
- |
- Starting from the start node the breadth first search proceeds along |
- outgoing edges. |
- """ |
- return [node for node, step in self._iterbfs(start, end, forward=True)] |
- |
- def back_bfs(self, start, end=None): |
- """ |
- Returns a list of nodes in some backward BFS order. |
- |
- Starting from the start node the breadth first search proceeds along |
- incoming edges. |
- """ |
- return [node for node, step in self._iterbfs(start, end, forward=False)] |
- |
- def forw_dfs(self, start, end=None): |
- """ |
- Returns a list of nodes in some forward DFS order. |
- |
- Starting with the start node the depth first search proceeds along |
- outgoing edges. |
- """ |
- return list(self.iterdfs(start, end, forward=True)) |
- |
- def back_dfs(self, start, end=None): |
- """ |
- Returns a list of nodes in some backward DFS order. |
- |
- Starting from the start node the depth first search proceeds along |
- incoming edges. |
- """ |
- return list(self.iterdfs(start, end, forward=False)) |
- |
- def connected(self): |
- """ |
- Returns :py:data:`True` if the graph's every node can be reached from every |
- other node. |
- """ |
- node_list = self.node_list() |
- for node in node_list: |
- bfs_list = self.forw_bfs(node) |
- if len(bfs_list) != len(node_list): |
- return False |
- return True |
- |
- def clust_coef(self, node): |
- """ |
- Computes and returns the local clustering coefficient of node. The |
- local cluster coefficient is proportion of the actual number of edges between |
- neighbours of node and the maximum number of edges between those neighbours. |
- |
- See <http://en.wikipedia.org/wiki/Clustering_coefficient#Local_clustering_coefficient> |
- for a formal definition. |
- """ |
- num = 0 |
- nbr_set = set(self.out_nbrs(node)) |
- |
- if node in nbr_set: |
- nbr_set.remove(node) # loop defense |
- |
- for nbr in nbr_set: |
- sec_set = set(self.out_nbrs(nbr)) |
- if nbr in sec_set: |
- sec_set.remove(nbr) # loop defense |
- num += len(nbr_set & sec_set) |
- |
- nbr_num = len(nbr_set) |
- if nbr_num: |
- clust_coef = float(num) / (nbr_num * (nbr_num - 1)) |
- else: |
- clust_coef = 0.0 |
- return clust_coef |
- |
- def get_hops(self, start, end=None, forward=True): |
- """ |
- Computes the hop distance to all nodes centered around a specified node. |
- |
- First order neighbours are at hop 1, their neigbours are at hop 2 etc. |
- Uses :py:meth:`forw_bfs` or :py:meth:`back_bfs` depending on the value of the forward |
- parameter. If the distance between all neighbouring nodes is 1 the hop |
- number corresponds to the shortest distance between the nodes. |
- |
- :param start: the starting node |
- :param end: ending node (optional). When not specified will search the whole graph. |
- :param forward: directionality parameter (optional). If C{True} (default) it uses L{forw_bfs} otherwise L{back_bfs}. |
- :return: returns a list of tuples where each tuple contains the node and the hop. |
- |
- Typical usage:: |
- |
- >>> print (graph.get_hops(1, 8)) |
- >>> [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)] |
- # node 1 is at 0 hops |
- # node 2 is at 1 hop |
- # ... |
- # node 8 is at 5 hops |
- """ |
- if forward: |
- return list(self._iterbfs(start=start, end=end, forward=True)) |
- else: |
- return list(self._iterbfs(start=start, end=end, forward=False)) |