o
    GiB                     @   s   d dl mZ d dl mZ d dl mZ 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Zd dlZd dlZd dlZd d	lmZ d d
lmZ G dd deZdee dee deee  deeee f fddZdS )    )Any)Dict)Iterator)List)Optional)Tuple)UnionN)	RaggedArc)fsa_propertiesc                   @   s  e Zd ZdZ		ddeejef deeeje	j
f  ddfddZdd	eddfd
dZddedefddZdefddZdedeeef fddZ	ddee dee ddfddZdededdfddZedefddZedefd d!Zedefd"d#Zedefd$d%Zedejfd&d'Zdedefd(d)Zdeddfd*d+Zde	j
fd,d-Zdejfd.d/Z de	j
fd0d1Z!de	j
fd2d3Z"de	j
fd4d5Z#d6ed7edejfd8d9Z$d6ed7edejfd:d;Z%d6ed7edejfd<d=Z&d6ed7edejfd>d?Z'd6ed7edejfd@dAZ(d6ed7edejfdBdCZ)d6ed7edejfdDdEZ*d6ed7edejfdFdGZ+d6ed7edejfdHdIZ,d6edejfdJdKZ-dLedd fdMdNZ.dOedPedd fdQdRZ/ddSdTZ0ddUdVZ1defdWdXZ2defdYdZZ3edej4fd[d\Z4d]edd fd^d_Z5dejfd`daZ6de7eef fdbdcZ8e9dde7eef dd fdedfZ:dgeeej4f dd fdhdiZ;ddjdkZ<ddldmZ=ddnede>e?eejf  fdodpZ@de>e?eef  fdqdrZAede?edsf fdtduZBe9dddg dfdvedwee dxee dyeeCe  dzeCe dedd fd{d|ZDe9dddg fdvedwee dxee dyeeCe  dzeCe dd fd}d~ZEeFdeCd  dd fddZGdddZH	ddededd fddZIdS )Fsaa  This class represents a single fsa or a vector of fsas.

    When it denotes a single FSA, its attribute :attr:`shape` is a tuple
    containing two elements `(num_states, None)`; when it represents
    a vector of FSAs it is a tuple with three
    elements `(num_fsas, None, None)`.

    CAUTION:
      It's possible for a vector of FSAs to have zero or one elements.

    An instance of FSA has the following attributes:

    arcs
      You will NOT use it directly in Python. It is an instance of
      `_k2.RaggedArc` with only one method `values()` which
      returns a 2-D `torch.Tensor` of dtype `torch.int32` with 4
      columns. Its number of rows indicates the number of arcs in the
      FSA. The first column represents the source states, second
      column the destination states, third column the labels and the
      fourth column is the score. Note that the score is actually
      a float number but it is **reinterpreted** as an integer.

    scores
      A 1-D `torch.Tensor` of dtype `torch.float32`. It has
      as many entries as the number of arcs representing the score
      of every arc.

    labels
      1-D `torch.Tensor` of dtype `torch.int32`. It has as
      many entries as the number of arcs representing the label of
      every arc.


    It MAY have the following attributes:

    symbols
      An instance of `k2.SymbolTable`. It maps an entry in
      `labels` to an integer and vice versa. It is used for
      visualization only.

    aux_labels
     A 1-D `torch.Tensor` of dtype `torch.int32` or a ragged tensor with type
     `k2.RaggedTensor`. It contains auxiliary labels per arc.  If it's a tensor,
     `aux_labels.numel()` equals to the number of arcs.  if it's
     `k2.RaggedTensor`, then `aux_labels.dim0()` equals to the number of arcs.

    aux_symbols
      An instance of `k2.SymbolTable`. It maps an entry in
      `aux_labels` to an integer and vice versa.

    properties
      An integer that encodes the properties of the FSA. It is
      accessed as fsa.properties (read-only!)

    It MAY have other attributes that set by users.  Tensor attributes should
    have the same 1st dimension as the number of arcs in the FSA, Ragged
    attributes should have the same `dim0` as the number of arcs in the FSA.

    CAUTION:
      When an attribute is an instance of `torch.Tensor`, its `shape[0]`
      has to be equal to the number arcs. Otherwise, an assertion error
      will be thrown.
      When an attribute is an instance of `k2.RaggedTensor`, its `dim0`
      has to be equal to the number arcs. Otherwise, an assertion error
      will be thrown.

    NOTE:
      `symbols` and `aux_symbols` are symbol tables, while `labels`
      is instances of `torch.Tensor` and `aux_labels` is instances of
      `torch.Tensor` or `k2.RaggedTensor`.

      Implementation note: most of this class's attributes are not
      real attributes in the object's dict; the real attributes are
      `arcs`, `_non_tensor_attr`, `_tensor_attr`, `_properties`,
      `_cache`.

    Narcs
