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: Create a `FunctionGraph` which operates on the subgraph between the
  `inputs` and `outputs`.
  
  Parameters
  ----------
  inputs
      Input variables of the graph.
  outputs
      Output variables of the graph.
  features
      A list of features to be added to the `FunctionGraph`.
  clone
      If ``True``, the graph will be cloned.
  update_mapping
      Mapping between the `inputs` with updates and the `outputs`
      corresponding to their updates.
  clone_kwds
      Keywords passed to `clone_get_equiv` when `clone` is ``True``.
docstr_tok: ["Create","a","`","FunctionGraph","`","which","operates","on","the","subgraph","between","the","`","inputs","`","and","`","outputs","`",".","Parameters","--","--","--","--","--","inputs","Input","variables","of","the","graph",".","outputs","Output","variables","of","the","graph",".","features","A","list","of","features","to","be","added","to","the","`","FunctionGraph","`",".","clone","If","``","True","``",",","the","graph","will","be","cloned",".","update_mapping","Mapping","between","the","`","inputs","`","with","updates","and","the","`","outputs","`","corresponding","to","their","updates",".","clone_kwds","Keywords","passed","to","`","clone_get_equiv","`","when","`","clone","`","is","``","True","``","."]
code: def __init__(
      self,
      inputs: Optional[Sequence[Variable]] = None,
      outputs: Optional[Sequence[Variable]] = None,
      features: Optional[Sequence[Feature]] = None,
      clone: bool = True,
      update_mapping: Optional[dict[Variable, Variable]] = None,
      **clone_kwds,
  ):
      """
      Create a `FunctionGraph` which operates on the subgraph between the
      `inputs` and `outputs`.
  
      Parameters
      ----------
      inputs
          Input variables of the graph.
      outputs
          Output variables of the graph.
      features
          A list of features to be added to the `FunctionGraph`.
      clone
          If ``True``, the graph will be cloned.
      update_mapping
          Mapping between the `inputs` with updates and the `outputs`
          corresponding to their updates.
      clone_kwds
          Keywords passed to `clone_get_equiv` when `clone` is ``True``.
      """
      if outputs is None:
          raise ValueError("No outputs specified")
  
      if inputs is None:
          inputs = [
              i
              for i in graph_inputs(outputs)
              if not isinstance(i, AtomicVariable)
          ]
  
      if clone:
          _memo = clone_get_equiv(
              inputs,
              outputs,
              **clone_kwds,
          )
          outputs = [cast(Variable, _memo[o]) for o in outputs]
          inputs = [cast(Variable, _memo[i]) for i in inputs]
  
      self.execute_callbacks_time: float = 0.0
      self.execute_callbacks_times: dict[Feature, float] = {}
  
      if features is None:
          features = []
  
      self._features: list[Feature] = []
  
      # All apply nodes in the subgraph defined by inputs and
      # outputs are cached in this field
      self.apply_nodes: set[Apply] = set()
  
      # It includes inputs, outputs, and all intermediate variables
      # connecting the inputs and outputs.  It also contains irrelevant
      # outputs the nodes in `self.apply_nodes`.
      self.variables: set[Variable] = set()
  
      self.inputs: list[Variable] = []
      self.outputs: list[Variable] = []
      self.clients: dict[Variable, list[ClientType]] = {}
  
      for f in features:
          self.attach_feature(f)
  
      self.attach_feature(ReplaceValidate())
  
      for in_var in inputs:
          if in_var.owner is not None:
              raise ValueError(
                  "One of the provided inputs is the output of "
                  "an already existing node. "
                  "If that is okay, either discard that "
                  "input's owner or use graph.clone."
              )
  
          self.add_input(in_var, check=False)
  
      for output in outputs:
          self.add_output(output, reason="init")
  
      self.profile = None
      self.update_mapping = update_mapping
  
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 1 to create a new FunctionGraph object from inherited base classes: MetaObject with example: obj = FunctionGraph(inputs, outputs, features, clone, update_mapping)