class_docstr: Class to represent node in Markov decision process. A stage has states
  to represent different schedules of the current node. Since in this problem
  the action is the schedule selected for current node, action can be fully
  represented by states. No extra attribute needs for action.
  
  In most cases, instance of this class should be created through DPTuner.
class_docstr_tok: ["Class","to","represent","node","in","Markov","decision","process",".","A","stage","has","states","to","represent","different","schedules","of","the","current","node",".","Since","in","this","problem","the","action","is","the","schedule","selected","for","current","node",",","action","can","be","fully","represented","by","states",".","No","extra","attribute","needs","for","action",".","In","most","cases",",","instance","of","this","class","should","be","created","through","DPTuner","."]
docstr: State creation routine for multi_input operator
  
  In tvm, layout transformation for an elemwise-like follow the rule which
  all input operators transform their layouts to the leftmost input operator
  layout. For example:
                      elemwise-sum
                      |    |    |
                      |    |    |
                     op0  op1  op2
  In this block, the possible layout transformations are: op1 -> op0 and op2 -> op0.
  In graph tuning, a 3-D array with shape (k0, k1, k2) can represent the layout
  transformations between these three nodes. It is also possible some earlier states
  belong to other nodes(We name them as dependency) are required for dynamic programming.
  The final states array for this elemwise-sum can be with shape (e0, k0, k1, e1, k2).
  To iterate through all states, we first align the shape of op0, op1 and op2 to be
  (e0, k0, k1, e1, k2) by broadcasting the original states. We also record the axis of
  each input node in the states array, together with the multiplier. For example,
  the axis index for op0 is 1, and multiplier is k1 * e1 * k2. If current iterating index
  in the flatten array is i, the index of op0 can be computed as:
  i % (k0 * k1 * e1 * k2) // (k1 * e1 * k2).
