o
    Gi                     @   s  d dl mZ d dl mZ d dl mZ d dl mZ d dlZd dlZd dlZddlm	Z	 ddl
mZ dd	lmZ d d
lmZ 	dndeee eee  ejf deeejef  defddZdejfddZdeee eee  f deee eee  f defddZdejfddZdedefddZ		dodededejd ed!edeeeeejejf f fd"d#Z	$	dpd%ed&ed'ed!edeeeeejejf f f
d(d)Z	$	dqd%ed&ed'ed*ee dd+f
d,d-Zdedefd.d/Zdrded0edeeeeejf f fd1d2Zded3edefd4d5Zdrded0edeeeeejf f fd6d7Zdedefd8d9Z dedefd:d;Z!dedefd<d=Z"dedefd>d?Z#	$dsded@edefdAdBZ$ej%j&fdedCej%defdDdEZ'dedefdFdGZ(	drded0edeeeeejf f fdHdIZ)ded3edJedejfdKdLZ*dedMe+d3edefdNdOZ,		dtded0edPeee  deeeeejf f fdQdRZ-		dudSedTedUed0edeeeeejejf f f
dVdWZ.		XdvdYeeee  ejf dZedeeejef  defd[d\Z/		dtd]edZedeeejef  dejfd^d_Z0	dnd]edeeejef  dejfd`daZ1	b	XdwdYeejeee  f dce+deeejef  defdddeZ2	drdfedgedhejdiedef
djdkZ3dedefdldmZ4dS )x    )List)Optional)Tuple)UnionN   )fsa_properties)Fsa)index_select)Tensorlabelsdevicereturnc                 C   s0   t | tjr|du sJ t| |}t|}|S )aw  Construct an linear FSA from labels.

    Note:
      The scores of arcs in the returned FSA are all 0.

    Args:
      labels:
        It can be one of the following types:

            - A list of integers, e.g., `[1, 2, 3]`
            - A list of list-of-integers, e..g, `[ [1, 2], [1, 2, 3] ]`
            - An instance of :class:`k2.RaggedTensor`.
              Must have `num_axes == 2`.
      device:
        Optional. It can be either a string (e.g., 'cpu', 'cuda:0') or a
        torch.device.
        If it is ``None``, then the returned FSA is on CPU. It has to be None
        if ``labels`` is an instance of :class:`k2.RaggedTensor`.

    Returns:
      - If ``labels`` is a list of integers, return an FSA
      - If ``labels`` is a list of list-of-integers, return an FsaVec
      - If ``labels`` is an instance of :class:`k2.RaggedTensor`, return
        an FsaVec
    N)
isinstancek2RaggedTensor_k2
linear_fsar   )r   r   
ragged_arcfsa r   ?/home/ubuntu/.local/lib/python3.10/site-packages/k2/fsa_algo.pyr   %   s
   r   fsasc                 C   s   t | jdkr!| j}tjjd| jd d}||| j }n| j }|	d}t
|| j }|d}tt|}t | jdkrJ|d }|S )a  Create a linear FSA with epsilon self-loops by first removing epsilon
    transitions from the input linear FSA.

    Args:
      fsas:
        An FSA or an FsaVec. It MUST be a linear FSA or a vector of linear FSAs.
    Returns:
      Return an FSA or FsaVec, where each FSA contains epsilon self-loops but
      contains no epsilon transitions for arcs that are not self-loops.
       r   r   dim0dim1)lenshaper   r   RaggedShaperegular_ragged_shapetocomposearcsremove_axisr   r   r   
contiguousremove_values_leqadd_epsilon_self_loopsr   )r   r   shape0r   r   ansr   r   r   linear_fsa_with_self_loopsG   s   


r)   
aux_labelsc                 C   s   t | }t| d tr/t|d tsJ dg }|D ]
}||dg  qtj|tjd}ntj|dg tjd}t	||d}|S )a  Construct a linear FST from labels and its corresponding
    auxiliary labels.

    Note:
      The scores of arcs in the returned FST are all 0.

    Args:
      labels:
        A list of integers or a list of list of integers.
      aux_labels:
        A list of integers or a list of list of integers.

    Returns:
      An FST if the labels is a list of integers.
      A vector of FSTs (FsaVec) if the input is a list of list of integers.
    r   z#aux_labels and labels do not match.dtyper*   )
r   r   r   r   listextendtorchtensorint32r   )r   r*   r   bufauxaux_labels_tmpr   r   r   r   
linear_fstf   s   
r7   fstsc                 C   sz  t | dsJ | jdk}tjt| jtjd| j}|dd |dd< tj|dtjdd }t	j
j|| d}t	|| j }|d}tj| j tjd| j}t	||}t	j
| j |}t| jd	kr| j}	tjjd| jd d
}
|
|	| j }n| j }|d}t	|| j }|d}t	|}||_||_t	|}t| jd	kr|d }|S )a  Create a linear FST with epsilon self-loops by first removing epsilon
    transitions from the input linear FST.


    Note:
      The main difference to :func:`linear_fsa_with_self_loops` is that
      aux_labels and scores are also kept here.

    Args:
      fsas:
        An FST or an FstVec. It MUST be a linear FST or a vector of linear FSTs.
    Returns:
      Return an FST or FstVec, where each FST contains epsilon self-loops but
      contains no epsilon transitions for arcs that are not self-loops.
    r*   r   r,   r+   r   N)dimr-   )row_idscached_tot_sizer   r   )hasattrr   r1   onesr   r3   r    r   cumsumr   raggedcreate_ragged_shape2numelr   r*   r$   r%   aranger"   num_elementsindex_and_sumscoresr   r   r   r   r!   r#   r   r&   )r8   non_epsilon_positionsnew_arc_flagdest_arc_row_idsdest_arc_shapedest_aux_labelsarc_indexesarc_mapdest_scoresr   r'   r   r   r(   r   r   r   linear_fst_with_self_loops   sN   







rN   r   c                 C   s,   d}t j| j|d\}}tj| ||}|S )af  Sort an FSA topologically.

    Note:
      It returns a new FSA. The input FSA is NOT changed.

    Args:
      fsa:
        The input FSA to be sorted. It can be either a single FSA
        or a vector of FSAs.
    Returns:
      It returns a single FSA if the input is a single FSA; it returns
      a vector of FSAs if the input is a vector of FSAs.
    Tneed_arc_map)r   top_sortr"   r   utilsfsa_from_unary_function_tensorr   rP   r   rL   out_fsar   r   r   rQ      s   rQ   Fa_fsasb_fsas
b_to_a_mapsorted_match_aret_arc_mapsc           
   	   C   sN   d}t | j| j|j|j|||\}}}tj| ||||}	|r%|	||fS |	S )a	  Compute the intersection of two FsaVecs treating epsilons
    as real, normal symbols.

    This function supports both CPU and GPU. But it is very slow on CPU.
    That's why this function name ends with `_device`. It is intended for GPU.
    See :func:`k2.intersect` which is a more general interface
    (it will call the same underlying code, IntersectDevice(), if
    the inputs are on GPU and a_fsas is arc-sorted).

    Caution:
      Epsilons are treated as real, normal symbols.

    Hint:
      The two inputs do not need to be arc-sorted.

    Refer to :func:`k2.intersect` for how we assign the attributes of the
    output FsaVec.

    Args:
      a_fsas:
        An FsaVec (must have 3 axes, i.e., `len(a_fsas.shape) == 3`.
      b_fsas:
        An FsaVec (must have 3 axes) on the same device as `a_fsas`.
      b_to_a_map:
        A 1-D torch.Tensor with dtype torch.int32 on the same device
        as `a_fsas`. Map from FSA-id in `b_fsas` to the corresponding
        FSA-id in `a_fsas` that we want to compose it with.
        E.g. might be an identity map, or all-to-zero, or something the
        user chooses.

        Requires
            - `b_to_a_map.shape[0] == b_fsas.shape[0]`
            - `0 <= b_to_a_map[i] < a_fsas.shape[0]`
      sorted_match_a:
        If true, the arcs of a_fsas must be sorted by label (checked by
        calling code via properties), and we'll use a matching approach
        that requires this.
      ret_arc_maps:
        If False, return the resulting Fsa. If True, return a tuple
        containing three entries:

            - the resulting Fsa

            - a_arc_map, a 1-D torch.Tensor with dtype torch.int32.
              a_arc_map[i] is the arc index in a_fsas that corresponds
              to the i-th arc in the resulting Fsa. a_arc_map[i] is -1
              if the i-th arc in the resulting Fsa has no corresponding
              arc in a_fsas.

            - b_arc_map, a 1-D torch.Tensor with dtype torch.int32.
              b_arc_map[i] is the arc index in b_fsas that corresponds
              to the i-th arc in the resulting Fsa. b_arc_map[i] is -1
              if the i-th arc in the resulting Fsa has no corresponding
              arc in b_fsas.

    Returns:
      If ret_arc_maps is False, return intersected FsaVec;
      will satisfy `ans.shape == b_fsas.shape`.
      If ret_arc_maps is True, it returns additionally two arc maps:
      a_arc_map and b_arc_map.
    T)r   intersect_devicer"   
propertiesr   rR   fsa_from_binary_function_tensor)
rV   rW   rX   rY   rZ   rP   r   	a_arc_map	b_arc_mapout_fsasr   r   r   r[      s   D


r[   Ta_fsab_fsatreat_epsilons_speciallyc           	      C   s   |   s|  r| jtj@ dksJ |jtj@ dksJ d}t| j| j|j|j||\}}}tj	| ||||}|r@|||fS |S )a
  Compute the intersection of two FSAs.

    When `treat_epsilons_specially` is True, this function works only on CPU.
    When `treat_epsilons_specially` is False and both `a_fsa` and `b_fsa`
    are on GPU, then this function works on GPU; in this case, the two
    input FSAs do not need to be arc sorted.

    Args:
      a_fsa:
        The first input FSA. It can be either a single FSA or an FsaVec.
      b_fsa:
        The second input FSA. it can be either a single FSA or an FsaVec.
        If both a_fsa and b_fsa are FsaVec, they must contain the same
        number of FSAs.
      treat_epsilons_specially:
        If True, epsilons will be treated as epsilon, meaning epsilon arcs can
        match with an implicit epsilon self-loop.
        If False, epsilons will be treated as real, normal symbols (to have
        them treated as epsilons in this case you may have to add epsilon
        self-loops to whichever of the inputs is naturally epsilon-free).
      ret_arc_maps:
        If False, return the resulting Fsa. If True, return a tuple
        containing three entries:

            - the resulting Fsa

            - a_arc_map, a 1-D torch.Tensor with dtype torch.int32.
              a_arc_map[i] is the arc index in a_fsa that corresponds
              to the i-th arc in the resulting Fsa. a_arc_map[i] is -1
              if the i-th arc in the resulting Fsa has no corresponding
              arc in a_fsa.

            - b_arc_map, a 1-D torch.Tensor with dtype torch.int32.
              b_arc_map[i] is the arc index in b_fsa that corresponds
              to the i-th arc in the resulting Fsa. b_arc_map[i] is -1
              if the i-th arc in the resulting Fsa has no corresponding
              arc in b_fsa.

    Caution:
      The two input FSAs MUST be arc sorted if `treat_epsilons_specially`
      is True.

    Caution:
      The rules for assigning the attributes of the output Fsa are as follows:

      - (1) For attributes where only one source (a_fsa or b_fsa) has that
        attribute: Copy via arc_map, or use zero if arc_map has -1. This rule
        works for both floating point and integer attributes.

      - (2) For attributes where both sources (a_fsa and b_fsa) have that
        attribute: For floating point attributes: sum via arc_maps, or use zero
        if arc_map has -1. For integer attributes, it's not supported for now
        (the attributes will be discarded and will not be kept in the output
        FSA).

    Returns:
      If ret_arc_maps is False, return the result of intersecting a_fsa and
      b_fsa. len(out_fsa.shape) is 2 if and only if the two input FSAs are
      single FSAs; otherwise, len(out_fsa.shape) is 3.
      If ret_arc_maps is True, it returns additionally two arc_maps:
      a_arc_map and b_arc_map.
    r   T)
is_cpur\   r   
ARC_SORTEDr   	intersectr"   r   rR   r]   )	ra   rb   rc   rZ   rP   r   r^   r_   rU   r   r   r   rf   K  s   C


rf   inner_labelsr   c              
   C   s   z
t | jts	J W n ty } ztdt|d}~ww |  }t|}|du s.| r8|j	t
j@ dks8J |dd t|||d}|durO|d| t|dsa|du rYnJ |dd |dd |S )	a  Compute the composition of two FSAs.

    When `treat_epsilons_specially` is True, this function works only on CPU.
    When `treat_epsilons_specially` is False and both `a_fsa` and `b_fsa`
    are on GPU, then this function works on GPU; in this case, the two
    input FSAs do not need to be arc sorted.

    Note:
      `a_fsa.aux_labels` is required to be defined and it can be either
      a `torch.Tensor` or a ragged tensor of type `k2.RaggedTensor`.
      If it is a ragged tensor, then it requires that a_fsa.requires_grad is
      False.

      For both FSAs, the `aux_labels` attribute is interpreted as output labels,
      (olabels), and the composition involves matching the olabels of a_fsa with
      the ilabels of b_fsa.  This is implemented by intersecting the inverse of
      a_fsa (a_fsa_inv) with b_fsa, then replacing the ilabels of the result
      with the original ilabels on a_fsa which are now the aux_labels of
      a_fsa_inv.  If `b_fsa.aux_labels` is not defined, `b_fsa` is treated as an
      acceptor (as in OpenFST), i.e. its olabels and ilabels are assumed to be
      the same.

    Refer to :func:`k2.intersect` for how we assign the attributes of the
    output FSA.

    Args:
      a_fsa:
        The first input FSA. It can be either a single FSA or an FsaVec.
      b_fsa:
        The second input FSA. it can be either a single FSA or an FsaVec.
      treat_epsilons_specially:
        If True, epsilons will be treated as epsilon, meaning epsilon arcs can
        match with an implicit epsilon self-loop.
        If False, epsilons will be treated as real, normal symbols (to have
        them treated as epsilons in this case you may have to add epsilon
        self-loops to whichever of the inputs is naturally epsilon-free).
      inner_labels:
        If specified (and if a_fsa has `aux_labels`), the labels that we matched
        on, which would normally be discarded, will instead be copied to
        this attribute name.

    Caution:
      `b_fsa` has to be arc sorted if the function runs on CPU.

    Returns:
      The result of composing a_fsa and b_fsa. `len(out_fsa.shape)` is 2
      if and only if the two input FSAs are single FSAs;
      otherwise, `len(out_fsa.shape)` is 3.
    z0Expected a_fsa to have aux_labels (not ragged): NTr   r*   left_labels)rc   r   )r   r*   r
   	Exception
ValueErrorstrinvertarc_sortrd   r\   r   re   rename_tensor_attribute_rf   r<   )ra   rb   rc   rg   e	a_fsa_invr(   r   r   r   r!     s0   5
r!   c                 C   sP   | j tj@ dkr| j tj@ dkr| S d}tj| j|d\}}tj	| ||}|S )a  Connect an FSA.

    Removes states that are neither accessible nor co-accessible.

    Note:
      A state is not accessible if it is not reachable from the start state.
      A state is not co-accessible if it cannot reach the final state.

    Caution:
      If the input FSA is already connected, it is returned directly.
      Otherwise, a new connected FSA is returned.

    Args:
     fsa:
        The input FSA to be connected.

    Returns:
      An FSA that is connected.
    r   TrO   )
r\   r   
ACCESSIBLECOACCESSIBLEr   connectr"   r   rR   rS   rT   r   r   r   rs     s   rs   ret_arc_mapc                 C   sn   | j tj@ dkr|rtj| jtj| jd}| |fS | S d}tj	| j
|d\}}tj| ||}|r5||fS |S )a  Sort arcs of every state.

    Note:
      Arcs are sorted by labels first, and then by dest states.

    Caution:
      If the input `fsa` is already arc sorted, we return it directly.
      Otherwise, a new sorted fsa is returned.

    Args:
      fsa:
        The input FSA.
      ret_arc_map:
        True to return an extra arc_map (a 1-D tensor with dtype being
        torch.int32). arc_map[i] is the arc index in the input `fsa` that
        corresponds to the i-th arc in the output Fsa.
    Returns:
      If ret_arc_map is False, return the sorted FSA. It is the same as the
      input `fsa` if the input `fsa` is arc sorted. Otherwise, a new sorted
      fsa is returned and the input `fsa` is NOT modified.
      If ret_arc_map is True, an extra arc map is also returned.

    **Example: Sort a single FSA**

        .. literalinclude:: ./code/arc_sort/main.py
           :language: python
           :linenos:
           :caption: Sort a single FSA

        .. figure:: code/arc_sort/arc_sort_single_before.svg
            :alt: Fsa before k2.arc_sort
            :align: center
            :figwidth: 600px

        .. figure:: code/arc_sort/arc_sort_single_after.svg
            :alt: Fsa before k2.arc_sort
            :align: center
            :figwidth: 600px

        .. figure:: code/arc_sort/arc_sort_single_after_aux_labels.svg
            :alt: Fsa before k2.arc_sort
            :align: center
            :figwidth: 600px
    r   r-   r   TrO   )r\   r   re   r1   rB   num_arcsr3   r   r   rm   r"   r   rR   rS   )r   rt   rL   rP   r   rU   r   r   r   rm     s   .rm   use_double_scoresc                 C   s6   |  |}t| j|\}}|j}tj| ||}|S )a  Return the shortest paths as linear FSAs from the start state
    to the final state in the tropical semiring.

    Note:
      It uses the opposite sign. That is, It uses `max` instead of `min`.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
      use_double_scores:
        False to use float, i.e., single precision floating point, for scores.
        True to use double.

    Returns:
      FsaVec, it contains the best paths as linear FSAs
    )_get_entering_arcsr   shortest_pathr"   valuesr   rR   rS   )r   rw   entering_arcsr   
ragged_intrL   rU   r   r   r   ry   V  s
   
ry   c                 C   s8   d}t j| j|d\}}tj| ||}|r||fS |S )a!  Add epsilon self-loops to an Fsa or FsaVec.

    This is required when composing using a composition method that does not
    treat epsilons specially, if the other FSA has epsilons in it.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
      ret_arc_map:
        If False, return the resulting Fsa.
        If True, return an extra arc map.

    Returns:
      If ret_arc_map is False, return an instance of :class:`Fsa` that has an
      epsilon self-loop on every non-final state.
      If ret_arc_map is True, it returns an extra arc_map. arc_map[i] is the
      arc index in the input `fsa` that corresponds to the i-th arc in the
      resulting Fsa. arc_map[i] is -1 if the i-th arc in the resulting Fsa
      has no counterpart in the input `fsa`.
    TrO   )r   r&   r"   r   rR   rS   )r   rt   rP   r   rL   rU   r   r   r   r&   o  s   
r&   c                 C   *   d}t | j|\}}tj| ||}|S )af  Remove epsilon self-loops of an Fsa or an FsaVec.

    Caution:
      Unlike :func:`remove_epsilon`, this funciton removes only
      epsilon self-loops.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.

    Returns:
      An instance of :class:`Fsa` that has no epsilon self-loops on every
      non-final state.
    T)r   remove_epsilon_self_loopsr"   r   rR   rS   rT   r   r   r   r~     s   r~   c                 C   r}   )a  Reverse the input Fsa.  If the input Fsa accepts string 'x' with weight
    'x.weight', then the reversed Fsa accepts the reverse of string 'x' with
    weight 'x.weight.reverse'. As the Fsas of k2 run on the Log-semiring or
    Tropical-semiring, the 'weight.reverse' will equal to the orignal 'weight'.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.

    Returns:
      An instance of :class:`Fsa` which has been reversed.
    T)r   reverser"   r   rR   rS   rT   r   r   r   r     s   r   c                 C   sR   t | j| j\}}tjj| ||dd}t|dr't|j	tj
r'|j	d|_	|S )a  Remove epsilons (symbol zero) in the input Fsa.

    Caution:
        Call :func:`k2.connect` if you are using a GPU version.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
        Works either for CPU or GPU, but the algorithm is different.
        We can only use the CPU algorithm if the input is top-sorted,
        and the GPU algorithm, while it works for CPU, may not be
        very fast.

        `fsa` must be free of epsilon loops that have score
        greater than 0.


    Returns:
      The resulting Fsa is equivalent to the input `fsa` under the
      tropical semiring but will be epsilon-free.  Any linear tensor
      attributes, such as 'aux_labels', will have been turned into
      ragged labels after removing fillers (i.e. labels whose
      value equals fsa.XXX_filler if the attribute name is XXX),
      counting -1's on final-arcs as fillers even if the filler
      value for that attribute is not -1.
    Tremove_fillerr*   r   )r   remove_epsilonr"   r\   r   rR   fsa_from_unary_function_raggedr<   r   r*   r   remove_values_eq)r   r   rL   rU   r   r   r   r     s   
r   c                 C   s   t | S )zRA wrapper for remove_epsilon(), deprecated but provided
    for back compatibility)r   )r   r   r   r   !remove_epsilon_iterative_tropical  s   r   r   c                 C   sD   | j tj@ dkrt| S t| j| j \}}tjj	| |||d}|S )a  Remove epsilons (symbol zero) in the input Fsa, and then add
    epsilon self-loops to all states in the input Fsa (usually as
    a preparation for intersection with treat_epsilons_specially=0).

    Caution:
        Call :func:`k2.connect` if you are using a GPU version.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
      remove_filler:
        If true, we will remove any `filler values` of attributes when
        converting linear to ragged attributes.
    Returns:
      The resulting Fsa.   See :func:`remove_epsilon` for details.
      The only epsilons will be epsilon self-loops on all states.
    r   r   )
r\   r   EPSILON_FREEr&   r   !remove_epsilon_and_add_self_loopsr"   r   rR   r   )r   r   r   rL   rU   r   r   r   r     s   r   weight_pushing_typec                 C   sT   |   sJ | jdu sJ | jtj@ dkr| S t| j|\}}tj	
| ||}|S )a  Determinize the input Fsa.

    Caution:
      - It only works on for CPU.
      - Any weight_pushing_type value other than kNoWeightPushing causes
        the 'arc_derivs' to not accurately reflect the real derivatives,
        although this will not matter as long as the derivatives ultimately
        derive from FSA operations such as getting total scores or
        arc posteriors, which are insensitive to pushing.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
        Must be connected. It's also expected to be epsilon-free,
        but this is not checked; in any case,
        epsilon will be treated as a normal symbol.
      weight_pushing_type:
        An enum value that determines what kind of weight pushing is desired,
        default kNoWeightPushing.

          kTropicalWeightPushing:
            use tropical semiring (actually, max on scores) for weight pushing.
          kLogWeightPushing:
            use log semiring (actually, log-sum on score) for weight pushing
          kNoWeightPushing:
            do no weight pushing; this will cause some delay in scores being
            emitted, and the weights created in this way will correspond
            exactly to those that would be produced by the arc_derivs.

        For decoding graph creation, we recommend kLogSumWeightPushing.
    Returns:
      The resulting Fsa, it's equivalent to the input `fsa` under
      tropical semiring but will be deterministic.
      It will be the same as the input `fsa` if the input
      `fsa` has property kFsaPropertiesArcSortedAndDeterministic.
      Otherwise, a new deterministic fsa is returned and the
      input `fsa` is NOT modified.
    Fr   )rd   requires_gradr\   r   ARC_SORTED_AND_DETERMINISTICr   determinizer"   r   rR   r   )r   r   r   rL   rU   r   r   r   r   	  s   )r   c           	      C   s   dt jdt jdt jdt jfdd}d}tj| j|d\}}t|}|  D ]\}}|d	kr8||| jd
|}nt||}t	||| q%| 
 D ]
\}}t	||| qH|S )zCompute the Kleene closure of the input FSA.

    Args:
      fsa:
        The input FSA. It has to be a single FSA. That is,
        len(fsa.shape) == 2.
    Returns:
      The resulting FSA which is the Kleene closure of the input FSA.
    src_aux_labelssrc_row_splits1rL   r   c                 S   sL   t j| dkdd}|d }|||k  d7  < t| |}d||< d||< |S )a  Fix the aux labels of the output FSA.

        Since :func:`_k2.closure` changes the labels of arcs entering
        the final state to 0, we need to change their corresponding
        aux labels to 0.

        Args:
          src_aux_labels:
            The aux labels of the input FSA.
          src_row_splits:
            The row splits1 of the input FSA.
          arc_map:
            The arc map produced by :func:`_k2.closure`.
        Returns:
          The aux_labels of the output fsa after converting -1 to 0.
        r+   F)as_tupler   r   )r1   nonzeror	   )r   r   rL   minus_one_indexsrc_start_state_last_arc_indexans_aux_labelsr   r   r   fix_aux_labelsG  s   
zclosure.<locals>.fix_aux_labelsTrO   r*   r   )r1   r
   r   closurer"   r   named_tensor_attr
row_splitsr	   setattrnamed_non_tensor_attr)	r   r   rP   r   rL   rU   namevalue	new_valuer   r   r   r   <  s$   

r   c                 C   s   t | jtjr |du r|  S tj| jtj| jd}|  |fS t | jt	j
s)J | jjtjks2J t| ddgd\} }|  } |rF| |fS | S )a  Invert an FST, swapping the labels in the FSA with the auxiliary labels.

    Args:
      fsa:
        The input FSA. It can be either a single FSA or an FsaVec.
      ret_arc_map:
        True to return an extra arc map, which is a 1-D tensor with dtype
        torch.int32. The returned arc_map[i] is the arc index in the input
        fsa that corresponds to the i-th arc in the returned fsa. arc_map[i]
        is -1 if the i-th arc in the returned fsa has no counterpart in the
        input fsa.
    Returns:
      If ret_arc_map is False, return the inverted Fsa, it's top-sorted if
      `fsa` is top-sorted.
      If ret_arc_map is True, return an extra arc map.
    Fru   Tr*   )rt   ragged_attribute_names)r   r*   r1   r
   rl   rB   rv   r3   r   r   r   r-   expand_ragged_attributesinvert_)r   rt   rL   r   r   r   rl   v  s"   
rl   	num_pathsc           	      C   sh   |dksJ d| d}| j ||d}| j||d}|  }|r%tj}ntj}|| j||||d}|S )ax  Compute pseudo-random paths through the FSAs in this vector of FSAs
    (this object must have 3 axes, `self.arcs.num_axes() == 3`)

    Caution:
      It does not support autograd.

    Caution:
      Do not be confused by the function name. There is no
      randomness at all, thus no `seed`. It uses a deterministic algorithm
      internally, similar to arithmetic coding
      (see `<https://en.wikipedia.org/wiki/Arithmetic_coding>`_).

      Look into the C++ implementation code for more details.

    Args:
      fsas:
        A FsaVec, i.e., `len(fsas.shape) == 3`
      use_double_scores:
        If true, do computation with double-precision,
        else float (single-precision)
      num_paths:
        Number of paths requested through each FSA. FSAs that have no successful
        paths will have zero paths returned.
    Returns:
      Returns a k2.RaggedTensor (dtype is torch.int32) with 3 axes:
      [fsa][path][arc_pos]; the final
      sub-lists (indexed with arc_pos) are sequences of arcs starting from the
      start state and terminating in the final state. The values are arc_idx012,
      i.e. arc indexes.
    r   znum_paths: Trw   log_semiring)r   arc_cdfr   
