o
    Gi>                  
   @   s   d dl Z d dlmZmZ d dlZd dlZd dlZddlmZ 	ddejde	deeee
  ejf fd	d
ZG dd deZdededefddZdede
defddZdS )    N)ListUnion   )FsaF
best_pathsreturn_raggedreturnc                 C   s   t | jtjr(| jd}| j |j}|d}|d}t||j	}n| j d}t|| j}|d}|j
dksCJ |rG|S | S )a  Extract the texts (as word IDs) from the best-path FSAs.

    Note:
        Used by Nbest.build_levenshtein_graphs during MWER computation.
        Copied from icefall.

    Args:
      best_paths:
        A k2.Fsa with best_paths.arcs.num_axes() == 3, i.e.
        containing multiple FSAs, which is expected to be the result
        of k2.shortest_path (otherwise the returned values won't
        be meaningful).
      return_ragged:
        True to return a ragged tensor with two axes [utt][word_id].
        False to return a list-of-list word IDs.
    Returns:
      Returns a list of lists of int, containing the label sequences we
      decoded.
    r   r      )
isinstance
aux_labelsk2RaggedTensorremove_values_leqarcsshapecomposeremove_axisvaluesnum_axestolist)r   r   r   	aux_shape r   </home/ubuntu/.local/lib/python3.10/site-packages/k2/nbest.py
_get_texts   s   


r   c                   @   s   e Zd ZdZ	ddejdejdejddfddZd	d
 Z	e
		ddejdedededd f
ddZdedd fddZdejfddZdedd fddZdejfddZdS )Nbesta  
    An Nbest object contains two fields:

        (1) fsa, its type is k2.Fsa
        (2) shape, its type is k2.RaggedShape (alias to _k2.RaggedShape)

    The field `fsa` is an FsaVec containing a vector of **linear** FSAs.

    The field `shape` has two axes [utt][path]. `shape.dim0()` contains
    the number of utterances, which is also the number of rows in the
    supervision_segments. `shape.tot_size(1)` contains the number
    of paths, which is also the number of FSAs in `fsa`.
    Nfsar   	kept_pathr   c                 C   s   t |jdksJ d|j |jdksJ d|j |jd |dks5J |jd  d|d || _|| _|| _d S )N   zfsa.shape: r	   z
num_axes: r   r    vs )lenr   r   tot_sizer   r   )selfr   r   r   r   r   r   __init__P   s   
zNbest.__init__c                 C   s6   d}|d| j   d7 }|d| jj d  d7 }|S )NzNbest(z	num_seqs:z, z	num_fsas:r   ))r   dim0r   )r!   sr   r   r   __str__^   s   zNbest.__str__T      ?lattice	num_pathsuse_double_scoresnbest_scalec                 C   sT  | j  }|  j |9  _ tj| ||d}|| _ t| jtjr'tj	| j|}n| j	|}|
|jd }|d}|jddd\}}}|j	|ddd\}	}|	jd}
|	
d}	tj	| j |	}|d	}t| jtjr|| jj	|	jddd
\}}nt| jtjsJ t| j|	j}t|}||_t| j |	j| j j|_ t||
|	dS )ab  Construct an Nbest object by **sampling** `num_paths` from a lattice.

        Each sampled path is a linear FSA.

        We assume `lattice.labels` contains token IDs and `lattice.aux_labels`
        contains word IDs.

        Args:
          lattice:
            An FsaVec with axes [utt][state][arc].
          num_paths:
            Number of paths to **sample** from the lattice
            using :func:`k2.random_paths`.
          use_double_scores:
            True to use double precision in :func:`k2.random_paths`.
            False to use single precision.
          nbest_scale:
            Scale `lattice.score` before passing it to :func:`k2.random_paths`.
            A smaller value leads to more unique paths at the risk of being not
            to sample the path with the best score.
        Returns:
          Return an Nbest instance.
        r)   r*   r	   r   FTneed_num_repeatsneed_new2old_indexesr   )axisneed_value_indexes)indexesr0   r1   )r   r   r   )scorescloner   random_pathsr
   r   torchTensorraggedindexr   r   r   uniquer   	get_layerlabels
contiguousremove_values_eqr   r   index_select
linear_fsatodevicer   )r(   r)   r*   r+   saved_scorespathword_seq_new2oldr   utt_to_path_shaper=   r   r   r   r   r   from_latticed   s>   

	


zNbest.from_latticelatsc                 C   s   | j j|jksJ | j j d|j t|jdks J |j |j | j ks9J |j  d| j  t|}t| j }| j	d}tj
|||dd}tj|dd}t|}t|| jdS )aP  Intersect this Nbest object with a lattice and get 1-best
        path from the resulting FsaVec.

        Caution:
          We assume FSAs in `self.fsa` don't have epsilon self-loops.
          We also assume `self.fsa.labels` and `lats.labels` are token IDs.

        Args:
          lats:
            An FsaVec. It can be the return value of
            :func:`whole_lattice_rescoring`.
        Returns:
          Return a new Nbest. This new Nbest shares the same shape with `self`,
          while its `fsa` is the 1-best path from intersecting `self.fsa` and
          `lats`.
        r   r   r   T)a_fsasb_fsas