aux_labelsreturnc                 C   s   t |tjrt|}t |tsJ || jd< || jd< dD ]}t | j|< qt| j	
 dddf | jd< | j	
 dddf | jd< | jd j| _|durbt |tjr_|tj| _n|| _| j}dS )	aa  Build an Fsa from a tensor with optional aux_labels.

        It is useful when loading an Fsa from file.

        Args:
          arcs:
            When the `arcs` is an instance of `torch.Tensor`, it is
            a torch tensor of dtype `torch.int32` with 4 columns.
            Each row represents an arc. Column 0 is the src_state,
            column 1 the dest_state, column 2 the label, and column
            3 the score.
            When the `arcs` is an instance of `_k2.RaggedArc`, it is a
            Ragged containing `_k2.Arc` returned by internal functions
            (i.e. C++/CUDA functions) or got from other Fsa object
            by `fsa.arcs`.

            Caution:
              Scores are floats and their binary pattern is
              **reinterpreted** as integers and saved in a tensor
              of dtype `torch.int32`.

          aux_labels:
            Optional. If not None, it associates an aux_label with every arc,
            so it has as many rows as `tensor`. It is a 1-D tensor of dtype
            `torch.int32` or `k2.RaggedTensor` whose `dim0` equals to the
            number of arcs.

          properties:
            Tensor properties if known (should only be provided by
            internal code, as they are not checked; intended for use
            by :func:`clone`)

        Returns:
          An instance of Fsa.
        r   _properties)_tensor_attr_non_tensor_attr_cacheNscores   labels)
isinstancetorchTensor_k2fsa_from_tensorr	   __dict__dictas_floatr   valuesr   _versionlabels_versiontoint32r   
properties)selfr   r   r$   name_ r(   :/home/ubuntu/.local/lib/python3.10/site-packages/k2/fsa.py__init__w   s   (


7"
zFsa.__init__Tscores_onlyc                 C   s|   |du rt  | jd< dS td}g }| jd D ]}||r$|| qd| jd v r1|d |D ]}| jd |= q3dS )a}  Intended for internal use only so its
        name begins with an underline.

        Also, it changes `self` in-place.

        Currently, it is used only when the `scores` field
        are re-assigned.

        Args:
          scores_only:
            It True, it invalidates only cached entries related
            to scores. If False, the whole cache is invalidated.

        Fr   zscore|arc_cdf|arc_postentering_arcsN)r   r   recompilesearchappend)r%   r+   pattern	to_removekeyr(   r(   r)   _invalidate_cache_   s   



zFsa._invalidate_cache_Fopenfstc           
   
      s  g }g }t | jddD ]!\}}t|tjr"|jtjkr"|| qt|tj	r-|| q| j
 dkrBdtj| j
|||d }nAd}t| jd D ]7}| j
d|\}| jd   |dt| d	 tj|| fd
d|D  fdd|D d 7 }qK|dt| j d 7 }| jddD ]\}}d}	||	 | d| 7 }q|  D ]\}}|dkrqd}	||	 | d| 7 }q|S )NFinclude_scoresr   zk2.Fsa: )r5   extra_labelsragged_labelszk2.FsaVec: 
r   zFsaVec[z]: c                       g | ]}|  qS r(   r(   .0xendstartr(   r)   
<listcomp>(      zFsa.to_str.<locals>.<listcomp>c                    r:   r(   r(   r;   r>   r(   r)   rA   )  rB   zproperties_str = .
z: 
labels_sym)sortednamed_tensor_attrr   r   r   dtyper#   r0   k2RaggedTensorr   num_axesr   
fsa_to_strrangeshapeindexr   strfsa_properties_as_strr   named_non_tensor_attr)
r%   r5   r8   r9   r&   valueansi
ragged_arcsepr(   r>   r)   to_str  sH   

z
Fsa.to_strc                 C   s   | j ddS )zaReturn a string representation of this object

        For visualization and debug only.
        Fr5   )rX   r%   r(   r(   r)   __str__7  s   zFsa.__str__attribute_namec                 C   s,   t | |d d}|dks|dksJ d|S )a  Return the filler value associated with attribute names.

        This is 0 unless otherwise specified, but you can override this by
        for example, doing::

            fsa.foo_filler = -1

        which will mean the "filler" for attribute fsa.foo is -1; and this will
        get propagated when you do FSA operations, like any other non-tensor
        attribute.  The filler is the value that means "nothing is here" (like
        epsilon).

        Caution::
          you should use a value that is castable to float and back to integer
          without loss of precision, because currently the `default_value`
          parameter of `index_select` in ./ops.py is a float.
        _fillerr   r   z(you cannot set the filler for aux_labels)getattr)r%   r\   rT   r(   r(   r)   
get_filler>  s
   zFsa.get_fillerfilenametitleDigraphc           	      C   s   t jj| |d}tj|\}}|dks|d dkr!td||rOddl}|	 }|j
