class_docstr: A data structure to cache the sequence of functional calls.
  
  Each autoobject contains a callcache object. The call sequences will
  be gradually passed down to the last function call.
  Internally, it containes a callqueue which collects the function calls in    order and a tensors table which is used to collect the resulting    tensors from function execution.
class_docstr_tok: ["A","data","structure","to","cache","the","sequence","of","functional","calls",".","Each","autoobject","contains","a","callcache","object",".","The","call","sequences","will","be","gradually","passed","down","to","the","last","function","call",".","Internally",",","it","containes","a","callqueue","which","collects","the","function","calls","in","order","and","a","tensors","table","which","is","used","to","collect","the","resulting","tensors","from","function","execution","."]
docstr: Execute the function calls and construct the tensor graph.
  
  :param inputs: model input
  :param outputs: model outputs
  :param trial: the current trial which provides the sampled
      hyperparameters.
docstr_tok: ["Execute","the","function","calls","and","construct","the","tensor","graph",".",":","param","inputs",":","model","input",":","param","outputs",":","model","outputs",":","param","trial",":","the","current","trial","which","provides","the","sampled","hyperparameters","."]
code: def execute(inputs, outputs, trial, backend):
      """
      Execute the function calls and construct the tensor graph.
  
      :param inputs: model input
      :param outputs: model outputs
      :param trial: the current trial which provides the sampled
          hyperparameters.
      """
  
      def _replace_autoobj(n, cache):
          if isinstance(n, AutoObject):
              new_n = cache.get_tensor(n)
          else:
              new_n = n
          return new_n
  
      def _process_arguments(arguments, cache):
          # TODO refactor
          if isinstance(arguments, list):
              new_arguments = [
                  _process_arguments(arg, cache) for arg in arguments
              ]
          elif isinstance(arguments, tuple):
              lst = [
                  _process_arguments(arg, cache) for arg in arguments
              ]
              new_arguments = tuple(lst)
          elif isinstance(arguments, dict):
              new_arguments = arguments.copy()
              for name, arg in new_arguments.items():
                  new_arg = _process_arguments(arg, cache)
                  new_arguments[name] = new_arg
          else:
              new_arguments = _replace_autoobj(arguments, cache)
          return new_arguments
  
      out_cache = outputs._callgraph
      # get output tensors
      for call_type, caller, arguments in out_cache.calls:
          if call_type == CALLTYPE.LAYER_CALL:
              new_arguments = _process_arguments(arguments, out_cache)
              # layer is an auto object
              invalidInputError(
                  isinstance(caller, AutoObject),
                  "caller should be AutoObject",
              )
              instance = backend.instantiate(trial, caller)
              # the actual excution of the functional API
              out_tensor = instance(new_arguments)
          elif call_type == CALLTYPE.FUNC_SLICE:
              source, slice_args = arguments
              slice_args, slice_kwargs = slice_args
              source_tensor = out_cache.get_tensor(source)
              # the actual excution of the functional API
              out_tensor = source_tensor.__getitem__(
                  *slice_args, **slice_kwargs
              )
          elif call_type == CALLTYPE.FUNC_CALL:
              # out_tensor = backend.instantiate(trial, caller)
              new_arguments = _process_arguments(arguments, out_cache)
              invalidInputError(
                  isinstance(caller, AutoObject),
                  "caller should be AutoObject",
              )
              # assume tensors does not exist in kwargs
              # replace only the non-kwargs with new_arguments
              # TODO revisit to validate the parent tensors in kwargs
              caller.args, caller.kwargs = new_arguments
              out_tensor = backend.instantiate(trial, caller)
          else:
              invalidInputError(False, "Unexpected CallType: %s" % type)
          out_cache.add_tensor(caller, out_tensor)
      out_tensors = out_cache.get_tensor(outputs)
  
      # get input tensors
      if isinstance(inputs, list):
          in_tensors = [out_cache.get_tensor(inp) for inp in inputs]
      else:
          in_tensors = out_cache.get_tensor(inputs)
  
      return (in_tensors, out_tensors)
  
file_path: BigDL/python/nano/src/bigdl/nano/automl/hpo/callgraph.py
imports: from .space import AutoObject
  from enum import Enum
  from bigdl.nano.utils.common import invalidInputError
label_desc: Use image node_id 10 for calling the CallCache obj's underlying member method code with example usage: obj.execute(inputs, outputs, trial, backend) and returns: in_tensors,  out_tensors, new_n, new_arguments