o
    Si*                     @   s   d dl mZ d dlmZ d dlmZmZmZmZm	Z	m
Z
mZ d dlZd dlZd dlmZ d dlmZ d dlmZmZ G dd	 d	eZdS )
    )reduce)add)AnyCallableDictListOptionalTupleUnionN)CutSet)Cut)
CutSamplerSamplingDiagnosticsc                       sr  e Zd ZdZdddddededeeee f de	d	d
f
 fddZ
ed	ee fddZed	ee	 fddZed	ee	 fddZ fddZd	eeef f fddZdeeef d	d
f fddZdd Zd	eeee f fddZd.ddZd e	d	d
f fd!d"Zd#eegef d	d
fd$d%Zd&eeeed'f f d	d
fd(d)Zed	e fd*d+Z!d	efd,d-Z"  Z#S )/RoundRobinSamplera  
    :class:`.RoundRobinSampler` takes several samplers as input, and yields
    a mini-batch of cuts from each of those samplers in turn.
    E.g., with two samplers, the first mini-batch is from ``sampler0``,
    the seconds from ``sampler1``, the third from ``sampler0``, and so on.
    It is helpful for alternating mini-batches from multiple datasets or manually
    creating batches of different sizes.

    The input samplers do not have to provide the same number of batches -- when
    any of the samplers becomes depleted, we continue to iterate the non-depleted
    samplers, until all of them are exhausted.

    Example::

        >>> sampler = RoundRobinSampler(
        ...     SimpleCutSampler(cuts_corpusA, max_cuts=32, shuffle=True),
        ...     SimpleCutSampler(cuts_corpusB, max_cuts=64, shuffle=True),
        ... )
        >>> for cut in sampler:
        ...     pass  # profit
    Fr   )
stop_early	randomizeseedsamplersr   r   r   returnNc                   s   t  jdd|d || _|| _d| _ttt| j| _d| _	d| _
t|tr3t|t| jks2J n|dkrDdt| j gt| j }|| _dS )a  
        RoundRobinSampler's constructor.

        :param samplers: The list of samplers from which we sample batches in turns.
        :param stop_early: Should we finish the epoch once any of the samplers becomes
            depleted.
            By default, we will keep iterating until all the samplers are exhausted.
            This setting can be used to balance datasets of different sizes.
        :param randomize: Select the next sampler according to a distribution, instead of
            in order. If a list of floats is provided, it must contain the same number of
            elements as the number of samplers, and the values will be used as probabilities.
            If ``True`` is provided, the probabilities will be uniform. If ``False`` is provided,
            the samplers will be selected in order.
        :param seed: Random seed used to select the next sampler (only used if ``randomize`` is True)
        r      )rank
world_sizer   NTg      ?)super__init__r   r   rnglistrangelen_nondepleted_samplers_indices_cur_sampler_idx_num_dl_workers
isinstancer   )selfr   r   r   r   	__class__ W/home/ubuntu/.local/lib/python3.10/site-packages/lhotse/dataset/sampling/round_robin.pyr   $   s   

zRoundRobinSampler.__init__c                 C   ,   zt dd | jD W S  ty   Y dS w )zj
        Remaining duration of data left in the sampler (may be inexact due to float arithmetic).
        c                 s       | ]}|j V  qd S N)remaining_duration.0sr%   r%   r&   	<genexpr>O       z7RoundRobinSampler.remaining_duration.<locals>.<genexpr>Nsumr   	TypeErrorr"   r%   r%   r&   r*   I   s
   z$RoundRobinSampler.remaining_durationc                 C   r'   )z
        Remaining number of cuts in the sampler.
        Not available when the CutSet is read in lazy mode (returns None).
        c                 s   r(   r)   )remaining_cutsr+   r%   r%   r&   r.   Z   r/   z3RoundRobinSampler.remaining_cuts.<locals>.<genexpr>Nr0   r3   r%   r%   r&   r4   S   
   z RoundRobinSampler.remaining_cutsc                 C   r'   )z
        Total number of cuts in the sampler.
        Not available when the CutSet is read in lazy mode (returns None).
        c                 s   r(   r)   )num_cutsr+   r%   r%   r&   r.   e   r/   z-RoundRobinSampler.num_cuts.<locals>.<genexpr>Nr0   r3   r%   r%   r&   r6   ^   r5   zRoundRobinSampler.num_cutsc                    s"   t    | jD ]}|  qdS )a8  
        Enables re-setting to the start of an epoch when iter() is called.
        This is only needed in one specific scenario: when we restored previous
        sampler state via ``sampler.load_state_dict()`` but want to discard
        the progress in the current epoch and start from the beginning.
        N)r   allow_iter_to_reset_stater   )r"   r-   r#   r%   r&   r7   i   s   


z+RoundRobinSampler.allow_iter_to_reset_statec              	      s@   t   }|dd | jD | j| j| j| jt| j	d |S )z
        Return the current state of the sampler in a state_dict.
        Together with ``load_state_dict()``, this can be used to restore the
        training loop's state to the one stored in the state_dict.
        c                 S   s   g | ]}|  qS r%   )