d||dd d	d
}t|| W d   |S 1 sJw   Y  |S )ap  
        Render FSA as an image via graphviz, and return the Digraph object;
        and optionally save to file `filename`.
        `filename` must have a suffix that graphviz understands, such as
        `pdf`, `svg` or `png`.

        Note:
          You need to install graphviz to use this function::

            pip install graphviz

        Args:
           filename:
              Filename to (optionally) save to, e.g. 'foo.png', 'foo.svg',
              'foo.png'  (must have a suffix that graphviz understands).
           title:
              Title to be displayed in image, e.g. 'A simple FSA example'
        )ra    r   rC   z9Filename needs to have a suffix like .png, .pdf, .svg: {}Ntemp   T)r`   	directoryformatcleanup)rI   utilsto_dotospathsplitext
ValueErrorrg   tempfileTemporaryDirectoryrendershutilmove)	r%   r`   ra   digraphr'   	extensionro   tmp_dirtemp_fnr(   r(   r)   drawV  s(   


zFsa.drawr&   rS   c                 C   sr  |dvsJ t |tjr{|jd | j jd ksJ |j| jks#J |dkrR|jtjks/J || j dddf< | j	d j
| _t| jd d| jd< | j dS || j	|< |dkry|jtjkscJ t| | j dddf< |   dS dS t |tjr|j| j jd ksJ d	|j d
| j jd  |j| jksJ |j| jf|| j	|< dS || j|< dS )a  
        Caution:
          We save a reference to `value`. If you need to change `value`
          afterwards, please consider passing a copy of it.

        Args:
          name:
            Name of the attribute.
          value:
            Value of the attribute.
        )r   r   r   r   r   r$   r   r   Nr   r   r   r   zvalue.dim0: z, shape[0]: )r   r   r   rN   r   r   devicerH   r#   r   r    r!   r   fix_final_labelsr   r$   float32as_intdetachr4   rI   rJ   dim0r   r%   r&   rS   r(   r(   r)   __setattr__~  s2   

 zFsa.__setattr__c                 C   s
   | j  S )z/Return the number of arcs in this Fsa.
        )r   num_elementsrZ   r(   r(   r)   num_arcs  s   
zFsa.num_arcsc                 C   s   | j dd }|d ur| jj| jkrtd|S | j dkr&t	| j}nt
| j}|| j d< |tj@ dkrGtd|t|t| j|S )Nr   zThe fsa attribute (labels) has been inappropriately modified like:
    fsa.labels[xxx] = yyy
