o
    ei7                     @   s   d 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 ddl	m
Z
 e
eZG dd dejjZG dd	 d	eZG d
d dejjZG dd dejjZdS )zkLibrary for implementing cascade (sequences) of different neural modules.

Authors
 * Peter Plantinga 2020
    N)Linear)lengths_arg_exists)
get_loggerc                       sD   e Zd ZdZdd fdd
ZddddZd	d
 Zdd Z  ZS )
Sequentiala  A sequence of modules with potentially inferring shape on construction.

    If layers are passed with names, these can be referenced with dot notation.

    Arguments
    ---------
    *layers : tuple
        Layers to be applied in sequence.
    input_shape : iterable
        A list or tuple of ints or None, representing the expected shape of an
        input tensor. None represents a variable-length dimension. If no
        ``input_shape`` is passed, no shape inference will be performed.
    **named_layers : dict
        The inputs are treated as a list of layers to be
        applied in sequence. The output shape of each layer is used to
        infer the shape of the following layer. If a tuple is returned,
        only the shape of the first element is used to determine input
        shape of the next layer (e.g. RNN returns output, hidden).

    Example
    -------
    >>> inputs = torch.rand(10, 40, 50)
    >>> model = Sequential(input_shape=inputs.shape)
    >>> model.append(Linear, n_neurons=100, layer_name="layer1")
    >>> model.append(Linear, n_neurons=200, layer_name="layer2")
    >>> outputs = model(inputs)
    >>> outputs.shape
    torch.Size([10, 40, 200])
    >>> outputs = model.layer1(inputs)
    >>> outputs.shape
    torch.Size([10, 40, 100])
    Ninput_shapec                   s   t    |s|d u r|stdg | _|| _|r=d |v r=t|| _t| jD ]\}}|dkr5|d u r5d}|p8d| j|< q'|D ]}| | q?| D ]\}}| j||d qKd S )Nz&Must pass either layers or input shaper         
layer_name)	super__init__
ValueErrorlength_layersr   list	enumerateappenditems)selfr   layersnamed_layersidimlayername	__class__ Y/home/ubuntu/transcripts/venv/lib/python3.10/site-packages/speechbrain/nnet/containers.pyr   6   s    

zSequential.__init__r
   c                O   s   |du rt t| }n#|| v r.d}| d| | v r'|d7 }| d| | v s| d| }| jrKt|}d|j|j v rK|  }||d|i|}z	| || W dS  t	y_   t
dw )a  Add a layer to the list of layers, inferring shape if necessary.

        Arguments
        ---------
        layer : A torch.nn.Module class or object
            If the layer is a class, it should accept an argument called
            ``input_shape`` which will be inferred and passed. If the layer
            is a module object, it is added as-is.
        *args : tuple
            These are passed to the layer if it is constructed.
        layer_name : str
            The name of the layer, for reference. If the name is in use,
            ``_{count}`` will be appended.
        **kwargs : dict
            These are passed to the layer if it is constructed.
        Nr   _r   r   zwMust pass `input_shape` at initialization and use modules that take `input_shape` to infer shape when using `append()`.)strlenr   inspectgetfullargspecargs
kwonlyargsget_output_shape
add_module	TypeErrorr   )r   r   r   r$   kwargsindexargspecr   r   r   r   r   U   s(   
zSequential.appendc                 C   sF   t   t | j}| |}W d   |jS 1 sw   Y  |jS )zReturns expected shape of the output.

        Computed by passing dummy input constructed with the
        ``self.input_shape`` attribute.

        Returns
        -------
        Expected shape of the output after all layers applied.
        N)torchno_gradzerosr   shape)r   dummy_inputdummy_outputr   r   r   r&      s   



zSequential.get_output_shapec                 C   s,   |   D ]}||}t|tr|d }q|S )a0  Applies layers in sequence, passing only the first element of tuples.

        Arguments
        ---------
        x : torch.Tensor
            The input tensor to run through the network.

        Returns
        -------
        x : torch.Tensor
            Output after all layers are applied.
        r   )values
isinstancetupler   xr   r   r   r   forward   s   
zSequential.forward)	__name__
__module____qualname____doc__r   r   r&   r7   __classcell__r   r   r   r   r      s    !,r   c                       s6   e Zd ZdZ fddZ fddZd	ddZ  ZS )
LengthsCapableSequentiala  Sequential model that can take ``lengths`` in the forward method.

    This is useful for Sequential models that include RNNs where it is
    important to avoid padding, or for some feature normalization layers.

    Unfortunately, this module is not jit-able because the compiler doesn't
    know ahead of time if the length will be passed, and some layers don't
    accept the length parameter.
    c                    s   g | _ t j|i | d S N)takes_lengthsr   r   )r   r$   r)   r   r   r   r      s   z!LengthsCapableSequential.__init__c                    s8   t  j|i | t|  d j}| jt| dS )z@Add a layer to the list of layers, inferring shape if necessary.N)r   r   r   r2   r7   r?   r   )r   r$   r)   latest_forward_methodr   r   r   r      s   zLengthsCapableSequential.appendNc                 C   sJ   t |  | jD ]\}}|r|||d}n||}t|tr"|d }q|S )a.  Applies layers in sequence, passing only the first element of tuples.

        In addition, forward the ``lengths`` argument to all layers that accept
        a ``lengths`` argument in their ``forward()`` method (e.g. RNNs).

        Arguments
        ---------
        x : torch.Tensor
            The input tensor to run through the network.
        lengths : torch.Tensor
            The relative lengths of each signal in the tensor.

        Returns
        -------
        x : torch.Tensor
            The outputs after all layers are applied.
        )lengthsr   )zipr2   r?   r3   r4   )r   r6   rB   r   give_lengthsr   r   r   r7      s   
