class_docstr: Represents Operation in MIL.
  
  # Properties
  name (str):
      The name of the operation
  
  input_types (InputSpec, class attr):
      Read-only named input types from all subclasses. Input types are used
      to validate `inputs`.
      If an input arg name start with prefix `_`, that indicates the input has the following properties:
      1. Most of the time, the input is type of ``InternalInputType`` and
         used only in pymil scope. It doesn't have the corresponding arg / attr
         in the MIL framework definition.
      2. It won't be printed in pymil.
  
  inputs [_input_vars] (dict of str --> Var):
      An Operation (subclass of Operation) only has access to input Var,
      which is already validated against `input_spec`.
  
  outputs [_output_vars] (list of Var):
      List of output var based on type inference. Read-only
class_docstr_tok: ["Represents","Operation","in","MIL",".","#","Properties","name","(","str",")",":","The","name","of","the","operation","input_types","(","InputSpec",",","class","attr",")",":","Read-only","named","input","types","from","all","subclasses",".","Input","types","are","used","to","validate","`","inputs","`",".","If","an","input","arg","name","start","with","prefix","`","_","`",",","that","indicates","the","input","has","the","following","properties",":","1",".","Most","of","the","time",",","the","input","is","type","of","``","InternalInputType","``","and","used","only","in","pymil","scope",".","It","does","n't","have","the","corresponding","arg","\/","attr","in","the","MIL","framework","definition",".","2",".","It","wo","n't","be","printed","in","pymil",".","inputs","[","_input_vars","]","(","dict","of","str","--",">","Var",")",":","An","Operation","(","subclass","of","Operation",")","only","has","access","to","input","Var",",","which","is","already","validated","against","`","input_spec","`",".","outputs","[","_output_vars","]","(","list","of","Var",")",":","List","of","output","var","based","on","type","inference",".","Read-only"]
docstr: Perform type inference and auto_val computation based on new input Vars
  in kwargs. If self._output_vars is None then we generate _output_vars;
  otherwise no new Var is created, but type inference result is verified
  against existing _output_vars, if overwrite_output is False.
  
  If overwrite_output is True, then the type inference result overwrites the
  existing _output_vars
docstr_tok: ["Perform","type","inference","and","auto_val","computation","based","on","new","input","Vars","in","kwargs",".","If","self._output_vars","is","None","then","we","generate","_output_vars",";","otherwise","no","new","Var","is","created",",","but","type","inference","result","is","verified","against","existing","_output_vars",",","if","overwrite_output","is","False",".","If","overwrite_output","is","True",",","then","the","type","inference","result","overwrites","the","existing","_output_vars"]
code: def type_value_inference(self, overwrite_output=False):
      """
      Perform type inference and auto_val computation based on new input Vars
      in kwargs. If self._output_vars is None then we generate _output_vars;
      otherwise no new Var is created, but type inference result is verified
      against existing _output_vars, if overwrite_output is False.
  
      If overwrite_output is True, then the type inference result overwrites the
      existing _output_vars
      """
      output_types = self.type_inference()
      if not isinstance(output_types, tuple):
          output_types = (output_types,)
      output_vals = self._auto_val(output_types)
      try:
          output_names = self.output_names()
          if not isinstance(output_names, tuple):
              output_names = (output_names,)
      except NotImplementedError:
          if len(output_types) > 1:
              output_names = tuple(
                  str(i) for i, _ in enumerate(output_types)
              )
          else:
              output_names = ("",)  # output name same as op name.
  
      # Combine (output_names, output_types, output_vals) to create output
      # Vars.
      if self._output_vars is None:
          self._output_vars = []
          for i, (n, sym_type, sym_val) in enumerate(
              zip(output_names, output_types, output_vals)
          ):
              name = self.name + "_" + n if n != "" else self.name
              if types.is_list(sym_type):
                  new_var = ListVar(
                      name,
                      elem_type=sym_type.T[0],
                      init_length=sym_type.T[1],
                      dynamic_length=sym_type.T[2],
                      sym_val=sym_val
                      if (
                          sym_val is not None
                          and isinstance(sym_val.val, list)
                      )
                      else None,
                      op=self,
                      op_output_idx=i,
                  )
                  elem_shape = new_var.elem_shape
                  if elem_shape is not None and len(elem_shape) >= 5:
                      msg = (
                          "Core ML only supports list of elements with rank <= 4. "
                          'Layer "{}", with type "{}", outputs a list of rank {} tensors.'
                      ).format(self.name, self.op_type, len(elem_shape))
                      raise ValueError(msg)
              else:
                  if types.is_tensor(sym_type) and types.is_complex(
                      sym_type.T[0]
                  ):
                      # Only `complex` op needs to maintain the real/imag data in the ComplexVar.
                      # For other ops, this ComplexVar is just a placeholder here, which will be
                      # replaced by a newly created ComplexVar during complex ops lowering pass.
                      real_data = (
                          self.real_data
                          if self.op_type == "complex"
                          else None
                      )
                      imag_data = (
                          self.imag_data
                          if self.op_type == "complex"
                          else None
                      )
                      new_var = ComplexVar(
                          name,
                          sym_type,
                          sym_val,
                          op=self,
                          op_output_idx=i,
                          real=real_data,
                          imag=imag_data,
                      )
                  else:
                      new_var = Var(
                          name,
                          sym_type,
                          sym_val,
                          op=self,
                          op_output_idx=i,
                      )
              self._output_vars.append(new_var)
      else:
          # Check new inference result against existing self._output_vars.
          for i, (sym_type, sym_val) in enumerate(
              zip(output_types, output_vals)
          ):
              out_var = self._output_vars[i]
              # Check type inference
              if overwrite_output:
                  out_var._sym_type = sym_type
              elif not is_compatible_type(sym_type, out_var.sym_type):
                  msg = "Output Var {} in op {} type changes with new input Vars"
                  raise ValueError(msg.format(out_var.name, self.name))
  
              # Check value inference
              if overwrite_output:
                  out_var._sym_val = sym_val
  
              if sym_val is not None and out_var.sym_val is not None:
                  if np.any(sym_val.val != out_var.sym_val):
                      if overwrite_output:
                          out_var._sym_val = sym_val
                      else:
                          msg = "value_inference differs for var {} in op {}"
                          if not _is_compatible_symbolic_array(
                              sym_val.val, out_var.sym_val
                          ):
                              raise ValueError(
                                  msg.format(out_var.name, self.name)
                              )
  
              for o in self.outputs:
                  o._set_nonreplaceable_vars_upstream()
  
imports: from typing import Any, Dict, Tuple
  import numpy
  from coremltools.converters.mil.mil import types
  from coremltools.converters.mil.mil.types import is_compatible_type
  from coremltools.converters.mil.mil.types.symbolic import any_symbolic, is_symbolic
  from .None import SPACES
  from .input_type import DefaultInputs, TensorInputType, TupleInputType
  from .var import ComplexVar, InternalVar, ListVar, Var
label_desc: Use image node_id 5 for calling the Operation obj's underlying member method code with example usage: obj.type_value_inference(overwrite_output) without return types