code: def construct_ir_fgraph(
      rv_values: Dict[Variable, Variable],
      ir_rewriter: Optional[GraphRewriter] = None,
  ) -> Tuple[
      FunctionGraph, Dict[Variable, Variable], Dict[Variable, Variable]
  ]:
      r"""Construct a `FunctionGraph` in measurable IR form for the keys in `rv_values`.
  
      A custom IR rewriter can be specified. By default,
      `logprob_rewrites_db.query(RewriteDatabaseQuery(include=["basic"]))` is used.
  
      Our measurable IR takes the form of an PyTensor graph that is more-or-less
      equivalent to a given PyTensor graph (i.e. the keys of `rv_values`) but
      contains `Op`s that are subclasses of the `MeasurableVariable` type in
      place of ones that do not inherit from `MeasurableVariable` in the original
      graph but are nevertheless measurable.
  
      `MeasurableVariable`\s are mapped to log-probabilities, so this IR is how
      non-trivial log-probabilities are constructed, especially when the
      "measurability" of a term depends on the measurability of its inputs
      (e.g. a mixture).
  
      In some cases, entire sub-graphs in the original graph are replaced with a
      single measurable node.  In other cases, the relevant nodes are already
      measurable and there is no difference between the resulting measurable IR
      graph and the original.  In general, some changes will be present,
      because--at the very least--canonicalization is always performed and the
      measurable IR includes manipulations that are not applicable to outside of
      the context of measurability/log-probabilities.
  
      For instance, some `Op`s will be lifted through `MeasurableVariable`\s in
      this IR, and the resulting graphs will not be computationally sound,
      because they wouldn't produce independent samples when the original graph
      would.  See https://github.com/aesara-devs/aeppl/pull/78.
  
      Returns
      -------
      A `FunctionGraph` of the measurable IR, a copy of `rv_values` containing
      the new, cloned versions of the original variables in `rv_values`, and
      a ``dict`` mapping all the original variables to their cloned values in
      `FunctionGraph`.
      """
  
      # Since we're going to clone the entire graph, we need to keep a map from
      # the old nodes to the new ones; otherwise, we won't be able to use
      # `rv_values`.
      # We start the `dict` with mappings from the value variables to themselves,
      # to prevent them from being cloned. This also includes ancestors
      memo = {
          v: v
          for v in ancestors(rv_values.values())
          if not isinstance(v, Constant)
      }
  
      # We add `ShapeFeature` because it will get rid of references to the old
      # `RandomVariable`s that have been lifted; otherwise, it will be difficult
      # to give good warnings when an unaccounted for `RandomVariable` is
      # encountered
      fgraph = FunctionGraph(
          outputs=list(rv_values.keys()),
          clone=True,
          memo=memo,
          copy_orphans=False,
          copy_inputs=False,
          features=[ShapeFeature()],
      )
  
      # Update `rv_values` so that it uses the new cloned variables
      rv_values = {memo[k]: v for k, v in rv_values.items()}
  
      # This `Feature` preserves the relationships between the original
      # random variables (i.e. keys in `rv_values`) and the new ones
      # produced when `Op`s are lifted through them.
      rv_remapper = PreserveRVMappings(rv_values)
      fgraph.attach_feature(rv_remapper)
  
      if ir_rewriter is None:
          ir_rewriter = logprob_rewrites_db.query(
              RewriteDatabaseQuery(include=["basic"])
          )
      ir_rewriter.rewrite(fgraph)
  
      return fgraph, rv_values, memo
  
imports: import warnings
  from collections import deque
  from typing import Dict, List, Optional, Sequence, Tuple
  import pytensor.tensor
  from pytensor import config
  from pytensor.compile.mode import optdb
  from pytensor.graph.basic import Constant, Variable, ancestors, io_toposort, truncated_graph_inputs
  from pytensor.graph.features import Feature
  from pytensor.graph.fg import FunctionGraph
  from pytensor.graph.replace import clone_replace
  from pytensor.graph.rewriting.basic import ChangeTracker, EquilibriumGraphRewriter, GraphRewriter, node_rewriter, out2in
  from pytensor.graph.rewriting.db import LocalGroupDB, RewriteDatabase, RewriteDatabaseQuery, SequenceDB, TopoDB
  from pytensor.tensor.basic import Alloc
  from pytensor.tensor.elemwise import DimShuffle, Elemwise
  from pytensor.tensor.random.rewriting import local_subtensor_rv_lift
  from pytensor.tensor.rewriting.basic import register_canonicalize
  from pytensor.tensor.rewriting.math import local_exp_over_1_plus_exp
  from pytensor.tensor.rewriting.shape import ShapeFeature
  from pytensor.tensor.rewriting.uncanonicalize import local_max_and_argmax
  from pytensor.tensor.subtensor import AdvancedIncSubtensor, AdvancedIncSubtensor1, AdvancedSubtensor, AdvancedSubtensor1, IncSubtensor, Subtensor
  from pytensor.tensor.variable import TensorVariable
  from pymc.logprob.abstract import MeasurableVariable
  from pymc.logprob.utils import DiracDelta, indices_from_subtensor
label_desc: Use image node_id 4 for calling a global function with example usage: construct_ir_fgraph(rv_values, ir_rewriter) and returns: fgraph,  rv_values,  memo