o
    %ݫi%                     @   s   d Z ddlm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jjZG dd dejjZG dd dejjZG dd dejjZG dd dejjZdS )zeLibrary implementing quaternion-valued recurrent neural networks.

Authors
 * Titouan Parcollet 2020
    )OptionalN)QLinear)
QBatchNorm)
get_loggerc                       sb   e Zd ZdZ							d fdd		Zd
d Zddeej fddZ	deej fddZ
  ZS )QLSTMa  This function implements a quaternion-valued LSTM as first introduced
    in : "Quaternion Recurrent Neural Networks", Parcollet T. et al.

    Input format is (batch, time, fea) or (batch, time, fea, channel).
    In the latter shape, the two last dimensions will be merged:
    (batch, time, fea * channel)

    Arguments
    ---------
    hidden_size : int
        Number of output neurons (i.e, the dimensionality of the output).
        Specified value is in terms of quaternion-valued neurons. Thus, the output
        is 4*hidden_size.
    input_shape : tuple
        The expected shape of the input tensor.
    num_layers : int, optional
        Number of layers to employ in the RNN architecture (default 1).
    bias : bool, optional
        If True, the additive bias b is adopted (default True).
    dropout : float, optional
        It is the dropout factor (must be between 0 and 1) (default 0.0).
    bidirectional : bool, optional
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used (default False).
    init_criterion : str , optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init : str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Quaternion Recurrent Neural Networks",
        Parcollet T. et al.
    autograd : bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).


    Example
    -------
    >>> inp_tensor = torch.rand([10, 16, 40])
    >>> rnn = QLSTM(hidden_size=16, input_shape=inp_tensor.shape)
    >>> out_tensor = rnn(inp_tensor)
    >>>
    torch.Size([10, 16, 64])
       T        Fglorot
quaternionc
           
         s   t    |d | _|| _|| _|| _|| _d| _|| _|| _	|	| _
t|dkr+d| _tt|dd  | _|d | _|  | _d S N   F   T   r   )super__init__hidden_size
num_layersbiasdropoutbidirectionalreshapeinit_criterionweight_initautogradlentorchprodtensorfea_dim
batch_size_init_layersrnn)
selfr   input_shaper   r   r   r   r   r   r   	__class__ ^/home/ubuntu/.local/lib/python3.10/site-packages/speechbrain/nnet/quaternion_networks/q_RNN.pyr   G   s   


zQLSTM.__init__c                 C   sr   t jg }| j}t| jD ](}t|| j| j| j| j	| j
| j| j| jd	}|| | j
r3| jd }q| j}q|S )zInitializes the layers of the quaternionLSTM.

        Returns
        -------
        rnn : ModuleList
            The initialized QLSTM_Layers
        )r   r   r   r   r   r   )r   nn
ModuleListr   ranger   QLSTM_Layerr   r   r   r   r   r   r   appendr"   r!   current_dimirnn_layr&   r&   r'   r    g   s&   
zQLSTM._init_layersNhxc                 C   T   | j r|jdkr| |jd |jd |jd |jd  }| j||d\}}||fS )a]  Returns the output of the vanilla QuaternionRNN.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        output : torch.Tensor
            Output of Quaternion RNN
        hh : torch.Tensor
            Hidden states
        r   r   r   r   r   r1   r   ndimshape_forward_rnnr"   xr1   outputhhr&   r&   r'   forward   s
   
*zQLSTM.forwardc                 C      g }|dur| j r|| j| jd | j}t| jD ]%\}}|dur+|||| d}n||dd}||dddddf  qtj	|dd}| j r^||j
d d |j
d | j}||fS |dd}||fS )al  Returns the output of the vanilla QuaternionRNN.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        x : torch.Tensor
            The output of the Quaternion RNN layer.
        h : torch.Tensor
            The hiddens states.
        Nr   r3   r   dimr   r   r   r   r   r   	enumerater!   r,   r   stackr6   	transposer"   r9   r1   hr/   r0   r&   r&   r'   r7      s"    zQLSTM._forward_rnn)r   Tr   Fr	   r
   TN__name__
