class_docstr: `FunctionMaker` is the class to `create` `Function` instances.
  
  This class has the fgraph, the rewriter, and the linker. When
  copying a `Function`, there is no need to duplicate the
  `FunctionMaker` instance. Deepcopy still copies both, which can
  variable in re-compilation.
  
  Parameters
  ----------
  inputs : list of SymbolicInput instances
  outputs : list of SymbolicOutput instances
      Outputs may also be a single Variable (not a list), in which case the
      functions produced by FunctionMaker will return their output value
      directly.
  mode : Mode instance
      Telling FunctionMaker how to rewrite and link. None means to use the
      `config.mode`.
  accept_inplace : bool
      True iff it is acceptable to have inplace operations in the graph from
      the inputs to the outputs.
  on_unused_input : {'raise', 'warn', 'ignore', None}
      What to do if a variable in the 'inputs' list is not used in the graph.
      Possible values are:
      - 'raise': raise an error
      - 'warn': log a warning
      - 'ignore': do not do anything
      - None: Use the value in the PyTensor flags on_unused_input.
  name : str
      An optional name for this function. If used, the profile mode will
      print the time spent in this function.
code: def prepare_fgraph(
      inputs,
      outputs,
      additional_outputs,
      fgraph: FunctionGraph,
      mode: "Mode",
      profile,
  ):
      rewriter = mode.optimizer
  
      try:
          start_rewriter = time.perf_counter()
  
          rewriter_profile = None
          rewrite_time = None
  
          with config.change_flags(
              mode=mode,
              compute_test_value=config.compute_test_value_opt,
              traceback__limit=config.traceback__compile_limit,
          ):
              rewriter_profile = rewriter(fgraph)
  
              end_rewriter = time.perf_counter()
              rewrite_time = end_rewriter - start_rewriter
              _logger.debug(f"Rewriting took {rewrite_time:f} seconds")
  
              # Add deep copy to respect the memory interface
              insert_deepcopy(
                  fgraph, inputs, outputs + additional_outputs
              )
      finally:
          # If the rewriter got interrupted
          if rewrite_time is None:
              end_rewriter = time.perf_counter()
              rewrite_time = end_rewriter - start_rewriter
  
          pytensor.compile.profiling.total_graph_rewrite_time += (
              rewrite_time
          )
  
          if profile:
              if rewriter_profile is None and hasattr(
                  rewriter, "pre_profile"
              ):
                  rewriter_profile = rewriter.pre_profile
  
              profile.rewriting_time += rewrite_time
  
              if config.profile_optimizer:
                  profile.rewriter_profile = (
                      rewriter,
                      rewriter_profile,
                  )
          elif config.profile_optimizer and profile is not False:
              # If False, it means the profiling for that function was
              # explicitly disabled
              warnings.warn(
                  (
                      "config.profile_optimizer requires config.profile to "
                      " be set to True as well"
                  ),
                  stacklevel=3,
              )
  
      if not hasattr(mode.linker, "accept"):
          raise ValueError(
              "'linker' parameter of FunctionMaker should be "
              f"a Linker with an accept method or one of {list(pytensor.compile.mode.predefined_linkers.keys())}"
          )
  
imports: import copy
  import copyreg
  import logging
  import time
  import warnings
  from itertools import chain
  from typing import TYPE_CHECKING, Optional
  import numpy
  import pytensor
  import pytensor.compile.profiling
  from pytensor.compile.io import In, SymbolicInput, SymbolicOutput
  from pytensor.compile.ops import deep_copy_op, view_op
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Constant, Variable, ancestors, clone_get_equiv, graph_inputs
  from pytensor.graph.destroyhandler import DestroyHandler
  from pytensor.graph.features import AlreadyThere, Feature, PreserveVariableAttributes
  from pytensor.graph.fg import FunctionGraph
  from pytensor.graph.op import HasInnerGraph
  from pytensor.graph.utils import InconsistencyError, get_variable_trace_string
  from pytensor.link.basic import Container
  from pytensor.link.utils import raise_with_op
label_desc: Use image node_id 4 for calling the FunctionMaker obj's underlying member method code with example usage: obj.prepare_fgraph(inputs, outputs, additional_outputs, fgraph, mode, profile) without return types