class_docstr: This creates an `Op` from inputs and outputs lists of variables.
  The signature is similar to :func:`pytensor.function <pytensor.function>`
  and the resulting `Op`'s perform will do the same operation as::
  
      orig_function(inputs, outputs, **kwargs)
  
  Currently does not support ``updates`` or ``givens`` argument.
  
  .. TODO:
      - examples for a multi-layer mlp. where?
      - __hash__, __eq__ otherwise won't merge, try
        is_same_graph_with_merge(op1.local_outputs, op2,
        local_outputs)
      - c_code() to remove the double overhead?
      - grad() make it support DisconnectedType and the new interface
      - add support for NullType and DisconnectedType when R_op supports them
      - check how it works with updates.
      - add test with constant as input or inside the inner graph.
      - Add support for the GPU? Probably just need an opt to remove transfer
      - Add support to pickle this Op.
      - Add support/test with random generator
      - Add optimization to removing unused inputs/outputs
      - Add optimization to work inplace on inputs when not inline
  
  Notes
  -----
  - We support shared variables in the inner graph. This is automatic
    and invisible to the user. They can be as input to the node or in
    the inner graph.
  - We support unused inputs. This is needed for the grad.
  - We support nested OpFromGraph.
  - ``inline=True`` will cause better runtime optimization at the cost
    of compilation time. Currently only works with ``fast_compile`` or
    ``fast_run`` mode.
  - For overriding, it's recommended to provide pure functions (no side
    effects like setting global variable) as callable(s). The callable(s)
    supplied for overriding gradient/rop will be called only once at the
    first call to grad/R_op, and will be converted to OpFromGraph instances.
  
  Examples
  --------
  
  Example 1:
  
  .. code-block:: python
  
      from pytensor import function, tensor as pt
      from pytensor.compile.builders import OpFromGraph
      x, y, z = pt.scalars('xyz')
      e = x + y * z
      op = OpFromGraph([x, y, z], [e])
      # op behaves like a normal pytensor op
      e2 = op(x, y, z) + op(z, y, x)
      fn = function([x, y, z], [e2])
  
  Example 2 with shared variable:
  
  .. code-block:: python
  
      import numpy as np
      import pytensor
      from pytensor import config, function, tensor as pt
      from pytensor.compile.builders import OpFromGraph
  
      x, y, z = pt.scalars('xyz')
      s = pytensor.shared(np.random.random((2, 2)).astype(config.floatX))
      e = x + y * z + s
      op = OpFromGraph([x, y, z], [e])
      # op behaves like a normal pytensor op
      e2 = op(x, y, z) + op(z, y, x)
      fn = function([x, y, z], [e2])
  
  Example 3 override gradient
  
  .. code-block:: python
  
      from pytensor import function, tensor as pt, grad
      from pytensor.compile.builders import OpFromGraph
  
      x, y, z = pt.scalars('xyz')
      e = x + y * z
      def rescale_dy(inps, grads):
          x, y, z = inps
          g, = grads
          return z*2
      op = OpFromGraph(
          [x, y, z], [e], grad_overrides=['default', rescale_dy, 'default']
      e2 = op(x, y, z)
      dx, dy, dz = grad(e2, [x, y, z])
      fn = function([x, y, z], [dx, dy, dz])
      # the gradient wrt y is now doubled
      fn(2., 3., 4.) # [1., 8., 3.]
class_docstr_tok: ["This","creates","an","`","Op","`","from","inputs","and","outputs","lists","of","variables",".","The","signature","is","similar","to",":","func",":","`","pytensor.function","<","pytensor.function",">","`","and","the","resulting","`","Op","`","'s","perform","will","do","the","same","operation","as",":",":","orig_function","(","inputs",",","outputs",",","*","*","kwargs",")","Currently","does","not","support","``","updates","``","or","``","givens","``","argument",".","..","TODO",":","-","examples","for","a","multi-layer","mlp",".","where","?","-","__hash__",",","__eq__","otherwise","wo","n't","merge",",","try","is_same_graph_with_merge","(","op1.local_outputs",",","op2",",","local_outputs",")","-","c_code","(",")","to","remove","the","double","overhead","?","-","grad","(",")","make","it","support","DisconnectedType","and","the","new","interface","-","add","support","for","NullType","and","DisconnectedType","when","R_op","supports","them","-","check","how","it","works","with","updates",".","-","add","test","with","constant","as","input","or","inside","the","inner","graph",".","-","Add","support","for","the","GPU","?","Probably","just","need","an","opt","to","remove","transfer","-","Add","support","to","pickle","this","Op",".","-","Add","support\/test","with","random","generator","-","Add","optimization","to","removing","unused","inputs\/outputs","-","Add","optimization","to","work","inplace","on","inputs","when","not","inline","Notes","--","--","-","-","We","support","shared","variables","in","the","inner","graph",".","This","is","automatic","and","invisible","to","the","user",".","They","can","be","as","input","to","the","node","or","in","the","inner","graph",".","-","We","support","unused","inputs",".","This","is","needed","for","the","grad",".","-","We","support","nested","OpFromGraph",".","-","``","inline=True","``","will","cause","better","runtime","optimization","at","the","cost","of","compilation","time",".","Currently","only","works","with","``","fast_compile","``","or","``","fast_run","``","mode",".","-","For","overriding",",","it","'s","recommended","to","provide","pure","functions","(","no","side","effects","like","setting","global","variable",")","as","callable","(","s",")",".","The","callable","(","s",")","supplied","for","overriding","gradient\/rop","will","be","called","only","once","at","the","first","call","to","grad\/R_op",",","and","will","be","converted","to","OpFromGraph","instances",".","Examples","--","--","--","--","Example","1",":","..","code-block",":",":","python","from","pytensor","import","function",",","tensor","as","pt","from","pytensor.compile.builders","import","OpFromGraph","x",",","y",",","z","=","pt.scalars","(","'xyz","'",")","e","=","x","+","y","*","z","op","=","OpFromGraph","(","[","x",",","y",",","z","]",",","[","e","]",")","#","op","behaves","like","a","normal","pytensor","op","e2","=","op","(","x",",","y",",","z",")","+","op","(","z",",","y",",","x",")","fn","=","function","(","[","x",",","y",",","z","]",",","[","e2","]",")","Example","2","with","shared","variable",":","..","code-block",":",":","python","import","numpy","as","np","import","pytensor","from","pytensor","import","config",",","function",",","tensor","as","pt","from","pytensor.compile.builders","import","OpFromGraph","x",",","y",",","z","=","pt.scalars","(","'xyz","'",")","s","=","pytensor.shared","(","np.random.random","(","(","2",",","2",")",")",".astype","(","config.floatX",")",")","e","=","x","+","y","*","z","+","s","op","=","OpFromGraph","(","[","x",",","y",",","z","]",",","[","e","]",")","#","op","behaves","like","a","normal","pytensor","op","e2","=","op","(","x",",","y",",","z",")","+","op","(","z",",","y",",","x",")","fn","=","function","(","[","x",",","y",",","z","]",",","[","e2","]",")","Example","3","override","gradient","..","code-block",":",":","python","from","pytensor","import","function",",","tensor","as","pt",",","grad","from","pytensor.compile.builders","import","OpFromGraph","x",",","y",",","z","=","pt.scalars","(","'xyz","'",")","e","=","x","+","y","*","z","def","rescale_dy","(","inps",",","grads",")",":","x",",","y",",","z","=","inps","g",",","=","grads","return","z","*","2","op","=","OpFromGraph","(","[","x",",","y",",","z","]",",","[","e","]",",","grad_overrides=","[","'default","'",",","rescale_dy",",","'default","'","]","e2","=","op","(","x",",","y",",","z",")","dx",",","dy",",","dz","=","grad","(","e2",",","[","x",",","y",",","z","]",")","fn","=","function","(","[","x",",","y",",","z","]",",","[","dx",",","dy",",","dz","]",")","#","the","gradient","wrt","y","is","now","doubled","fn","(","2.",",","3.",",","4",".",")","#","[","1.",",","8.",",","3",".","]"]
docstr: Parameters
  ----------
  inputs
      The inputs to the graph.
  outputs
      The outputs to the graph.
  inline
      Defaults to ``False``
  
      ``True`` : Cause the :class:`Op`'s original graph being used during
      compilation, the :class:`Op` will not be visible in the compiled
      graph but rather its internal graph.
  
      ``False`` : will use a pre-compiled function inside.
  grad_overrides
      Defaults to ``'default'``.
      This argument is mutually exclusive with ``lop_overrides``.
  
      ``'default'`` : Do not override, use default grad() result
  
      `OpFromGraph`: Override with another `OpFromGraph`, should
      accept inputs as the same order and types of ``inputs`` and ``output_grads``
      arguments as one would specify in :meth:`Op.grad`() method.
  
      `callable`: Should take two args: ``inputs`` and ``output_grads``.
      Each argument is expected to be a list of :class:`Variable `.
      Must return list of :class:`Variable `.
  lop_overrides
      Defaults to ``'default'``.
  
      This argument is mutually exclusive with ``grad_overrides``.
  
      These options are similar to the ``grad_overrides`` above, but for
      the :meth:`Op.L_op` method.
  
      ``'default'``: Do not override, use the default :meth:`Op.L_op` result
  
      `OpFromGraph`: Override with another `OpFromGraph`, should
      accept inputs as the same order and types of ``inputs``,
      ``outputs`` and ``output_grads`` arguments as one would specify in
      :meth:`Op.grad` method.
  
      `callable`: Should take three args: ``inputs``, ``outputs`` and ``output_grads``.
      Each argument is expected to be a list of :class:`Variable`.
      Must return list of :class:`Variable`.
  
      `NullType` instance: Treat as non-differentiable
      `DisconnectedType` instance: Treat as disconnected gradient,
      numerically gives zero
  
      ``list``: Each `OpFromGraph`/callable must return a single
      :class:`Variable`. Each list element corresponds to gradient of
      a specific input, length of list must be equal to number of inputs.
  
  rop_overrides
      One of ``{'default', OpFromGraph, callable, Variable}``.
  
      Defaults to ``'default'``.
  
      ``'default'``: Do not override, use the default :meth:`Op.R_op` result
  
      `OpFromGraph`: Override with another `OpFromGraph`, should
      accept inputs as the same order and types of ``inputs`` and ``eval_points``
      arguments as one would specify in :meth:`Op.R_op` method.
  
      `callable`: Should take two args: ``inputs`` and ``eval_points``.
      Each argument is expected to be a list of :class:`Variable`.  Must
      return list of :class:`Variable`.
  
      `NullType` instance: Treat as non-differentiable `DisconnectedType`
      instance: Treat as zero since `DisconnectedType` is not yet supported
      in :meth:`Op.R_op`.
  
      ``list``:
      Each :class:`OpFromGraph`/callable must return a single
      :class:`Variable <pytensor.graph.basic.Variable>`. Each list element
      corresponds to a specific output of :meth:`Op.R_op`, length of list
      must be equal to number of outputs.  connection_pattern If not
      ``None``, this will be used as the connection_pattern for this
      :class:`Op`.
  name
      A name for debugging purposes.
  kwargs
      Check :func:`pytensor.function` for more arguments, only works when not
      inline.
docstr_tok: ["Parameters","--","--","--","--","--","inputs","The","inputs","to","the","graph",".","outputs","The","outputs","to","the","graph",".","inline","Defaults","to","``","False","``","``","True","``",":","Cause","the",":","class",":","`","Op","`","'s","original","graph","being","used","during","compilation",",","the",":","class",":","`","Op","`","will","not","be","visible","in","the","compiled","graph","but","rather","its","internal","graph",".","``","False","``",":","will","use","a","pre-compiled","function","inside",".","grad_overrides","Defaults","to","``","'default","'","``",".","This","argument","is","mutually","exclusive","with","``","lop_overrides","``",".","``","'default","'","``",":","Do","not","override",",","use","default","grad","(",")","result","`","OpFromGraph","`",":","Override","with","another","`","OpFromGraph","`",",","should","accept","inputs","as","the","same","order","and","types","of","``","inputs","``","and","``","output_grads","``","arguments","as","one","would","specify","in",":","meth",":","`","Op.grad","`","(",")","method",".","`","callable","`",":","Should","take","two","args",":","``","inputs","``","and","``","output_grads","``",".","Each","argument","is","expected","to","be","a","list","of",":","class",":","`","Variable","`",".","Must","return","list","of",":","class",":","`","Variable","`",".","lop_overrides","Defaults","to","``","'default","'","``",".","This","argument","is","mutually","exclusive","with","``","grad_overrides","``",".","These","options","are","similar","to","the","``","grad_overrides","``","above",",","but","for","the",":","meth",":","`","Op.L_op","`","method",".","``","'default","'","``",":","Do","not","override",",","use","the","default",":","meth",":","`","Op.L_op","`","result","`","OpFromGraph","`",":","Override","with","another","`","OpFromGraph","`",",","should","accept","inputs","as","the","same","order","and","types","of","``","inputs","``",",","``","outputs","``","and","``","output_grads","``","arguments","as","one","would","specify","in",":","meth",":","`","Op.grad","`","method",".","`","callable","`",":","Should","take","three","args",":","``","inputs","``",",","``","outputs","``","and","``","output_grads","``",".","Each","argument","is","expected","to","be","a","list","of",":","class",":","`","Variable","`",".","Must","return","list","of",":","class",":","`","Variable","`",".","`","NullType","`","instance",":","Treat","as","non-differentiable","`","DisconnectedType","`","instance",":","Treat","as","disconnected","gradient",",","numerically","gives","zero","``","list","``",":","Each","`","OpFromGraph","`","\/callable","must","return","a","single",":","class",":","`","Variable","`",".","Each","list","element","corresponds","to","gradient","of","a","specific","input",",","length","of","list","must","be","equal","to","number","of","inputs",".","rop_overrides","One","of","``","{","'default","'",",","OpFromGraph",",","callable",",","Variable","}","``",".","Defaults","to","``","'default","'","``",".","``","'default","'","``",":","Do","not","override",",","use","the","default",":","meth",":","`","Op.R_op","`","result","`","OpFromGraph","`",":","Override","with","another","`","OpFromGraph","`",",","should","accept","inputs","as","the","same","order","and","types","of","``","inputs","``","and","``","eval_points","``","arguments","as","one","would","specify","in",":","meth",":","`","Op.R_op","`","method",".","`","callable","`",":","Should","take","two","args",":","``","inputs","``","and","``","eval_points","``",".","Each","argument","is","expected","to","be","a","list","of",":","class",":","`","Variable","`",".","Must","return","list","of",":","class",":","`","Variable","`",".","`","NullType","`","instance",":","Treat","as","non-differentiable","`","DisconnectedType","`","instance",":","Treat","as","zero","since","`","DisconnectedType","`","is","not","yet","supported","in",":","meth",":","`","Op.R_op","`",".","``","list","``",":","Each",":","class",":","`","OpFromGraph","`","\/callable","must","return","a","single",":","class",":","`","Variable","<","pytensor.graph.basic.Variable",">","`",".","Each","list","element","corresponds","to","a","specific","output","of",":","meth",":","`","Op.R_op","`",",","length","of","list","must","be","equal","to","number","of","outputs",".","connection_pattern","If","not","``","None","``",",","this","will","be","used","as","the","connection_pattern","for","this",":","class",":","`","Op","`",".","name","A","name","for","debugging","purposes",".","kwargs","Check",":","func",":","`","pytensor.function","`","for","more","arguments",",","only","works","when","not","inline","."]
code: def __init__(
      self,
      inputs: list[Variable],
      outputs: list[Variable],
      inline: bool = False,
      lop_overrides: str = "default",
      grad_overrides: str = "default",
      rop_overrides: str = "default",
      connection_pattern: Optional[list[list[bool]]] = None,
      name: Optional[str] = None,
      **kwargs,
  ):
      """
      Parameters
      ----------
      inputs
          The inputs to the graph.
      outputs
          The outputs to the graph.
      inline
          Defaults to ``False``
  
          ``True`` : Cause the :class:`Op`'s original graph being used during
          compilation, the :class:`Op` will not be visible in the compiled
          graph but rather its internal graph.
  
          ``False`` : will use a pre-compiled function inside.
      grad_overrides
          Defaults to ``'default'``.
          This argument is mutually exclusive with ``lop_overrides``.
  
          ``'default'`` : Do not override, use default grad() result
  
          `OpFromGraph`: Override with another `OpFromGraph`, should
          accept inputs as the same order and types of ``inputs`` and ``output_grads``
          arguments as one would specify in :meth:`Op.grad`() method.
  
          `callable`: Should take two args: ``inputs`` and ``output_grads``.
          Each argument is expected to be a list of :class:`Variable `.
          Must return list of :class:`Variable `.
      lop_overrides
          Defaults to ``'default'``.
  
          This argument is mutually exclusive with ``grad_overrides``.
  
          These options are similar to the ``grad_overrides`` above, but for
          the :meth:`Op.L_op` method.
  
          ``'default'``: Do not override, use the default :meth:`Op.L_op` result
  
          `OpFromGraph`: Override with another `OpFromGraph`, should
          accept inputs as the same order and types of ``inputs``,
          ``outputs`` and ``output_grads`` arguments as one would specify in
          :meth:`Op.grad` method.
  
          `callable`: Should take three args: ``inputs``, ``outputs`` and ``output_grads``.
          Each argument is expected to be a list of :class:`Variable`.
          Must return list of :class:`Variable`.
  
          `NullType` instance: Treat as non-differentiable
          `DisconnectedType` instance: Treat as disconnected gradient,
          numerically gives zero
  
          ``list``: Each `OpFromGraph`/callable must return a single
          :class:`Variable`. Each list element corresponds to gradient of
          a specific input, length of list must be equal to number of inputs.
  
      rop_overrides
          One of ``{'default', OpFromGraph, callable, Variable}``.
  
          Defaults to ``'default'``.
  
          ``'default'``: Do not override, use the default :meth:`Op.R_op` result
  
          `OpFromGraph`: Override with another `OpFromGraph`, should
          accept inputs as the same order and types of ``inputs`` and ``eval_points``
          arguments as one would specify in :meth:`Op.R_op` method.
  
          `callable`: Should take two args: ``inputs`` and ``eval_points``.
          Each argument is expected to be a list of :class:`Variable`.  Must
          return list of :class:`Variable`.
  
          `NullType` instance: Treat as non-differentiable `DisconnectedType`
          instance: Treat as zero since `DisconnectedType` is not yet supported
          in :meth:`Op.R_op`.
  
          ``list``:
          Each :class:`OpFromGraph`/callable must return a single
          :class:`Variable <pytensor.graph.basic.Variable>`. Each list element
          corresponds to a specific output of :meth:`Op.R_op`, length of list
          must be equal to number of outputs.  connection_pattern If not
          ``None``, this will be used as the connection_pattern for this
          :class:`Op`.
      name
          A name for debugging purposes.
      kwargs
          Check :func:`pytensor.function` for more arguments, only works when not
          inline.
      """
  
      if not (isinstance(inputs, list) and isinstance(outputs, list)):
          raise TypeError("Inputs and outputs must be lists")
  
      for out in outputs:
          if not isinstance(out, Variable):
              raise TypeError(
                  f"Inputs and outputs must be Variable instances; got {out}"
              )
  
      if "updates" in kwargs or "givens" in kwargs:
          raise NotImplementedError(
              "Updates and givens are not supported"
          )
  
      self.is_inline = inline
  
      self.fgraph, self.shared_inputs, _, _ = construct_nominal_fgraph(
          inputs, outputs
      )
  
      self.kwargs = kwargs
      self.input_types = [inp.type for inp in inputs]
      self.output_types = [out.type for out in outputs]
  
      self.lop_overrides = lop_overrides
      self.grad_overrides = grad_overrides
      self.rop_overrides = rop_overrides
  
      if lop_overrides != "default":
          if grad_overrides != "default":
              raise ValueError(
                  "lop_overrides and grad_overrides are mutually exclusive"
              )
          else:
              self.set_lop_overrides(lop_overrides)
              self._lop_type = "lop"
      elif grad_overrides != "default":
          self.set_lop_overrides(grad_overrides)
          self._lop_type = "grad"
      else:
          self.set_lop_overrides("default")
          self._lop_type = "lop"
  
      self.set_rop_overrides(rop_overrides)
  
      self._connection_pattern = connection_pattern
  
      if name is not None:
          assert isinstance(
              name, str
          ), "name must be None or string object"
      self.name = name
  
imports: from collections import OrderedDict
  from collections.abc import Sequence
  from copy import copy
  from functools import partial
  from typing import Optional, cast
  import pytensor.tensor
  from pytensor import function
  from pytensor.compile.function.pfunc import rebuild_collect_shared
  from pytensor.compile.mode import optdb
  from pytensor.compile.sharedvalue import SharedVariable
  from pytensor.configdefaults import config
  from pytensor.gradient import DisconnectedType, Rop, grad
  from pytensor.graph.basic import Apply, Constant, NominalVariable, Variable, graph_inputs, io_connection_pattern
  from pytensor.graph.fg import FunctionGraph
  from pytensor.graph.null_type import NullType
  from pytensor.graph.op import HasInnerGraph, Op
  from pytensor.graph.replace import clone_replace
  from pytensor.graph.rewriting.basic import in2out, node_rewriter
  from pytensor.graph.utils import MissingInputError
  from pytensor.tensor.rewriting.shape import ShapeFeature
label_desc: Use image node_id 3 to create a new OpFromGraph object from inherited base classes: Op, HasInnerGraph with example: obj = OpFromGraph(inputs, outputs, inline, lop_overrides, grad_overrides, rop_overrides, connection_pattern, name)