code: def pfunc(
      params,
      outputs=None,
      mode=None,
      updates=None,
      givens=None,
      no_default_updates=False,
      accept_inplace=False,
      name=None,
      rebuild_strict=True,
      allow_input_downcast=None,
      profile=None,
      on_unused_input=None,
      output_keys=None,
  ):
      """
      Function-constructor for graphs with shared variables.
  
      Parameters
      ----------
      params : list of either Variable or In instances
          Function parameters, these are not allowed to be shared variables.
      outputs : list of Variables or Out instances
          Expressions to compute.
      mode : string or `theano.compile.Mode` instance
          Compilation mode.
      updates : iterable over pairs (shared_variable, new_expression). List, tuple or dict.
          Update the values for SharedVariable inputs according to these
          expressions
      givens : iterable over pairs (Var1, Var2) of Variables. List, tuple or dict.
          The Var1 and Var2 in each pair must have the same Type. Specific
          substitutions to make in the computation graph (Var2 replaces Var1).
      no_default_updates : either bool or list of Variables
          If True, do not perform any automatic update on Variables.
          If False (default), perform them all. Else, perform automatic updates
          on all Variables that are neither in "updates" nor in
          "no_default_updates".
      accept_inplace : bool
          True iff the graph can contain inplace operations prior to the
          optimization phase (default is False). *Note* this parameter is unsupported,
          and its use is not recommended.
      name : None or string
          Attaches a name to the profiling result of this function.
      allow_input_downcast : bool
          True means that the values passed as inputs when calling the function
          can be silently downcasted to fit the dtype of the corresponding
          Variable, which may lose precision. False means that it will only be cast to a more
          general, or precise, type. None (default) is almost like
          False, but allows downcasting of Python float scalars to
          floatX.
      profile : None, True, str, or ProfileStats instance
          Accumulate profiling information into a given ProfileStats instance.
          None is the default, and means to use the value of config.profile.
          If argument is `True` then a new ProfileStats instance will be used.
          If argument is a string, a new ProfileStats instance will be created
          with that string as its `message` attribute. This profiling object will
          be available via self.profile.
      on_unused_input : {'raise', 'warn','ignore', None}
          What to do if a variable in the 'inputs' list is not used in the graph.
  
      Returns
      -------
      theano.compile.Function
          A callable object that will compute the outputs (given the inputs) and
          update the implicit function arguments according to the `updates`.
  
      Notes
      -----
      Regarding givens: Be careful to make sure that these substitutions are
      independent--behaviour when Var1 of one pair appears in the graph leading
      to Var2 in another expression is undefined. Replacements specified with
      givens are different from optimizations in that Var2 is not expected to be
      equivalent to Var1.
  
      """
      #
      # This function works by cloning the graph (except for the
      # inputs), and then shipping it off to compile.function (There it
      # will be cloned again, unnecessarily, because it doesn't know
      # that we already cloned it.)
      #
      # First, it clones the replacements named in the givens argument,
      # and points each Var1 to the clone of Var2.  Then it sets the
      # inputs in the clone dictionary.  After these steps, we are
      # assuming that the clone dictionary contains all the inputs to
      # the computation graph.
      #
      # Then it clones the outputs and the update expressions.  This
      # rebuilds a computation graph from the inputs and the givens.
      #
      if updates is None:
          updates = []
      if givens is None:
          givens = []
      if profile is None:
          profile = config.profile or config.print_global_stats
          # profile -> True or False
          if profile is False:
              profile = None
      if profile is True:
          profile = ProfileStats(message=name)
          # profile -> object
      elif type(profile) == str:
          profile = ProfileStats(message=profile)
      # profile is typically either False or an object at this point.
      # No need to block other objects being passed through though. It might be
      # useful.
  
      if not isinstance(params, (list, tuple)):
          raise Exception(
              "in pfunc() the first argument must be a list or "
              "a tuple"
          )
  
      if not isinstance(no_default_updates, bool) and not isinstance(
          no_default_updates, list
      ):
          raise TypeError(
              "no_default_update should be either a boolean or "
              "a list"
          )
  
      if len(updates) > 0 and any(
          isinstance(v, Variable) for v in iter_over_pairs(updates)
      ):
          raise ValueError(
              "The updates parameter must be an OrderedDict/dict or a list of "
              "lists/tuples with 2 elements"
          )
  
      # transform params into theano.compile.In objects.
      inputs = [
          _pfunc_param_to_in(p, allow_downcast=allow_input_downcast)
          for p in params
      ]
  
      # Check if some variable is present more than once in inputs
      in_variables = [input.variable for input in inputs]
      for i, v in enumerate(in_variables):
          if v in in_variables[(i + 1) :]:
              dup_v_i = in_variables.index(v, (i + 1))
              raise UnusedInputError(
                  (
                      "Variable %s is used twice in inputs to theano.function, "
                      "at indices %i and %i.  This would result in values "
                      "provided for it being ignored. Please do not duplicate "
                      "variables in the inputs list." % (v, i, dup_v_i)
                  )
              )
  
      # Check that we are not using `givens` to replace input variables, because
      # this typically does nothing, contrary to what one may expect.
      in_var_set = set(in_variables)
      try:
          givens_pairs = list(givens.items())
      except AttributeError:
          givens_pairs = givens
      for x, y in givens_pairs:
          if x in in_var_set:
              raise RuntimeError(
                  "You are trying to replace variable '%s' through the "
                  "`givens` parameter, but this variable is an input to your "
                  "function. Replacing inputs is currently forbidden because it "
                  "has no effect. One way to modify an input `x` to a function "
                  "evaluating f(x) is to define a new input `y` and use "
                  "`theano.function([y], f(x), givens={x: g(y)})`. Another "
                  "solution consists in using `theano.clone`, e.g. like this: "
                  "`theano.function([x], "
                  "theano.clone(f(x), replace={x: g(x)}))`." % x
              )
  
      # Extend the outputs with the updates on input variables so they are also
      # cloned
      additional_outputs = [i.update for i in inputs if i.update]
      if outputs is None:
          out_list = []
      else:
          if isinstance(outputs, (list, tuple)):
              out_list = list(outputs)
          else:
              out_list = [outputs]
      extended_outputs = out_list + additional_outputs
  
      output_vars = rebuild_collect_shared(
          extended_outputs,
          in_variables,
          replace=givens,
          updates=updates,
          rebuild_strict=rebuild_strict,
          copy_inputs_over=True,
          no_default_updates=no_default_updates,
      )
      # extracting the arguments
      (
          input_variables,
          cloned_extended_outputs,
          other_stuff,
      ) = output_vars
      clone_d, update_d, update_expr, shared_inputs = other_stuff
  
      # Recover only the clones of the original outputs
      if outputs is None:
          cloned_outputs = []
      else:
          if isinstance(outputs, (list, tuple)):
              cloned_outputs = cloned_extended_outputs[: len(outputs)]
          else:
              cloned_outputs = cloned_extended_outputs[0]
  
      for i, iv in zip(inputs, input_variables):
          i.variable = iv
  
          # If needed, replace the input's update by its cloned equivalent
          if i.update:
              i.update = clone_d[i.update]
  
      for sv in shared_inputs:
          # pass value of None
          # value will be stored in the resulting functions' defaults
          # list but since the value of shared variables never needs to
          # be refed, it is not needed
          if sv in update_d:
              si = In(
                  variable=sv,
                  value=sv.container,
                  mutable=True,
                  borrow=True,
                  update=update_d[sv],
                  shared=True,
              )
          else:
              si = In(
                  variable=sv,
                  value=sv.container,
                  mutable=False,
                  borrow=True,
                  shared=True,
              )
          inputs.append(si)
  
      return orig_function(
          inputs,
          cloned_outputs,
          mode,
          accept_inplace=accept_inplace,
          name=name,
          profile=profile,
          on_unused_input=on_unused_input,
          output_keys=output_keys,
      )
  
file_path: Theano/theano/compile/pfunc.py
imports: from __future__ import absolute_import, print_function, division
  import warnings
  from theano import config
  from six import iteritems
  from theano.compile import orig_function, In, Out
  from theano.compile import UnusedInputError
  from theano.compile.sharedvalue import SharedVariable, shared
  from theano.compile.profiling import ProfileStats
  from theano.gof import Variable, Constant
  import logging
label_desc: Use image node_id 2 for calling a global function with example usage: pfunc(params, outputs, mode, updates, givens, no_default_updates, accept_inplace, name, rebuild_strict, allow_input_downcast, profile, on_unused_input, output_keys) and returns: orig_function