The correct way should be like:
    labels = fsa.labels
    labels[xxx] = yyy
    fsa.labels = labelsr   re   z7Fsa is not valid, properties are: {} = {}, arcs are: {})r   getr   r    r!   RuntimeErrorr   rK   r   get_fsa_basic_propertiesget_fsa_vec_basic_propertiesr
   VALIDrn   rg   rX   rP   )r%   r$   r(   r(   r)   r$     s&   	
zFsa.propertiesc                 C      t | jS N)r   rQ   r$   rZ   r(   r(   r)   properties_str  s   zFsa.properties_strc                 C      | j jS r   )r   requires_gradrZ   r(   r(   r)   r        zFsa.requires_gradc                 C   r   r   )r   gradrZ   r(   r(   r)   r     r   zFsa.gradc                 C   sJ   || j v r
| j | S || jv r| j| S || jv r| j| S td| )a  
        Note: for attributes that exist as properties, e.g.
        self.labels, self.properties, self.requires_grad, we won't
        reach this code because Python checks the class dict before
        calling getattr.  The same is true for instance attributes
        such as self.{_tensor_attr,_non_tensor_attr,_cache,_properties}

        The 'virtual' members of this class are those in self._tensor_attr
        and self._non_tensor_attr.
        zUnknown attribute )r   r   r   AttributeErrorr%   r&   r(   r(   r)   __getattr__  s   





zFsa.__getattr__c                 C   sr   |t jvr	|dksJ || jvsJ || jv r| j|= d S || jv r(| j|= d S || jv r3| j|= d S td| )Nr   zNo such attribute in Fsa: )r   r   r   r   r   r   r   r(   r(   r)   __delattr__  s   


zFsa.__delattr__c                 C   0   d| j }}||vrtj| jdd||< || S )zGet (and compute if necessary) cached property `state_batches`.

        For use by internal k2 code.  Used in many algorithms.
        state_batchesT)	transpose)r   r   get_state_batchesr   r%   r&   cacher(   r(   r)   _get_state_batches     zFsa._get_state_batchesc                 C   r   )zGet (and compute if necessary) cached property self.dest_states.

        For use by internal k2 code, relates to best-path.
        dest_statesT)as_idx01)r   r   get_dest_statesr   r   r(   r(   r)   _get_dest_states  r   zFsa._get_dest_statesc                 C   2   d| j }}||vrt| j|  ||< || S )zGet (and compute if necessary) cached property self.incoming_arcs.

        For use by internal k2 code, relates to best-path
        incoming_arcs)r   r   get_incoming_arcsr   r   r   r(   r(   r)   _get_incoming_arcs  s   zFsa._get_incoming_arcsc                 C   s:   d| j }}||vrtj| j|  |  d||< || S )zGet (and compute if necessary) cached property
        `self.entering_arc_batches`.

        For use by internal k2 code, used in many algorithms.
        entering_arc_batches)r   r   )r   r   get_entering_arc_index_batchesr   r   r   r   r(   r(   r)   _get_entering_arc_batches%  s   
zFsa._get_entering_arc_batchesc                 C   r   )zGet (and compute if necessary) cached property
        `self.leaving_arc_batches`.

        For use by internal k2 code, used in many algorithms.
        leaving_arc_batches)r   r   get_leaving_arc_index_batchesr   r   r   r(   r(   r)   _get_leaving_arc_batches3  s   
zFsa._get_leaving_arc_batchesuse_double_scoreslog_semiringc                 C   sd   d|rdnd |rdnd }| j }||vr.| ||}|r tjntj}|| j|d}|||< || S )Narc_cdf_double_float_logtropical)fsasarc_post)r   _get_arc_postr   get_arc_cdf_doubleget_arc_cdf_floatr   )r%   r   r   r&   r   r   funcarc_cdfr(   r(   r)   _get_arc_cdf?  s   

zFsa._get_arc_cdfc                 C   sr   d|rdnd |rdnd }| j }||vr5|rtj}ntj}|| j|  |  |d\||< }|s5||d< || S )a}  Get (and compute if necessary) cached property
        `self.forward_scores_xxx_yyy` (where xxx indicates float-type and
        yyy indicates semiring).

        For use by internal k2 code; returns the total score from start-state to
        each state.  Not differentiable; see :func:`get_forward_scores` which is
        the differentiable version.

        Args:
          use_double_scores:
            True to use `double precision` floating point.
            False to use `single precision`.
          log_semiring:
            True to use log semiring (log-sum), false to use tropical (i.e. max
            on scores).
        forward_scores_r   r   r   r   )r   r   r   r,   )r   r   get_forward_scores_doubleget_forward_scores_floatr   r   r   )r%   r   r   r&   r   r   r,   r(   r(   r)   _get_forward_scoresM  s&   

zFsa._get_forward_scoresc                 C      t jj| ||| j}|S )a}  Compute forward-scores, i.e. total weight (or best-path weight)
        from start state to each state.

        Supports autograd.

        Args:
          use_double_scores:
            if True, use double precision.
          log_semiring:
            if True, use log semiring, else tropical.
        Returns:
          A torch.Tensor with shape equal to (num_states,)
        )rI   autograd_GetForwardScoresFunctionapplyr   )r%   r   r   forward_scoresr(   r(   r)   get_forward_scoresq     
zFsa.get_forward_scoresc                 C   sh   d|rdnd |rdnd }| j }||vr0|du rtj}ntj}| ||}|| j|}|||< || S )a  Compute total-scores (one per FSA) as the best-path score.

        This version is not differentiable; see also :func:`get_tot_scores`
        which is differentiable.

        Args:
          use_double_scores:
            If True, use `double precision` floating point; false;
            else single precision.
          log_semiring:
            True to use log semiring (log-sum), false to use tropical (i.e. max
            on scores).
        tot_scores_r   r   r   r   T)r   r   get_tot_scores_doubleget_tot_scores_floatr   r   )r%   r   r   r&   r   r   r   total_scoresr(   r(   r)   _get_tot_scores  s    

zFsa._get_tot_scoresc                 C   r   )a  Compute total-scores (one per FSA) as the
        best-path score.

        This version is differentiable.

        Args:
          use_double_scores:
            True to use `double precision` floating point;
            False to use `single precision`.
          log_semiring:
            True to use log semiring (log-sum), false to use tropical (i.e. max
            on scores).
        )rI   r   _GetTotScoresFunctionr   r   )r%   r   r   
