-
Notifications
You must be signed in to change notification settings - Fork 0
/
graph_util.py
143 lines (111 loc) · 4.45 KB
/
graph_util.py
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def adjacency_matrix(graph, order=None):
"""Construct an adjacency matrix from a given graph.
:param graph: a graph_types.GraphType to construct adjacency matrix from.
:param order: array of node ids containing order of adjacency matrix. If
None, the order is constructed by sorting graph.nodes().
:return: an array of arrays containing 0's and 1's where a[i][j] is 1 if
an edge exists from node i to node j. Each row and column corresponds
to a node where the nodes are sorted by node id in ascending order.
"""
if order is None:
order = [n.id() for n in sorted(graph.nodes())]
# Initialize result adjacency matrix.
result = [[0]*len(order) for _ in xrange(len(order))]
index_map = {node_id: index
for index, node_id in enumerate(order)}
for e in graph.edges():
nodes = e.nodes()
i = index_map[nodes[0].id()]
j = index_map[nodes[-1].id()]
result[i][j] = 1
return result
def parents(graph, node_id):
"""Returns the parents of the node in the graph.
:param graph: a graph_types.GraphType the node is in.
:param node_id: id value of the node to get parents of.
:return: an array of ids corresponding to the parents of the node.
"""
result = set()
for e in graph.edges():
nodes = e.nodes()
if nodes[-1].id() == node_id:
result.add(nodes[0].id())
return list(result)
def children(graph, node_id):
"""Returns the children of the node in the graph.
:param graph: a graph_types.GraphType the node is in.
:param node_id: id of the node to get children of.
:return: an array of ids corresponding to the children of the node.
"""
result = set()
for e in graph.edges():
nodes = e.nodes()
if nodes[0].id() == node_id:
result.add(nodes[-1].id())
return list(result)
def markov_blanket(graph, node_id):
"""Returns the Markov blanket of a node.
The Markov blanket of a node is defined as the node's parents, children,
and parents of children. The node itself is excluded.
:param graph: a graph_types.GraphType to fetch the markov blanket from.
:param node_id: id of node to get Markov blanket of.
:return: an array of nodes representing the Markov blanket of the specified
node in the graph.
"""
child_nodes = children(graph, node_id)
# Add children.
results = set(child_nodes)
# Add parents of children.
for c in child_nodes:
if c == node_id:
continue
results = results.union(parents(graph, c))
# Add parents.
results = results.union(parents(graph, node_id))
# Exclude self.
results.discard(node_id)
return list(results)
def neighbor_map(graph, directed=True):
"""Construct and return dictionary mapping nodes to a list of its neighbors.
:param graph: a graph_types.GraphType to fetch neighbors from.
:param directed: optional argument (default True) that specifies whether
edges should be treated as directed or bi-directional.
:return: a dictionary mapping a node id to a list of neighboring node ids.
"""
result = {}
for e in graph.edges():
nodes = e.nodes()
from_id = nodes[0].id()
to_id = nodes[-1].id()
edges = [(from_id, to_id)]
if not directed and from_id != to_id:
edges.append((to_id, from_id))
for node_id, neighbor_id in edges:
if node_id not in result:
result[node_id] = []
result[node_id].append(neighbor_id)
return result
def is_connected(graph, start_id, end_id, directed=True):
"""Returns True if a path exists between the start and end node ids.
:param graph: a graph_types.GraphType containing information about the
graph.
:param start_id: starting node id.
:param end_id: ending node id.
:param directed: optional argument (default True) that specifies whether
edges should be treated as directed or bi-directional.
:return: True if a path exists between start_id and end_id in the graph.
"""
neighbors = neighbor_map(graph, directed=directed)
id_queue = [start_id]
visited = {}
while id_queue:
node_id = id_queue.pop()
if node_id in visited:
continue
visited[node_id] = True
if node_id not in neighbors:
continue
if end_id in neighbors[node_id]:
return True
id_queue.extend(neighbors[node_id])
return False