__module____qualname____doc__r   r    r   r   Tensorr<   r7   __classcell__r&   r&   r$   r'   r      s    8  r   c                       sb   e Zd ZdZ					d fdd	Zdd
eej fddZdd Z	dd Z
dd Zdd Z  ZS )r+   a  This function implements quaternion-valued LSTM layer.

    Arguments
    ---------
    input_size : int
        Feature dimensionality of the input tensors (in term of real values).
    hidden_size : int
        Number of output values (in term of real values).
    num_layers : int, optional
        Number of layers to employ in the RNN architecture (default 1).
    batch_size : int
        Batch size of the input tensors.
    dropout : float, optional
        It is the dropout factor (must be between 0 and 1) (default 0.0).
    bidirectional : bool, optional
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used (default False).
    init_criterion : str , optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init : str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Quaternion Recurrent Neural Networks",
        Parcollet T. et al.
    autograd : bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).
    r   Fr	   r
   truec
           
         s   t    t|d | _t|| _|| _|| _|| _|| _|| _	|	| _
t| j| jd d| j	| j| j
d| _t| jd | jd d| j	| j| j
d| _| jrR| jd | _| dtd| jd  | | j tjj| jdd| _td	g | _d S )
Nr   Tr#   	n_neuronsr   r   r   r   r   h_initr   Fpinplace      ?)r   r   intr   
input_sizer   r   r   r   r   r   r   wuregister_bufferr   zeros
_init_dropr(   Dropoutdropr   floatdrop_mask_te)
r"   rX   r   r   r   r   r   r   r   r   r$   r&   r'   r      s>   

		zQLSTM_Layer.__init__Nr1   c                 C      | j r|d}tj||gdd}| | | |}|dur&| ||}n| || j}| j rG|jddd\}}|d}tj||gdd}|S )a1  Returns the output of the QuaternionRNN_layer.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        h : torch.Tensor
            The output of the Quaternion RNN layer.
        r   r   r?   Nr   )	r   flipr   cat_change_batch_sizerY   _quaternionlstm_cellrR   chunkr"   r9   r1   x_fliprY   rF   h_fh_br&   r&   r'   r<   (     



zQLSTM_Layer.forwardc                 C   s  g }| j }| |}t|jd D ]q}|dd|f | | }|dd\}}	}
}}}}}}}}}}}}}ttj||	|
|gdd}ttj||||gdd}ttj||||gdd}|t	tj||||gdd | ||  }|t	| }|
| qtj|dd}|S )a7  Returns the hidden states for each time step.

        Arguments
        ---------
        w : torch.Tensor
            Linearly transformed input.
        ht : torch.Tensor
            Hidden layer.

        Returns
        -------
        h : torch.Tensor
            The hidden states for all steps.
        r   N   r>   r?   )rR   _sample_drop_maskr*   r6   rZ   rg   r   sigmoidrd   tanhr,   rC   )r"   rY   hthiddensct	drop_maskkgatesitritiitjitkftrftiftjftkotrotiotjotkctrctictjctkitftotrF   r&   r&   r'   rf   O  sL   

