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",".","]"]
code: def inner_inputs(self):
      return self.fgraph.inputs
  
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 21 for calling the OpFromGraph obj's underlying member method code with example usage: obj.inner_inputs() and returns: self