tot_scoresr(   r(   r)   get_tot_scores  s   
zFsa.get_tot_scoresc           	      C   sn   d|rdnd |rdnd }| j }||vr3|rtj}ntj}|  }|  }|| j|||d}|||< || S )aO  Compute backward-scores, i.e. total weight (or best-path weight)
        from each state to the final state.

        For internal k2 use. Not differentiable.

        See also :func:`get_backward_scores` which is differentiable.

        Args:
          use_double_scores:
            True to use `double precision` floating point.
            False to use `single precision`.
          log_semiring:
            True to use log semiring (log-sum), false to use tropical (i.e. max
            on scores).

        Returns:
          A torch.Tensor with shape equal to (num_states,)
        backward_scores_r   r   r   r   )r   r   r   )r   r   get_backward_scores_doubleget_backward_scores_floatr   r   r   )	r%   r   r   r&   r   r   r   r   backward_scoresr(   r(   r)   _get_backward_scores  s&   

zFsa._get_backward_scoresc                 C   r   )a  Compute backward-scores, i.e. total weight (or best-path weight)
        from each state to the final state.

        Supports autograd.

        Args:
          use_double_scores:
            if True, use double precision.
          log_semiring:
            if True, use log semiring, else tropical.

        Returns:
          A torch.Tensor with shape equal to (num_states,)
        )rI   r   _GetBackwardScoresFunctionr   r   )r%   r   r   r   r(   r(   r)   get_backward_scores  r   zFsa.get_backward_scoresc           	      C   sr   d|rdnd |rdnd }| j }||vr5| ||}| ||}|r&tjntj}|| j||d}|||< || S )a  Compute scores on arcs, representing log probabilities;
        with log_semiring=True you could call these log posteriors,
        but if log_semiring=False they can only be interpreted as the
        difference between the best-path score and the score of the
        best path that includes this arc.

        This version is not differentiable; see also :func:`get_arc_post`.

        Args:
          use_double_scores:
            if True, use double precision.
          log_semiring:
            if True, use log semiring, else tropical.
        Returns:
          A torch.Tensor with shape equal to (num_arcs,)
          and non-positive elements.
        	arc_post_r   r   r   r   )r   r   r   )r   r   r   r   get_arc_post_doubleget_arc_post_floatr   )	r%   r   r   r&   r   r   r   r   r   r(   r(   r)   r     s.   

zFsa._get_arc_postc                 C   s6   |  ||}| ||}tjj| ||| j||}|S )ag  Compute scores on arcs, representing log probabilities;
        with log_semiring=True you could call these log posteriors,
        but if log_semiring=False they can only be interpreted as the
        difference between the best-path score and the score of the
        best path that includes this arc.
        This version is differentiable; see also :func:`_get_arc_post`.

        Caution:
          Because of how the autograd mechanics works and the
          need to avoid circular references, this is not cached;
          it's best to store it if you'll need it multiple times.

        Args:
          use_double_scores:
            if True, use double precision.
          log_semiring:
            if True, use log semiring, else tropical.
        Returns:
          A torch.Tensor with shape equal to (num_arcs,)
          and non-positive elements.
        )r   r   rI   r   _GetArcPostFunctionr   r   )r%   r   r   r   r   r   r(   r(   r)   get_arc_post  s   zFsa.get_arc_postc                 C   s(   d| j }}||vr| |d || S )zCompute, for each state, the index of the best arc entering it.

        For internal k2 use.

        Args:
          use_double_scores:
            True to use `double precision` floating point.
            False to use `single precision`.
        r,   F)r   r   )r%   r   r&   r   r(   r(   r)   _get_entering_arcs;  s   
zFsa._get_entering_arcsr   c                 C   s   | j | | S )aa  Change if autograd should record operations on this FSA:

        Sets the `scores`'s requires_grad attribute in-place.

        Returns this FSA.

        You can test whether this object has the requires_grad property
        true or false by accessing :py:attr:`requires_grad` (handled in
        :func:`__getattr__`).

        Caution:
          This is an **in-place** operation as you can see that the function
          name ends with `_`.

        Args:
          requires_grad:
            If autograd should record operations on this FSA or not.

        Returns:
          This FSA itself.
        )r   requires_grad_)r%   r   r(   r(   r)   r   K  s   zFsa.requires_grad_src_name	dest_namec           
   
   C   sL  ||ksJ || j v s|dksJ z!t| |}|dkr| }t| || |dkr1|dkr1| j |= W n tyJ } ztd| dt| d}~ww t|}t|}g }t| j	
 D ]-\}}|d| |kr}|dkr}|||d  }	|||	|f q\|d| |kr| j	|= q\|D ]\}}	}|| j	|	< | j	|= q|dkr| jj| _| S )a  Rename a tensor attribute (or, as a special case 'labels'),
        and also rename non-tensor attributes that are associated with it,
        i.e. that have it as a prefix.

        Args:
          src_name:
            The original name, exist as a tensor attribute, e.g. 'aux_labels',
            or, as a special case, equal 'labels'; special attributes 'labels'
            and 'scores' are allowed but won't be deleted.
          dest_name:
            The new name, that we are renaming it to. If it already existed as
            a tensor attribute, it will be rewritten; and any previously
            existing non-tensor attributes that have this as a prefix will be
            deleted.  As a special case, may equal 'labels'.
        Returns:
          Return `self`.

        Note::
          It is OK if src_name and/or dest_name equals 'labels' or 'scores',
          but these special attributes won't be deleted.
        r   r   zName z5 does not exist as a tensor attribute: exception was Nr!   )r   r^   clonesetattrKeyErrorrn   rP   lenlistr   itemsr0   r   r    r!   )
