ó
    óó�jq©  ã                   óÀ   • S r SSKrSSKrSSKrSSKrSSKrSSKJrJ	r	J
r
  SSKJr  \" \5      rSrSrSrSr\
 " S	 S
5      5       r " S S\5      r " S S\5      rS rg)z[Encoding categorical data as integers

Authors
  * Samuele Cornell 2020
  * Aku Rouhe 2020
é    N)Úmark_as_loaderÚmark_as_saverÚregister_checkpoint_hooks)Ú
get_loggerz<unk>ú<bos>ú<eos>z<blank>c                   óH  • \ rS rSrSrSrSrS'S jrS rS r	\
S 5       rS(S
 jr S(S jr S)S jr/ / S	S0 4S jrS rS rS rS r\4S jrS rS rS*S jrS*S jrS*S jrS*S jrS rS r\S 5       rS r \!S(S j5       r"S r#S  r$S! r%S" r&S# r'\(S$ 5       r)\(S% 5       r*S&r+g)+ÚCategoricalEncoderé    a  Encode labels of a discrete set.

Used for encoding, e.g., speaker identities in speaker recognition.
Given a collection of hashables (e.g a strings) it encodes
every unique item to an integer value: ["spk0", "spk1"] --> [0, 1]
Internally the correspondence between each label to its index is handled by
two dictionaries: lab2ind and ind2lab.

The label integer encoding can be generated automatically from a SpeechBrain
DynamicItemDataset by specifying the desired entry (e.g., spkid) in the annotation
and calling update_from_didataset method:

>>> from speechbrain.dataio.encoder import CategoricalEncoder
>>> from speechbrain.dataio.dataset import DynamicItemDataset
>>> dataset = {
...     "ex_{}".format(x): {"spkid": "spk{}".format(x)} for x in range(20)
... }
>>> dataset = DynamicItemDataset(dataset)
>>> encoder = CategoricalEncoder()
>>> encoder.update_from_didataset(dataset, "spkid")
>>> assert len(encoder) == len(
...     dataset
... )  # different speaker for each utterance

However can also be updated from an iterable:

>>> from speechbrain.dataio.encoder import CategoricalEncoder
>>> from speechbrain.dataio.dataset import DynamicItemDataset
>>> dataset = ["spk{}".format(x) for x in range(20)]
>>> encoder = CategoricalEncoder()
>>> encoder.update_from_iterable(dataset)
>>> assert len(encoder) == len(dataset)

Note
----
In both methods it can be specified it the single element in the iterable
or in the dataset should be treated as a sequence or not (default False).
If it is a sequence each element in the sequence will be encoded.


>>> from speechbrain.dataio.encoder import CategoricalEncoder
>>> from speechbrain.dataio.dataset import DynamicItemDataset
>>> dataset = [[x + 1, x + 2] for x in range(20)]
>>> encoder = CategoricalEncoder()
>>> encoder.ignore_len()
>>> encoder.update_from_iterable(dataset, sequence_input=True)
>>> assert len(encoder) == 21  # there are only 21 unique elements 1-21

This class offers 4 different methods to explicitly add a label in the internal
dicts: add_label, ensure_label, insert_label, enforce_label.
add_label and insert_label will raise an error if it is already present in the
internal dicts. insert_label, enforce_label allow also to specify the integer value
to which the desired label is encoded.

Encoding can be performed using 4 different methods:
encode_label, encode_sequence, encode_label_torch and encode_sequence_torch.
encode_label operate on single labels and simply returns the corresponding
integer encoding:

>>> from speechbrain.dataio.encoder import CategoricalEncoder
>>> from speechbrain.dataio.dataset import DynamicItemDataset
>>> dataset = ["spk{}".format(x) for x in range(20)]
>>> encoder.update_from_iterable(dataset)
>>>
22
>>>
encode_sequence on sequences of labels:
>>> encoder.encode_sequence(["spk1", "spk19"])
[22, 40]
>>>
encode_label_torch and encode_sequence_torch return torch tensors
>>> encoder.encode_sequence_torch(["spk1", "spk19"])
tensor([22, 40])
>>>
Decoding can be performed using decode_torch and decode_ndim methods.
>>> encoded = encoder.encode_sequence_torch(["spk1", "spk19"])
>>> encoder.decode_torch(encoded)
['spk1', 'spk19']
>>>
decode_ndim is used for multidimensional list or pytorch tensors
>>> encoded = encoded.unsqueeze(0).repeat(3, 1)
>>> encoder.decode_torch(encoded)
[['spk1', 'spk19'], ['spk1', 'spk19'], ['spk1', 'spk19']]
>>>

In some applications, it can happen that during testing a label which has not
been encountered during training is encountered. To handle this out-of-vocabulary
problem add_unk can be used. Every out-of-vocab label is mapped to this special
<unk> label and its corresponding integer encoding.

>>> import torch
>>> try:
...     encoder.encode_label("spk42")
... except KeyError:
...     print("spk42 is not in the encoder this raises an error!")
spk42 is not in the encoder this raises an error!
>>> encoder.add_unk()
41
>>> encoder.encode_label("spk42")
41
>>>
returns the <unk> encoding

This class offers also methods to save and load the internal mappings between
labels and tokens using: save and load methods as well as load_or_create.
z => z================
c                 óN   • 0 U l         0 U l        Xl        U R                  U5        g ©N)Úlab2indÚind2labÚstarting_indexÚhandle_special_labels)Úselfr   Úspecial_labelss      Ú¨C:\Users\johnn\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\speechbrain/dataio/encoder.pyÚ__init__ÚCategoricalEncoder.__init__�   s&   € ØˆŒØˆŒØ,Ôð
 	×"Ñ" >Õ2ó    c                 ó:   • SU;   a  U R                  US   5        gg)z)Handles special labels such as unk_label.Ú	unk_labelN)Úadd_unk)r   r   s     r   r   Ú(CategoricalEncoder.handle_special_labelsš   s    € à˜.Ó(Ø�L‰L˜¨Ñ4Õ5ð )r   c                 ó,   • [        U R                  5      $ r   )Úlenr   ©r   s    r   Ú__len__ÚCategoricalEncoder.__len__Ÿ   s   € Ü�4—<‘<Ó Ð r   c                 ó6   • U " 5       nUR                  U5        U$ )z,Recreate a previously saved encoder directly)Úload)ÚclsÚpathÚobjs      r   Ú
from_savedÚCategoricalEncoder.from_saved¢   s   € ñ ‹eˆØ�‰�ŒØˆ
r   Fc                 óœ   • U(       a   [         R                  R                  U5      nO[        U5      nU H  nU R	                  U5        M     g)zßUpdate from iterator

Arguments
---------
iterable : iterable
    Input sequence on which to operate.
sequence_input : bool
    Whether iterable yields sequences of labels or individual labels
    directly. (default False)
N)Ú	itertoolsÚchainÚfrom_iterableÚiterÚensure_label)r   ÚiterableÚsequence_inputÚlabel_iteratorÚlabels        r   Úupdate_from_iterableÚ'CategoricalEncoder.update_from_iterable©   s<   € ö Ü&Ÿ_™_×:Ñ:¸8ÓD‰Nä! (›^ˆNÛ#ˆEØ×Ñ˜eÖ$ò $r   c                 ó’   ^• UR                  T/5         U R                  U4S jU 5       US9  SSS5        g! , (       d  f       g= f)aQ  Update from DynamicItemDataset.

Arguments
---------
didataset : DynamicItemDataset
    Dataset on which to operate.
output_key : str
    Key in the dataset (in data or a dynamic item) to encode.
sequence_input : bool
    Whether the data yielded with the specified key consists of
    sequences of labels or individual labels directly.
c              3   ó,   >#   • U  H	  oT   v •  M     g 7fr   © )Ú.0Ú
data_pointÚ
output_keys     €r   Ú	<genexpr>Ú;CategoricalEncoder.update_from_didataset.<locals>.<genexpr>Ì   s   øé € ÐDº)¨J˜JÖ'º)ùs   ƒ)r/   N)Úoutput_keys_asr2   )r   Ú	didatasetr9   r/   s     ` r   Úupdate_from_didatasetÚ(CategoricalEncoder.update_from_didataset»   s?   ø€ ð ×%Ñ% z lÕ3Ø×%Ñ%ÜD¹)ÓDØ-ð &ñ ÷ 4×3Ö3ús	   ”8¸
ANc                 ó|  • U R                   (       a/  U R                  R                  n[        R	                  SU S35        U(       a   [
        R                  R                  U5      nO[        U5      n[        R                  " U5      nUR                  U5       H  u  p‰X”:  a    U$ U R                  U5        M      U$ )ab  Produce label mapping from iterable based on label counts

Used to limit label set size.

Arguments
---------
iterable : iterable
    Input sequence on which to operate.
sequence_input : bool
    Whether iterable yields sequences of labels or individual labels
    directly. False by default.
n_most_common : int, None
    Take at most this many labels as the label set, keeping the most
    common ones. If None (as by default), take all.
min_count : int
    Don't take labels if they appear less than this many times.

Returns
-------
collections.Counter
    The counts of the different labels (unfiltered).
z+Limited_labelset_from_iterable called, but zx is not empty. The new labels will be added, i.e. won't overwrite. This is normal if there is e.g. an unk label already.)r   Ú	__class__Ú__name__ÚloggerÚinfor)   r*   r+   r,   ÚcollectionsÚCounterÚmost_commonÚ	add_label)
r   r.   r/   Ún_most_commonÚ	min_countÚclsnamer0   Úcountsr1   Úcounts
             r   Úlimited_labelset_from_iterableÚ1CategoricalEncoder.limited_labelset_from_iterableÐ   sª   € ð2 �<�<Ø—n‘n×-Ñ-ˆGÜ�K‰KðØ�ið  HðHôö Ü&Ÿ_™_×:Ñ:¸8ÓD‰Nä! (›^ˆNÜ×$Ò$ ^Ó4ˆØ"×.Ñ.¨}Ö=‰LˆEØÓ ð àˆð �N‰N˜5Ö!ñ >ð ˆr   c                 óD  •  [         R                  R                  R                  5       (       a{  U R	                  U5      (       de  U H  nU R                  Xt5        M     U H#  nUc  [        S5      eU R                  X…U5        M%     U R                  U5        U R                  U5        [         R                  R                  R                  5         U R                  U5        g! [         R                  R                  R                  5         U R                  U5        f = f)z×Convenient syntax for creating the encoder conditionally

This pattern would be repeated in so many experiments that
we decided to add a convenient shortcut for it here. The
current version is multi-gpu (DDP) safe.
Nz,Provide an output_key for DynamicItemDataset)ÚsbÚutilsÚdistributedÚif_main_processÚload_if_possibler2   Ú
ValueErrorr>   r   ÚsaveÚddp_barrierr"   )	r   r$   Úfrom_iterablesÚfrom_didatasetsr/   r9   r   r.   r=   s	            r   Úload_or_createÚ!CategoricalEncoder.load_or_createþ   sß   € ð	Ü�x‰x×#Ñ#×3Ñ3×5Ñ5Ø×,Ñ,¨T×2Ñ2Û$2˜Ø×1Ñ1°(ÖKñ %3ã%4˜	Ø%Ñ-Ü",Ø Nó#ð ð ×2Ñ2Ø%°>öñ &5ð ×.Ñ.¨~Ô>Ø—I‘I˜d”Oä�H‰H× Ñ ×,Ñ,Ô.Ø�I‰I�d�Oøô �H‰H× Ñ ×,Ñ,Ô.Ø�I‰I�d�Oús   ‚B(C$ Ã$;Dc                 óÄ   • XR                   ;   a$  U R                  R                  n[        SU 35      eU R	                  5       nX0R                   U'   XR
                  U'   U$ )a<  Add new label to the encoder, at the next free position.

Arguments
---------
label : hashable
    Most often labels are str, but anything that can act as dict key is
    supported. Note that default save/load only supports Python
    literals.

Returns
-------
int
    The index that was used to encode this label.
úLabel already present in )r   rA   rB   ÚKeyErrorÚ_next_indexr   )r   r1   rK   Úindexs       r   rH   ÚCategoricalEncoder.add_label   s[   € ð —L‘LÓ Ø—n‘n×-Ñ-ˆGÜÐ6°w°iÐ@ÓAÐAØ× Ñ Ó"ˆØ#�‰�UÑØ#�‰�UÑØˆr   c                 ó`   • XR                   ;   a  U R                   U   $ U R                  U5      $ )a-  Add a label if it is not already present.

Arguments
---------
label : hashable
    Most often labels are str, but anything that can act as dict key is
    supported. Note that default save/load only supports Python
    literals.

Returns
-------
int
    The index that was used to encode this label.
)r   rH   )r   r1   s     r   r-   ÚCategoricalEncoder.ensure_label7  s,   € ð —L‘LÓ Ø—<‘< Ñ&Ð&à—>‘> %Ó(Ð(r   c                 óŒ   • XR                   ;   a$  U R                  R                  n[        SU 35      eU R	                  X5        g)as  Add a new label, forcing its index to a specific value.

If a label already has the specified index, it is moved to the end
of the mapping.

Arguments
---------
label : hashable
    Most often labels are str, but anything that can act as dict key is
    supported. Note that default save/load only supports Python
    literals.
index : int
    The specific index to use.
r^   N)r   rA   rB   r_   Úenforce_label)r   r1   ra   rK   s       r   Úinsert_labelÚCategoricalEncoder.insert_labelK  s>   € ð —L‘LÓ Ø—n‘n×-Ñ-ˆGÜÐ6°w°iÐ@ÓAÐAà×Ñ˜uÕ,r   c           
      óÚ  • [        U5      nXR                  ;   a-  X R                  U   :X  a  gU R                  U R                  U   	 X R                  ;   a  U R                  U   nSnOSnX R                  U'   XR                  U'   U(       a^  [        R	                  S[        W5       SU S[        U5       S35        U R                  5       nXPR                  U'   X0R                  U'   gg)zþMake sure label is present and encoded to a particular index.

If the label is present but encoded to some other index, it is
moved to the given index.

If there is already another label at the
given index, that label is moved to the next free position.
NTFzMoving label z from index z
, because z was put at its place.)Úintr   r   rC   rD   Úreprr`   )r   r1   ra   Úsaved_labelÚmoving_otherÚ	new_indexs         r   rf   Ú CategoricalEncoder.enforce_label`  sÝ   € ô �E“
ˆØ—L‘LÓ ØŸ™ UÑ+Ó+Øð —L‘L §¡¨eÑ!4Ð5à—L‘LÓ ØŸ,™, uÑ-ˆKØ‰Là ˆLà#�‰�UÑØ#�‰�UÑæÜ�K‰KØ¤ [Ó 1Ð2°,Ø�'˜¤D¨£K =Ð0FðHôð ×(Ñ(Ó*ˆIØ(1�L‰L˜Ñ%Ø&1�L‰L˜Ò#ð r   c                 ó0   • Xl         U R                  U5      $ )a¥  Add label for unknown tokens (out-of-vocab).

When asked to encode unknown labels, they can be mapped to this.

Arguments
---------
unk_label : hashable, optional
    Most often labels are str, but anything that can act as dict key is
    supported. Note that default save/load only supports Python
    literals. Default: <unk>. This can be None, as well!

Returns
-------
int
    The index that was used to encode this.
)r   rH   )r   r   s     r   r   ÚCategoricalEncoder.add_unkƒ  s   € ð" #ŒØ�~‰~˜iÓ(Ð(r   c                 óh   • U R                   nXR                  ;   a  US-  nXR                  ;   a  M  U$ )z'The index to use for the next new labelé   )r   r   )r   ra   s     r   r`   ÚCategoricalEncoder._next_index—  s1   € à×#Ñ#ˆØ—|‘|Ó#Ø�Q‰JˆEð —|‘|Õ#àˆr   c           	      ó¸   • [        U R                  R                  5       5      nU R                  U;   =(       a"    [	        S [        USS USS 5       5       5      $ )zÜCheck that the set of indices doesn't have gaps

For example:
If starting index = 1
Continuous: [1,2,3,4]
Continuous: [0,1,2]
Non-continuous: [2,3,4]
Non-continuous: [1,2,4]

Returns
-------
bool
    True if continuous.
c              3   ó4   #   • U  H  u  pX!-
  S :H  v •  M     g7f)rs   Nr6   )r7   ÚiÚjs      r   r:   Ú3CategoricalEncoder.is_continuous.<locals>.<genexpr>°  s   é € ð 6
Ú#A™4˜1ˆA‰E�QŽJÒ#Aùs   ‚Néÿÿÿÿrs   )Úsortedr   Úkeysr   ÚallÚzip)r   Úindicess     r   Úis_continuousÚ CategoricalEncoder.is_continuousž  sZ   € ô" ˜Ÿ™×*Ñ*Ó,Ó-ˆØ×"Ñ" gÑ-÷ 
´#ñ 6
Ü#& w¨s° |°W¸Q¸R°[Ô#Aó6
ó 3
ð 	
r   c                 ó~  • U R                  5          U R                  U   $ ! [         a‘    [        U S5      (       a"  U(       a  U R                  U R                     s $ [        U S5      (       a  U(       d  [        SU S35      e[        U S5      (       d  U(       a  [        SU S35      e[        SU S35      ef = f)aA  Encode label to int

Arguments
---------
label : hashable
    Label to encode, must exist in the mapping.
allow_unk : bool
    If given, that label is not in the label set
    AND unk_label has been added with add_unk(),
    allows encoding to unk_label's index.

Returns
-------
int
    Corresponding encoded int value.
r   zUnknown label z=, and explicitly disallowed the use of the existing unk-labelzCannot encode unknown label zN. You have not called add_unk() to add a special unk-label for unknown labels.z+Couldn't and wouldn't encode unknown label Ú.)Ú_assert_lenr   r_   Úhasattrr   ©r   r1   Ú	allow_unks      r   Úencode_labelÚCategoricalEncoder.encode_label´  sÎ   € ð" 	×ÑÔð	Ø—<‘< Ñ&Ð&øÜó 	Ü�t˜[×)Ñ)®iØ—|‘| D§N¡NÑ3Ò3Ü˜˜{×+Ñ+¶IÜØ$ U Gð ,Cð Cóð ô ˜T ;×/Ñ/¶IÜØ2°5°'ð :4ð 4óð ô ØAÀ%ÀÈÐJóð ð	ús   ’! ¡;B<ÁAB<c                 óN   • [         R                  " U R                  X5      /5      $ )ar  Encode label to torch.LongTensor.

Arguments
---------
label : hashable
    Label to encode, must exist in the mapping.
allow_unk : bool
    If given, that label is not in the label set
    AND unk_label has been added with add_unk(),
    allows encoding to unk_label's index.

Returns
-------
torch.LongTensor
    Corresponding encoded int value.
    Tensor shape [1].
©ÚtorchÚ
LongTensorrˆ   r†   s      r   Úencode_label_torchÚ%CategoricalEncoder.encode_label_torchÛ  s#   € ô$ ×Ò ×!2Ñ!2°5Ó!DÐ EÓFÐFr   c                 ón   • U R                  5         U Vs/ s H  o0R                  X25      PM     sn$ s  snf )aS  Encode a sequence of labels to list

Arguments
---------
sequence : iterable
    Labels to encode, must exist in the mapping.
allow_unk : bool
    If given, that label is not in the label set
    AND unk_label has been added with add_unk(),
    allows encoding to unk_label's index.

Returns
-------
list
    Corresponding integer labels.
)r„   rˆ   ©r   Úsequencer‡   r1   s       r   Úencode_sequenceÚ"CategoricalEncoder.encode_sequenceï  s1   € ð" 	×ÑÔÙAIÓJÂ¸×!Ñ! %Ö3ÁÑJÐJùÒJs   •2c           	      óv   • [         R                  " U Vs/ s H  o0R                  X25      PM     sn5      $ s  snf )a�  Encode a sequence of labels to torch.LongTensor

Arguments
---------
sequence : iterable
    Labels to encode, must exist in the mapping.
allow_unk : bool
    If given, that label is not in the label set
    AND unk_label has been added with add_unk(),
    allows encoding to unk_label's index.

Returns
-------
torch.LongTensor
    Corresponding integer labels.
    Tensor shape [len(sequence)].
r‹   r‘   s       r   Úencode_sequence_torchÚ(CategoricalEncoder.encode_sequence_torch  s5   € ô$ ×ÒÙ>FÓGºh°U×Ñ˜uÖ0¹hÑGó
ð 	
ùÚGs   •6c                 ó   • U R                  5         / nUR                  S:X  a2  U H*  nUR                  U R                  [	        U5         5        M,     U$ U H#  nUR                  U R                  U5      5        M%     U$ )aN  Decodes an arbitrarily nested torch.Tensor to a list of labels.

Provided separately because Torch provides clearer introspection,
and so doesn't require try-except.

Arguments
---------
x : torch.Tensor
    Torch tensor of some integer dtype (Long, int) and any shape to
    decode.

Returns
-------
list
    list of original labels
rs   )r„   ÚndimÚappendr   rj   Údecode_torch)r   ÚxÚdecodedÚelementÚ	subtensors        r   r›   ÚCategoricalEncoder.decode_torch  sr   € ð" 	×ÑÔØˆà�6‰6�Q‹;Û�Ø—‘˜tŸ|™|¬C°«LÑ9Ö:ñ ð
 ˆó �	Ø—‘˜t×0Ñ0°Ó;Ö<ñ àˆr   c                 óÎ   • U R                  5          / nU H#  nUR                  U R                  U5      5        M%     U$ ! [         a    U R                  [        U5         s $ f = f)a|  Decodes an arbitrarily nested iterable to a list of labels.

This works for essentially any pythonic iterable (including torch), and
also single elements.

Arguments
---------
x : Any
    Python list or other iterable or torch.Tensor or a single integer element

Returns
-------
list, Any
    ndim list of original labels, or if input was single element,
    output will be, too.
)r„   rš   Údecode_ndimÚ	TypeErrorr   rj   )r   rœ   r�   rŸ   s       r   r¢   ÚCategoricalEncoder.decode_ndim5  sb   € ð" 	×ÑÔð	(ØˆGÛ�	Ø—‘˜t×/Ñ/°	Ó:Ö;ñ àˆNøÜó 	(Ø—<‘<¤ A£Ñ'Ò'ð	(ús   ’,? ¿"A$Á#A$c                 ó\   • U R                  5       nU R                  XR                  U5        g)a  Save the categorical encoding for later use and recovery

Saving uses a Python literal format, which supports things like
tuple labels, but is considered safe to load (unlike e.g. pickle).

Arguments
---------
path : str, Path
    Where to save. Will overwrite.
N)Ú_get_extrasÚ_save_literalr   )r   r$   Úextrass      r   rW   ÚCategoricalEncoder.saveP  s&   € ð ×!Ñ!Ó#ˆØ×Ñ˜4§¡¨vÕ6r   c                 ó  • U R                   (       a/  U R                  R                  n[        R	                  SU S35        U R                  U5      u  p4nX0l         X@l        U R                  U5        [        R                  SU 35        g)zéLoads from the given path.