z QLSTM_Layer._quaternionlstm_cellc                 C   T   t jj| jdd| _t dg | _d| _d| _	| t 
| j| jd j| _dS wInitializes the recurrent dropout operation. To speed it up,
        the dropout masks are sampled in advance.
        FrS   rV   >  r   r   Nr   r(   r^   r   r_   r   r`   ra   N_drop_masksdrop_mask_cntonesr   data
drop_masksr"   r   r&   r&   r'   r]     s   zQLSTM_Layer._init_dropc                 C   v   | j r6| j| j | jkr!d| _| tj| j| jd |jdj	| _
| j
| j| j| j  }| j| j | _|S | j}|S z-Selects one of the pre-defined dropout masks.r   r   devicetrainingr   r   r   r_   r   r   r   r   r   r   ra   r"   rY   rt   r&   r&   r'   rn     s"   zQLSTM_Layer._sample_drop_maskc                 C   R   | j |jd kr%|jd | _ | jr'| tj| j| jd |jdj	| _
dS dS dS   This function changes the batch size when it is different from
        the one detected in the initialization method. This might happen in
        the case of multi-gpu or when we have different batch sizes in train
        and test. We also update the h_int and drop masks.
        r   r   r   Nr   r6   r   r_   r   r   r   r   r   r   r   r"   r9   r&   r&   r'   re        zQLSTM_Layer._change_batch_size)r   Fr	   r
   rO   rG   )rI   rJ   rK   rL   r   r   r   rM   r<   rf   r]   rn   re   rN   r&   r&   r$   r'   r+      s    *7'>r+   c                       d   e Zd ZdZ								d fd	d
	Zdd Zddeej fddZ	deej fddZ
  ZS )QRNNa  This function implements a vanilla quaternion-valued RNN.

    Input format is (batch, time, fea) or (batch, time, fea, channel).
    In the latter shape, the two last dimensions will be merged:
    (batch, time, fea * channel)

    Arguments
    ---------
    hidden_size : int
        Number of output neurons (i.e, the dimensionality of the output).
        Specified value is in term of quaternion-valued neurons. Thus, the output
        is 4*hidden_size.
    input_shape : tuple
        Expected shape of the input tensor.
    nonlinearity : str, optional
        Type of nonlinearity (tanh, relu) (default "tanh").
    num_layers : int, optional
        Number of layers to employ in the RNN architecture (default 1).
    bias : bool, optional
        If True, the additive bias b is adopted (default True).
    dropout : float, optional
        It is the dropout factor (must be between 0 and 1) (default 0.0).
    bidirectional : bool, optional
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used (default False).
    init_criterion : str , optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init : str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Quaternion Recurrent Neural Networks",
        Parcollet T. et al.
    autograd : bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).


    Example
    -------
    >>> inp_tensor = torch.rand([10, 16, 40])
    >>> rnn = QRNN(hidden_size=16, input_shape=inp_tensor.shape)
    >>> out_tensor = rnn(inp_tensor)
    >>>
    torch.Size([10, 16, 64])
    rp   r   Tr   Fr	   r
   c                       t    |d | _|| _|| _|| _|| _|| _d| _|| _	|	| _
|
| _t|dkr.d| _tt|dd  | _|d | _|  | _d S r   r   r   r   nonlinearityr   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$   r&   r'   r     s    


zQRNN.__init__c                 C   v   t jg }| j}t| jD ]*}t|| j| j| j| j	| j
| j| j| j| jd
}|| | jr5| jd }q| j}q|S )z
        Initializes the layers of the quaternionRNN.

        Returns
        -------
        rnn : ModuleList
            The initialized QRNN_Layers.
        r   r   r   r   r   r   r   )r   r(   r)   r   r*   r   
QRNN_Layerr   r   r   r   r   r   r   r   r,   r-   r&   r&   r'   r      s(   

zQRNN._init_layersNr1   c                 C   r2   )a  Returns the output of the vanilla QuaternionRNN.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        output : torch.Tensor
        hh : torch.Tensor
        r   r   r   r   r   r3   r4   r8   r&   r&   r'   r<   ?  s
   
*zQRNN.forwardc                 C   r=   )aG  Returns the output of the vanilla QuaternionRNN.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        x : torch.Tensor
            Outputs
        h : torch.Tensor
            Hidden states.
        Nr   r3   r>   r   r?   r   rA   rE   r&   r&   r'   r7   W  s"    zQRNN._forward_rnn)rp   r   Tr   Fr	   r
   TrG   rH   r&   r&   r$   r'   r     s    9"#r   c                       sd   e Zd ZdZ						d fdd		Zddeej fddZdd Z	dd Z
dd Zdd Z  ZS )r   a  This function implements quaternion-valued recurrent layer.

    Arguments
    ---------
    input_size : int
        Feature dimensionality of the input tensors (in term of real values).
    hidden_size : int
        Number of output values (in term of real values).
    num_layers : int, optional
        Number of layers to employ in the RNN architecture (default 1).
    batch_size : int
        Batch size of the input tensors.
    dropout : float, optional
        It is the dropout factor (must be between 0 and 1) (default 0.0).
    nonlinearity : str, optional
        Type of nonlinearity (tanh, relu) (default "tanh").
    bidirectional : bool, optional
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used (default False).
    init_criterion : str , optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init : str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Quaternion Recurrent Neural Networks",
        Parcollet T. et al.
    autograd : bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).
    r   rp   Fr	   r
   rO   c                    s  t    t|d | _t|| _|| _|| _|| _|| _|	| _	|
| _
t| j| jd| j	| j| j
d| _t| jd | jd| j	| j| j
d| _| jrN| jd | _| dtd| jd  | | j tjj| jdd| _td	g | _|d
krtj | _d S tj | _d S )Nr   TrP   r   rR   r   FrS   rV   rp   )r   r   rW   r   rX   r   r   r   r   r   r   r   rY   rZ   r[   r   r\   r]   r(   r^   r_   r   r`   ra   TanhactReLU)r"   rX   r   r   r   r   r   r   r   r   r   r$   r&   r'   r     sD   

		zQRNN_Layer.__init__Nr1   c                 C   rb   )a&  Returns the output of the QuaternionRNN_layer.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        h : torch.Tensor
            Output of the Quaternion RNN
        r   r   r?   Nr   )	r   rc   r   rd   re   rY   _quaternionrnn_cellrR   rg   rh   r&   r&   r'   r<     rl   zQRNN_Layer.forwardc                 C   sf   g }|  |}t|jd D ]}|dd|f | | }| || }|| qtj|dd}|S )a7  Returns the hidden states for each time step.

        Arguments
        ---------
        w : torch.Tensor
            Linearly transformed input.
        ht : torch.Tensor
            The hidden layer.

        Returns
        -------
        h : torch.Tensor
            Hidden states for each step.
        r   Nr?   )rn   r*   r6   rZ   r   r,   r   rC   )r"   rY   rq   rr   rt   ru   atrF   r&   r&   r'   r     s   