r%   r   r   rS   esrc_name_lendest_name_lento_mover&   new_namer(   r(   r)   rename_tensor_attribute_d  s@   



zFsa.rename_tensor_attribute_c                 C   sn   t | ds	tdt| jtjstd| dd | dd | dd t| j	d d| j
d< | j | S )a  Swap the `labels` and `aux_labels`.

        If there are symbol tables associated with `labels` and
        `aux_labels`, they are also swapped.

        It is an error if the FSA contains no `aux_labels`.

        CAUTION:
          The function name ends with an underscore which means this
          is an **in-place** operation.

        Returns:
          Return `self`.
        r   z5invert_ cannot be called on acceptors (no aux_labels)zFcurrent invert_ method only supports case where aux_labels is a tensorr   __tempNr   )hasattrr   r   r   r   r   r   r   rz   r   r   r$   rZ   r(   r(   r)   invert_  s   

zFsa.invert_c                 C   s   |    S )a  Swap the `labels` and `aux_labels`.

        If there are symbol tables associated with `labels` and
        `aux_labels`, they are also swapped.

        It is an error if the FSA contains no `aux_labels`.

        Returns:
          Return a new Fsa.
        )r   r   rZ   r(   r(   r)   invert  s   z
Fsa.invertc                 C      | j jdkS )zsReturn true if this FSA is on CPU.

        Returns:
          True if the FSA is on CPU; False otherwise.
        cpury   typerZ   r(   r(   r)   is_cpu     z
Fsa.is_cpuc                 C   r   )zsReturn true if this FSA is on GPU.

        Returns:
          True if the FSA is on GPU; False otherwise.
        cudar   rZ   r(   r(   r)   is_cuda  r   zFsa.is_cudac                 C   r   r   )r   ry   rZ   r(   r(   r)   ry     r   z
Fsa.devicerU   c              	   C   s   t | jdks	J d|  kr| jd k sJ  J | jd|\}}|| jd  }t|}| jddD ])\}}t|tj	rKt
|||||  q6t|tjsSJ t
|||jd||d q6|  D ]
\}}t
||| qdtj|| j||  |S )zGet the i-th FSA.

        Caution:
          `self` has to be an FsaVec, i.e. len(self.shape) == 3
        Args:
          i: The i-th FSA to select. 0 <= i < self.arcs.dim0().
        Returns:
          The i-th FSA. Note it is a single FSA.
           r   Fr6   )axisbeginr?   )r   rN   r   rO   r   r   rG   r   r   r   r   rI   rJ   arangerR   autograd_utilsphantom_set_scores_tor   )r%   rU   rV   r@   r?   out_fsar&   rS   r(   r(   r)   __getitem__  s$   
"zFsa.__getitem__c                 C   r   )a  Return the core part of the Fsa (the arcs) serialized to a Tensor
           of int32 type, with shape (num_arcs, 4); the floats are reinterpreted
           as int32 and will appear as garbage if printed.  This can be passed
           to the constructor, along with the aux_labels if present, to
           reconstruct this object.  A more convenient way to serialize a Tensor
           is to use :func:`as_dict` and :func:`from_dict`
        )r   fsa_to_tensorr   rZ   r(   r(   r)   arcs_as_tensor  s   zFsa.arcs_as_tensorc                 C   sR   t  }t| j|d< | jddD ]\}}|||< q|  D ]\}}|||< q|S )a"  Convert this Fsa to a dict (probably for purposes of serialization
        , e.g., torch.save).

        Caution:
          `self.requires_grad` attribute is not saved.
        Returns:
          A `dict` that can be used to reconstruct this FSA by using
          `Fsa.from_dict`.
        r   Fr6   )r   r   r   r   rG   rR   r%   rT   r&   rS   r(   r(   r)   as_dict#  s   


zFsa.as_dictdict_inc                 C   sD   t |d |dd d}| D ]\}}|dv rqt||| q|S )Nr   r   )r   )r   r   )r   r   r   r   )clsr   fsar3   rS   r(   r(   r)   	from_dict8  s   zFsa.from_dictry   c                 C   s   t |tr
t|}|jdv sJ || jjkr| S t| j|| j	d}| j
ddD ]\}}t|||| q*|  D ]
\}}t||| q<tj|| j| |S )a  Move the FSA onto a given device.

        Args:
          device:
            An instance of `torch.device` or a string that can be used to
            construct a `torch.device`, e.g., 'cpu', 'cuda:0'.
            It supports only cpu and cuda devices.

        Returns:
          Returns a new Fsa which is this object copied to the given device
          (or this object itself, if the device was the same)
        )r   r   r$   Fr6   )r   rP   r   ry   r   r   r   r   r"   r$   rG   r   rR   rI   r   r   )r%   ry   rT   r&   rS   r(   r(   r)   r"   A  s   

