code: def _logistic_regression_rows_nd(
      test,
      y,
      x,
      covariates,
      pass_through=(),
      *,
      max_iterations: Optional[int] = None,
      tolerance: Optional[float] = None,
  ) -> hail.Table:
      r"""For each row, test an input variable for association with a
      binary response variable using logistic regression.
  
      Examples
      --------
      Run the logistic regression Wald test per variant using a Boolean
      phenotype, intercept and two covariates stored in column-indexed
      fields:
  
      >>> result_ht = hl.logistic_regression_rows(
      ...     test='wald',
      ...     y=dataset.pheno.is_case,
      ...     x=dataset.GT.n_alt_alleles(),
      ...     covariates=[1, dataset.pheno.age, dataset.pheno.is_female])
  
      Run the logistic regression Wald test per variant using a list of binary (0/1)
      phenotypes, intercept and two covariates stored in column-indexed
      fields:
  
      >>> result_ht = hl.logistic_regression_rows(
      ...     test='wald',
      ...     y=[dataset.pheno.is_case, dataset.pheno.is_case],  # where pheno values are 0, 1, or missing
      ...     x=dataset.GT.n_alt_alleles(),
      ...     covariates=[1, dataset.pheno.age, dataset.pheno.is_female])
  
      Warning
      -------
      :func:`.logistic_regression_rows` considers the same set of
      columns (i.e., samples, points) for every row, namely those columns for
      which **all** response variables and covariates are defined. For each row, missing values of
      `x` are mean-imputed over these columns. As in the example, the
      intercept covariate ``1`` must be included **explicitly** if desired.
  
      Notes
      -----
      This method performs, for each row, a significance test of the input
      variable in predicting a binary (case-control) response variable based
      on the logistic regression model. The response variable type must either
      be numeric (with all present values 0 or 1) or Boolean, in which case
      true and false are coded as 1 and 0, respectively.
  
      Hail supports the Wald test ('wald'), likelihood ratio test ('lrt'),
      Rao score test ('score'), and Firth test ('firth'). Hail only includes
      columns for which the response variable and all covariates are defined.
      For each row, Hail imputes missing input values as the mean of the
      non-missing values.
  
      The example above considers a model of the form
  
      .. math::
  
          \mathrm{Prob}(\mathrm{is\_case}) =
              \mathrm{sigmoid}(\beta_0 + \beta_1 \, \mathrm{gt}
                              + \beta_2 \, \mathrm{age}
                              + \beta_3 \, \mathrm{is\_female} + \varepsilon),
          \quad
          \varepsilon \sim \mathrm{N}(0, \sigma^2)
  
      where :math:`\mathrm{sigmoid}` is the `sigmoid function`_, the genotype
      :math:`\mathrm{gt}` is coded as 0 for HomRef, 1 for Het, and 2 for
      HomVar, and the Boolean covariate :math:`\mathrm{is\_female}` is coded as
      for ``True`` (female) and 0 for ``False`` (male). The null model sets
      :math:`\beta_1 = 0`.
  
      .. _sigmoid function: https://en.wikipedia.org/wiki/Sigmoid_function
  
      The structure of the emitted row field depends on the test statistic as
      shown in the tables below.
  
      ========== ================== ======= ============================================
      Test       Field              Type    Value
      ========== ================== ======= ============================================
      Wald       `beta`             float64 fit effect coefficient,
                                            :math:`\hat\beta_1`
      Wald       `standard_error`   float64 estimated standard error,
                                            :math:`\widehat{\mathrm{se}}`
      Wald       `z_stat`           float64 Wald :math:`z`-statistic, equal to
                                            :math:`\hat\beta_1 / \widehat{\mathrm{se}}`
      Wald       `p_value`          float64 Wald p-value testing :math:`\beta_1 = 0`
      LRT, Firth `beta`             float64 fit effect coefficient,
                                            :math:`\hat\beta_1`
      LRT, Firth `chi_sq_stat`      float64 deviance statistic
      LRT, Firth `p_value`          float64 LRT / Firth p-value testing
                                            :math:`\beta_1 = 0`
      Score      `chi_sq_stat`      float64 score statistic
      Score      `p_value`          float64 score p-value testing :math:`\beta_1 = 0`
      ========== ================== ======= ============================================
  
      For the Wald and likelihood ratio tests, Hail fits the logistic model for
      each row using Newton iteration and only emits the above fields
      when the maximum likelihood estimate of the coefficients converges. The
      Firth test uses a modified form of Newton iteration. To help diagnose
      convergence issues, Hail also emits three fields which summarize the
      iterative fitting process:
  
      ================ =================== ======= ===============================
      Test             Field               Type    Value
      ================ =================== ======= ===============================
      Wald, LRT, Firth `fit.n_iterations`  int32   number of iterations until
                                                   convergence, explosion, or
                                                   reaching the max (25 for
                                                   Wald, LRT; 100 for Firth)
      Wald, LRT, Firth `fit.converged`      bool    ``True`` if iteration converged
      Wald, LRT, Firth `fit.exploded`       bool    ``True`` if iteration exploded
      ================ =================== ======= ===============================
  
      We consider iteration to have converged when every coordinate of
      :math:`\beta` changes by less than :math:`10^{-6}`. For Wald and LRT,
      up to 25 iterations are attempted; in testing we find 4 or 5 iterations
      nearly always suffice. Convergence may also fail due to explosion,
      which refers to low-level numerical linear algebra exceptions caused by
      manipulating ill-conditioned matrices. Explosion may result from (nearly)
      linearly dependent covariates or complete separation_.
  
      .. _separation: https://en.wikipedia.org/wiki/Separation_(statistics)
  
      A more common situation in genetics is quasi-complete seperation, e.g.
      variants that are observed only in cases (or controls). Such variants
      inevitably arise when testing millions of variants with very low minor
      allele count. The maximum likelihood estimate of :math:`\beta` under
      logistic regression is then undefined but convergence may still occur
      after a large number of iterations due to a very flat likelihood
      surface. In testing, we find that such variants produce a secondary bump
      from 10 to 15 iterations in the histogram of number of iterations per
      variant. We also find that this faux convergence produces large standard
      errors and large (insignificant) p-values. To not miss such variants,
      consider using Firth logistic regression, linear regression, or
      group-based tests.
  
      Here's a concrete illustration of quasi-complete seperation in R. Suppose
      we have 2010 samples distributed as follows for a particular variant:
  
      ======= ====== === ======
      Status  HomRef Het HomVar
      ======= ====== === ======
      Case    1000   10  0
      Control 1000   0   0
      ======= ====== === ======
  
      The following R code fits the (standard) logistic, Firth logistic,
      and linear regression models to this data, where ``x`` is genotype,
      ``y`` is phenotype, and ``logistf`` is from the logistf package:
  
      .. code-block:: R
  
          x <- c(rep(0,1000), rep(1,1000), rep(1,10)
          y <- c(rep(0,1000), rep(0,1000), rep(1,10))
          logfit <- glm(y ~ x, family=binomial())
          firthfit <- logistf(y ~ x)
          linfit <- lm(y ~ x)
  
      The resulting p-values for the genotype coefficient are 0.991, 0.00085,
      and 0.0016, respectively. The erroneous value 0.991 is due to
      quasi-complete separation. Moving one of the 10 hets from case to control
      eliminates this quasi-complete separation; the p-values from R are then
      0.0373, 0.0111, and 0.0116, respectively, as expected for a less
      significant association.
  
      The Firth test reduces bias from small counts and resolves the issue of
      separation by penalizing maximum likelihood estimation by the `Jeffrey's
      invariant prior <https://en.wikipedia.org/wiki/Jeffreys_prior>`__. This
      test is slower, as both the null and full model must be fit per variant,
      and convergence of the modified Newton method is linear rather than
      quadratic. For Firth, 100 iterations are attempted for the null model
      and, if that is successful, for the full model as well. In testing we
      find 20 iterations nearly always suffices. If the null model fails to
      converge, then the `logreg.fit` fields reflect the null model;
      otherwise, they reflect the full model.
  
      See
      `Recommended joint and meta-analysis strategies for case-control association testing of single low-count variants <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4049324/>`__
      for an empirical comparison of the logistic Wald, LRT, score, and Firth
      tests. The theoretical foundations of the Wald, likelihood ratio, and score
      tests may be found in Chapter 3 of Gesine Reinert's notes
      `Statistical Theory <http://www.stats.ox.ac.uk/~reinert/stattheory/theoryshort09.pdf>`__.
      Firth introduced his approach in
      `Bias reduction of maximum likelihood estimates, 1993 <http://www2.stat.duke.edu/~scs/Courses/Stat376/Papers/GibbsFieldEst/BiasReductionMLE.pdf>`__.
      Heinze and Schemper further analyze Firth's approach in
      `A solution to the problem of separation in logistic regression, 2002 <https://cemsiis.meduniwien.ac.at/fileadmin/msi_akim/CeMSIIS/KB/volltexte/Heinze_Schemper_2002_Statistics_in_Medicine.pdf>`__.
  
      Hail's logistic regression tests correspond to the ``b.wald``,
      ``b.lrt``, and ``b.score`` tests in `EPACTS`_. For each variant, Hail
      imputes missing input values as the mean of non-missing input values,
      whereas EPACTS subsets to those samples with called genotypes. Hence,
      Hail and EPACTS results will currently only agree for variants with no
      missing genotypes.
  
      .. _EPACTS: http://genome.sph.umich.edu/wiki/EPACTS#Single_Variant_Tests
  
      Note
      ----
      Use the `pass_through` parameter to include additional row fields from
      matrix table underlying ``x``. For example, to include an "rsid" field, set
      ``pass_through=['rsid']`` or ``pass_through=[mt.rsid]``.
  
      Parameters
      ----------
      test : {'wald', 'lrt', 'score', 'firth'}
          Statistical test.
      y : :class:`.Float64Expression` or :obj:`list` of :class:`.Float64Expression`
          One or more column-indexed response expressions.
          All non-missing values must evaluate to 0 or 1.
          Note that a :class:`.BooleanExpression` will be implicitly converted to
          a :class:`.Float64Expression` with this property.
      x : :class:`.Float64Expression`
          Entry-indexed expression for input variable.
      covariates : :obj:`list` of :class:`.Float64Expression`
          Non-empty list of column-indexed covariate expressions.
      pass_through : :obj:`list` of :class:`str` or :class:`.Expression`
          Additional row fields to include in the resulting table.
  
      Returns
      -------
      :class:`.Table`
      """
      if max_iterations is None:
          max_iterations = 25 if test != "firth" else 100
  
      if tolerance is None:
          tolerance = 1e-8
      assert tolerance > 0.0
  
      if len(covariates) == 0:
          raise ValueError(
              "logistic regression requires at least one covariate expression"
          )
  
      mt = matrix_table_source("logistic_regresion_rows/x", x)
      raise_unless_entry_indexed("logistic_regresion_rows/x", x)
  
      y_is_list = isinstance(y, list)
      if y_is_list and len(y) == 0:
          raise ValueError(
              "'logistic_regression_rows': found no values for 'y'"
          )
  
      y = [
          raise_unless_column_indexed("logistic_regression_rows/y", y)
          or y
          for y in wrap_to_list(y)
      ]
  
      for e in covariates:
          analyze(
              "logistic_regression_rows/covariates", e, mt._col_indices
          )
  
      # _warn_if_no_intercept('logistic_regression_rows', covariates)
  
      x_field_name = Env.get_uid()
      y_field_names = [f"__y_{i}" for i in range(len(y))]
  
      y_dict = dict(zip(y_field_names, y))
  
      cov_field_names = [f"__cov{i}" for i in range(len(covariates))]
      row_fields = _get_regression_row_fields(
          mt, pass_through, "logistic_regression_rows"
      )
  
      # Handle filtering columns with missing values:
      mt = mt.filter_cols(hl.array(y + covariates).all(hl.is_defined))
  
      # FIXME: selecting an existing entry field should be emitted as a SelectFields
      mt = mt._select_all(
          col_exprs=dict(
              **y_dict, **dict(zip(cov_field_names, covariates))
          ),
          row_exprs=row_fields,
          col_key=[],
          entry_exprs={x_field_name: x},
      )
  
      ht = mt._localize_entries("entries", "samples")
  
      # covmat rows are samples, columns are the different covariates
      ht = ht.annotate_globals(
          covmat=hl.nd.array(
              ht.samples.map(
                  lambda s: [
                      s[cov_name] for cov_name in cov_field_names
                  ]
              )
          )
      )
  
      # yvecs is a list of sample-length vectors, one for each dependent variable.
      ht = ht.annotate_globals(
          yvecs=[
              hl.nd.array(ht.samples[y_name])
              for y_name in y_field_names
          ]
      )
  
      # Fit null models, which means doing a logreg fit with just the covariates for each phenotype.
      def fit_null(yvec):
          def error_if_not_converged(null_fit):
              return (
                  hl.case()
                  .when(
                      ~null_fit.exploded,
                      (
                          hl.case()
                          .when(null_fit.converged, null_fit)
                          .or_error(
                              "Failed to fit logistic regression null model (standard MLE with covariates only): "
                              "Newton iteration failed to converge"
                          )
                      ),
                  )
                  .or_error(
                      hl.format(
                          "Failed to fit logistic regression null model (standard MLE with covariates only): "
                          "exploded at Newton iteration %d",
                          null_fit.n_iterations,
                      )
                  )
              )
  
          null_fit = logreg_fit(
              ht.covmat,
              yvec,
              None,
              max_iterations=max_iterations,
              tolerance=tolerance,
          )
          return hl.bind(error_if_not_converged, null_fit)
  
      ht = ht.annotate_globals(null_fits=ht.yvecs.map(fit_null))
  
      ht = ht.transmute(
          x=hl.nd.array(mean_impute(ht.entries[x_field_name]))
      )
      ht = ht.annotate(
          covs_and_x=hl.nd.hstack([ht.covmat, ht.x.reshape((-1, 1))])
      )
  
      def run_test(yvec, null_fit):
          if test == "score":
              return logistic_score_test(ht.covs_and_x, yvec, null_fit)
          if test == "firth":
              return _firth_test(
                  null_fit,
                  ht.covs_and_x,
                  yvec,
                  max_iterations=max_iterations,
                  tolerance=tolerance,
              )
  
          test_fit = logreg_fit(
              ht.covs_and_x,
              yvec,
              null_fit,
              max_iterations=max_iterations,
              tolerance=tolerance,
          )
          if test == "wald":
              return wald_test(ht.covs_and_x, test_fit)
          assert test == "lrt", test
          return lrt_test(ht.covs_and_x, null_fit, test_fit)
  
      ht = ht.select(
          logistic_regression=hl.starmap(
              run_test, hl.zip(ht.yvecs, ht.null_fits)
          ),
          **{f: ht[f] for f in row_fields},
      )
      assert "null_fits" not in row_fields
      assert "logistic_regression" not in row_fields
  
      if not y_is_list:
          assert all(f not in row_fields for f in ht.null_fits[0])
          assert all(
              f not in row_fields for f in ht.logistic_regression[0]
          )
          ht = ht.select_globals(**ht.null_fits[0])
          return ht.transmute(**ht.logistic_regression[0])
      ht = ht.select_globals("null_fits")
      return ht
  