z LengthsCapableSequential.forwardr>   )r8   r9   r:   r;   r   r   r7   r<   r   r   r   r   r=      s
    
r=   c                       s@   e Zd ZdZ fddZdd Zdd Zdd	 Zd
d Z  Z	S )
ModuleLista  This class implements a wrapper to torch.nn.ModuleList with a forward()
    method to forward all the layers sequentially.
    For some pretrained model with the SpeechBrain older implementation of
    Sequential class, user can use this class to load those pretrained models

    Arguments
    ---------
    *layers : torch class
        Torch objects to be put in a ModuleList.
    c                    s   t    tj|| _d S r>   )r   r   r,   nnrE   r   )r   r   r   r   r   r      s   
zModuleList.__init__c                 C   s*   | j D ]}||}t|tr|d }q|S )z!Applies the computation pipeline.r   )r   r3   r4   r5   r   r   r   r7      s   

zModuleList.forwardc                 C      | j | dS z"Appends module to the layers list.N)r   r   )r   moduler   r   r   r         zModuleList.appendc                 C   rG   rH   )r   extend)r   modulesr   r   r   rK      rJ   zModuleList.extendc                 C   s   | j | dS )z"Inserts module to the layers list.N)r   insert)r   r*   rI   r   r   r   rM      rJ   zModuleList.insert)
r8   r9   r:   r;   r   r7   r   rK   rM   r<   r   r   r   r   rE      s    rE   c                       sD   e Zd ZdZddejf fdd	Zdd Zdd	 ZdddZ	  Z
S )ConnectBlocksa  Connect a sequence of blocks with shortcut connections.

    Note: all shortcuts start from the output of the first block,
    since the first block may change the shape significantly.

    Arguments
    ---------
    input_shape : tuple
        The shape of the
    shortcut_type : str
        One of:
        * "residual" - first block output passed to final output,
        * "dense" - input of each block is from all previous blocks,
        * "skip" - output of each block is passed to final output.
    shortcut_projection : bool
        Only has an effect if `shortcut_type` is passed. Whether to add a
        linear projection layer to the shortcut connection before combining
        with the output, to handle different sizes.
    shortcut_combine_fn : str or function
        Either a pre-defined function (one of "add", "sub", "mul", "div",
        "avg", "cat") or a user-defined function that takes the shortcut
        and next input, and combines them, as well as `init_params`
        in case parameters need to be initialized inside of the function.

    Example
    -------
    >>> inputs = torch.rand(10, 100, 20)
    >>> model = ConnectBlocks(
    ...     input_shape=inputs.shape, shortcut_projection=True
    ... )
    >>> model.append(Linear, n_neurons=10)
    >>> model.append(Linear, n_neurons=10, end_of_block=True)
    >>> model.append(Linear, n_neurons=10)
    >>> model.append(Linear, n_neurons=10, end_of_block=True)
    >>> outputs = model(inputs)
    >>> outputs.shape
    torch.Size([10, 100, 10])
    residualFc                    s^   t    || _|| _d| _tj | _|dvrt	d|| _
|| _|r*tj | _|| _d S )NT)rO   denseskipz9'shortcuts' must be one of 'residual', 'dense', or 'skip')r   r   first_input_shapeblock_input_shape	new_blockr,   rF   rE   blocksr   shortcut_typeshortcut_projectionprojectionsshortcut_combine_fn)r   r   rV   rW   rY   r   r   r   r   $  s   

zConnectBlocks.__init__c           	      O   s   | j r| jt| jd d| _ d}d|v r|d }|d= | jd j|g|R i | |ryt| j}| jd |}| jrit	t
j|jdd d}| jdkrZ| j}t| j}n| j}| jt||dd	d
 d	| _ | ||d}|j| _dS dS )a  Appends the specified module to the shortcut model.

        Arguments
        ---------
        layer : torch.nn.Module class
            This layer will get initialized with *args and **kwargs. Also,
            the argument ``input_shape`` will be passed if the layer takes it.
        *args : tuple
        **kwargs : dict
            Passed unchanged to the layer **EXCEPT** the kwarg ``end_of_block``
            which is used to indicate that the shortcut should be added in.
        r   Fend_of_blockr@      Nr   rO   T)	n_neuronsr   biascombine_dims)rT   rU   r   r   rS   r,   r.   rW   	functoolsreduceoperatormulr/   rV   rR   rX   r   _combine)	r   r   r$   r)   rZ   r0   r1   projection_sizer/   r   r   r   r   ;  s>   

zConnectBlocks.appendc                 C   s~   |}t | jD ].\}}||}| jdkr| |||}| jdkr)| ||| }}| jdkr5| |||}q| jdkr=|S |S )z
        Arguments
        ---------
        x : torch.Tensor
            The inputs to the replicated modules.

        Returns
        -------
        x : torch.Tensor
            The output processed by all blocks.
        rQ   rP   rO   )r   rU   rV   rc   )r   r6   shortcutr   blockr   r   r   r7   s  s   



zConnectBlocks.forwardr   c                 C   s,   | j r| j| |}||j}| ||S )z'Handle combining shortcut with outputs.)rW   rX   reshaper/   rY   )r   re   r6   block_indexr   r   r   rc     s   zConnectBlocks._combine)r   )r8   r9   r:   r;   r,   addr   r   r7   rc   r<   r   r   r   r   rN      s    *8rN   )r;   r_   r"   ra   r,   speechbrain.nnet.linearr   speechbrain.utils.callchainsr   speechbrain.utils.loggerr   r8   loggerrF   
ModuleDictr   r=   ModulerE   rN   r   r   r   r   <module>   s     2%