zFsa.toc                 C   s   t | j | jd}| jddD ]\}}t|||  q|  D ]
\}}t||| q!| j D ]	\}}||j|< q1t	j
|| j |S )z
        Return an Fsa that is a clone of this one, i.e. a close approximation
        to what you'd get if you did .clone() on all its tensor members.
        Any non-tensor attributes are copied over.
        r  Fr6   )r   r   r   r$   rG   r   rR   r   r   rI   r   r   r   r   r(   r(   r)   r   g  s   z	Fsa.clonec                 C   s   t | j| jd}| jddD ]!\}}t|tjr!t|||  qt|t	j
s)J t||| q|  D ]
\}}t||| q4| j D ]	\}}||j|< qD|S )z
        Return an Fsa that shares the underlying data with this one,
        except gradients are not tracked.
        Any non-tensor attributes are copied over.
        r  Fr6   )r   r   r$   rG   r   r   r   r   r}   rI   rJ   rR   r   r   r   r(   r(   r)   r}     s   z
Fsa.detachr7   c                 c   sZ    |r| j  D ]\}}|dkr||fV  qdS | j  D ]\}}|dvr*||fV  qdS )zReturn an iterator over tensor attributes containing both
        the name of the attribute as well as the tensor value.

        Returns:
          A tuple containing the name and the value.
        r   )r   r   N)r   r   )r%   r7   r&   rS   r(   r(   r)   rG     s   

zFsa.named_tensor_attrc                 c   s$    | j  D ]	\}}||fV  qdS )zReturn an iterator over non-tensor attributes containing both
        the name of the attribute as well as the value.

        Returns:
          A tuple containing the name and the value.
        N)r   r   r   r(   r(   r)   rR     s   zFsa.named_non_tensor_attr.c                 C   sN   | j  dkr| j  dfS | j  dkr| j  ddfS td| j   )z
        Returns:
          `(num_states, None)` if this is an Fsa;
          `(num_fsas, None, None)` if this is an FsaVec.
        r   Nr   zUnsupported num_axes: )r   rK   r~   rn   rZ   r(   r(   r)   rN     s
   z	Fsa.shapesacceptornum_aux_labelsaux_label_namesragged_label_namesc              
   C   s   t |||\}}t|}z?tj||||d\}}	}
