class_docstr: A `FunctionGraph` represents a subgraph bound by a set of input variables and
  a set of output variables, ie a subgraph that specifies an PyTensor function.
  The inputs list should contain all the inputs on which the outputs depend.
  `Variable`\s of type `Constant` are not counted as inputs.
  
  The `FunctionGraph` supports the replace operation which allows to replace
  a variable in the subgraph by another, e.g. replace ``(x + x).out`` by
  ``(2 * x).out``. This is the basis for optimization in PyTensor.
  
  This class is also responsible for verifying that a graph is valid
  (ie, all the dtypes and broadcast patterns are compatible with the
  way the `Variable`\s are used) and for tracking the `Variable`\s with
  a :attr:`FunctionGraph.clients` ``dict`` that specifies which `Apply` nodes
  use the `Variable`.  The :attr:`FunctionGraph.clients` field, combined with
  the :attr:`Variable.owner` and each :attr:`Apply.inputs`, allows the graph
  to be traversed in both directions.
  
  It can also be extended with new features using
  :meth:`FunctionGraph.attach_feature`.  See `Feature` for event types and
  documentation.  Extra features allow the `FunctionGraph` to verify new
  properties of a graph as it is optimized.
  
  The constructor creates a `FunctionGraph` which operates on the subgraph
  bound by the inputs and outputs sets.
  
  This class keeps lists for the inputs and outputs and modifies them
  in-place.
docstr: Return a map of node to node evaluation dependencies.
  
  Each key node is mapped to a list of nodes that must be evaluated
  before the key nodes can be evaluated.
  
  This is used primarily by the :class:`DestroyHandler` :class:`Feature`
  to ensure that the clients of any destroyed inputs have already
  computed their outputs.
  
  Notes
  -----
  This only calls the :meth:`Feature.orderings` method of each
  :class:`Feature` attached to the :class:`FunctionGraph`. It does not
  take care of computing the dependencies by itself.
docstr_tok: ["Return","a","map","of","node","to","node","evaluation","dependencies",".","Each","key","node","is","mapped","to","a","list","of","nodes","that","must","be","evaluated","before","the","key","nodes","can","be","evaluated",".","This","is","used","primarily","by","the",":","class",":","`","DestroyHandler","`",":","class",":","`","Feature","`","to","ensure","that","the","clients","of","any","destroyed","inputs","have","already","computed","their","outputs",".","Notes","--","--","-","This","only","calls","the",":","meth",":","`","Feature.orderings","`","method","of","each",":","class",":","`","Feature","`","attached","to","the",":","class",":","`","FunctionGraph","`",".","It","does","not","take","care","of","computing","the","dependencies","by","itself","."]
code: def orderings(self) -> dict[Apply, list[Apply]]:
      """Return a map of node to node evaluation dependencies.
  
      Each key node is mapped to a list of nodes that must be evaluated
      before the key nodes can be evaluated.
  
      This is used primarily by the :class:`DestroyHandler` :class:`Feature`
      to ensure that the clients of any destroyed inputs have already
      computed their outputs.
  
      Notes
      -----
      This only calls the :meth:`Feature.orderings` method of each
      :class:`Feature` attached to the :class:`FunctionGraph`. It does not
      take care of computing the dependencies by itself.
  
      """
      assert isinstance(self._features, list)
      all_orderings: list[OrderedDict] = []
  
      for feature in self._features:
          if hasattr(feature, "orderings"):
              orderings = feature.orderings(self)
              if not isinstance(orderings, OrderedDict):
                  raise TypeError(
                      "Non-deterministic return value from "
                      + str(feature.orderings)
                      + ". Nondeterministic object is "
                      + str(orderings)
                  )
              if len(orderings) > 0:
                  all_orderings.append(orderings)
                  for node, prereqs in orderings.items():
                      if not isinstance(prereqs, (list, OrderedSet)):
                          raise TypeError(
                              "prereqs must be a type with a "
                              "deterministic iteration order, or toposort "
                              " will be non-deterministic."
                          )
      if len(all_orderings) == 1:
          # If there is only 1 ordering, we reuse it directly.
          return all_orderings[0].copy()
      else:
          # If there is more than 1 ordering, combine them.
          ords: dict[Apply, list[Apply]] = OrderedDict()
          for orderings in all_orderings:
              for node, prereqs in orderings.items():
                  ords.setdefault(node, []).extend(prereqs)
          return ords
  
imports: import time
  from collections import OrderedDict
  from collections.abc import Iterable, Sequence
  from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
  import pytensor
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Apply, AtomicVariable, Variable, applys_between, clone_get_equiv, graph_inputs, io_toposort, vars_between
  from pytensor.graph.basic import as_string
  from pytensor.graph.features import AlreadyThere, Feature, ReplaceValidate
  from pytensor.graph.utils import MetaObject, MissingInputError, TestValueError
  from pytensor.misc.ordered_set import OrderedSet
label_desc: Use image node_id 22 for calling the FunctionGraph obj's underlying member method code with example usage: obj.orderings() and returns: all_orderings, ords