tot_scoresstate_batches)_get_arc_cdf_get_tot_scores_get_state_batchesr   random_paths_doublerandom_paths_floatr"   )	r   rw   r   r   r   r   r   funcr(   r   r   r   random_paths  s&    r   threshold_probc           	      C   sL   | j |dd}d}|rtj}ntj}|| j|||\}}tj| ||}|S )a  Remove arcs whose posteriors are less than the given threshold.

    Args:
      fsas:
        An FsaVec. Must have 3 axes.
      threshold_prob:
        Arcs whose posteriors are less than this value are removed.
        Note:
          0 < threshold_prob < 1
      use_double_scores:
        True to use double precision during computation; False to use
        single precision.
    Returns:
      Return a pruned FsaVec.
    Tr   )get_arc_postr   prune_on_arc_post_doubleprune_on_arc_post_floatr"   r   rR   rS   )	r   r   rw   arc_postrP   r   r   rL   rU   r   r   r   prune_on_arc_post  s   

r   r   c                    s  |du r,g }g } j ddD ]\}}t|tjr*|| || |jtjks*J qn fdd|D }|D ]}t|tjsAJ |jtjksIJ q7t|dkrc|ratj	 j
tj jd} |fS  S t j|\}}	}t|}
 j ddD ]C\}}t|tjrt |}t|||d}t|