t|}|	dur9t|	jd D ]}t||| |	|ddf  q(t||
D ]
\}}t||| q>|W S  t	ye   |rVdnd}t
d| d| d| w )	a  Create an Fsa from a string in the k2 or OpenFst format.
        (See also :func:`from_openfst`).

        The given string `s` consists of lines with the following format::

          src_state dest_state label [aux_label1 aux_label2...] [score]

        The line for the final state consists of only one field::

                final_state

        Note:
          Fields are separated by space(s), tab(s) or both. The `score`
          field is a float, while other fields are integers.

        Caution:
          The first column has to be non-decreasing.

        Caution:
          The final state has the largest state number. There is **ONLY**
          ONE final state. All arcs that are connected to the final state
          have label -1. If there are aux_labels, they are also -1 for
          arcs entering the final state.

        Note:
          At most one of `acceptor`, `num_aux_labels`, and `aux_label_names`
          must be supplied; if none are supplied, acceptor format is assumed.

        Args:
          s:
            The input string. Refer to the above comment for its format.

          acceptor:
            Set to true to denote acceptor format which is num_aux_labels == 0,
            or false to denote transducer format (i.e. num_aux_labels == 1
            with name 'aux_labels').
          num_aux_labels:
            The number of auxiliary labels to expect on each line (in addition
            to the 'acceptor' label; is 1 for traditional transducers but can be
            any non-negative number.  The names of the aux_labels default to
            'aux_labels' then 'aux_labels2', 'aux_labels3' and so on.
          aux_label_names:
            If provided, the length of this list dictates the number of
            aux_labels and this list dictates their names.
          ragged_label_names:
            If provided, expect this number of ragged labels, in the order
            of this list.  It is advisable that this list be in
            alphabetical order, so that the format when we write back to
            a string will be unchanged.
          openfst:
            If true, will expect the OpenFST format (costs not scores, i.e.
            negated; final-probs rather than final-state specified).
        rY   Nr   zin the OpenFst format rc   z!The following is not a valid Fsa z(with num_aux_labels=z): )get_aux_label_infor   r   fsa_from_strr   rM   rN   r   zip	Exceptionrn   )r   r  r  r  r  r  r5   num_ragged_labelsr   r   r9   rT   rU   r&   rS   or(   r(   r)   from_str  s2   
>
zFsa.from_strc                 C   s   t j|||||ddS )a	  Create an Fsa from a string in OpenFST format (or a slightly more
        general format, if num_aux_labels > 1). See also :func:`from_str`.

        The given string `s` consists of lines with the following format::

           src_state dest_state label [aux_label1 aux_label2...] [cost]

        (the cost defaults to 0.0 if not present).

        The line for the final state consists of two fields::

           final_state [cost]

        Note:
          Fields are separated by space(s), tab(s) or both. The `cost`
          field is a float, while other fields are integers.

          There might be multiple final states. Also, OpenFst may omit the cost
          if it is 0.0.

        Caution:
          We use `cost` here to indicate that its value will be negated so that
          we can get `scores`. That is, `score = -1 * cost`.

        Note:
          At most one of `acceptor`, `num_aux_labels`, and `aux_label_names`
          must be supplied; if none are supplied, acceptor format is assumed.

        Args:
          s:
            The input string. Refer to the above comment for its format.
          acceptor:
            Set to true to denote acceptor format which is num_aux_labels == 0,
            or false to denote transducer format (i.e. num_aux_labels == 1
            with name 'aux_labels').
          num_aux_labels:
            The number of auxiliary labels to expect on each line (in addition
            to the 'acceptor' label; is 1 for traditional transducers but can be
            any non-negative number.
          aux_label_names:
            If provided, the length of this list dictates the number of
            aux_labels. By default the names are 'aux_labels', 'aux_labels2',
            'aux_labels3' and so on.
          ragged_label_names:
            If provided, expect this number of ragged labels, in the order
            of this list.  It is advisable that this list be in
            alphabetical order, so that the format when we write back to
            a string will be unchanged.
        TrY   )r   r  )r   r  r  r  r  r  r(   r(   r)   from_openfst  s   
8zFsa.from_openfstr   c                 C   s
   t | S )zCreate an FsaVec from a list of FSAs.

        See also :func:`k2.create_fsa_vec`. This function is just
        a wrapper of that function.
        )rI   create_fsa_vec)r   r(   r(   r)   	from_fsasZ  s   
zFsa.from_fsasc                 C   sp   |j dksJ |jtjksJ | | j ksJ t| j	 
|j|}|jdd}|j
| jj| _dS )a  Normalize the given `scores` and assign it to `self.scores`.

        Args:
          scores:
            Tensor of scores of dtype torch.float32, and shape equal to
            `self.scores.shape` (one axis). Will be normalized so the
            sum, after exponentiating, of the scores leaving each state
            that has at least one arc leaving it is 1.

        Caution:
          The function name ends with an underline indicating this function
          will modify `self` **in-place**.
        re   T)use_logN)ndimrH   r   r{   numelr   rI   rJ   r   rN   r"   ry   	normalizer   )r%   r   ragged_scoresr(   r(   r)   set_scores_stochastic_c  s   zFsa.set_scores_stochastic_
remove_epsc                 C   s   t | |sJ t| |}t|tjsJ |jdksJ |jtjks#J tj	j
| dd|j}t|| }|rA|jdd}t| || | S )a  Convert the attribute given by `name` from a 1-D torch.tensor
        to a k2.RaggedTensor.

        Caution:
          This function ends with an underscore, meaning it changes the FSA
          **in-place**.

        Args:
          name:
            The attribute name. This attribute is expected to be a 1-D tensor
            with dtype torch.int32.
          remove_eps:
            True to remove 0s in the resulting ragged tensor.

        Returns:
          Return self.
        re   )r~   dim1r   )target)r   r^   r   r   r   r  rH   r#   rI   raggedregular_ragged_shaper  r"   ry   rJ   
contiguousremove_values_eqr   )r%   r&   r  rS   rN   	new_valuer(   r(   r)   convert_attr_to_ragged_}  s   
zFsa.convert_attr_to_ragged_)NN)T)Fr   )r   r   )r   N)J__name__
__module____qualname____doc__r   r   r   r	   r   rI   rJ   r*   boolr4   rP   rX   r[   intfloatr_   rx   r   r   propertyr   r$   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   ry   r   r   r   r   classmethodr  r"   r   r}   r   r   rG   rR   rN   r   r  r  staticmethodr  r  r!  r(   r(   r(   r)   r   (   s6   Q
{!$
(2



$



'

$
&
7
;"

&



Q
;
r   r  r  r  r   c                 C   s   | dur|du r|du sJ | rdg fS ddgfS |dur)|du s#J t ||fS |durT|dks3J g }|dkr>|d td|D ]}|d|d   qC||fS dg fS )a  Given either acceptor or num_aux_labels or aux_label_names, at most
    one of which should be supplied, returns a pair (num_aux_labels,
    aux_label_names) specifying the number of auxiliary labels and the
    names to use for them.

    Args:
      acceptor:
        None, or a bool; False means no aux_labels and True means aux_label
        with name 'aux_label'.
      num_aux_labels:
        None, or a number >= 0
      aux_label_names:
        None, or a list of names, such as ['aux_labels', 'aux_labels2']
    Returns:
       Returns a tuple (num_aux_labels, aux_label_names).
       out from the other.  If all inputs are None, we assume there
       are no aux-labels and return (0, []).
    Nr   re   r   )r   r0   rM   )r  r  r  rU   r(   r(   r)   r	    s   
r	  )typingr   r   r   r   r   r   r   rk   r-   rr   r   rI   	k2.raggedr   r	   r
   objectr   r&  r'  rP   r	  r(   r(   r(   r)   <module>   sB              
