def in_degrees(g: Mapping): """ >>> g = dict(a='c', b='ce', c='abde', d='c', e=['c', 'z'], f={}) >>> assert dict(in_degrees(g)) == ( ... {'a': 1, 'b': 1, 'c': 4, 'd': 1, 'e': 2, 'f': 0, 'z': 1} ... ) """ return out_degrees(edge_reversed_graph(g))
def leaf_nodes(g: Mapping): """ >>> g = dict(a='c', b='ce', c='abde', d='c', e=['c', 'z'], f={}) >>> sorted(leaf_nodes(g)) ['f', 'z'] Note that `f` is present: Isolated nodes are considered both as root and leaf nodes both. """ return root_nodes(edge_reversed_graph(g))
def parents(g: Mapping, source: Iterable): """Set of all nodes (not in source) adjacent TO 'source' in 'g' >>> g = { ... 0: [1, 2], ... 1: [2, 3, 4], ... 2: [1, 4], ... 3: [4] ... } >>> parents(g, [2, 3]) {0, 1} >>> parents(g, [0]) set() """ return children(edge_reversed_graph(g), source)
def descendants(g: Mapping, source: Iterable, _exclude_nodes=None): """Returns the set of all nodes reachable FROM `source` in `g`. >>> g = { ... 0: [1, 2], ... 1: [2, 3, 4], ... 2: [1, 4], ... 3: [4] ... } >>> descendants(g, [2, 3]) {1, 4} >>> descendants(g, [4]) set() """ return ancestors(edge_reversed_graph(g), source, _exclude_nodes)
def predecessors(g: Mapping, node): """Iterator of nodes that have directed paths TO node >>> g = { ... 0: [1, 2], ... 1: [2, 3, 4], ... 2: [1, 4], ... 3: [4]} >>> set(predecessors(g, 4)) {0, 1, 2, 3} >>> set(predecessors(g, 2)) {0, 1, 2} >>> set(predecessors(g, 0)) set() Notice that 2 is a predecessor of 2 here because of the presence of a 2-1-2 directed path. """ yield from successors(edge_reversed_graph(g), node)