class_docstr: Fuses 3 operators reshape->transpose->reshape which is equivalent to
  pytorch's pixel_shuffle layer
class_docstr_tok: ["Fuses","3","operators","reshape-",">","transpose-",">","reshape","which","is","equivalent","to","pytorch","'s","pixel_shuffle","layer"]
docstr: Pixel shuffle is implemented using 3 operators:
      - Reshape(1, channels, scale, scale, height, width)
      - Transpose(0, 1, 4, 2, 5, 3)
      - Reshape(1, channels, height * scale, width * scale)
  CoreML Reshape and Transpose layers don't support tensors with more
  than 4 dimensions. Thus we change above sequence of operators to the
  following equivalent sequence:
      - Reshape(channels, scale * scale, height, width)
      - Transpose(0, 2, 1, 3)
      - Reshape(channels * height, scale, scale, width)
      - Transpose(0, 1, 3, 2)
      - Reshape(1, channels, height * scale, width * scale)
code: def merge(
      self, graph, nodes
  ):  # type: (Graph, Sequence[Node]) -> Sequence[Node]
      """
      Pixel shuffle is implemented using 3 operators:
          - Reshape(1, channels, scale, scale, height, width)
          - Transpose(0, 1, 4, 2, 5, 3)
          - Reshape(1, channels, height * scale, width * scale)
      CoreML Reshape and Transpose layers don't support tensors with more
      than 4 dimensions. Thus we change above sequence of operators to the
      following equivalent sequence:
          - Reshape(channels, scale * scale, height, width)
          - Transpose(0, 2, 1, 3)
          - Reshape(channels * height, scale, scale, width)
          - Transpose(0, 1, 3, 2)
          - Reshape(1, channels, height * scale, width * scale)
      """
      reshape_1 = nodes[0]
      transpose_1 = nodes[1]
      transpose_1.children = []
  
      shape = reshape_1.input_tensors[reshape_1.inputs[1]]
  
      channels = shape[1]
      scale = shape[2]
      height = shape[4]
      width = shape[5]
  
      reshape_1.input_tensors[reshape_1.inputs[1]] = np.asarray(
          [channels, scale * scale, height, width]
      )
      transpose_1.attrs["perm"] = [0, 2, 1, 3]
  
      reshape_output_name = "pixel_shuffle_reshape"
      transpose_output_name = "pixel_shuffle_transpose"
  
      transpose_1.outputs = [
          self.get_unique_edge_name(graph, transpose_output_name)
      ]
  
      shape_name_second_reshape = self.get_unique_edge_name(
          graph, reshape_output_name
      )
      output_name_second_reshape = self.get_unique_edge_name(
          graph, reshape_output_name
      )
      reshape_2 = Node(
          reshape_output_name,
          "Reshape",
          {},
          [transpose_1.outputs[0], shape_name_second_reshape],
          [output_name_second_reshape],
      )
      reshape_2.input_tensors[shape_name_second_reshape] = np.asarray(
          [channels * height, scale, scale, width]
      )
      transpose_1.add_child(reshape_2)
  
      transpose_2 = Node(
          transpose_output_name,
          "Transpose",
          {"perm": [0, 1, 3, 2]},
          reshape_2.outputs,
          [self.get_unique_edge_name(graph, transpose_output_name)],
      )
      reshape_2.add_child(transpose_2)
  
      final_reshape = nodes[2]
      final_reshape.inputs = [
          transpose_2.outputs[0],
          nodes[2].inputs[1],
      ]
      final_reshape.parents = []
      transpose_2.add_child(final_reshape)
      return [
          reshape_1,
          transpose_1,
          reshape_2,
          transpose_2,
          final_reshape,
      ]
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  from typing import Sequence, Text, Dict, List
  import numpy
  from onnx import TensorProto
  from ._graph import Graph, Node
label_desc: Use image node_id 4 for calling the PixelShuffleFuser obj's underlying member method code with example usage: obj.merge(graph, nodes) and returns: list