code: def filter_alleles_hts(
      mt: MatrixTable, f: Callable, subset: bool = False
  ) -> MatrixTable:
      """Filter alternate alleles and update standard GATK entry fields.
  
      Examples
      --------
      Filter to SNP alleles using the subset strategy:
  
      >>> ds_result = hl.filter_alleles_hts(
      ...     ds,
      ...     lambda allele, _: hl.is_snp(ds.alleles[0], allele),
      ...     subset=True)
  
      Update the AC field of the resulting dataset:
  
      >>> updated_info = ds_result.info.annotate(AC = ds_result.new_to_old.map(lambda i: ds_result.info.AC[i-1]))
      >>> ds_result = ds_result.annotate_rows(info = updated_info)
  
      Notes
      -----
      For usage of the `f` argument, see the :func:`.filter_alleles`
      documentation.
  
      :func:`.filter_alleles_hts` requires the dataset have the GATK VCF schema,
      namely the following entry fields in this order:
  
      .. code-block:: text
  
          GT: call
          AD: array<int32>
          DP: int32
          GQ: int32
          PL: array<int32>
  
      Use :meth:`.MatrixTable.select_entries` to rearrange these fields if
      necessary.
  
      The following new fields are generated:
  
       - `old_locus` (``locus``) -- The old locus, before filtering and computing
         the minimal representation.
       - `old_alleles` (``array<str>``) -- The old alleles, before filtering and
         computing the minimal representation.
       - `old_to_new` (``array<int32>``) -- An array that maps old allele index to
         new allele index. Its length is the same as `old_alleles`. Alleles that
         are filtered are missing.
       - `new_to_old` (``array<int32>``) -- An array that maps new allele index to
         the old allele index. Its length is the same as the modified `alleles`
         field.
  
      **Downcode algorithm**
  
      We will illustrate the behavior on the example genotype below
      when filtering the first alternate allele (allele 1) at a site
      with 1 reference allele and 2 alternate alleles.
  
      .. code-block:: text
  
        GT: 1/2
        GQ: 10
        AD: 0,50,35
  
        0 | 1000
        1 | 1000   10
        2 | 1000   0     20
          +-----------------
             0     1     2
  
      The downcode algorithm recodes occurances of filtered alleles
      to occurances of the reference allele (e.g. 1 -> 0 in our
      example). So the depths of filtered alleles in the AD field
      are added to the depth of the reference allele. Where
      downcoding filtered alleles merges distinct genotypes, the
      minimum PL is used (since PL is on a log scale, this roughly
      corresponds to adding probabilities). The PLs are then
      re-normalized (shifted) so that the most likely genotype has a
      PL of 0, and GT is set to this genotype.  If an allele is
      filtered, this algorithm acts similarly to
      :func:`.split_multi_hts`.
  
      The downcode algorithm would produce the following:
  
      .. code-block:: text
  
        GT: 0/1
        GQ: 10
        AD: 35,50
  
        0 | 20
        1 | 0    10
          +-----------
            0    1
  
      In summary:
  
       - GT: Downcode filtered alleles to reference.
       - AD: Columns of filtered alleles are eliminated and their
         values are added to the reference column, e.g., filtering
         alleles 1 and 2 transforms ``25,5,10,20`` to ``40,20``.
       - DP: No change.
       - PL: Downcode filtered alleles to reference, combine PLs
         using minimum for each overloaded genotype, and shift so
         the overall minimum PL is 0.
       - GQ: The second-lowest PL (after shifting).
  
      **Subset algorithm**
  
      We will illustrate the behavior on the example genotype below
      when filtering the first alternate allele (allele 1) at a site
      with 1 reference allele and 2 alternate alleles.
  
      .. code-block:: text
  
        GT: 1/2
        GQ: 10
        AD: 0,50,35
  
        0 | 1000
        1 | 1000   10
        2 | 1000   0     20
          +-----------------
             0     1     2
  
      The subset algorithm subsets the AD and PL arrays
      (i.e. removes entries corresponding to filtered alleles) and
      then sets GT to the genotype with the minimum PL.  Note that
      if the genotype changes (as in the example), the PLs are
      re-normalized (shifted) so that the most likely genotype has a
      PL of 0.  Qualitatively, subsetting corresponds to the belief
      that the filtered alleles are not real so we should discard
      any probability mass associated with them.
  
      The subset algorithm would produce the following:
  
      .. code-block:: text
  
        GT: 1/1
        GQ: 980
        AD: 0,50
  
        0 | 980
        1 | 980    0
          +-----------
             0      1
  
      In summary:
  
       - GT: Set to most likely genotype based on the PLs ignoring
         the filtered allele(s).
       - AD: The filtered alleles' columns are eliminated, e.g.,
         filtering alleles 1 and 2 transforms ``25,5,10,20`` to
         ``25,20``.
       - DP: Unchanged.
       - PL: Columns involving filtered alleles are eliminated and
         the remaining columns' values are shifted so the minimum
         value is 0.
       - GQ: The second-lowest PL (after shifting).
  
      Warning
      -------
      :func:`.filter_alleles_hts` does not update any row fields other than
      `locus` and `alleles`. This means that row fields like allele count (AC) can
      become meaningless unless they are also updated. You can update them with
      :meth:`.annotate_rows`.
  
      See Also
      --------
      :func:`.filter_alleles`
  
      Parameters
      ----------
      mt : :class:`.MatrixTable`
      f : callable
          Function from (allele: :class:`.StringExpression`, allele_index:
          :class:`.Int32Expression`) to :class:`.BooleanExpression`
      subset : :obj:`.bool`
          Subset PL field if ``True``, otherwise downcode PL field. The
          calculation of GT and GQ also depend on whether one subsets or
          downcodes the PL.
  
      Returns
      -------
      :class:`.MatrixTable`
      """
      if mt.entry.dtype != hl.hts_entry_schema:
          raise FatalError(
              "'filter_alleles_hts': entry schema must be the HTS entry schema:\n"
              "  found: {}\n"
              "  expected: {}\n"
              "  Use 'hl.filter_alleles' to split entries with non-HTS entry fields.".format(
                  mt.entry.dtype, hl.hts_entry_schema
              )
          )
  
      mt = filter_alleles(mt, f)
  
      if subset:
          newPL = hl.if_else(
              hl.is_defined(mt.PL),
              hl.bind(
                  lambda unnorm: unnorm - hl.min(unnorm),
                  hl.range(0, hl.triangle(mt.alleles.length())).map(
                      lambda newi: hl.bind(
                          lambda newc: mt.PL[
                              hl.call(
                                  mt.new_to_old[newc[0]],
                                  mt.new_to_old[newc[1]],
                              ).unphased_diploid_gt_index()
                          ],
                          hl.unphased_diploid_gt_index_call(newi),
                      )
                  ),
              ),
              hl.missing(tarray(tint32)),
          )
          return mt.annotate_entries(
              GT=hl.unphased_diploid_gt_index_call(
                  hl.argmin(newPL, unique=True)
              ),
              AD=hl.if_else(
                  hl.is_defined(mt.AD),
                  hl.range(0, mt.alleles.length()).map(
                      lambda newi: mt.AD[mt.new_to_old[newi]]
                  ),
                  hl.missing(tarray(tint32)),
              ),
              # DP unchanged
              GQ=hl.gq_from_pl(newPL),
              PL=newPL,
          )
      # otherwise downcode
      else:
          mt = mt.annotate_rows(
              __old_to_new_no_na=mt.old_to_new.map(
                  lambda x: hl.or_else(x, 0)
              )
          )
          newPL = hl.if_else(
              hl.is_defined(mt.PL),
              (
                  hl.range(0, hl.triangle(hl.len(mt.alleles))).map(
                      lambda newi: hl.min(
                          hl.range(
                              0, hl.triangle(hl.len(mt.old_alleles))
                          )
                          .filter(
                              lambda oldi: hl.bind(
                                  lambda oldc: hl.call(
                                      mt.__old_to_new_no_na[oldc[0]],
                                      mt.__old_to_new_no_na[oldc[1]],
                                  )
                                  == hl.unphased_diploid_gt_index_call(
                                      newi
                                  ),
                                  hl.unphased_diploid_gt_index_call(
                                      oldi
                                  ),
                              )
                          )
                          .map(lambda oldi: mt.PL[oldi])
                      )
                  )
              ),
              hl.missing(tarray(tint32)),
          )
          return mt.annotate_entries(
              GT=hl.call(
                  mt.__old_to_new_no_na[mt.GT[0]],
                  mt.__old_to_new_no_na[mt.GT[1]],
              ),
              AD=hl.if_else(
                  hl.is_defined(mt.AD),
                  (
                      hl.range(0, hl.len(mt.alleles)).map(
                          lambda newi: hl.sum(
                              hl.range(0, hl.len(mt.old_alleles))
                              .filter(
                                  lambda oldi: mt.__old_to_new_no_na[
                                      oldi
                                  ]
                                  == newi
                              )
                              .map(lambda oldi: mt.AD[oldi])
                          )
                      )
                  ),
                  hl.missing(tarray(tint32)),
              ),
              # DP unchanged
              GQ=hl.gq_from_pl(newPL),
              PL=newPL,
          ).drop("__old_to_new_no_na")
  
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 34 for calling a global function with example usage: filter_alleles_hts(mt, f, subset) and returns: mt, mt