code: def topological_generations(G):
      """Stratifies a DAG into generations.
  
      A topological generation is node collection in which ancestors of a node in each
      generation are guaranteed to be in a previous generation, and any descendants of
      a node are guaranteed to be in a following generation. Nodes are guaranteed to
      be in the earliest possible generation that they can belong to.
  
      Parameters
      ----------
      G : NetworkX digraph
          A directed acyclic graph (DAG)
  
      Yields
      ------
      sets of nodes
          Yields sets of nodes representing each generation.
  
      Raises
      ------
      NetworkXError
          Generations are defined for directed graphs only. If the graph
          `G` is undirected, a :exc:`NetworkXError` is raised.
  
      NetworkXUnfeasible
          If `G` is not a directed acyclic graph (DAG) no topological generations
          exist and a :exc:`NetworkXUnfeasible` exception is raised.  This can also
          be raised if `G` is changed while the returned iterator is being processed
  
      RuntimeError
          If `G` is changed while the returned iterator is being processed.
  
      Examples
      --------
      >>> DG = nx.DiGraph([(2, 1), (3, 1)])
      >>> [sorted(generation) for generation in nx.topological_generations(DG)]
      [[2, 3], [1]]
  
      Notes
      -----
      The generation in which a node resides can also be determined by taking the
      max-path-distance from the node to the farthest leaf node. That value can
      be obtained with this function using `enumerate(topological_generations(G))`.
  
      See also
      --------
      topological_sort
      """
      if not G.is_directed():
          raise nx.NetworkXError(
              "Topological sort not defined on undirected graphs."
          )
  
      multigraph = G.is_multigraph()
      indegree_map = {v: d for v, d in G.in_degree() if d > 0}
      zero_indegree = [v for v, d in G.in_degree() if d == 0]
  
      while zero_indegree:
          this_generation = zero_indegree
          zero_indegree = []
          for node in this_generation:
              if node not in G:
                  raise RuntimeError("Graph changed during iteration")
              for child in G.neighbors(node):
                  try:
                      indegree_map[child] -= (
                          len(G[node][child]) if multigraph else 1
                      )
                  except KeyError as err:
                      raise RuntimeError(
                          "Graph changed during iteration"
                      ) from err
                  if indegree_map[child] == 0:
                      zero_indegree.append(child)
                      del indegree_map[child]
          yield this_generation
  
      if indegree_map:
          raise nx.NetworkXUnfeasible(
              "Graph contains a cycle or graph changed during iteration"
          )
  
imports: import heapq
  from collections import deque
  from functools import partial
  from itertools import chain, combinations, product, starmap
  from math import gcd
  import networkx
  from networkx.utils import arbitrary_element, not_implemented_for, pairwise
label_desc: Use image node_id 5 for calling a global function with example usage: topological_generations(G) without return types