zQRNN_Layer._quaternionrnn_cellc                 C   r   r   r   r   r&   r&   r'   r]   *  s   zQRNN_Layer._init_dropc                 C   r   r   r   r   r&   r&   r'   rn   9  s"   zQRNN_Layer._sample_drop_maskc                 C   r   )r   r   r   r   Nr   r   r&   r&   r'   re   Q  r   zQRNN_Layer._change_batch_size)r   rp   Fr	   r
   rO   rG   )rI   rJ   rK   rL   r   r   r   rM   r<   r   r]   rn   re   rN   r&   r&   r$   r'   r     s    ,>'r   c                       r   )QLiGRUaB
  This function implements a quaternion-valued Light GRU (liGRU).

    Ligru is single-gate GRU model based on batch-norm + relu
    activations + recurrent dropout. For more info see:

    "M. Ravanelli, P. Brakel, M. Omologo, Y. Bengio,
    Light Gated Recurrent Units for Speech Recognition,
    in IEEE Transactions on Emerging Topics in Computational Intelligence,
    2018" (https://arxiv.org/abs/1803.10225)

    To speed it up, it is compiled with the torch just-in-time compiler (jit)
    right before using it.

    It accepts in input tensors formatted as (batch, time, fea).
    In the case of 4d inputs like (batch, time, fea, channel) the tensor is
    flattened as (batch, time, fea*channel).

    Arguments
    ---------
    hidden_size : int
        Number of output neurons (i.e, the dimensionality of the output).
        Specified value is in term of quaternion-valued neurons. Thus, the output
        is 2*hidden_size.
    input_shape : tuple
        Expected shape of the input.
    nonlinearity : str
        Type of nonlinearity (tanh, relu).
    num_layers : int
        Number of layers to employ in the RNN architecture.
    bias : bool
        If True, the additive bias b is adopted.
    dropout: float
        It is the dropout factor (must be between 0 and 1).
    bidirectional : bool
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used.
    init_criterion : str, optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init : str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion-valued
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Deep quaternion Networks", Trabelsi C. et al.
    autograd : bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).

    Example
    -------
    >>> inp_tensor = torch.rand([10, 16, 40])
    >>> rnn = QLiGRU(input_shape=inp_tensor.shape, hidden_size=16)
    >>> out_tensor = rnn(inp_tensor)
    >>>
    torch.Size([4, 10, 5])
    
