class_docstr: `FunctionMaker` is the class to `create` `Function` instances.
  
  This class has the fgraph, the optimizer, 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 optimize 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 Theano 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.
class_docstr_tok: ["`","FunctionMaker","`","is","the","class","to","`","create","`","`","Function","`","instances",".","This","class","has","the","fgraph",",","the","optimizer",",","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","optimize","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","Theano","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 __init__(
      self,
      inputs,
      outputs,
      mode=None,
      accept_inplace=False,
      function_builder=Function,
      profile=None,
      on_unused_input=None,
      fgraph=None,
      output_keys=None,
      name=None,
  ):
      # Save the provided mode, not the instanciated mode.
      # The instanciated mode don't pickle and if we unpickle a Theano
      # function and it get re-compiled, we want the current optimizer to be
      # used, not the optimizer when it was saved.
      self.mode = mode
      mode = theano.compile.mode.get_mode(mode)
  
      # Assert old way of working isn't used
      if getattr(mode, "profile", None):
          raise TypeError(
              "profile passed via 'mode'. This isn't supported anymore"
          )
      self.profile = profile
      if profile:
          # This is very important:
          # 1) We preload the cache here to don't have its timming
          #    included in optimization that compile function.
          # 2) Do not refresh the cache here by default. It cause
          #    too much execution time during testing as we compile
          #    much more functions then the number of compile c
          #    module.
          theano.gof.cc.get_module_cache().refresh()
      # Handle the case where inputs and/or outputs is a single
      # Variable (not in a list)
      unpack_single = False
      return_none = False
      if outputs is None:
          return_none = True
          outputs = []
      if not isinstance(outputs, (list, tuple)):
          unpack_single = True
          outputs = [outputs]
      if not isinstance(inputs, (list, tuple)):
          inputs = [inputs]
  
      # Wrap them in In or Out instances if needed.
      inputs = [self.wrap_in(i) for i in inputs]
      outputs = [self.wrap_out(o) for o in outputs]
      _inputs = gof.graph.inputs(
          [o.variable for o in outputs]
          + [i.update for i in inputs if getattr(i, "update", False)]
      )
  
      # Check if some input variables are unused
      self._check_unused_inputs(inputs, outputs, on_unused_input)
  
      # Make a list of (SymbolicInput|SymblicInputKits, indices,
      # [SymbolicInput,...]), one tuple for each input. (See
      # Function.indices for more details)
      indices = [
          [input] + self.expand_in(input, _inputs) for input in inputs
      ]
  
      if fgraph is None:
          need_opt = True
          # make the fgraph (copies the graph, creates NEW INPUT AND
          # OUTPUT VARIABLES)
          fgraph, additional_outputs = std_fgraph(
              inputs, outputs, accept_inplace
          )
          fgraph.profile = profile
      else:
          # fgraph is already an optimized one
          need_opt = False
          updates = [spec.update for spec in inputs if spec.update]
          additional_outputs = list(map(SymbolicOutput, updates))
  
      self.fgraph = fgraph
  
      # Fetch the optimizer and linker
      optimizer, linker = mode.optimizer, copy.copy(mode.linker)
      if need_opt:
          compute_test_value_orig = theano.config.compute_test_value
          limit_orig = theano.config.traceback.limit
          # Why we add stack on node when it get done in output var?
          try:
              # optimize the fgraph
              theano.config.compute_test_value = (
                  theano.config.compute_test_value_opt
              )
              theano.config.traceback.limit = (
                  theano.config.traceback.compile_limit
              )
              start_optimizer = time.time()
  
              # In case there is an error during optimization.
              optimizer_profile = None
              opt_time = None
  
              # now optimize the graph
              if theano.config.cache_optimizations:
                  optimizer_profile = self.optimize_graph_with_cache(
                      optimizer, inputs, outputs
                  )
              else:
                  optimizer_profile = optimizer(fgraph)
  
              end_optimizer = time.time()
              opt_time = end_optimizer - start_optimizer
              _logger.debug("Optimizing took %f seconds", opt_time)
  
              # Add deep copy to respect the memory interface
              insert_deepcopy(
                  fgraph, inputs, outputs + additional_outputs
              )
          finally:
              theano.config.compute_test_value = compute_test_value_orig
              theano.config.traceback.limit = limit_orig
  
              # If the optimizer got interrupted
              if opt_time is None:
                  end_optimizer = time.time()
                  opt_time = end_optimizer - start_optimizer
              theano.compile.profiling.total_graph_opt_time += opt_time
              if profile:
                  if optimizer_profile is None and hasattr(
                      optimizer, "pre_profile"
                  ):
                      optimizer_profile = optimizer.pre_profile
                  profile.optimizer_time += opt_time
                  if theano.config.profile_optimizer:
                      profile.optimizer_profile = (
                          optimizer,
                          optimizer_profile,
                      )
              # IF False, if mean the profile for that function was explicitly disabled
              elif (
                  theano.config.profile_optimizer
                  and profile is not False
              ):
                  warnings.warn(
                      (
                          "config.profile_optimizer requires config.profile to "
                          " be set to True as well"
                      ),
                      stacklevel=3,
                  )
  
      # initialize the linker
      if not hasattr(linker, "accept"):
          raise ValueError(
              "'linker' parameter of FunctionMaker should be "
              "a Linker with an accept method or one of %s"
              % list(theano.compile.mode.predefined_linkers.keys())
          )
  
      # the 'no_borrow' outputs are the ones for which that we can't
      # return the internal storage pointer.
      assert len(fgraph.outputs) == len(outputs + additional_outputs)
      no_borrow = [
          output
          for output, spec in zip(
              fgraph.outputs, outputs + additional_outputs
          )
          if not spec.borrow
      ]
      if no_borrow:
          self.linker = linker.accept(
              fgraph,
              no_recycling=infer_reuse_pattern(fgraph, no_borrow),
              profile=profile,
          )
      else:
          self.linker = linker.accept(fgraph, profile=profile)
  
      if hasattr(linker, "accept_var_updates"):
          # hacky thing so VMLinker knows about updates
          self.linker.accept_var_updates(
              fgraph_updated_vars(fgraph, inputs)
          )
      fgraph.name = name
      self.indices = indices
      self.inputs = inputs
      self.expanded_inputs = inputs
      self.outputs = outputs
      self.unpack_single = unpack_single
      self.return_none = return_none
      self.accept_inplace = accept_inplace
      self.function_builder = function_builder
      self.on_unused_input = (
          on_unused_input  # Used for the pickling/copy
      )
      self.output_keys = output_keys
      self.name = name
  
      self.required = [(i.value is None) for i in self.inputs]
      self.refeed = [
          (
              i.value is not None
              and not isinstance(i.value, gof.Container)
              and i.update is None
          )
          for i in self.inputs
      ]
  
imports: from __future__ import absolute_import, print_function, division
  import copy
  from six import string_types, iteritems, iterkeys
  from six.moves import xrange
  import six.moves.copyreg
  import six.moves.cPickle
  from itertools import chain
  import time
  import warnings
  import numpy
  import theano
  from theano import config, gof
  from theano.compat import izip
  from theano.gof import graph
  import theano.compile.profiling
  from theano.compile.io import In, SymbolicInput, SymbolicOutput
  from theano.compile.ops import deep_copy_op, view_op
  from theano.gof.graph import is_same_graph
  from theano.gof.op import ops_with_inner_function
  import logging
label_desc: Use image node_id 5 to create a new FunctionMaker object from inherited base classes: object with example: obj = FunctionMaker(inputs, outputs, mode, accept_inplace, function_builder, profile, on_unused_input, fgraph, output_keys, name)