CategoricalEncoder uses a Python literal format, which supports things
like tuple labels, but is considered safe to load (unlike e.g. pickle).

Arguments
---------
path : str, Path
    Where to load from.
zLoad called, but zs is not empty. Loaded data will overwrite everything. This is normal if there is e.g. an unk label defined at init.z!Loaded categorical encoding from N)	r   rA   rB   rC   rD   Ú_load_literalr   Ú_set_extrasÚdebug)r   r$   rK   r   r   r¨   s         r   r"   ÚCategoricalEncoder.load_  s|   € ð �<�<Ø—n‘n×-Ñ-ˆGÜ�K‰KØ# G 9ð -Pð Pôð
 $(×#5Ñ#5°dÓ#;Ñ ˆ˜&ØŒØŒØ×Ñ˜Ô ä�‰Ð8¸¸Ð?Õ@r   c                 óÐ   • A U R                  U5        g! [         a    [        R                  SU S35         g[        [
        4 a    [        R                  SU S35         gf = f)az  Loads if possible, returns a bool indicating if loaded or not.

Arguments
---------
path : str, Path
    Where to load from.
end_of_epoch : bool
    Whether the checkpoint was end-of-epoch or not.

Returns
-------
bool :
    If load was successful.

Example
-------
>>> encoding_file = getfixture("tmpdir") / "encoding.txt"
>>> encoder = CategoricalEncoder()
>>> # The idea is in an experiment script to have something like this:
>>> if not encoder.load_if_possible(encoding_file):
...     encoder.update_from_iterable("abcd")
...     encoder.save(encoding_file)
>>> # So the first time you run the experiment, the encoding is created.
>>> # However, later, the encoding exists:
>>> encoder = CategoricalEncoder()
>>> encoder.expect_len(4)
>>> if not encoder.load_if_possible(encoding_file):
...     assert False  # We won't get here!
>>> encoder.decode_ndim(range(4))
['a', 'b', 'c', 'd']
z%Would load categorical encoding from z, but file doesn't exist yet.FzH, and file existed but seems to be corrupted or otherwise couldn't load.T)r"   ÚFileNotFoundErrorrC   r­   rV   ÚSyntaxError)r   r$   Úend_of_epochs      r   rU   Ú#CategoricalEncoder.load_if_possiblex  s�   € ðB ð	Ø�I‰I�dŒOð øô !ó 	Ü�L‰LØ7¸°vð >.ð .ôñ ÜœKÐ(ó 	Ü�L‰LØ7¸°vð >Yð Yôñ ð	ús   ƒ •#A%º(A%Á$A%c                 ó   • Xl         g)ae  Specify the expected category count. If the category count observed
during encoding/decoding does NOT match this, an error will be raised.