state_dictr+   r%   r%   r&   
<listcomp>}   s    z0RoundRobinSampler.state_dict.<locals>.<listcomp>)r   r   r   r   r    r   )
r   r8   updater   r   r   r   r    r   r   )r"   r8   r#   r%   r&   r8   t   s   
zRoundRobinSampler.state_dictr8   c                    s   | d| _| d| _| d| _| d| _| d| _t| jt|d ks;J dt| j dt|d  d	t| j| dD ]	\}}|	| qDt
 	| d
S )aX  
        Restore the state of the sampler that is described in a state_dict.
        This will result in the sampler yielding batches from where the previous training left it off.

        .. caution::
            The samplers are expected to be initialized with the same CutSets,
            but this is not explicitly checked anywhere.

        .. caution::
            The input ``state_dict`` is being mutated: we remove each consumed key, and expect
            it to be empty at the end of loading. If you don't want this behavior, pass a copy
            inside of this function (e.g., using ``import deepcopy``).

        .. note::
            For implementers of sub-classes of CutSampler: the flag ``self._just_restored_state`` has to be
            handled in ``__iter__`` to make it avoid resetting the just-restored state (only once).
        r   r   r   r    r   r   zmError in RoundRobinSampler.load_state_dict(): Inconsistent number of samplers: current RoundRobinSampler has z, the state_dict has .N)popr   r   r   r    r   r   r   zipload_state_dictr   )r"   r8   sampler
sampler_sdr#   r%   r&   r>      s"   
z!RoundRobinSampler.load_state_dictc                 C   s   t jj| j| j d| _| jD ]}t| q| jr| S t	t
t| j| _d| _d| _tjj }|d urB|jt| j | _|j| _| S )N)r   r   r   )nprandomdefault_rngr   epochr   r   iter_just_restored_stater   r   r   r   r   r    torchutilsdataget_worker_infoidnum_workers)r"   r?   worker_infor%   r%   r&   __iter__   s   

zRoundRobinSampler.__iter__c                 C   s   t | jdkr
t | j| j }| j| }zt|}W n# ty>   | j| j | js3t | jdkr4 |   | 	  Y S w |   |S )Nr   )
r   r   StopIterationr   r   nextr<   r   _set_next_idx_next_batch)r"   sampler_idxr?   batchr%   r%   r&   rR      s   
zRoundRobinSampler._next_batchc                    s   j dur5tjdkr5ttj}fddjD   fdd D  jj|dd dd _d S jj tj _d S )NFr   c                    s   g | ]} j | qS r%   )r   )r,   ir3   r%   r&   r9      s    z3RoundRobinSampler._set_next_idx.<locals>.<listcomp>c                    s   g | ]}|t   qS r%   )r1   )r,   x)pr%   r&   r9      s    )sizereplacerW   r   )r   r   r   r   r   choicer   r    )r"   Nr%   )rW   r"   r&   rQ      s   

zRoundRobinSampler._set_next_idxrD   c                    s&   | j D ]}|| qt | dS )a  
        Sets the epoch for this sampler. When :attr:`shuffle=True`, this ensures all replicas
        use a different random ordering for each epoch. Otherwise, the next iteration of this
        sampler will yield the same ordering.

        :param epoch: Epoch number.
        N)r   	set_epochr   )r"   rD   r-   r#   r%   r&   r\      s   
zRoundRobinSampler.set_epoch	predicatec                 C   s   | j D ]}|| qdS )a
  
        Add a constraint on individual cuts that has to be satisfied to consider them.

        Can be useful when handling large, lazy manifests where it is not feasible to
        pre-filter them before instantiating the sampler.

        Example:
            >>> cuts = CutSet(...)
            ... sampler = SimpleCutSampler(cuts, max_duration=100.0)
            ... # Retain only the cuts that have at least 1s and at most 20s duration.
            ... sampler.filter(lambda cut: 1.0 <= cut.duration <= 20.0)
        N)r   filter)r"   r]   r?   r%   r%   r&   r^      s   
zRoundRobinSampler.filterrT   .c                 C   s   d S r)   r%   )r"   rT   r%   r%   r&   _log_diagnostics   s   z"RoundRobinSampler._log_diagnosticsc                 C   s   t tdd | jD S )Nc                 s   r(   r)   )diagnosticsr+   r%   r%   r&   r.     r/   z0RoundRobinSampler.diagnostics.<locals>.<genexpr>)r   r   r   r3   r%   r%   r&   r`      s   zRoundRobinSampler.diagnosticsc                 C   s
   | j  S )zJReturns a string describing the statistics of the sampling process so far.)r`   
get_reportr3   r%   r%   r&   ra     s   
zRoundRobinSampler.get_report)r   N)$__name__
__module____qualname____doc__r   boolr
   r   floatintr   propertyr   r*   r4   r6   r7   r   strr   r8   r>   rN   r   r	   rR   rQ   r\   r   r   r^   r_   r   r`   ra   __classcell__r%   r%   r#   r&   r      sD    %	

!
"r   )	functoolsr   operatorr   typingr   r   r   r   r   r	   r
   numpyrA   rG   lhotser   
lhotse.cutr   lhotse.dataset.sampling.baser   r   r   r%   r%   r%   r&   <module>   s    $