leaky_relur   Tr   Fr	   r
   c                    r   r   r   r   r$   r&   r'   r     s    


zQLiGRU.__init__c                 C   r   )z
        Initializes the layers of the liGRU.

        Returns
        -------
        rnn : ModuleList
            The initialized QLiGRU_Layers.
        r   r   )r   r(   r)   r   r*   r   QLiGRU_Layerr   r   r   r   r   r   r   r   r,   r-   r&   r&   r'   r      s(   	
zQLiGRU._init_layersNr1   c                 C   r2   )a  Returns the output of the QuaternionliGRU.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        output : torch.Tensor
        hh : torch.Tensor
        r   r   r   r   r   r3   )r   r5   r6   _forward_ligrur8   r&   r&   r'   r<     s
   
*zQLiGRU.forwardc                 C   r=   )a?  Returns the output of the quaternionliGRU.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor
            Hidden layer.

        Returns
        -------
        x : torch.Tensor
            Output
        h : torch.Tensor
            Hidden states
        Nr   r3   r>   r   r?   r   rA   )r"   r9   r1   rF   r/   	ligru_layr&   r&   r'   r     s"    zQLiGRU._forward_ligru)r   r   Tr   Fr	   r
   TrG   )rI   rJ   rK   rL   r   r    r   r   rM   r<   r   rN   r&   r&   r$   r'   r   c  s    B !r   c                       sf   e Zd ZdZ							d fd	d
	Zddeej fddZdd Z	dd Z
dd Zdd Z  ZS )r   a  This function implements quaternion-valued Light-Gated Recurrent Units
    (ligru) layer.

    Arguments
    ---------
    input_size: int
        Feature dimensionality of the input tensors.
    hidden_size: int
        Number of output values.
    num_layers: int
        Number of layers to employ in the RNN architecture.
    batch_size: int
        Batch size of the input tensors.
    dropout: float
        It is the dropout factor (must be between 0 and 1).
    nonlinearity: str
        Type of nonlinearity (tanh, relu).
    normalization: str
        The type of normalization to use (batchnorm or none)
    bidirectional: bool
        If True, a bidirectional model that scans the sequence both
        right-to-left and left-to-right is used.
    init_criterion: str , optional
        (glorot, he).
        This parameter controls the initialization criterion of the weights.
        It is combined with weights_init to build the initialization method of
        the quaternion-valued weights (default "glorot").
    weight_init: str, optional
        (quaternion, unitary).
        This parameter defines the initialization procedure of the
        quaternion-valued weights. "quaternion" will generate random quaternion
        weights following the init_criterion and the quaternion polar form.
        "unitary" will normalize the weights to lie on the unit circle (default "quaternion").
        More details in: "Deep quaternion Networks", Trabelsi C. et al.
    autograd: bool, optional
        When True, the default PyTorch autograd will be used. When False, a
        custom backpropagation will be used, reducing by a factor 3 to 4 the
        memory consumption. It is also 2x slower (default True).
    r   r   	batchnormFr	   r
   Tc                    s  t    t|d | _t|| _|| _|| _|| _|	| _|
