Returns a (non-unique) topological sort / linearization of the nodes IFF the graph is acyclic, otherwise returns None. Notes ----- A topological sort is an ordering on the nodes in `G` such that for every directed edge :math:`u \\rightarrow v` in the
(self)
| 198 | return DiGraph(self.vertices, [e.reverse() for e in self.edges]) |
| 199 | |
| 200 | def topological_ordering(self): |
| 201 | """ |
| 202 | Returns a (non-unique) topological sort / linearization of the nodes |
| 203 | IFF the graph is acyclic, otherwise returns None. |
| 204 | |
| 205 | Notes |
| 206 | ----- |
| 207 | A topological sort is an ordering on the nodes in `G` such that for every |
| 208 | directed edge :math:`u \\rightarrow v` in the graph, `u` appears before |
| 209 | `v` in the ordering. The topological ordering is produced by ordering |
| 210 | the nodes in `G` by their DFS "last visit time," from greatest to |
| 211 | smallest. |
| 212 | |
| 213 | This implementation follows a recursive, DFS-based approach [1]_ which |
| 214 | may break if the graph is very large. For an iterative version, see |
| 215 | Khan's algorithm [2]_ . |
| 216 | |
| 217 | References |
| 218 | ---------- |
| 219 | .. [1] Tarjan, R. (1976), Edge-disjoint spanning trees and depth-first |
| 220 | search, *Acta Informatica, 6 (2)*: 171–185. |
| 221 | .. [2] Kahn, A. (1962), Topological sorting of large networks, |
| 222 | *Communications of the ACM, 5 (11)*: 558–562. |
| 223 | |
| 224 | Returns |
| 225 | ------- |
| 226 | ordering : list or None |
| 227 | A topoligical ordering of the vertex indices if the graph is a DAG, |
| 228 | otherwise None. |
| 229 | """ |
| 230 | ordering = [] |
| 231 | visited = set() |
| 232 | |
| 233 | def dfs(v_i, path=None): |
| 234 | """A simple DFS helper routine""" |
| 235 | path = set([v_i]) if path is None else path |
| 236 | for nbr_i in self.get_neighbors(v_i): |
| 237 | if nbr_i in path: |
| 238 | return True # cycle detected! |
| 239 | elif nbr_i not in visited: |
| 240 | visited.add(nbr_i) |
| 241 | path.add(nbr_i) |
| 242 | is_cyclic = dfs(nbr_i, path) |
| 243 | if is_cyclic: |
| 244 | return True |
| 245 | |
| 246 | # insert to the beginning of the ordering |
| 247 | ordering.insert(0, v_i) |
| 248 | path -= set([v_i]) |
| 249 | return False |
| 250 | |
| 251 | for s_i in self.indices: |
| 252 | if s_i not in visited: |
| 253 | visited.add(s_i) |
| 254 | is_cyclic = dfs(s_i) |
| 255 | |
| 256 | if is_cyclic: |
| 257 | return None |
no outgoing calls