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: Replace a variable in the `FunctionGraph`.
  
  This is the main interface to manipulate the subgraph in `FunctionGraph`.
  For every node that uses `var` as input, makes it use `new_var` instead.
  
  Parameters
  ----------
  var
      The variable to be replaced.
  new_var
      The variable to replace `var`.
  reason
      The name of the optimization or operation in progress.
  verbose
      Print `reason`, `var`, and `new_var`.
  import_missing
      Import missing variables.
docstr_tok: ["Replace","a","variable","in","the","`","FunctionGraph","`",".","This","is","the","main","interface","to","manipulate","the","subgraph","in","`","FunctionGraph","`",".","For","every","node","that","uses","`","var","`","as","input",",","makes","it","use","`","new_var","`","instead",".","Parameters","--","--","--","--","--","var","The","variable","to","be","replaced",".","new_var","The","variable","to","replace","`","var","`",".","reason","The","name","of","the","optimization","or","operation","in","progress",".","verbose","Print","`","reason","`",",","`","var","`",",","and","`","new_var","`",".","import_missing","Import","missing","variables","."]
code: def replace(
      self,
      var: Variable,
      new_var: Variable,
      reason: Optional[str] = None,
      verbose: Optional[bool] = None,
      import_missing: bool = False,
  ) -> None:
      """Replace a variable in the `FunctionGraph`.
  
      This is the main interface to manipulate the subgraph in `FunctionGraph`.
      For every node that uses `var` as input, makes it use `new_var` instead.
  
      Parameters
      ----------
      var
          The variable to be replaced.
      new_var
          The variable to replace `var`.
      reason
          The name of the optimization or operation in progress.
      verbose
          Print `reason`, `var`, and `new_var`.
      import_missing
          Import missing variables.
  
      """
      if verbose is None:
          verbose = config.optimizer_verbose
      if verbose:
          print(
              f"rewriting: rewrite {reason} replaces {var} of {var.owner} with {new_var} of {new_var.owner}"
          )
  
      new_var = var.type.filter_variable(new_var, allow_convert=True)
  
      if var not in self.variables:
          # TODO: Raise an actual exception here.
          # Old comment:
          # this variable isn't in the graph... don't raise an
          # exception here, just return silently because it makes it
          # easier to implement some optimizations for
          # multiple-output ops
          # raise ValueError()
          return
  
      if config.compute_test_value != "off":
          try:
              tval = pytensor.graph.op.get_test_value(var)
              new_tval = pytensor.graph.op.get_test_value(new_var)
          except TestValueError:
              pass
          else:
              tval_shape = getattr(tval, "shape", None)
              new_tval_shape = getattr(new_tval, "shape", None)
              if tval_shape != new_tval_shape:
                  raise AssertionError(
                      "The replacement variable has a test value with "
                      "a shape different from the original variable's "
                      f"test value. Original: {tval_shape}, new: {new_tval_shape}"
                  )
  
      for node, i in list(self.clients[var]):
          self.change_node_input(
              node,
              i,
              new_var,
              reason=reason,
              import_missing=import_missing,
          )
  
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 11 for calling the FunctionGraph obj's underlying member method code with example usage: obj.replace(var, new_var, reason, verbose, import_missing) without return types