|| qw||vrt|tjsJ |jtjksJ |j|ddd	\}}t|
|| qwt||	D ]
\}}t|
|| q  D ]
\}}t|
|| qtj|
 j| t|
d
rt|
j|
j |r|
|fS |
S )a  
    Turn ragged labels attached to this FSA into linear (Tensor) labels,
    expanding arcs into sequences of arcs as necessary to achieve this.
    Supports autograd.  If `fsas` had no ragged attributes, returns `fsas`
    itself.

    Caution:
      This function will ensure that for final-arcs in the returned
      fsa, the corresponding labels for all ragged attributes are -1; it will
      add an extra arc at the end if necessary to ensure this, if the
      original ragged attributes did not have -1 as their final element on
      final-arcs (note: our intention is that -1's on final arcs, like filler
      symbols, are removed when making attributes ragged; this is what
      fsa_from_unary_function_ragged() does if remove_filler==True (the
      default).

    Args:
      fsas:
        The source Fsa
      ret_arc_map:
        If true, will return a pair (new_fsas, arc_map)
        with `arc_map` a tensor of int32 that maps from arcs in the
        result to arcs in `fsas`, with -1's for newly created arcs.
        If false, just returns new_fsas.
      ragged_attribute_names:
        If specified, just this list of ragged
        attributes will be expanded to linear tensor attributes, and
        the rest will stay ragged.
    NF)include_scoresc                    s   g | ]}t  |qS r   )getattr).0r   r   r   r   