b_to_a_mapsorted_match_a)r*   r   r   )r   rC   r   r   r   r$   r   arc_sortadd_epsilon_self_loopsrow_idsintersect_deviceshortest_pathremove_epsilonr   )r!   rK   fsas_with_epsilon_loopspath_to_seq_mapans_latsone_bestr   r   r   	intersect   s$   

zNbest.intersectc                 C   s"   | j jddd}t| j| S )a  Get total scores of the FSAs in this Nbest.

        Note:
          Since FSAs in Nbest are just linear FSAs, log-semirng and tropical
          semiring produce the same total scores.

        Returns:
          Return a ragged tensor with two axes [utt][path_scores].
        TF)r*   log_semiring)r   get_tot_scoresr   r   r   float)r!   r4   r   r   r   total_scores   s   
zNbest.total_scoreskc           	      C   s   |   }|jddd}t| j|}|jddd}t|d s,J d| j	d |d	d	d	|f 
  }t| j|}tjj| jj|d
}t||S )a>  Get a subset of paths in the Nbest. The resulting Nbest is regular
        in that each sequence (i.e., utterance) has the same number of
        paths (k).

        We select the top-k paths according to the total_scores of each path.
        If a utterance has less than k paths, then its last path, after sorting
        by tot_scores in descending order, is repeated so that each utterance
        has exactly k paths.

        Args:
          k:
            Number of paths in each utterance.
        Returns:
          Return a new Nbest with a regular shape.
        T)
descendingr/   	replicater2   )modepadding_valuer   z&Some utterances contain empty n-best: r   N)r$   dim1)r_   sort_r   r   r   padr7   geall
row_splitsflattenr>   	index_fsar   r9   regular_ragged_shaper$   r   )	r!   r`   ragged_scoresr3   ragged_indexespadded_indexestop_k_indexes
top_k_fsastop_k_shaper   r   r   top_k   s"   

zNbest.top_kc                 C   s   t | jdd}t|S )z-Return an FsaVec with axes [utt][state][arc].T)r   )r   r   r   levenshtein_graph)r!   word_idsr   r   r   build_levenshtein_graphs&  s   
zNbest.build_levenshtein_graphs)N)Tr'   )__name__
__module____qualname____doc__r   r   RaggedShaper   r"   r&   staticmethodintboolr^   rJ   r[   r_   rt   rw   r   r   r   r   r   A   s>    
c((r   rK   G_with_epsilon_loopsc           	   
   C   s0  t | jdksJ | j t| dsJ |jdksJ |j | j}| j| j | _| `t| ddu s4J t| }| jd }tj	||tj
d}	 ztj|||dd}W n5 ty } z(td	| d
 td|j  t|dd}td|j  W Y d}~nd}~ww qHtt|}t|}|S )a  Rescore the 1st pass lattice with an LM.

    In general, the G in HLG used to obtain `lats` is a 3-gram LM.
    This function replaces the 3-gram LM in `lats` with a 4-gram LM.

    Args:
      lats:
        The decoding lattice from the 1st pass. We assume it is the result
        of intersecting HLG with the network output.
      G_with_epsilon_loops:
        An LM. It is usually a 4-gram LM with epsilon self-loops.
        It should be arc sorted.
    Returns:
      Return a new lattice rescored with a given G.
    r   	lm_scores)r   NNFr   )rC   dtypeT)rO   zCaught exception:

znum_arcs before: gh㈵>znum_arcs after: N)r   r   hasattrrC   r4   r   r   invertr7   zerosint32rT   RuntimeErrorlogginginfonum_arcsprune_on_arc_posttop_sortconnect)	rK   r   rC   inverted_latsnum_seqsrN   rescoring_latseinv_rescoring_latsr   r   r   whole_lattice_rescoring,  s>   


r   r)   c                 C   s   t | jdks	J t| dsJ t| drJ | j| _tj| |dd}tj| j|}|	d}|j
ddd\}}}|jd}|d}t|}t||d	S )
a  Generate an n-best list from a lattice.

    Args:
      lats:
        The decoding lattice from the first pass after LM rescoring.
        lats is an FsaVec. It can be the return value of
        :func:`whole_lattice_rescoring`
      num_paths:
        Size of n for n-best list. CAUTION: After removing paths
        that represent the same token sequences, the number of paths
        in different sequences may not be equal.
    Return:
      Return an Nbest object. Note the returned FSAs don't have epsilon
      self-loops.
    r   phonestokensTr,   r   Fr-   rP   )r   r   r   r   r   r   r6   r9   r:   r   r;   r<   r   rA   r   )rK   r)   paths
token_seqsunique_token_seqsrG   seq_to_path_shape
token_fsasr   r   r   generate_nbest_listl  s   


r   )F)r   typingr   r   r7   _k2r   r   r   r   r~   r   r   objectr   r   r   r   r   r   r   <module>   s$   
. l@