docstr_tok: ["State","creation","routine","for","multi_input","operator","In","tvm",",","layout","transformation","for","an","elemwise-like","follow","the","rule","which","all","input","operators","transform","their","layouts","to","the","leftmost","input","operator","layout",".","For","example",":","elemwise-sum","|","|","|","|","|","|","op0","op1","op2","In","this","block",",","the","possible","layout","transformations","are",":","op1","-",">","op0","and","op2","-",">","op0",".","In","graph","tuning",",","a","3-D","array","with","shape","(","k0",",","k1",",","k2",")","can","represent","the","layout","transformations","between","these","three","nodes",".","It","is","also","possible","some","earlier","states","belong","to","other","nodes","(","We","name","them","as","dependency",")","are","required","for","dynamic","programming",".","The","final","states","array","for","this","elemwise-sum","can","be","with","shape","(","e0",",","k0",",","k1",",","e1",",","k2",")",".","To","iterate","through","all","states",",","we","first","align","the","shape","of","op0",",","op1","and","op2","to","be","(","e0",",","k0",",","k1",",","e1",",","k2",")","by","broadcasting","the","original","states",".","We","also","record","the","axis","of","each","input","node","in","the","states","array",",","together","with","the","multiplier",".","For","example",",","the","axis","index","for","op0","is","1",",","and","multiplier","is","k1","*","e1","*","k2",".","If","current","iterating","index","in","the","flatten","array","is","i",",","the","index","of","op0","can","be","computed","as",":","i","%","(","k0","*","k1","*","e1","*","k2",")","\/\/","(","k1","*","e1","*","k2",")","."]
code: def _create_multi_inputs_states(self):
      """State creation routine for multi_input operator
  
      In tvm, layout transformation for an elemwise-like follow the rule which
      all input operators transform their layouts to the leftmost input operator
      layout. For example:
                          elemwise-sum
                          |    |    |
                          |    |    |
                         op0  op1  op2
      In this block, the possible layout transformations are: op1 -> op0 and op2 -> op0.
      In graph tuning, a 3-D array with shape (k0, k1, k2) can represent the layout
      transformations between these three nodes. It is also possible some earlier states
      belong to other nodes(We name them as dependency) are required for dynamic programming.
      The final states array for this elemwise-sum can be with shape (e0, k0, k1, e1, k2).
      To iterate through all states, we first align the shape of op0, op1 and op2 to be
      (e0, k0, k1, e1, k2) by broadcasting the original states. We also record the axis of
      each input node in the states array, together with the multiplier. For example,
      the axis index for op0 is 1, and multiplier is k1 * e1 * k2. If current iterating index
      in the flatten array is i, the index of op0 can be computed as:
      i % (k0 * k1 * e1 * k2) // (k1 * e1 * k2).
      """
      full_input_node_list = list(self._global_in_nodes_dict[self._idx])
      input_index_list = []
      # Remove input and ruled_out nodes
      for input_idx in full_input_node_list:
          input_node = self._global_node_list[input_idx]
          if not is_boundary_node(input_node, self._global_input_names):
              input_index_list.append(input_idx)
  
      # Generate new states
      states_list, aligned_node_list = DPStage.align_states(
          input_index_list,
          self._global_stage_dict,
          self._global_node_list,
      )
      (
          target_node_idx,
          target_major_axis,
          target_multiplier,
          target_states,
      ) = states_list[0]
      aligned_shape = target_states.shape
      self._full_states = (
          np.zeros(aligned_shape).astype("float32").flatten()
      )
      self._full_states_idx = list(aligned_node_list)
      num_states = self._full_states.shape[0]
      node_time_counted = [
          item[0] in self._global_counted_nodes_set
          for item in states_list
      ]
      target_states = target_states.flatten()
      src_states_list = [
          states_list[i][3].flatten()
          for i in range(1, len(states_list))
      ]
  
      for i in range(num_states):
          target_sch_idx = (
              i % (target_multiplier * aligned_shape[target_major_axis])
          ) // target_multiplier
          if node_time_counted[0]:
              new_state = 0
          else:
              new_state = target_states[i]
  
          for j in range(1, len(states_list)):
              src_states = src_states_list[j - 1]
              (
                  src_node_idx,
                  src_major_axis,
                  src_multiplier,
                  _,
              ) = states_list[j]
              src_sch_idx = (
                  i % (src_multiplier * aligned_shape[src_major_axis])
              ) // src_multiplier
              layout_transform_time = (
                  self._global_layout_transform_interlayer_cost[
                      (src_node_idx, target_node_idx)
                  ][src_sch_idx][target_sch_idx]
              )
  
              if node_time_counted[j]:
                  new_state += layout_transform_time
              else:
                  new_state += layout_transform_time + src_states[i]
              self._full_states[i] = new_state
  
      for i, node_counted in enumerate(node_time_counted):
          if not node_counted:
              self._global_counted_nodes_set.add(states_list[i][0])
      self._full_states = self._full_states.reshape(aligned_shape)
  
      # Remove dependency to reduce states
      reduced_states = np.array(self._full_states)
      reduced_states_transpose = [states_list[0][1]]
      reduced_states_dep_list = []
      self._dep = []
      for i in range(len(reduced_states.shape)):
          if i != states_list[0][1]:
              reduced_states_transpose.append(i)
              reduced_states_dep_list.append(aligned_node_list[i])
      reduced_states = np.transpose(
          reduced_states, reduced_states_transpose
      )
      shift = 0
      for i, dep in enumerate(reduced_states_dep_list):
          if (
              dep not in self._global_dep_dict
              or len(self._global_dep_dict[dep]) == 1
          ):
              self._global_dep_dict.pop(dep, None)
              reduced_states = np.amin(
                  reduced_states, axis=i + 1 - shift
              )
              shift += 1
          else:
              self._dep.append(dep)
      self._states = reduced_states
  
      # Update dependency
      for dep in self._dep:
          self._global_dep_dict[dep].remove(self._idx)
          for child in self._global_out_nodes_dict[self._idx]:
              self._global_dep_dict[dep].add(child)
      if len(self._global_out_nodes_dict[self._idx]) > 1:
          self._global_dep_dict[self._idx] = set()
          for child in self._global_out_nodes_dict[self._idx]:
              self._global_dep_dict[self._idx].add(child)
  
file_path: tvm/python/tvm/autotvm/graph_tuner/dynamic_programming_stage.py
imports: import numpy
  from .utils import is_boundary_node
label_desc: Use image node_id 4 for calling the DPStage obj's underlying member method code with example usage: obj._create_multi_inputs_states() without return types