<listcomp>  s    
z,expand_ragged_attributes.<locals>.<listcomp>r   ru   )default_value)axisneed_value_indexesr*   )r   r   r   r   appendr-   r1   r3   r   rB   rv   r   r   expand_arcsr"   r   r
   float
get_fillerr	   r   indexzipr   autograd_utilsphantom_index_select_scoresrE   r<   fix_final_labelsr*   )r   rt   r   ragged_attribute_tensorsr   r   trL   	dest_arcsdest_labelsdestfillerr   _r   r   r   r     sh   !




r   srcr   symbol_begin_rangec                 C   s>   t | j|j|\}}}tj| ||||}|r|||fS |S )a  
    Replace arcs in index FSA with the corresponding fsas in a vector of
    FSAs(src). For arcs in `index` with label
    `symbol_range_begin <= label < symbol_range_begin + src.Dim0()` will be
    replaced with fsa indexed `label - symbol_begin_range` in `src`.
    The destination state of the arc in `index` is identified with the
    `final-state` of the corresponding FSA in `src`, and the arc in `index`
    will become an epsilon arc leading to a new state in the output that is
    a copy of the start-state of the corresponding FSA in `src`. Arcs with
    labels outside this range are just copied. Labels on final-arcs in `src`
    (Which will be -1) would be set to 0(epsilon) in the result fsa.

    Caution:
      Attributes of the result inherits from `index` and `src` via
      `arc_map_index` and `arc_map_src`, But if there are attributes
      with same name, only the attributes with dtype `torch.float32`
      are supported, the other kinds of attributes are discarded.
      See docs in `fsa_from_binary_function_tensor` for details.

    Args:
      src:
          Fsa that we'll be inserting into the result, MUST have 3 axes.
      index:
          The Fsa that is to be replaced, It can be a single FSA or a vector of
          FSAs.
      symbol_range_begin:
          Beginning of the range of symbols that are to be replaced with Fsas.
      ret_arc_map:  if true, will return a tuple
           (new_fsas, arc_map_index, arc_map_src) with `arc_map_index` and
           `arc_map_src` tensors of int32 that maps from arcs in the result to
           arcs in `index` and `src` , with -1's for the arcs not mapped.
           If false, just returns new_fsas.
    )r   replace_fsar"   r   rR   r]   )r   r   r   rt   dest_arcarc_map_srcarc_map_indexr   r   r   r   r   V  s   (
