code: def tile(cls, op):
      if len(op.input.chunks) == 1:
          return cls._tile_one_chunk(op)
  
      from ..arithmetic.subtract import TensorSubtract
      from ..arithmetic.add import TensorTreeAdd
      from ..base.transpose import TensorTranspose
      from ..merge.vstack import vstack
      from ..merge.hstack import hstack
      from ..datasource.zeros import TensorZeros, zeros
      from .dot import TensorDot
      from .solve_triangular import TensorSolveTriangular
  
      P, L, U = op.outputs
      raw_in_tensor = in_tensor = op.input
      out_tensor = op.outputs[0]
  
      if in_tensor.shape[0] > in_tensor.shape[1]:
          zero_tensor = zeros(
              (
                  in_tensor.shape[0],
                  in_tensor.shape[0] - in_tensor.shape[1],
              ),
              dtype=in_tensor.dtype,
              sparse=in_tensor.issparse(),
              gpu=in_tensor.op.gpu,
              chunk_size=(
                  in_tensor.nsplits[0],
                  max(in_tensor.nsplits[1]),
              ),
              order=in_tensor.order.value,
          )
          in_tensor = yield from recursive_tile(
              hstack([in_tensor, zero_tensor])
          )
      elif in_tensor.shape[0] < in_tensor.shape[1]:
          zero_tensor = zeros(
              (
                  in_tensor.shape[1] - in_tensor.shape[0],
                  in_tensor.shape[1],
              ),
              dtype=in_tensor.dtype,
              sparse=in_tensor.issparse(),
              gpu=in_tensor.op.gpu,
              chunk_size=(
                  max(in_tensor.nsplits[0]),
                  in_tensor.nsplits[1],
              ),
              order=in_tensor.order.value,
          )
          in_tensor = yield from recursive_tile(
              vstack([in_tensor, zero_tensor])
          )
  
      if has_unknown_shape(in_tensor):
          yield
      if in_tensor.nsplits[0] != in_tensor.nsplits[1]:
          # all chunks on diagonal should be square
          nsplits = in_tensor.nsplits[0]
          in_tensor = yield from recursive_tile(
              in_tensor.rechunk([nsplits, nsplits])
          )
  
      (
          p_chunks,
          p_invert_chunks,
          lower_chunks,
          l_permuted_chunks,
          upper_chunks,
      ) = (
          {},
          {},
          {},
          {},
          {},
      )
      for i in range(in_tensor.chunk_shape[0]):
          for j in range(in_tensor.chunk_shape[1]):
              if i < j:
                  chunk_shape = (
                      in_tensor.nsplits[0][i],
                      in_tensor.nsplits[1][j],
                  )
                  p_chunk = TensorZeros(
                      sparse=op.sparse,
                      order=out_tensor.order.value,
                      shape=chunk_shape,
                  ).new_chunk(
                      None,
                      shape=chunk_shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  lower_chunk = TensorZeros(
                      sparse=op.sparse,
                      order=out_tensor.order.value,
                      shape=chunk_shape,
                  ).new_chunk(
                      None,
                      shape=chunk_shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  p_chunks[p_chunk.index] = p_chunk
                  lower_chunks[lower_chunk.index] = lower_chunk
  
                  target_u = in_tensor.cix[i, j]
                  p_invert = p_invert_chunks[i, i]
                  target = TensorDot(
                      dtype=U.dtype, sparse=U.op.sparse
                  ).new_chunk(
                      [p_invert, target_u],
                      shape=(p_invert.shape[0], target_u.shape[1]),
                      order=out_tensor.order,
                  )
                  if i > 0:
                      prev_chunks_u = []
                      for p in range(i):
                          a, b = lower_chunks[i, p], upper_chunks[p, j]
                          prev_chunk = TensorDot(
                              dtype=U.dtype, sparse=U.op.sparse
                          ).new_chunk(
                              [a, b],
                              shape=(a.shape[0], b.shape[1]),
                              order=out_tensor.order,
                          )
                          prev_chunks_u.append(prev_chunk)
                      if len(prev_chunks_u) == 1:
                          s = prev_chunks_u[0]
                      else:
                          tree_add_op = TensorTreeAdd(
                              args=prev_chunks_u,
                              dtype=prev_chunks_u[0].dtype,
                              sparse=op.sparse,
                          )
                          s = tree_add_op.new_chunk(
                              prev_chunks_u,
                              shape=prev_chunks_u[0].shape,
                          )
                      target = TensorSubtract(
                          dtype=U.dtype,
                          lhs=target,
                          rhs=s,
                          order=out_tensor.order.value,
                      ).new_chunk(
                          [target, s],
                          shape=target.shape,
                          order=out_tensor.order,
                      )
                  upper_chunk = TensorSolveTriangular(
                      lower=True,
                      dtype=U.dtype,
                      strict=False,
                      sparse=lower_chunks[i, i].op.sparse,
                  ).new_chunk(
                      [lower_chunks[i, i], target],
                      shape=target.shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  upper_chunks[upper_chunk.index] = upper_chunk
              elif i == j:
                  target = in_tensor.cix[i, j]
                  if i > 0:
                      prev_chunks = []
                      for p in range(i):
                          a, b = (
                              l_permuted_chunks[i, p],
                              upper_chunks[p, j],
                          )
                          prev_chunk = TensorDot(
                              dtype=a.dtype, sparse=op.sparse
                          ).new_chunk(
                              [a, b],
                              shape=(a.shape[0], b.shape[1]),
                              order=out_tensor.order,
                          )
                          prev_chunks.append(prev_chunk)
                      if len(prev_chunks) == 1:
                          s = prev_chunks[0]
                      else:
                          tree_add_op = TensorTreeAdd(
                              args=prev_chunks,
                              dtype=prev_chunks[0].dtype,
                              sparse=op.sparse,
                          )
                          s = tree_add_op.new_chunk(
                              prev_chunks, shape=prev_chunks[0].shape
                          )
                      target = TensorSubtract(
                          dtype=L.dtype,
                          lhs=target,
                          rhs=s,
                          order=out_tensor.order.value,
                      ).new_chunk([target, s], shape=target.shape)
                  new_op = TensorLU(
                      dtype=op.dtype, sparse=target.op.sparse
                  )
                  lu_chunks = new_op.new_chunks(
                      [target],
                      index=(i, j),
                      order=out_tensor.order,
                      kws=[
                          {
                              "side": "p",
                              "dtype": P.dtype,
                              "shape": target.shape,
                          },
                          {
                              "side": "l",
                              "dtype": L.dtype,
                              "shape": target.shape,
                          },
                          {
                              "side": "u",
                              "dtype": U.dtype,
                              "shape": target.shape,
                          },
                      ],
                  )
                  p_chunk, lower_chunk, upper_chunk = lu_chunks
                  # transposed p equals to inverted p
                  p_chunk_invert = TensorTranspose(
                      dtype=p_chunk.dtype, sparse=op.sparse
                  ).new_chunk(
                      [p_chunk],
                      shape=p_chunk.shape,
                      index=p_chunk.index,
                      order=out_tensor.order,
                  )
                  p_chunks[p_chunk.index] = p_chunk
                  p_invert_chunks[p_chunk_invert.index] = p_chunk_invert
                  lower_chunks[lower_chunk.index] = lower_chunk
                  upper_chunks[upper_chunk.index] = upper_chunk
  
                  # l_permuted should be transferred to the final lower triangular
                  for p in range(i):
                      l_permuted_chunk = l_permuted_chunks[i, p]
                      l_chunk = TensorDot(
                          dtype=L.dtype, sparse=L.op.sparse
                      ).new_chunk(
                          [p_chunk_invert, l_permuted_chunk],
                          shape=(
                              p_chunk_invert.shape[0],
                              l_permuted_chunk.shape[1],
                          ),
                          index=l_permuted_chunk.index,
                          order=out_tensor.order,
                      )
                      lower_chunks[l_permuted_chunk.index] = l_chunk
              else:
                  chunk_shape = (
                      in_tensor.nsplits[0][i],
                      in_tensor.nsplits[1][j],
                  )
                  p_chunk = TensorZeros(
                      sparse=op.sparse,
                      order=out_tensor.order.value,
                      shape=chunk_shape,
                  ).new_chunk(
                      None,
                      shape=chunk_shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  upper_chunk = TensorZeros(
                      sparse=op.sparse,
                      order=out_tensor.order.value,
                      shape=chunk_shape,
                  ).new_chunk(
                      None,
                      shape=chunk_shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  p_chunks[p_chunk.index] = p_chunk
                  upper_chunks[upper_chunk.index] = upper_chunk
                  target_l = in_tensor.cix[i, j]
                  if j > 0:
                      prev_chunks_l = []
                      for p in range(j):
                          a, b = (
                              l_permuted_chunks[i, p],
                              upper_chunks[p, j],
                          )
                          prev_chunk = TensorDot(
                              dtype=L.dtype, sparse=L.op.sparse
                          ).new_chunk(
                              [a, b],
                              shape=(a.shape[0], b.shape[1]),
                              order=out_tensor.order,
                          )
                          prev_chunks_l.append(prev_chunk)
                      if len(prev_chunks_l) == 1:
                          s = prev_chunks_l[0]
                      else:
                          tree_add_op = TensorTreeAdd(
                              args=prev_chunks_l,
                              dtype=prev_chunks_l[0].dtype,
                              sparse=op.sparse,
                          )
                          s = tree_add_op.new_chunk(
                              prev_chunks_l,
                              shape=prev_chunks_l[0].shape,
                          )
                      target_l = TensorSubtract(
                          dtype=L.dtype,
                          lhs=target_l,
                          rhs=s,
                          order=out_tensor.order.value,
                      ).new_chunk(
                          [target_l, s],
                          shape=target_l.shape,
                          order=out_tensor.order,
                      )
                  u = upper_chunks[j, j]
                  a_transpose = TensorTranspose(
                      dtype=u.dtype, sparse=op.sparse
                  ).new_chunk([u], shape=u.shape)
                  target_transpose = TensorTranspose(
                      dtype=target_l.dtype, sparse=op.sparse
                  ).new_chunk([target_l], shape=target_l.shape)
                  lower_permuted_chunk = TensorSolveTriangular(
                      lower=True,
                      dtype=L.dtype,
                      strict=False,
                      sparse=op.sparse,
                  ).new_chunk(
                      [a_transpose, target_transpose],
                      shape=target_l.shape,
                      index=(i, j),
                      order=out_tensor.order,
                  )
                  lower_transpose = TensorTranspose(
                      dtype=lower_permuted_chunk.dtype, sparse=op.sparse
                  ).new_chunk(
                      [lower_permuted_chunk],
                      shape=lower_permuted_chunk.shape,
                      index=lower_permuted_chunk.index,
                  )
                  l_permuted_chunks[
                      lower_permuted_chunk.index
                  ] = lower_transpose
  
      new_op = op.copy()
      kws = [
          {
              "chunks": list(p_chunks.values()),
              "nsplits": in_tensor.nsplits,
              "dtype": P.dtype,
              "shape": P.shape,
              "order": P.order,
          },
          {
              "chunks": list(lower_chunks.values()),
              "nsplits": in_tensor.nsplits,
              "dtype": L.dtype,
              "shape": L.shape,
              "order": L.order,
          },
          {
              "chunks": list(upper_chunks.values()),
              "nsplits": in_tensor.nsplits,
              "dtype": U.dtype,
              "shape": U.shape,
              "order": U.order,
          },
      ]
      if raw_in_tensor.shape[0] == raw_in_tensor.shape[1]:
          return new_op.new_tensors(op.inputs, kws=kws)
  
      p, l_, u = new_op.new_tensors(op.inputs, kws=kws)
      if raw_in_tensor.shape[0] > raw_in_tensor.shape[1]:
          l_ = yield from recursive_tile(
              l_[:, : raw_in_tensor.shape[1]]
          )
          u = yield from recursive_tile(
              u[: raw_in_tensor.shape[1], : raw_in_tensor.shape[1]]
          )
      else:
          p = yield from recursive_tile(
              p[: raw_in_tensor.shape[0], : raw_in_tensor.shape[0]]
          )
          l_ = yield from recursive_tile(
              l_[: raw_in_tensor.shape[0], : raw_in_tensor.shape[0]]
          )
          u = yield from recursive_tile(u[: raw_in_tensor.shape[0], :])
      kws = [
          {
              "chunks": p.chunks,
              "nsplits": p.nsplits,
              "dtype": P.dtype,
              "shape": p.shape,
              "order": p.order,
          },
          {
              "chunks": l_.chunks,
              "nsplits": l_.nsplits,
              "dtype": l_.dtype,
              "shape": l_.shape,
              "order": l_.order,
          },
          {
              "chunks": u.chunks,
              "nsplits": u.nsplits,
              "dtype": u.dtype,
              "shape": u.shape,
              "order": u.order,
          },
      ]
      return new_op.new_tensors(op.inputs, kws=kws)
  
file_path: mars/mars/tensor/linalg/lu.py
imports: import numpy
  from numpy.linalg import LinAlgError
  from ...None import opcodes
  from ...core import ExecutableTuple, recursive_tile
  from ...serialization.serializables import KeyField
  from ...utils import has_unknown_shape
  from ..array_utils import device, as_same_device, is_sparse_module
  from ..operands import TensorHasInput, TensorOperandMixin
  from ..datasource import tensor
label_desc: Use image node_id 4 for calling the TensorLU obj's underlying member method code with example usage: obj.tile(cls, op) and returns: new_op, cls, new_op