o
    %ݫi!                     @   s`   d Z ddlZddlmZmZmZmZmZmZm	Z	m
Z
 ddlmZ eeZG dd dejjZdS )zuLibrary implementing quaternion-valued linear transformation.

Authors
 * Titouan Parcollet 2020
 * Drew Wagner 2024
    N)QuaternionLinearCustomBackwardaffect_initcheck_quaternion_inputquaternion_initquaternion_linear_opquaternion_linear_rotation_op!renorm_quaternion_weights_inplaceunitary_init)
get_loggerc                       s@   e Zd ZdZ							d fdd	Zejjd	d
 Z  Z	S )QLineara  This function implements a fully connected quaternion-valued
    linear layer: y = Wx + b. y, W, x and b are thus quaternion
    numbers. A quaternion number is written as: r + xi + yj + zk.
    A tensor of quaternion numbers x = [batch, 32] can be understood as
    [batch, 0:7] = R, [batch, 8:15] = Xi, [batch, 16:23] = Yi, and
    [batch, 24:31] = Xi. Thus the features dimension is cut in four
    (must be divisible by 4).

    Arguments
    ---------
    n_neurons : int
        It is the number of output neurons (i.e, the dimensionality of the
        output). Please note that these are quaternion-valued neurons. If 256
        neurons are specified, the output dimension will be 1024.
    input_shape : tuple
        Expected size of the input.
    bias : bool
        If True, the additive bias b is adopted.
    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 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: "Quaternion recurrent neural networks", Parcollet T.
    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. This only works with
        spinor = False (default True).
    spinor : bool, optional
        When True, the layer will be turned into a spinor layer. More precisely
        W*x will be turned into W*x*W-1. The input x will be rotated by W such
        as in a spinor neural network. However, x MUST be a quaternion with
        the real part equal to zero. (0 + xi + yj + zk). Indeed, the rotation
        operation only acts on the vector part. Note that W will always be
        normalized before the rotation to ensure the quaternion algebra (default False).
        More details in: "Quaternion neural networks", Parcollet T.
    vector_scale : bool, optional
        The vector_scale is only used when spinor = True. In the context of a
        spinor neural network, multiple rotations of the input vector x are
        performed and summed. Hence, the norm of the output vector always
        increases with the number of layers, making the neural network instable
        with deep configurations. The vector_scale parameters are learnable
        parameters that acts like gates by multiplying the output vector with
        a small trainable parameter (default False).
    max_norm: float
        weight max-norm.

    Example
    -------
    >>> inputs = torch.rand(10, 50, 40)
    >>> lin = QLinear(n_neurons=100, input_shape=inputs.shape, weight_init='unitary')
    >>> output = lin(inputs)
    >>> output.shape
    torch.Size([10, 50, 400])
    Tglorot
quaternionFNc
           
         s  t    || _|| _|| _|| _|| _|| _|	| _t	|t
r#d|g}t| |d d | _| j| _tjt| j| j| _tjt| j| j| _tjt| j| j| _tjt| j| j| _| jrxtjjt| jjdd| _nt| jjd| _| jr| jrtjt| j| j| _tjj| jj nt| j| jd| _|rtjtd| | _ntd| d| _| jjd t t!d| j | _"t#| j| j| j| j| j"| d S )N      F)requires_gradr   )r   unitary)$super__init__	n_neuronsinit_criterionweight_initautogradspinorvector_scalemax_norm
isinstanceintr   in_featuresout_featurestorchnn	ParameterTensorr_weighti_weightj_weightk_weightzerosshapezero_kernelrequires_grad_scale_paraminitxavier_uniform_databiasfill_r   r	   winitr   )
selfr   input_shaper0   r   r   r   r   r   r   	__class__ a/home/ubuntu/.local/lib/python3.10/site-packages/speechbrain/nnet/quaternion_networks/q_linear.pyr   Y   sr   


zQLinear.__init__c              	   C   s   | j durt| j| j| j| j| j d | jr=| jr-t|| j| j| j| j| j	| j
| j}|S t|| j| j| j| j| j	}|S | }|dkrT| \}}}||| |}t|| j| j| j| j| j	}|dkrq||||d}|S )zReturns the linear transformation of input tensor.

        Arguments
        ---------
        x : torch.Tensor
            Input to transform linearly.

        Returns
        -------
        The linearly transformed input.
        N)r      r   )r   r   r$   r%   r&   r'   r   r   r   r0   r,   r*   r   dimsizeviewr   apply)r3   xout	input_dimbatchtimefear7   r7   r8   forward   sZ   
&	zQLinear.forward)Tr   r   TFFN)
__name__
__module____qualname____doc__r   r    jitignorerD   __classcell__r7   r7   r5   r8   r      s    CXr   )rH   r    *speechbrain.nnet.quaternion_networks.q_opsr   r   r   r   r   r   r   r	   speechbrain.utils.loggerr
   rE   loggerr!   Moduler   r7   r7   r7   r8   <module>   s    (
