code: def qr(nd, mode="reduced"):
      r"""Performs a QR decomposition.
  
      If K = min(M, N), then:
  
      - `reduced`: returns q and r with dimensions (M, K), (K, N)
      - `complete`: returns q and r with dimensions (M, M), (M, N)
      - `r`: returns only r with dimensions (K, N)
      - `raw`: returns h, tau with dimensions (N, M), (K,)
  
      Notes
      -----
  
      The reduced QR, the default output of this function, has the following properties:
  
      .. math::
  
          m \ge n \\
          nd : \mathbb{R}^{m \times n} \\
          Q : \mathbb{R}^{m \times n} \\
          R : \mathbb{R}^{n \times n} \\
          \\
          Q^T Q = \mathbb{1}
  
      The complete QR, has the following properties:
  
      .. math::
  
          m \ge n \\
          nd : \mathbb{R}^{m \times n} \\
          Q : \mathbb{R}^{m \times m} \\
          R : \mathbb{R}^{m \times n} \\
          \\
          Q^T Q = \mathbb{1}
          Q Q^T = \mathbb{1}
  
      Parameters
      ----------
      nd : :class:`.NDArrayExpression`
          A 2 dimensional ndarray, shape(M, N)
      mode : :class:`.str`
          One of "reduced", "complete", "r", or "raw". Defaults to "reduced".
  
      Returns
      -------
      - q: ndarray of float64
          A matrix with orthonormal columns.
      - r: ndarray of float64
          The upper-triangular matrix R.
      - (h, tau): ndarrays of float64
          The array h contains the Householder reflectors that generate q along with r.
          The tau array contains scaling factors for the reflectors
      """
  
      assert (
          nd.ndim == 2
      ), f"QR decomposition requires 2 dimensional ndarray, found: {nd.ndim}"
  
      if mode not in ["reduced", "r", "raw", "complete"]:
          raise ValueError(
              f"Unrecognized mode '{mode}' for QR decomposition"
          )
  
      float_nd = nd.map(lambda x: hl.float64(x))
      ir = NDArrayQR(float_nd._ir, mode)
      indices = nd._indices
      aggs = nd._aggregations
      if mode == "raw":
          return construct_expr(
              ir,
              ttuple(tndarray(tfloat64, 2), tndarray(tfloat64, 1)),
              indices,
              aggs,
          )
      elif mode == "r":
          return construct_expr(
              ir, tndarray(tfloat64, 2), indices, aggs
          )
      elif mode in ["complete", "reduced"]:
          return construct_expr(
              ir,
              ttuple(tndarray(tfloat64, 2), tndarray(tfloat64, 2)),
              indices,
              aggs,
          )
  
imports: from functools import reduce
  import hail
  from hail.expr.functions import _ndarray
  from hail.expr.functions import array
  from hail.expr.types import HailType, tfloat64, tfloat32, ttuple, tndarray
  from hail.typecheck import typecheck, nullable, oneof, tupleof, sequenceof
  from hail.expr.expressions import expr_int32, expr_int64, expr_tuple, expr_any, expr_array, expr_ndarray, expr_numeric, Int64Expression, cast_expr, construct_expr, expr_bool, unify_all
  from hail.expr.expressions.typed_expressions import NDArrayNumericExpression
  from hail.ir import NDArrayQR, NDArrayInv, NDArrayConcat, NDArraySVD, NDArrayEigh, Apply
label_desc: Use image node_id 11 for calling a global function with example usage: qr(nd, mode) and returns: construct_expr, construct_expr, construct_expr