This can prove useful to detect bugs in scenarios where the encoder is
dynamically built using a dataset, but downstream code expects a
specific category count (and may silently break otherwise).

This can be called anytime and the category count check will only be
performed during an actual encoding/decoding task.

Arguments
---------
expected_len : int
    The expected final category count, i.e. `len(encoder)`.

Example
-------
>>> encoder = CategoricalEncoder()
>>> encoder.update_from_iterable("abcd")
>>> encoder.expect_len(3)
>>> encoder.encode_label("a")
Traceback (most recent call last):
  ...
RuntimeError: .expect_len(3) was called, but 4 categories found
>>> encoder.expect_len(4)
>>> encoder.encode_label("a")
0
N©Úexpected_len)r   r¶   s     r   Ú
expect_lenÚCategoricalEncoder.expect_len«  s
   € ð: )Õr   c                 ó   • SU l         g)zÞSpecifies that category count shall be ignored at encoding/decoding
time.

Effectively inhibits the ".expect_len was never called" warning.
Prefer :py:meth:`~CategoricalEncoder.expect_len` when the category count
is known.Nrµ   r   s    r   Ú
ignore_lenÚCategoricalEncoder.ignore_lenÊ  s   € ð !ˆÕr   c                 ó@  • [        U S5      (       aE  U R                  c  g[        U 5      nXR                  :w  a  [        SU R                   SU S35      eg[        R                  U R                  R                   S[        U 5       S35        U R                  5         g)zºIf `expect_len` was called, then check if len(self) matches the
expected value. If it does not, raise a RuntimeError.
If neither `expect_len` or `ignore_len` were ever called, warn once.r¶   Nz.expect_len(z) was called, but z categories foundz9.expect_len was never called: assuming category count of zµ to be correct! Sanity check your encoder using `.expect_len`. Ensure that downstream code also uses the correct size. If you are sure this does not apply to you, use `.ignore_len`.)	r…   r¶   r   ÚRuntimeErrorrC   Úwarning_oncerA   rB   rº   )r   Úreal_lens     r   r„   ÚCategoricalEncoder._assert_lenÓ  s°   € ô �4˜×(Ñ(à× Ñ Ñ(Øä˜4“yˆHà×,Ñ,Ó,Ü"Ø" 4×#4Ñ#4Ð"5ð 6Ø#˜*Ð$5ð7óð ð -ô ×ÑØ—>‘>×*Ñ*Ð+ð ,.Ü.1°$«i¨[ð 9QðQôð �O‰OÔØr   c                 ób   • SU R                   0n[        U S5      (       a  U R                  US'   U$ )zjOverride this to provide any additional things to save

Call super()._get_extras() to get the base extras
r   r   )r   r…   r   ©r   r¨   s     r   r¦   ÚCategoricalEncoder._get_extrasî  s4   € ð
 # D×$7Ñ$7Ð8ˆÜ�4˜×%Ñ%Ø"&§.¡.ˆF�;ÑØˆr   c                 ó8   • SU;   a
  US   U l         US   U l        g)zfOverride this to e.g. load any extras needed

Call super()._set_extras(extras) to set the base extras
r   r   N)r   r   rÂ   s     r   r¬   ÚCategoricalEncoder._set_extrasø  s&   € ð
 ˜&Ó Ø# KÑ0ˆDŒNØ$Ð%5Ñ6ˆÕr   c                 óø  • [        U SSS9 nUR                  5        H?  u  pEUR                  [        U5      [        R
                  -   [        U5      -   S-   5        MA     UR                  [        R                  5        UR                  5        H?  u  pgUR                  [        U5      [        R
                  -   [        U5      -   S-   5        MA     UR                  5         SSS5        g! , (       d  f       g= f)z+Save which is compatible with _load_literalÚwúutf-8©ÚencodingÚ
N)	ÚopenÚitemsÚwriterk   r
   ÚVALUE_SEPARATORÚstrÚEXTRAS_SEPARATORÚflush)r$   r   r¨   Úfr1   ÚindÚkeyÚvalues           r   r§   Ú CategoricalEncoder._save_literal  sÐ   € ô �$˜ gÒ.°!Ø%Ÿm™mžo‘
�Ø—‘Ü˜“KÜ(×8Ñ8ñ9ä˜#“hñð ñöñ .ð �G‰GÔ&×7Ñ7Ô8Ø$Ÿl™lžn‘
�Ø—‘Ü˜“IÜ(×8Ñ8ñ9ä˜5“kñ"ð ñöñ -ð �G‰GŒI÷! /×.Ö.ús   ŒCC+Ã+
C9c                 ó  • 0 n0 n0 n[        U SS9 nU Hp  nU[        R                  :X  a    O[UR                  5       R	                  [        R
                  SS9u  pg[        U5      n[        R                  " U5      nXqU'   X‚U'   Mr     U Ha  nUR                  5       R	                  [        R
                  SS9u  pš[        R                  " U	5      n[        R                  " U
5      nXÃU'   Mc     SSS5        XU4$ ! , (       d  f       N= f)ztLoad which supports Python literals as keys.

This is considered safe for user input, as well (unlike e.g. pickle).
rÈ   rÉ   rs   )ÚmaxsplitN)	rÌ   r
   rÑ   ÚstripÚsplitrÏ   rj   ÚastÚliteral_eval)r$   r   r   r¨   rÓ   ÚlineÚliteralrÔ   r1   Úliteral_keyÚliteral_valuerÕ   rÖ   s                r   r«   Ú CategoricalEncoder._load_literal  s  € ð ˆØˆØˆÜ�$ Ò)¨Qã�ØÔ-×>Ñ>Ó>ÙØ#Ÿz™z›|×1Ñ1Ü&×6Ñ6Àð  2ð  ‘�ô ˜#“h�Ü×(Ò(¨Ó1�Ø!$˜‘Ø$˜“ñ ó �Ø-1¯Z©Z«\×-?Ñ-?Ü&×6Ñ6Àð .@ð .Ñ*�ô ×&Ò& {Ó3�Ü×(Ò(¨Ó7�Ø#�s“ñ ÷ *ð(  Ð'Ð'÷) *Õ)ús   ‘CC;Ã;
D	)r¶   r   r   r   r   )r   )F)FNrs   ©T),rB   Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rÏ   rÑ   r   r   r   Úclassmethodr&   r2   r>   rN   r[   rH   r-   rg   rf   ÚDEFAULT_UNKr   r`   r€   rˆ   rŽ   r“   r–   r›   r¢   r   rW   r"   r   rU   r·   rº   r„   r¦   r¬   Ústaticmethodr§   r«   Ú__static_attributes__r6   r   r   r
   r
       s&  † ñiðV €OØ+Ðô3ò6ò
!ð ñó ðô%ð& 5:ôð, MNô,ðb ØØØØô òDò.)ò(-ò*!2ðF !,ô )ò(ò
ô,%ôNGô(Kô(
ò,ò8(ð6 ñ7ó ð7òAð2 ó0ó ð0òd)ò>!òò6ò7ð ñó ðð( ñ(ó ó(r   r
   c                   óº   ^ • \ rS rSrSrU 4S jrSU 4S jjrSU 4S jjr SU 4S jjr\	\
4S jr\	\
S	S4S
 jrS rS rS rS rS rS rU 4S jrU 4S jrSrU =r$ )ÚTextEncoderi6  a2  CategoricalEncoder subclass which offers specific methods for encoding text and handle
special tokens for training of sequence to sequence models.
In detail, aside special <unk> token already present in CategoricalEncoder
for handling out-of-vocab tokens here special methods to handle
<bos> beginning of sequence and <eos> tokens are defined.

Note: update_from_iterable and update_from_didataset here have as default
sequence_input=True because it is assumed that this encoder is used on
iterables of strings: e.g.

>>> from speechbrain.dataio.encoder import TextEncoder
>>> dataset = [["encode", "this", "textencoder"], ["foo", "bar"]]
>>> encoder = TextEncoder()
>>> encoder.update_from_iterable(dataset)
>>> encoder.expect_len(5)
>>> encoder.encode_label("this")
1
>>> encoder.add_unk()
5
>>> encoder.expect_len(6)
>>> encoder.encode_sequence(["this", "out-of-vocab"])
[1, 5]
>>>

Two methods can be used to add <bos> and <eos> to the internal dicts:
insert_bos_eos, add_bos_eos.

>>> encoder.add_bos_eos()
>>> encoder.expect_len(8)
>>> encoder.lab2ind[encoder.eos_label]
7
>>>
add_bos_eos adds the special tokens at the end of the dict indexes
>>> encoder = TextEncoder()
>>> encoder.update_from_iterable(dataset)
>>> encoder.insert_bos_eos(bos_index=0, eos_index=1)
>>> encoder.expect_len(7)
>>> encoder.lab2ind[encoder.eos_label]
1
>>>
insert_bos_eos allows to specify whose index will correspond to each of them.
Note that you can also specify the same integer encoding for both.

Four methods can be used to prepend <bos> and append <eos>.
prepend_bos_label and append_eos_label add respectively the <bos> and <eos>
string tokens to the input sequence

>>> words = ["foo", "bar"]
>>> encoder.prepend_bos_label(words)
['<bos>', 'foo', 'bar']
>>> encoder.append_eos_label(words)
['foo', 'bar', '<eos>']

prepend_bos_index and append_eos_index add respectively the <bos> and <eos>
indexes to the input encoded sequence.

>>> words = ["foo", "bar"]
>>> encoded = encoder.encode_sequence(words)
>>> encoder.prepend_bos_index(encoded)
[0, 3, 4]
>>> encoder.append_eos_index(encoded)
[3, 4, 1]

c                 óœ   >• [         TU ]  U5        SU;   a  SU;   a  U R                  SSUS   US   S9  gSU;   d  SU;   a  [        S5      eg)z+Handles special labels such as bos and eos.Ú	bos_labelÚ	eos_labelr   r   )rï   rð   Ú	bos_indexÚ	eos_indexz.Only BOS or EOS specified. Need both for init.N)Úsuperr   Úinsert_bos_eosr£   ©r   r   rA   s     €r   r   Ú!TextEncoder.handle_special_labelsx  sm   ø€ ä‰Ñ% nÔ5ð ˜.Ó(¨[¸NÓ-JØ×ÑØ!Ø!Ø(¨Ñ5Ø(¨Ñ5ð	  ò ð ˜NÓ*¨k¸^Ó.KÜÐLÓMÐMð /Lr   c                 ó"   >• [         TU ]  X5      $ ©ú*Change default for sequence_input to True.)ró   r2   )r   r.   r/   rA   s      €r   r2   Ú TextEncoder.update_from_iterableŠ  s   ø€ ä‰wÑ+¨HÓEÐEr   c                 ó$   >• [         TU ]  XU5      $ rø   )ró   r>   )r   r=   r9   r/   rA   s       €r   r>   Ú!TextEncoder.update_from_didatasetŽ  s   ø€ ä‰wÑ,Ø >ó
ð 	
r   Nc                 ó$   >• [         TU ]  USSSS9$ )rù   TNrs   )r/   rI   rJ   )ró   rN   )r   r.   r/   rI   rJ   rA   s        €r   rN   Ú*TextEncoder.limited_labelset_from_iterable”  s%   ø€ ô ‰wÑ5Ø T¸Èð 6ð 
ð 	
r   c                 ó¸   • X:X  a'  [         R                  S5        U R                  U5        O"U R                  U5        U R                  U5        Xl        X l        g)aè  Add sentence boundary markers in the label set.

If the beginning-of-sentence and end-of-sentence markers
are the same, will just use one sentence-boundary label.

This method adds to the end of the index, rather than at the beginning,
like insert_bos_eos.

Arguments
---------
bos_label : hashable
    Beginning-of-sentence label, any label.
eos_label : hashable
    End-of-sentence label, any label. If set to the same label as
    bos_label, will just use one sentence-boundary label.
úIBOS and EOS labels are the same so using just one sentence boundary labelN)rC   r­   rH   rï   rð   )r   rï   rð   s      r   Úadd_bos_eosÚTextEncoder.add_bos_eosœ  sK   € ð* Ó!Ü�L‰Lð!ôð �N‰N˜9Õ%à�N‰N˜9Ô%Ø�N‰N˜9Ô%Ø"ŒØ"�r   r   c                 ó  • X:X  a'  [         R                  S5        U R                  X5        OOU R                  X5        Uc*  [         R                  S5        U R                  X#S-   5        OU R                  X$5        Xl        X l        g)a1  Insert sentence boundary markers in the label set.

If the beginning-of-sentence and end-of-sentence markers
are the same, will just use one sentence-boundary label.

Arguments
---------
bos_label : hashable
    Beginning-of-sentence label, any label
eos_label : hashable
    End-of-sentence label, any label. If set to the same label as
    bos_label, will just use one sentence-boundary label.
bos_index : int
    Where to insert bos_label. eos_index = bos_index + 1
eos_index : optional, int
    Where to insert eos_label. Default: eos_index = bos_index + 1
r   Nz,EOS label not specified, using BOS label + 1rs   )rC   r­   rg   rï   rð   )r   rï   rð   rñ   rò   s        r   rô   ÚTextEncoder.insert_bos_eos½  sv   € ð0 Ó!Ü�L‰Lð!ôð ×Ñ˜iÕ3à×Ñ˜iÔ3ØÑ Ü—‘ÐKÔLØ×!Ñ! )¸©]Õ;à×!Ñ! )Ô7Ø"ŒØ"�r   c                 óp   • [        U S5      (       d  [        S5      eU R                  U R                  5      $ )ú(Returns the index to which blank encodesrï   zBOS label is not set!)r…   r½   rˆ   rï   r   s    r   Úget_bos_indexÚTextEncoder.get_bos_indexå  ó0   € ä�t˜[×)Ñ)ÜÐ6Ó7Ð7Ø× Ñ  §¡Ó0Ð0r   c                 óp   • [        U S5      (       d  [        S5      eU R                  U R                  5      $ )r  rð   zEOS label is not set!)r…   r½   rˆ   rð   r   s    r   Úget_eos_indexÚTextEncoder.get_eos_indexë  r	  r   c                 ól   • [        U S5      (       d  [        S5      eU R                  /[        U5      -   $ )z/Returns a list version of x, with BOS prependedrï   ú*BOS label has not been added to label set!)r…   r_   rï   Úlist©r   rœ   s     r   Úprepend_bos_labelÚTextEncoder.prepend_bos_labelñ  s1   € ä�t˜[×)Ñ)ÜÐGÓHÐHØ—‘Ð¤$ q£'Ñ)Ð)r   c                 óF  • [        U S5      (       d  [        S5      e[        R                  " U5      (       aE  [        R                  " U R
                  U R                     /5      n[        R                  " X!/5      $ U R
                  U R                     /[        U5      -   $ )zfReturns a list version of x, with BOS index prepended.
If the input is a tensor, a tensor is returned.rï   r  )	r…   r_   rŒ   Ú	is_tensorÚTensorr   rï   Úcatr  )r   rœ   Úbos_inds      r   Úprepend_bos_indexÚTextEncoder.prepend_bos_index÷  sz   € ô �t˜[×)Ñ)ÜÐGÓHÐHÜ�?Š?˜1×ÑÜ—l’l D§L¡L°·±Ñ$@Ð#AÓBˆGÜ—9’9˜g˜\Ó*Ð*Ø—‘˜TŸ^™^Ñ,Ð-´°Q³Ñ7Ð7r   c                 ól   • [        U S5      (       d  [        S5      e[        U5      U R                  /-   $ )z/Returns a list version of x, with EOS appended.rð   ú*EOS label has not been added to label set!)r…   r_   r  rð   r  s     r   Úappend_eos_labelÚTextEncoder.append_eos_label  s1   € ä�t˜[×)Ñ)ÜÐGÓHÐHÜ�A‹w˜$Ÿ.™.Ð)Ñ)Ð)r   c                 óF  • [        U S5      (       d  [        S5      e[        R                  " U5      (       aE  [        R                  " U R
                  U R                     /5      n[        R                  " X/5      $ [        U5      U R
                  U R                     /-   $ )zeReturns a list version of x, with EOS index appended.
If the input is a tensor, a tensor is returned.rð   r  )	r…   r_   rŒ   r  r  r   rð   r  r  )r   rœ   Úeos_inds      r   Úappend_eos_indexÚTextEncoder.append_eos_index  sz   € ô �t˜[×)Ñ)ÜÐGÓHÐHÜ�?Š?˜1×ÑÜ—l’l D§L¡L°·±Ñ$@Ð#AÓBˆGÜ—9’9˜a˜\Ó*Ð*Ü�A‹w˜$Ÿ,™, t§~¡~Ñ6Ð7Ñ7Ð7r   c                 ó¤   >• [         TU ]  5       n[        U S5      (       a  U R                  US'   [        U S5      (       a  U R                  US'   U$ ©Nrï   rð   )ró   r¦   r…   rï   rð   ©r   r¨   rA   s     €r   r¦   ÚTextEncoder._get_extras  sK   ø€ Ü‘Ñ$Ó&ˆÜ�4˜×%Ñ%Ø"&§.¡.ˆF�;ÑÜ�4˜×%Ñ%Ø"&§.¡.ˆF�;ÑØˆr   c                 óf   >• [         TU ]  U5        SU;   a
  US   U l        SU;   a  US   U l        g g r#  )ró   r¬   rï   rð   r$  s     €r   r¬   ÚTextEncoder._set_extras  s=   ø€ Ü‰Ñ˜FÔ#Ø˜&Ó Ø# KÑ0ˆDŒNØ˜&Ó Ø# KÑ0ˆD�Nð !r   )rï   rð   rã   )TNrs   )rB   rä   rå   ræ   rç   r   r2   r>   rN   ÚDEFAULT_BOSÚDEFAULT_EOSr  rô   r  r  r  r  r  r   r¦   r¬   rë   Ú__classcell__©rA   s   @r   rí   rí   6  su   ø† ñ?õBN÷$F÷
ð LM÷
ð Øô#ðF ØØØô&#òP1ò1ò*ò8ò*ò8õ÷1ó 1r   rí   c                   óx   ^ • \ rS rSrSrU 4S jr\4S jr\S4S jrS r	SS jr
SS	 jrU 4S
 jrU 4S jrSrU =r$ )ÚCTCTextEncoderi!  a  Subclass of TextEncoder which also provides methods to handle CTC blank token.

add_blank and insert_blank can be used to add <blank> special token to the encoder
state.

>>> from speechbrain.dataio.encoder import CTCTextEncoder
>>> chars = ["a", "b", "c", "d"]
>>> encoder = CTCTextEncoder()
>>> encoder.update_from_iterable(chars)
>>> encoder.add_blank()
>>> encoder.expect_len(5)
>>> encoder.encode_sequence(chars)
[0, 1, 2, 3]
>>> encoder.get_blank_index()
4
>>> encoder.decode_ndim([0, 1, 2, 3, 4])
['a', 'b', 'c', 'd', '<blank>']

collapse_labels and collapse_indices_ndim can be used to apply CTC collapsing
rules:
>>> encoder.collapse_labels(["a", "a", "b", "c", "d"])
['a', 'b', 'c', 'd']
>>> encoder.collapse_indices_ndim([4, 4, 0, 1, 2, 3, 4, 4])  # 4 is <blank>
[0, 1, 2, 3]
c                 óT   >• SU;   a  U R                  US   S9  [        TU ]	  U5        g)z&Handles special labels such as blanks.Úblank_label)ra   N)Úinsert_blankró   r   rõ   s     €r   r   Ú$CTCTextEncoder.handle_special_labels<  s0   ø€ ð ˜NÓ*Ø×Ñ N°=Ñ$AÐÑBä‰Ñ% nÕ5r   c                 ó2   • U R                  U5        Xl        g)zAdd blank symbol to labelset.N)rH   r/  )r   r/  s     r   Ú	add_blankÚCTCTextEncoder.add_blankI  s   € à�‰�{Ô#Ø&Õr   r   c                 ó2   • U R                  X5        Xl        g)z(Insert blank symbol at a given labelset.N)rg   r/  )r   r/  ra   s      r   r0  ÚCTCTextEncoder.insert_blankN  s   € à×Ñ˜+Ô-Ø&Õr   c                 óp   • [        U S5      (       d  [        S5      eU R                  U R                  5      $ )z)Returns the index to which blank encodes.r/  zBlank label is not set!)r…   r½   rˆ   r/  r   s    r   Úget_blank_indexÚCTCTextEncoder.get_blank_indexS  s2   € ä�t˜]×+Ñ+ÜÐ8Ó9Ð9Ø× Ñ  ×!1Ñ!1Ó2Ð2r   c                 ó.  • [        U S5      (       d  [        S5      eU(       aD  [        U5       VVs/ s H+  u  p4US:X  d  XAUS-
     :w  d  M  X@R                  :w  d  M)  UPM-     snn$ U Vs/ s H  oDU R                  :w  d  M  UPM     sn$ s  snnf s  snf )a‚  Applies the CTC collapsing rules on one label sequence.

Arguments
---------
x : iterable
    Label sequence on which to operate.
merge_repeats : bool
    Whether to merge repeated labels before removing blanks.
    In the basic CTC label topology, repeated labels are merged.
    However, in RNN-T, they are not.

Returns
-------
list
    List of labels with collapsing rules applied.
r/  úBlank label has not been addedr   rs   )r…   r_   Ú	enumerater/  )r   rœ   Úmerge_repeatsrw   r1   s        r   Úcollapse_labelsÚCTCTextEncoder.collapse_labelsY  sš   € ô& �t˜]×+Ñ+ÜÐ;Ó<Ð<Þô !*¨!¤ôâ ,‘H�AØ˜“F˜e¨¨Q©¡xÑ/ó à5:×>NÑ>NÑ5N÷ Ù ,òð ñ ()ÓF¢q˜e°T×5EÑ5EÑ,E—E¡qÑFÐFùóùò Gs   ²BÁBÁBÁ,BÂBc                 ó¶  • [        U S5      (       d  [        S5      e/ nU H$  n UR                  U R                  XB5      5        M&     U$ ! [         a       Of = fU R
                  U R                     nU(       a@  [        U5       VVs/ s H!  u  pgUS:X  d  XqUS-
     :w  d  M  Xu:w  d  M  UPM#     snn$ s  snnf U Vs/ s H  owU:w  d  M
  UPM     sn$ s  snf )aŠ  Applies the CTC collapsing rules on arbitrarily label sequence.

Arguments
---------
x : iterable
    Label sequence on which to operate.
merge_repeats : bool
    Whether to merge repeated labels before removing blanks.
    In the basic CTC label topology, repeated labels are merged.
    However, in RNN-T, they are not.

Returns
-------
list
    List of labels with collapsing rules applied.
r/  r;  r   rs   )r…   r_   rš   Úcollapse_indices_ndimr£   r   r/  r<  )r   rœ   r=  Ú	collapsedrŸ   Úblank_indexrw   ra   s           r   rA  Ú$CTCTextEncoder.collapse_indices_ndimw  sí   € ô" �t˜]×+Ñ+ÜÐ;Ó<Ð<àˆ	ÛˆIðØ× Ñ Ø×.Ñ.¨yÓHöñ ð Ðøô	 ó âðúð —l‘l 4×#3Ñ#3Ñ4ˆÞô !*¨!¤ôâ ,‘H�AØ˜“F˜e¨¨Q©¡xÑ/ó à5:Ñ5I÷ Ù ,òð ùó ñ ()ÓA¢q˜e°[Ñ,@—E¡qÑAÐAùÒAs/   ¥ AÁ
AÁAÂ
B6Â%B6Â,B6Ã 	CÃCc                 ód   >• [         TU ]  5       n[        U S5      (       a  U R                  US'   U$ ©Nr/  )ró   r¦   r…   r/  r$  s     €r   r¦   ÚCTCTextEncoder._get_extras¡  s2   ø€ Ü‘Ñ$Ó&ˆÜ�4˜×'Ñ'Ø$(×$4Ñ$4ˆF�=Ñ!Øˆr   c                 óF   >• [         TU ]  U5        SU;   a  US   U l        g g rF  )ró   r¬   r/  r$  s     €r   r¬   ÚCTCTextEncoder._set_extras§  s*   ø€ Ü‰Ñ˜FÔ#Ø˜FÓ"Ø% mÑ4ˆDÕð #r   )r/  rã   )rB   rä   rå   ræ   rç   r   ÚDEFAULT_BLANKr3  r0  r8  r>  rA  r¦   r¬   rë   r*  r+  s   @r   r-  r-  !  sD   ø† ñõ46ð %2ô 'ð
 (5¸Aô 'ò
3ôGô<(BõT÷5ó 5r   r-  c                 ó~   • [        5       nUR                  U 5        [        UR                  R	                  5       5      $ )ag  Loads the encoder tokens from a pretrained model.

This method is useful when you used with a pretrained HF model.
It will load the tokens in the yaml and then you will be able
to instantiate any CTCBaseSearcher directly in the YAML file.

Arguments
---------
model_path : str, Path
    Path to the pretrained model.

Returns
-------
list
    List of tokens.
)rí   r"   r  r   r|   )Ú
model_pathÚlabel_encoders     r   Úload_text_encoder_tokensrN  ­  s3   € ô"  “M€MØ×Ñ�zÔ"Ü�×%Ñ%×*Ñ*Ó,Ó-Ð-r   )rç   rÜ   rE   r)   rŒ   ÚspeechbrainrQ   Úspeechbrain.utils.checkpointsr   r   r   Úspeechbrain.utils.loggerr   rB   rC   ré   r(  r)  rJ  r
   rí   r-  rN  r6   r   r   Ú<module>rR     s‹   ðñó Û Û ã ã ÷ñ õ
 0á	�HÓ	€ð €Ø€Ø€Ø€ð ÷R(ð R(ó ðR(ôjh1Ð$ô h1ôVI5�[ô I5óX.r   