| _	|| _
|| _|| _t| j| jd d| j	| j| jd| _t| jd | jd d| j	| j| jd| _| jrX| jd | _d| _| j
dkrmt|d dd| _d| _nt|d dd| _d| _| d	td
| jd  | | j tjj| jdd| _tdg | _| jdkrtj | _d S | jdkrtj | _d S tj  | _d S )Nr   r   FrP   r   r>   )rX   r@   TrR   r   rS   rV   rp   r   )!r   r   rW   r   rX   r   r   r   r   r   normalizationr   r   r   rY   rZ   	normalizer   normr[   r   r\   r]   r(   r^   r_   r   r`   ra   r   r   	LeakyReLUr   )r"   rX   r   r   r   r   r   r   r   r   r   r   r$   r&   r'   r   M  sX   

		


zQLiGRU_Layer.__init__Nr1   c           	      C   s   | j r|d}tj||gdd}| | | |}| jrB| ||j	d |j	d  |j	d }||j	d |j	d |j	d }|durM| 
||}n| 
|| j}| j rn|jddd\}}|d}tj||gdd}|S )zReturns the output of the quaternion liGRU layer.

        Arguments
        ---------
        x : torch.Tensor
            Input tensor.
        hx : torch.Tensor

        Returns
        -------
        Output of quaternion liGRU layer.
        r   r   r?   r   N)r   rc   r   rd   re   rY   r   r   r   r6   _quaternion_ligru_cellrR   rg   )	r"   r9   r1   ri   rY   w_bnrF   rj   rk   r&   r&   r'   r<     s    


( 
zQLiGRU_Layer.forwardc              	   C   s   g }|  |}t|jd D ]N}|dd|f | | }|dd\}}}	}
}}}}tj|||	|
gdd}tj||||gdd}t|}| || }|| d| |  }|	| qtj
|dd}|S )a  Returns the hidden states for each time step.

        Arguments
        ---------
        w : torch.Tensor
            Linearly transformed input.
        ht : torch.Tensor

        Returns
        -------
        h : torch.Tensor
            Hidden states for all steps.
        r   N   r>   r?   )rn   r*   r6   rZ   rg   r   rd   ro   r   r,   rC   )r"   rY   rq   rr   rt   ru   rv   atratiatjatkztrztiztjztkr   zthcandrF   r&   r&   r'   r     s   

z#QLiGRU_Layer._quaternion_ligru_cellc              
   C   sZ   t jj| jdd| _t dg | _d| _d| _	| 
d| t | j| jd j dS )	r   FrS   rV   r   r   r   r   N)r   r(   r^   r   r_   r   r`   ra   r   r   r[   r   r   r   r   r&   r&   r'   r]     s   zQLiGRU_Layer._init_dropc                 C   s   | j r6| j| j | jkr!d| _| tj| j| jd |jdj	| _
| j
| j| j| j  }| j| j | _|S | j|j| _| j}|S )z,Selects one of the pre-defined dropout masksr   r   r   )r   r   r   r   r_   r   r   r   r   r   r   ra   tor   r&   r&   r'   rn     s$   zQLiGRU_Layer._sample_drop_maskc                 C   r   r   r   r   r&   r&   r'   re     r   zQLiGRU_Layer._change_batch_size)r   r   r   Fr	   r
   TrG   )rI   rJ   rK   rL   r   r   r   rM   r<   r   r]   rn   re   rN   r&   r&   r$   r'   r   $  s    .N+#r   )rL   typingr   r   -speechbrain.nnet.quaternion_networks.q_linearr   4speechbrain.nnet.quaternion_networks.q_normalizationr   speechbrain.utils.loggerr   rI   loggerr(   Moduler   r+   r   r   r   r   r&   r&   r&   r'   <module>   s$     ; y = d B