r   cpusymbolsmodifiedc                 C   s:   t | tjstj| |d} t| |\}}t||d}|S )av
  Construct ctc graphs from symbols.

    Note:
      The scores of arcs in the returned FSA are all 0.

    Args:
      symbols:
        It can be one of the following types:

            - A list of list-of-integers, e..g, `[ [1, 2], [1, 2, 3] ]`
            - An instance of :class:`k2.RaggedTensor`.
              Must have `num_axes == 2`.

      standard:
        Option to specify the type of CTC topology: "standard" or "simplified",
        where the "standard" one makes the blank mandatory between a pair of
        identical symbols. Default True.
      device:
        Optional. It can be either a string (e.g., 'cpu', 'cuda:0') or a
        torch.device.
        By default, the returned FSA is on CPU.
        If `symbols` is an instance of :class:`k2.RaggedTensor`, the returned
        FSA will on the same device as `k2.RaggedTensor`.

    Returns:
        An FsaVec containing the returned ctc graphs, with "Dim0()" the same as
        "len(symbols)"(List[List[int]]) or "dim0"(k2.RaggedTensor)

    **Example 1**

        .. literalinclude:: ./code/ctc_graph/main.py
           :language: python
           :linenos:
           :caption: Usage of k2.ctc_graph

        .. figure:: code/ctc_graph/ctc_graph.svg
            :alt: CTC graph (modified=False)
            :align: center
            :figwidth: 600px

            Note: There is a mandatory blank between state 3 and 5.

        .. figure:: code/ctc_graph/modified_ctc_graph.svg
            :alt: CTC graph (modified=True)
            :align: center
            :figwidth: 600px

            Note: There is **no** mandatory blank between state 3 and 5.

    **Example 2 Construct a CTC graph using composition**

        .. literalinclude:: ./code/ctc_graph/main2.py
           :language: python
           :linenos:
           :caption: Construct a CTC graph using composition

        .. figure:: code/ctc_graph/linear_fsa.svg
            :alt: Linear FSA
            :align: center
            :figwidth: 600px

        .. figure:: code/ctc_graph/ctc_topo.svg
            :alt: CTC topology (modified=False)
            :align: center
            :figwidth: 600px

        .. figure:: code/ctc_graph/ctc_topo_compose_linear_fsa.svg
            :alt: k2.compose(ctc_topo, linear_fsa)
            :align: center
            :figwidth: 600px

        .. figure:: code/ctc_graph/ctc_topo_modified.svg
            :alt: CTC topology (modified=True)
            :align: center
            :figwidth: 600px

        .. figure:: code/ctc_graph/ctc_topo_modified_compose_linear_fsa.svg
            :alt: k2.compose(ctc_topo_modified, linear_fsa)
            :align: center
            :figwidth: 600px
    r   r.   )r   r   r   r   	ctc_graphr   )r   r   r   r   r*   r   r   r   r   r     s
   Tr   	max_tokenc                 C   s"   t | ||\}}t||d}|S )a  Create a CTC topology.

    A token which appears once on the right side (i.e. olabels) may
    appear multiple times on the left side (ilabels), possibly with
    epsilons in between.
    When 0 appears on the left side, it represents the blank symbol;
    when it appears on the right side, it indicates an epsilon. That
    is, 0 has two meanings here.

    A standard CTC topology is the conventional one, where there
    is a mandatory blank between two repeated neighboring symbols.
    A non-standard, i.e., modified CTC topology, imposes no such constraint.

    See https://github.com/k2-fsa/k2/issues/746#issuecomment-856421616
    and https://github.com/k2-fsa/snowfall/pull/209
    for more details.

    Args:
      max_token:
        The maximum token ID (inclusive). We assume that token IDs
        are contiguous (from 1 to `max_token`). 0 represents blank.
      modified:
        If False, create a standard CTC topology. Otherwise, create a
        modified CTC topology.
      device:
        Optional. It can be either a string (e.g., 'cpu',
        'cuda:0') or a torch.device.
        If it is None, then the returned FSA is on CPU.
    Returns:
      Return either a standard or a modified CTC topology as an FSA
      depending on whether `standard` is True or False.

    **Example**

        .. literalinclude:: ./code/ctc_topo/main.py
           :language: python
           :linenos:
           :caption: Usage of k2.ctc_topo

        .. figure:: code/ctc_topo/ctc_topo.svg
            :alt: CTC topology
            :align: center
            :figwidth: 600px

        .. figure:: code/ctc_topo/modified_ctc_topo.svg
            :alt: CTC topology
            :align: center
            :figwidth: 600px
    r.   )r   ctc_topor   )r   r   r   r   r*   r   r   r   r   r     s   4r   c                 C   s    t | |\}}t||d}|S )a  Create a trivial graph which has only two states. On state 0, there are
    `max_token` self loops(i.e. a loop for each symbol from 1 to max_token), and
    state 1 is the final state.

    Args:
      max_token:
        The maximum token ID (inclusive). We assume that token IDs
        are contiguous (from 1 to `max_token`).
      device:
        Optional. It can be either a string (e.g., 'cpu',
        'cuda:0') or a torch.device.
        If it is None, then the returned FSA is on CPU.

    Returns:
      Returns the expected trivial graph on the given device.
      Note: The returned graph does not contain arcs with label being 0.
    r.   )r   trivial_graphr   )r   r   r   r*   r   r   r   r   r     s   r   x&1ins_del_scorec                 C   sJ   t | tjstj| |d} t| |d\}}}t||d}t|d| |S )aD  Construct levenshtein graphs from symbols.

    See https://github.com/k2-fsa/k2/pull/828 for more details about levenshtein
    graph.

    Args:
      symbols:
        It can be one of the following types:

            - A list of list-of-integers, e..g, `[ [1, 2], [1, 2, 3] ]`
            - An instance of :class:`k2.RaggedTensor`.
              Must have `num_axes == 2` and with dtype `torch.int32`.

      ins_del_score:
        The score on the self loops arcs in the graphs, the main idea of this
        score is to set insertion and deletion penalty, which will affect the
        shortest path searching produre.
      device:
        Optional. It can be either a string (e.g., 'cpu', 'cuda:0') or a
        torch.device.
        By default, the returned FSA is on CPU.
        If `symbols` is an instance of :class:`k2.RaggedTensor`, the returned
        FSA will on the same device as `k2.RaggedTensor`.

    Returns:
        An FsaVec containing the returned levenshtein graphs, with "Dim0()"
        the same as "len(symbols)"(List[List[int]]) or "dim0"(k2.RaggedTensor).
    r   Tr.   %__ins_del_score_offset_internal_attr_)r   r   r   r   levenshtein_graphr   r   )r   r   r   r   r*   score_offsetsr   r   r   r   r   5  s   !
