code: def _cholesky(a):
      """
      Private function to perform Cholesky decomposition, which returns both
      lower and upper triangulars.
      """
  
      if a.ndim != 2:
          raise ValueError(
              "Dimension must be 2 to perform cholesky decomposition"
          )
  
      xdim, ydim = a.shape
      if xdim != ydim:
          raise ValueError(
              "Input must be a square matrix to perform cholesky decomposition"
          )
      if not len(set(a.chunks[0] + a.chunks[1])) == 1:
          msg = (
              "All chunks must be a square matrix to perform cholesky decomposition. "
              "Use .rechunk method to change the size of chunks."
          )
          raise ValueError(msg)
  
      vdim = len(a.chunks[0])
      hdim = len(a.chunks[1])
  
      token = tokenize(a)
      name = "cholesky-" + token
  
      # (name_lt_dot, i, j, k, l) corresponds to l_ij.dot(l_kl.T)
      name_lt_dot = "cholesky-lt-dot-" + token
      # because transposed results are needed for calculation,
      # we can build graph for upper triangular simultaneously
      name_upper = "cholesky-upper-" + token
  
      # calculates lower triangulars because subscriptions get simpler
      dsk = {}
      for i in range(vdim):
          for j in range(hdim):
              if i < j:
                  dsk[name, i, j] = (
                      partial(
                          np.zeros_like,
                          shape=(a.chunks[0][i], a.chunks[1][j]),
                      ),
                      meta_from_array(a),
                  )
                  dsk[name_upper, j, i] = (name, i, j)
              elif i == j:
                  target = (a.name, i, j)
                  if i > 0:
                      prevs = []
                      for p in range(i):
                          prev = name_lt_dot, i, p, i, p
                          dsk[prev] = (
                              np.dot,
                              (name, i, p),
                              (name_upper, p, i),
                          )
                          prevs.append(prev)
                      target = (operator.sub, target, (sum, prevs))
                  dsk[name, i, i] = (_cholesky_lower, target)
                  dsk[name_upper, i, i] = (np.transpose, (name, i, i))
              else:
                  # solving x.dot(L11.T) = (A21 - L20.dot(L10.T)) is equal to
                  # L11.dot(x.T) = A21.T - L10.dot(L20.T)
                  # L11.dot(x.T) = A12 - L10.dot(L02)
                  target = (a.name, j, i)
                  if j > 0:
                      prevs = []
                      for p in range(j):
                          prev = name_lt_dot, j, p, i, p
                          dsk[prev] = (
                              np.dot,
                              (name, j, p),
                              (name_upper, p, i),
                          )
                          prevs.append(prev)
                      target = (operator.sub, target, (sum, prevs))
                  dsk[name_upper, j, i] = (
                      _solve_triangular_lower,
                      (name, j, j),
                      target,
                  )
                  dsk[name, i, j] = (np.transpose, (name_upper, j, i))
  
      graph_upper = HighLevelGraph.from_collections(
          name_upper, dsk, dependencies=[a]
      )
      graph_lower = HighLevelGraph.from_collections(
          name, dsk, dependencies=[a]
      )
      a_meta = meta_from_array(a)
      cho = np.linalg.cholesky(
          array_safe([[1, 2], [2, 5]], dtype=a.dtype, like=a_meta)
      )
      meta = meta_from_array(a, dtype=cho.dtype)
  
      lower = Array(
          graph_lower, name, shape=a.shape, chunks=a.chunks, meta=meta
      )
      # do not use .T, because part of transposed blocks are already calculated
      upper = Array(
          graph_upper,
          name_upper,
          shape=a.shape,
          chunks=a.chunks,
          meta=meta,
      )
      return lower, upper
  
imports: from __future__ import annotations
  import operator
  import warnings
  from functools import partial
  from numbers import Number
  import numpy
  import tlz
  from dask.array.core import Array, concatenate, dotmany, from_delayed
  from dask.array.creation import eye
  from dask.array.random import RandomState, default_rng
  from dask.array.utils import array_safe, meta_from_array, solve_triangular_safe, svd_flip
  from dask.base import tokenize, wait
  from dask.blockwise import blockwise
  from dask.delayed import delayed
  from dask.highlevelgraph import HighLevelGraph
  from dask.utils import apply, derived_from
label_desc: Use image node_id 19 for calling a global function with example usage: _cholesky(a) and returns: lower,  upper