imports: import builtins
  import itertools
  import math
  from typing import Dict, Callable, Optional, Union, Tuple, List
  import hail
  import hail
  import hail.expr.aggregators
  from hail import ir
  from hail.expr import Expression, ExpressionException, expr_float64, expr_call, expr_any, expr_numeric, expr_locus, analyze, raise_unless_entry_indexed, raise_unless_row_indexed, matrix_table_source, table_source, raise_unless_column_indexed
  from hail.expr.types import tbool, tarray, tfloat64, tint32
  from hail.genetics.reference_genome import reference_genome_type
  from hail.linalg import BlockMatrix
  from hail.matrixtable import MatrixTable
  from hail.methods.misc import require_biallelic, require_row_key_variant
  from hail.stats import LinearMixedModel
  from hail.table import Table
  from hail.typecheck import typecheck, nullable, numeric, oneof, sized_tupleof, sequenceof, enumeration, anytype
  from hail.utils import wrap_to_list, new_temp_file, FatalError
  from hail.utils.java import Env, info, warning
  from .None import pca
  from .None import relatedness
  from ..backend.spark_backend import SparkBackend
label_desc: Use image node_id 14 for calling a global function with example usage: _logistic_regression_rows_nd(test, y, x, covariates, pass_through) and returns: ht, hl, lrt_test, ht, hl, logistic_score_test, _firth_test, wald_test