ó
    óó�j�*  ã                   ó–   • S r SSKrSSKrSSKJr  SSKJr  SSKJ	r	J
r
Jr  \R                  " SSS/5      r " S	 S
5      r " S S5      rg)z,Batch collation

Authors
  * Aku Rouhe 2020
é    N)Údefault_convert)Ú
pin_memory)Úbatch_pad_rightÚmod_default_collateÚrecursive_toÚ
PaddedDataÚdataÚlengthsc                   ód   • \ rS rSrSrSS\SSSS4S jrS rS rS r	S	 r
S
 rS r\S 5       rSrg)ÚPaddedBatché   aE  Collate_fn when examples are dicts and have variable-length sequences.

Different elements in the examples get matched by key.
All numpy tensors get converted to Torch (PyTorch default_convert)
Then, by default, all torch.Tensor valued elements get padded and support
collective pin_memory() and to() calls.
Regular Python data types are just collected in a list.

Arguments
---------
examples : list
    List of example dicts, as produced by Dataloader.
padded_keys : list, None
    (Optional) List of keys to pad on. If None, pad all torch.Tensors
device_prep_keys : list, None
    (Optional) Only these keys participate in collective memory pinning and moving with
    to().
    If None, defaults to all items with torch.Tensor values.
padding_func : callable, optional
    Called with a list of tensors to be padded together. Needs to return
    two tensors: the padded data, and another tensor for the data lengths.
padding_kwargs : dict, None
    (Optional) Extra kwargs to pass to padding_func. E.G. mode, value
    This is used as the default padding configuration for all keys.
per_key_padding_kwargs : dict, None
    (Optional) Per-key padding configuration. Keys in this dict should match
    the keys in the examples. Each value should be a dict with padding parameters
    (e.g., {'value': -100, 'mode': 'constant'}). If a key is not in this dict,
    the global padding_kwargs will be used.
apply_default_convert : bool
    Whether to apply PyTorch default_convert (numpy to torch recursively,
    etc.) on all data. Default:True, usually does the right thing.
nonpadded_stack : bool
    Whether to apply PyTorch-default_collate-like stacking on values that
    didn't get padded. This stacks if it can, but doesn't error out if it
    cannot. Default:True, usually does the right thing.

Example
-------
>>> batch = PaddedBatch(
...     [
...         {"id": "ex1", "foo": torch.Tensor([1.0])},
...         {"id": "ex2", "foo": torch.Tensor([2.0, 1.0])},
...     ]
... )
>>> # Attribute or key-based access:
>>> batch.id
['ex1', 'ex2']
>>> batch["id"]
['ex1', 'ex2']
>>> # torch.Tensors get padded
>>> type(batch.foo)
<class 'speechbrain.dataio.batch.PaddedData'>
>>> batch.foo.data
tensor([[1., 0.],
        [2., 1.]])
>>> batch.foo.lengths
tensor([0.5000, 1.0000])
>>> # Batch supports collective operations:
>>> _ = batch.to(dtype=torch.half)
>>> batch.foo.data
tensor([[1., 0.],
        [2., 1.]], dtype=torch.float16)