r   refshypshyp_to_ref_mapsorted_match_refc                 C   s   t | dsJ t |dsJ |dd tj| |||d}t|}tj|dd }|dd |dd | jt|d8  _|S )	a  Get the levenshtein alignment of two FsaVecs

    This function supports both CPU and GPU. But it is very slow on CPU.

    Args:
      refs:
        An FsaVec (must have 3 axes, i.e., `len(refs.shape) == 3`. It is the
        output Fsa of the :func:`levenshtein_graph`.
      hyps:
        An FsaVec (must have 3 axes) on the same device as `refs`. It is the
        output Fsa of the :func:`levenshtein_graph`.
      hyp_to_ref_map:
        A 1-D torch.Tensor with dtype torch.int32 on the same device
        as `refs`. Map from FSA-id in `hpys` to the corresponding
        FSA-id in `refs` that we want to get levenshtein alignment with.
        E.g. might be an identity map, or all-to-zero, or something the
        user chooses.

        Requires
            - `hyp_to_ref_map.shape[0] == hyps.shape[0]`
            - `0 <= hyp_to_ref_map[i] < refs.shape[0]`
      sorted_match_ref:
        If true, the arcs of refs must be sorted by label (checked by
        calling code via properties), and we'll use a matching approach
        that requires this.

    Returns:
      Returns an FsaVec containing the alignment information and satisfing
      `ans.Dim0() == hyps.Dim0()`. Two attributes named `ref_labels` and
      `hyp_labels` will be added to the returned FsaVec. `ref_labels` contains
      the aligned sequences of refs and `hyp_labels` contains the aligned
      sequences of hyps. You can get the levenshtein distance by calling
      `get_tot_scores` on the returned FsaVec.

    Examples:
      >>> hyps = k2.levenshtein_graph([[1, 2, 3], [1, 3, 3, 2]])
      >>> refs = k2.levenshtein_graph([[1, 2, 4]])
      >>> alignment = k2.levenshtein_alignment(
              refs, hyps,
              hyp_to_ref_map=torch.tensor([0, 0], dtype=torch.int32),
              sorted_match_ref=True)
      >>> alignment.labels
      tensor([ 1,  2,  0, -1,  1,  0,  0,  0, -1], dtype=torch.int32)
      >>> alignment.ref_labels
      tensor([ 1,  2,  4, -1,  1,  2,  4,  0, -1], dtype=torch.int32)
      >>> alignment.hyp_labels
      tensor([ 1,  2,  3, -1,  1,  3,  3,  2, -1], dtype=torch.int32)
      >>> -alignment.get_tot_scores(
              use_double_scores=False, log_semiring=False))
      tensor([1., 3.])
    r*   
hyp_labels)rX   rY   T)rw   r   
ref_labelsr   )	r<   rn   r   r[   r~   ry   r   rE   r   )r   r   r   r   lattice	alignmentr   r   r   levenshtein_alignmentb  s    9

r   c                 C   r}   )a  Compute the union of a FsaVec.

    Caution:
      We require that every fsa in fsas is non-empty, i.e.,
      contains at least two states

    Args:
      fsas:
        A FsaVec. That is, len(fsas.shape) == 3.

    Returns:
      A single Fsa that is the union of the input fsas.
    T)r   unionr"   r   rR   rS   )r   rP   r   rL   rU   r   r   r   r     s   
r   )N)FF)TF)TN)F)T)FN)r   F)Fr   )r   r   )5typingr   r   r   r   r1   r   r    r   r   r   opsr	   r
   intr   r   rk   r   r)   r7   rN   rQ   boolr[   rf   r!   rs   rm   ry   r&   r~   r   r   r   r   DeterminizeWeightPushingTypekNoWeightPushingr   r   rl   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s\   
"
 _
S
W
V
B
")
 
3;

&

4
"

h
3
]
:

1
N