code: def tile(cls, op):
      in_tensor = op.inputs[0]
      out_tensor = op.outputs[0]
  
      state = np.random.RandomState(op.seed)
      if len(op.input.chunks) == 1:
          chunk_op = op.copy().reset_key()
          chunk_op._state = None
          chunk_op.seed = gen_random_seeds(1, state)[0]
          c = op.input.chunks[0]
          chunk = chunk_op.new_chunk(
              [c], shape=c.shape, index=c.index, order=c.order
          )
          new_op = op.copy()
          return new_op.new_tensors(
              op.inputs,
              shape=out_tensor.shape,
              order=out_tensor.order,
              nsplits=op.input.nsplits,
              chunks=[chunk],
          )
  
      chunk_size = in_tensor.chunk_shape[op.axis]
      map_seeds = gen_random_seeds(chunk_size, state)
      reduce_seeds = gen_random_seeds(chunk_size, state)
      reduce_chunks = []
      if in_tensor.ndim > 1:
          cs = in_tensor.chunk_shape
          left_chunk_shape = cs[: op.axis] + cs[op.axis + 1 :]
          idx_iter = itertools.product(
              *[range(s) for s in left_chunk_shape]
          )
      else:
          idx_iter = [()]
      for idx in idx_iter:
          map_chunks = []
          for j in range(chunk_size):
              in_idx = list(idx)
              in_idx.insert(op.axis, j)
              c = in_tensor.cix[tuple(in_idx)]
              chunk_op = TensorPermutation(
                  stage=OperandStage.map,
                  seed=map_seeds[c.index[op.axis]],
                  axis=op.axis,
                  reduce_size=chunk_size,
                  dtype=c.dtype,
                  gpu=c.op.gpu,
              )
              map_chunk = chunk_op.new_chunk(
                  [c],
                  shape=c.shape,
                  index=c.index,
                  order=out_tensor.order,
              )
              map_chunks.append(map_chunk)
  
          proxy_chunk = TensorShuffleProxy(
              dtype=out_tensor.dtype, _tensor_keys=[in_tensor.key]
          ).new_chunk(map_chunks, shape=())
  
          for c in map_chunks:
              chunk_op = TensorPermutation(
                  stage=OperandStage.reduce,
                  n_reducers=len(map_chunks),
                  seed=reduce_seeds[c.index[op.axis]],
                  axis=op.axis,
              )
              chunk_shape = list(c.shape)
              chunk_shape[op.axis] = np.nan
              reduce_chunk = chunk_op.new_chunk(
                  [proxy_chunk],
                  shape=tuple(chunk_shape),
                  order=out_tensor.order,
                  index=c.index,
                  dtype=out_tensor.dtype,
              )
              reduce_chunks.append(reduce_chunk)
  
      new_op = op.copy()
      nsplits = list(in_tensor.nsplits)
      nsplits[op.axis] = [np.nan] * len(nsplits[op.axis])
      return new_op.new_tensors(
          op.inputs,
          out_tensor.shape,
          order=out_tensor.order,
          chunks=reduce_chunks,
          nsplits=nsplits,
      )
  
imports: import itertools
  from numbers import Integral
  import numpy
  from ...None import opcodes
  from ...core.operand import OperandStage
  from ...serialization.serializables import KeyField, Int32Field
  from ..operands import TensorOperandMixin, TensorShuffleProxy
  from ..utils import gen_random_seeds, validate_axis
  from ..datasource import tensor
  from ..array_utils import as_same_device, device
  from .core import TensorRandomMapReduceOperand
label_desc: Use image node_id 3 for calling the TensorPermutation obj's underlying member method code with example usage: obj.tile(cls, op) and returns: new_op, new_op