>>> batch.foo.lengths
tensor([0.5000, 1.0000], dtype=torch.float16)
>>> # Numpy tensors get converted to torch and padded as well:
>>> import numpy as np
>>> batch = PaddedBatch(
...     [{"wav": np.asarray([1, 2, 3, 4])}, {"wav": np.asarray([1, 2, 3])}]
... )
>>> batch.wav  # +ELLIPSIS
PaddedData(data=tensor([[1, 2,...
>>> # Basic stacking collation deals with non padded data:
>>> batch = PaddedBatch(
...     [
...         {
...             "spk_id": torch.tensor([1]),
...             "wav": torch.tensor([0.1, 0.0, 0.3]),
...         },
...         {
...             "spk_id": torch.tensor([2]),
...             "wav": torch.tensor([0.2, 0.3, -0.1]),
...         },
...     ],
...     padded_keys=["wav"],
... )
>>> batch.spk_id
tensor([[1],
        [2]])
>>> # And some data is left alone:
>>> batch = PaddedBatch(
...     [{"text": ["Hello"]}, {"text": ["How", "are", "you?"]}]
... )
>>> batch.text
[['Hello'], ['How', 'are', 'you?']]
>>> # Per-key padding configuration:
>>> batch = PaddedBatch(
...     [
...         {
...             "wav": torch.tensor([1, 2, 3]),
...             "labels": torch.tensor([1, 2]),
...         },
...         {"wav": torch.tensor([4, 5]), "labels": torch.tensor([3])},
...     ],
...     per_key_padding_kwargs={
...         "wav": {"value": 0},
...         "labels": {"value": -100},
...     },
... )
>>> batch.wav.data
tensor([[1, 2, 3],
        [4, 5, 0]])
>>> batch.labels.data
tensor([[   1,    2],
        [   3, -100]])

NTc	                 óÒ  • Ub  UO0 nUb  UO0 n[        U5      U l        [        US   R                  5       5      U l        / U l        / U l        U R                   GH  n	U V
s/ s H  oªU	   PM	     nn
U(       a  [        U5      nUb  X’;   d%  Uce  [        US   [        R                  5      (       aC  U R
                  R                  U	5        X–;   a  Xi   nOUn[        U" U40 UD66 n[        X	U5        OU(       a  [        U5      n[        X	U5        Ub  X“;   d)  Ub  MÅ  [        US   [        R                  5      (       d  Mé  U R                  R                  U	5        GM     g s  sn
f )Nr   )ÚlenÚ_PaddedBatch__lengthÚlistÚkeysÚ_PaddedBatch__keysÚ_PaddedBatch__padded_keysÚ_PaddedBatch__device_prep_keysr   Ú
isinstanceÚtorchÚTensorÚappendr   Úsetattrr   )ÚselfÚexamplesÚpadded_keysÚdevice_prep_keysÚpadding_funcÚpadding_kwargsÚper_key_padding_kwargsÚapply_default_convertÚnonpadded_stackÚkeyÚexampleÚvaluesÚkey_padding_kwargsÚpaddeds                 Ú¦C:\Users\johnn\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\speechbrain/dataio/batch.pyÚ__init__ÚPaddedBatch.__init__�   sM  € ð ,:Ñ+E™È2ˆà&<Ñ&HÑ"Èbð 	ô ˜H›ˆŒÜ˜8 A™;×+Ñ+Ó-Ó.ˆŒØˆÔØ"$ˆÔØ—;•;ˆCÙ2:Ó;²( w˜c”l±(ˆFÐ;æ$Ü(¨Ó0�ØÑ'¨CÓ,>ØÑ#¬
°6¸!±9¼e¿l¹l×(KÑ(Kð ×"Ñ"×)Ñ)¨#Ô.ð Ó0Ø)?Ñ)DÑ&à)7Ð&Ü#¡\°&Ñ%OÐ<NÑ%OÐP�Ü˜ 6Õ*ö #Ü0°Ó8�FÜ˜ 6Ô*Ø Ñ,°Ó1HØ Ó(¬Z¸¸q¹	Ä5Ç<Á<×-PÓ-Pà×'Ñ'×.Ñ.¨s×3ò7 ùÚ;s   Á"E$c                 ó   • U R                   $ ©N©r   ©r   s    r)   Ú__len__ÚPaddedBatch.__len__¿   s   € Ø�}‰}Ðó    c                 óR   • XR                   ;   a  [        X5      $ [        SU 35      e)NzBatch doesn't have key: )r   ÚgetattrÚKeyError)r   r$   s     r)   Ú__getitem__ÚPaddedBatch.__getitem__Â   s*   € Ø—+‘+ÓÜ˜4Ó%Ð%äÐ5°c°UÐ;Ó<Ð<r2   c                 óB   ^ • [        U 4S jT R                   5       5      $ )aA  Iterates over the different elements of the batch.

Returns
-------
Iterator over the batch.

Example
-------
>>> batch = PaddedBatch(
...     [
...         {"id": "ex1", "val": torch.Tensor([1.0])},
...         {"id": "ex2", "val": torch.Tensor([2.0, 1.0])},
...     ]
... )
>>> ids, vals = batch
>>> ids
['ex1', 'ex2']
c              3   ó<   >#   • U  H  n[        TU5      v •  M     g 7fr-   )r4   )Ú.0r$   r   s     €r)   Ú	<genexpr>Ú'PaddedBatch.__iter__.<locals>.<genexpr>Û   s   øé € Ð>²+¨3”G˜D #×&Ð&²+ùs   ƒ)Úiterr   r/   s   `r)   Ú__iter__ÚPaddedBatch.__iter__È   s   ø€ ô& Ô>°$·+²+Ó>Ó>Ð>r2   c                 óp   • U R                    H%  n[        X5      n[        U5      n[        XU5        M'     U $ )z3In-place, moves relevant elements to pinned memory.)r   r4   Úrecursive_pin_memoryr   )r   r$   ÚvalueÚpinneds       r)   r   ÚPaddedBatch.pin_memoryÝ   s7   € à×*Ô*ˆCÜ˜DÓ&ˆEÜ)¨%Ó0ˆFÜ�D˜vÖ&ñ +ð ˆr2   c                 óx   • U R                    H)  n[        X5      n[        U/UQ70 UD6n[        XU5        M+     U $ )zgIn-place move/cast relevant elements.

Passes all arguments to torch.Tensor.to, see its documentation.
)r   r4   r   r   )r   ÚargsÚkwargsr$   rB   Úmoveds         r)   ÚtoÚPaddedBatch.toå   sC   € ð
 ×*Ô*ˆCÜ˜DÓ&ˆEÜ  Ð8¨Ò8°Ñ8ˆEÜ�D˜uÖ%ñ +ð ˆr2   c                 ó6   • U R                   U   n[        X5      $ )zGets the position.)r   r4   )r   Úposr$   s      r)   Úat_positionÚPaddedBatch.at_positionð   s   € à�k‰k˜#ÑˆÜ�tÓ!Ð!r2   c                 ó   • U R                   $ )zReturns the bach sizer.   r/   s    r)   Ú	batchsizeÚPaddedBatch.batchsizeõ   s   € ð �}‰}Ðr2   )Ú__device_prep_keysÚ__keysÚ__lengthÚ__padded_keys)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   r*   r0   r6   r>   r   rI   rM   ÚpropertyrP   Ú__static_attributes__© r2   r)   r   r      sX   † ñtðr ØØ$ØØ#Ø"Øô.4ò`ò=ò?ò*ò	ò"ð
 ñó ór2   r   c                   óH   • \ rS rSrSrS rS rS rS rS r	S r
S	 rS
 rSrg)ÚBatchsizeGuesseréû   a  Try to figure out the batchsize, but never error out

If this cannot figure out anything else, will fallback to guessing 1

Example
-------
>>> guesser = BatchsizeGuesser()
>>> # Works with simple tensors:
>>> guesser(torch.randn((2, 3)))
2
>>> # Works with sequences of tensors:
>>> guesser((torch.randn((2, 3)), torch.randint(high=5, size=(2,))))
2
>>> # Works with PaddedBatch:
>>> guesser(
...     PaddedBatch([{"wav": [1.0, 2.0, 3.0]}, {"wav": [4.0, 5.0, 6.0]}])
... )
2
>>> guesser("Even weird non-batches have a fallback")
1

c                 ó   • S U l         g r-   ©Úmethodr/   s    r)   r*   ÚBatchsizeGuesser.__init__  s	   € Øˆ�r2   c                 óV   •  U R                  U5      $ !   U R                  U5      s $ = fr-   )rc   Úfind_suitable_method©r   Úbatchs     r)   Ú__call__ÚBatchsizeGuesser.__call__  s.   € ð	4Ø—;‘;˜uÓ%Ð%øð	4Ø×,Ñ,¨UÓ3Ò3ús   ‚ “(c                 ó´  •  U R                  U5      nU R                   U l        U$ !    O= f U R                  U5      nU R                  U l        U$ !    O= f U R                  U5      nU R                  U l        U$ !    O= f U R	                  U5      nU R                  U l        U$ !    O= fU R                  U5      nU R                  U5      U l        U$ )z/Try the different methods and note which worked)Ú
attr_basedrc   Útorch_tensor_bsÚlen_of_firstÚlen_of_iter_firstÚfallback)r   rh   Úbss      r)   rf   Ú%BatchsizeGuesser.find_suitable_method  så   € ð	Ø—‘ Ó'ˆBØŸ/™/ˆDŒKØˆIøð	Ùúð	Ø×%Ñ% eÓ,ˆBØ×.Ñ.ˆDŒKØˆIøð	Ùúð	Ø×"Ñ" 5Ó)ˆBØ×+Ñ+ˆDŒKØˆIøð	Ùúð	Ø×'Ñ'¨Ó.ˆBØ×0Ñ0ˆDŒKØˆIøð	Ùúà�]‰]˜5Ó!ˆØ—m‘m EÓ*ˆŒØˆ	s+   ‚#& ¦*®#A ÁAÁ#A> Á>BÂ#B* Â*B.c                 ó   • UR                   $ )zImplementation of attr_based.)rP   rg   s     r)   rl   ÚBatchsizeGuesser.attr_based;  s   € à�‰Ðr2   c                 ó    • UR                   S   $ )z"Implementation of torch_tensor_bs.r   )Úshaperg   s     r)   rm   Ú BatchsizeGuesser.torch_tensor_bs?  s   € à�{‰{˜1‰~Ðr2   c                 ó   • [        US   5      $ )zImplementation of len_of_first.r   )r   rg   s     r)   rn   ÚBatchsizeGuesser.len_of_firstC  s   € ä�5˜‘8‹}Ðr2   c                 ó<   • [        [        [        U5      5      5      $ )z$Implementation of len_of_iter_first.)r   Únextr=   rg   s     r)   ro   Ú"BatchsizeGuesser.len_of_iter_firstG  s   € ä”4œ˜U›Ó$Ó%Ð%r2   c                 ó   • g)zImplementation of fallback.é   r]   rg   s     r)   rp   ÚBatchsizeGuesser.fallbackK  s   € àr2   rb   N)rV   rW   rX   rY   rZ   r*   ri   rf   rl   rm   rn   ro   rp   r\   r]   r2   r)   r_   r_   û   s/   † ñò.ò4òò>òòò&õr2   r_   )rZ   Úcollectionsr   Útorch.utils.data._utils.collater   Ú"torch.utils.data._utils.pin_memoryr   rA   Úspeechbrain.utils.data_utilsr   r   r   Ú
namedtupler   r   r_   r]   r2   r)   Ú<module>r…      sV   ðñó ã Ý ;õ÷ñ ð ×#Ò# L°6¸9Ð2EÓF€
÷`ñ `÷FRò Rr2   