o
    oiZ                     @   s   d dl mZ d dlmZmZmZmZ d dlZd dlm	Z	m
Z
mZmZmZmZmZmZmZ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mZ G dd	 d	eZdd
d	deej dd	fddZ dS )    )pi)AnyOptionalTupleUnionN)
DeviceDtypeModule	ParameterTensorconcatenaterandstacktensorwhere)KORNIA_CHECK_TYPE)axis_angle_to_quaternioneuler_from_quaternionnormalize_quaternionquaternion_from_eulerquaternion_to_axis_anglequaternion_to_rotation_matrixrotation_matrix_to_quaternion)batched_dot_productc                       s  e Zd ZU dZeeef ed< deeef ddf fddZde	d	e	dd fd
dZ
deeef dd fddZdefddZdeeef dd fddZduddZded eef dd fddZded eef dd fddZded eef dd fddZdeeef dd fddZdeed ef dd fd d!Zdeed ef dd fd"d#Zdeeef dd fd$d%Zdeeef dd fd&d'Zdeeef dd fd(d)Zdeeef dd fd*d+Zd,edd fd-d.Zedefd/d0Zede eeeef fd1d2Z!edefd3d4Z"edefd5d6Z#edefd7d8Z$edefd9d:Z%edefd;d<Z&edefd=d>Z'edefd?d@Z(edefdAdBZ)ede edCf fdDdEZ*edefdFdGZ+defdHdIZ,e-dJedd fdKdLZ.e-dMedNedOedd fdPdQZ/de eeef fdRdSZ0e-dTedd fdUdVZ1defdWdXZ2e-	dvdYe3e dZe3e4 d[e5dd fd\d]Z6e-d^ed_ed`edaedd f
dbdcZ7e-	dvdYe3e dZe3e4 d[e5dd fdddeZ8dfd d,edd fdgdhZ9dwdje:defdkdlZ;dudmdnZ<dudodpZ=dudqdrZ>defdsdtZ?  Z@S )x
Quaterniona  Base class to represent a Quaternion.

    A quaternion is a four dimensional vector representation of a rotation transformation in 3d.
    See more: https://en.wikipedia.org/wiki/Quaternion

    The general definition of a quaternion is given by:

    .. math::

        Q = a + b \cdot \mathbf{i} + c \cdot \mathbf{j} + d \cdot \mathbf{k}

    Thus, we represent a rotation quaternion as a contiguous tensor structure to
    perform rigid bodies transformations:

    .. math::

        Q = \begin{bmatrix} q_w & q_x & q_y & q_z \end{bmatrix}

    Example:
        >>> q = Quaternion.identity(batch_size=4)
        >>> q.data
        tensor([[1., 0., 0., 0.],
                [1., 0., 0., 0.],
                [1., 0., 0., 0.],
                [1., 0., 0., 0.]])
        >>> q.real
        tensor([1., 1., 1., 1.])
        >>> q.vec
        tensor([[0., 0., 0.],
                [0., 0., 0.],
                [0., 0., 0.],
                [0., 0., 0.]])

    _datadatareturnNc                    s4   t    t|ttfstdt| || _dS )a"  Construct a quaternion from tensor or parameter data.

        Args:
            data: tensor or parameter containing the quaternion data with the shape of :math:`(B, 4)`.

        Example:
            >>> # Create with tensor (no gradients tracked by default)
            >>> data = torch.tensor([1., 0., 0., 0.])
            >>> q1 = Quaternion(data)
            >>> # Create with parameter (gradients tracked)
            >>> param_data = torch.nn.Parameter(torch.tensor([1., 0., 0., 0.]))
            >>> q2 = Quaternion(param_data)

        z"Expected Tensor or Parameter, got N)super__init__
isinstancer   r
   	TypeErrortyper   )selfr   	__class__ N/home/ubuntu/.local/lib/python3.10/site-packages/kornia/geometry/quaternion.pyr   O   s   

zQuaternion.__init__argskwargsc                 O   s   t | jj|i |S )zMove and/or cast the quaternion data.

        Args:
            *args: Arguments to pass to tensor.to()
            **kwargs: Keyword arguments to pass to tensor.to()

        Returns:
            A new Quaternion with converted data.
        )r   r   to)r#   r(   r)   r&   r&   r'   r*   d   s   
zQuaternion.tovaluec              
   C   s  t |ttfrtj|| jj| jjd}nt |tjr%|j	| jj| jjd}zt
| jj|j}W n tyJ } ztd| jj d|j |d}~ww | j||| }| dkrb||}nt||j}t|djg |jdR  }tj|d|gdd}t|S )	aM  Convert a scalar, tensor, or numeric value to a scalar quaternion.

        A scalar quaternion has the form [real, 0, 0, 0] where real is the input value.

        Args:
            value: The scalar, tensor, or numeric value to convert.

        Returns:
            A Quaternion object representing the scalar quaternion.
        devicedtypezCannot broadcast shapes z and Nr      dim)r    intfloattorchr   r   r-   r.   r   r*   broadcast_shapesrealshapeRuntimeError
ValueErrorexpandr2   	expand_asbroadcast_to
zeros_like	unsqueezecatr   )r#   r+   target_shapeebroadcastedexpanded_valuezerosscalar_quat_datar&   r&   r'   _to_scalar_quaternionp   s"   "z Quaternion._to_scalar_quaternionc                 C   s   | j  S Nr   r#   r&   r&   r'   __repr__   s   zQuaternion.__repr__idxc                 C   s   t | j| S rH   r   r   )r#   rL   r&   r&   r'   __getitem__   s   zQuaternion.__getitem__c                 C   s   t | j S )zInverts the sign of the quaternion data.

        Example:
            >>> q = Quaternion.identity()
            >>> -q.data
            tensor([-1., -0., -0., -0.])

        rM   rJ   r&   r&   r'   __neg__   s   	zQuaternion.__neg__rightc                 C   s4   t |trt| j|j S | |}t| j|j S )aQ  Add a given quaternion, scalar, or tensor.

        Args:
            right: the quaternion, scalar, or tensor to add.

        Example:
            >>> q1 = Quaternion.identity()
            >>> q2 = Quaternion(tensor([2., 0., 1., 1.]))
            >>> q3 = q1 + q2
            >>> q3.data
            tensor([3., 0., 1., 1.])

        )r    r   r   rG   )r#   rP   
right_quatr&   r&   r'   __add__   s   

zQuaternion.__add__c                 C   sH   t |trt| j|j S | |}| j|j }t |tr |j}t|S )a[  Subtract a given quaternion, scalar, or tensor.

        Args:
            right: the quaternion, scalar, or tensor to subtract.

        Example:
            >>> q1 = Quaternion(tensor([2., 0., 1., 1.]))
            >>> q2 = Quaternion.identity()
            >>> q3 = q1 - q2
            >>> q3.data
            tensor([1., 0., 1., 1.])

        )r    r   r   rG   r
   )r#   rP   rQ   result_datar&   r&   r'   __sub__   s   


zQuaternion.__sub__c                 C   s   t |tr8| j|j t| j|j }| jd |j |jd | j  tjj| j|jdd }tt|d |fdS | 	|}| j|j t| j|j }| jd |j |jd | j  tjj| j|jdd }tt|d |fdS )N.Nr/   r1   )
r    r   r7   r   vecr5   linalgcrossr   rG   )r#   rP   new_realnew_vecrQ   r&   r&   r'   __mul__   s$   

zQuaternion.__mul__leftc                 C   sp   |  |}|j| j t|j| j }|jd | j | jd |j  tjj|j| jdd }tt|d |fdS )zDRight multiplication (left * self) where left is a scalar or tensor.rU   r/   r1   )	rG   r7   r   rV   r5   rW   rX   r   r   )r#   r\   	left_quatrY   rZ   r&   r&   r'   __rmul__   s   
zQuaternion.__rmul__c                 C   s   t |tr| |  S t |ttfrtj|| jj| jj	d}n|j
| jj| jj	d}| dkr@|| jd d| j}n	|d| j}| j| }t |trV|j}t|S )Nr,   r   .r   r/   )r    r   invr3   r4   r5   r   r   r-   r.   r*   r2   r<   r?   r
   )r#   rP   right_tensordivisorrS   r&   r&   r'   __div__   s   
 

zQuaternion.__div__c                 C   
   |  |S rH   )rc   )r#   rP   r&   r&   r'   __truediv__  s   
zQuaternion.__truediv__c                 C   s   |  |}||  S )z>Right addition (left + self) where left is a scalar or tensor.rG   r#   r\   r]   r&   r&   r'   __radd__     
zQuaternion.__radd__c                 C   s   |  |}||  S )zARight subtraction (left - self) where left is a scalar or tensor.rf   rg   r&   r&   r'   __rsub__  ri   zQuaternion.__rsub__c                 C   s   |  |}||  S z>Right division (left / self) where left is a scalar or tensor.rf   rg   r&   r&   r'   __rtruediv__  ri   zQuaternion.__rtruediv__c                 C   rd   rk   )rl   )r#   r\   r&   r&   r'   __rdiv__  s   
zQuaternion.__rdiv__tc                 C   sd   | j d }| jjddd}t|dk| j| | jd }||  }||  | }tt||fdS )zReturn the power of a quaternion raised to exponent t.

        Args:
            t: raised exponent.

        Example:
            >>> q = Quaternion(tensor([1., .5, 0., 0.]))
            >>> q_pow = q**2

        rU   r/   T)r2   keepdimr   )polar_anglerV   normr   cossinr   r   )r#   rn   thetavec_normnwxyzr&   r&   r'   __pow__#  s   
zQuaternion.__pow__c                 C      | j S )z5Return the underlying data with shape :math:`(B, 4)`.)r   rJ   r&   r&   r'   r   5  s   zQuaternion.datac                 C   s   | j | j| j| jfS )z>Return a tuple with the underlying coefficients in WXYZ order.)rw   xyzrJ   r&   r&   r'   coeffs:  s   zQuaternion.coeffsc                 C   rz   )zReturn the real part with shape :math:`(B,)`.

        Alias for
        :func: `~kornia.geometry.quaternion.Quaternion.w`
        )rw   rJ   r&   r&   r'   r7   ?     zQuaternion.realc                 C   s   | j dddf S )zDReturn the vector with the imaginary part with shape :math:`(B, 3)`..   NrI   rJ   r&   r&   r'   rV   H  s   zQuaternion.vecc                 C   rz   )zReturn the underlying data with shape :math:`(B, 4)`.

        Alias for :func:`~kornia.geometry.quaternion.Quaternion.data`
        rI   rJ   r&   r&   r'   qM  s   zQuaternion.qc                 C   rz   )zReturn a scalar with the real with shape :math:`(B,)`.

        Alias for
        :func: `~kornia.geometry.quaternion.Quaternion.w`
        )r7   rJ   r&   r&   r'   scalarU  r   zQuaternion.scalarc                 C   
   | j d S )z/Return the :math:`q_w` with shape :math:`(B,)`.r_   rI   rJ   r&   r&   r'   rw   ^     
zQuaternion.wc                 C   r   )z/Return the :math:`q_x` with shape :math:`(B,)`.).r   rI   rJ   r&   r&   r'   r{   c  r   zQuaternion.xc                 C   r   )z/Return the :math:`q_y` with shape :math:`(B,)`.).   rI   rJ   r&   r&   r'   r|   h  r   zQuaternion.yc                 C   r   )z/Return the :math:`q_z` with shape :math:`(B,)`.).r0   rI   rJ   r&   r&   r'   r}   m  r   zQuaternion.z.c                 C   s   t | jjS )zBReturn the shape of the underlying data with shape :math:`(B, 4)`.)tupler   r8   rJ   r&   r&   r'   r8   r  s   zQuaternion.shapec                 C   s   | j |    S )zReturn the polar angle with shape :math:`(B,1)`.

        Example:
            >>> q = Quaternion.identity()
            >>> q.polar_angle
            tensor(0.)

        )r   rq   acosrJ   r&   r&   r'   rp   w  s   
zQuaternion.polar_anglec                 C   
   t | jS )a%  Convert the quaternion to a rotation matrix of shape :math:`(B, 3, 3)`.

        Example:
            >>> q = Quaternion.identity()
            >>> m = q.matrix()
            >>> m
            tensor([[1., 0., 0.],
                    [0., 1., 0.],
                    [0., 0., 1.]])

        )r   r   rJ   r&   r&   r'   matrix  s   
zQuaternion.matrixr   c                 C      | t |S )a8  Create a quaternion from a rotation matrix.

        Args:
            matrix: the rotation matrix to convert of shape :math:`(B, 3, 3)`.

        Example:
            >>> m = torch.eye(3)[None]
            >>> q = Quaternion.from_matrix(m)
            >>> q.data
            tensor([[1., 0., 0., 0.]])

        )r   )clsr   r&   r&   r'   from_matrix     zQuaternion.from_matrixrollpitchyawc           	      C   s0   t |||d\}}}}t||||fd}| |S )a  Create a quaternion from Euler angles.

        Args:
            roll: the roll euler angle.
            pitch: the pitch euler angle.
            yaw: the yaw euler angle.

        Example:
            >>> roll, pitch, yaw = tensor(0), tensor(1), tensor(0)
            >>> q = Quaternion.from_euler(roll, pitch, yaw)
            >>> q.data
            tensor([0.8776, 0.0000, 0.4794, 0.0000])

        )r   r   r   r/   )r   r   )	r   r   r   r   rw   r{   r|   r}   r   r&   r&   r'   
from_euler  s   zQuaternion.from_eulerc                 C   s   t | j| j| j| jS )a[  Convert the quaternion to a triple of Euler angles (roll, pitch, yaw).

        Example:
            >>> q = Quaternion(tensor([2., 0., 1., 1.]))
            >>> roll, pitch, yaw = q.to_euler()
            >>> roll
            tensor(2.0344)
            >>> pitch
            tensor(1.5708)
            >>> yaw
            tensor(2.2143)

        )r   rw   r{   r|   r}   rJ   r&   r&   r'   to_euler  s   zQuaternion.to_euler
axis_anglec                 C   r   )ab  Create a quaternion from axis-angle representation.

        Args:
            axis_angle: rotation vector of shape :math:`(B, 3)`.

        Example:
            >>> axis_angle = torch.tensor([[1., 0., 0.]])
            >>> q = Quaternion.from_axis_angle(axis_angle)
            >>> q.data
            tensor([[0.8776, 0.4794, 0.0000, 0.0000]])

        )r   )r   r   r&   r&   r'   from_axis_angle  r   zQuaternion.from_axis_anglec                 C   r   )zConvert the quaternion to an axis-angle representation.

        Example:
            >>> q = Quaternion.identity()
            >>> axis_angle = q.to_axis_angle()
            >>> axis_angle
            tensor([0., 0., 0.])

        )r   r   rJ   r&   r&   r'   to_axis_angle  s   

zQuaternion.to_axis_angle
batch_sizer-   r.   c                 C   s.   t g d||d}|dur||d}| |S )aa  Create a quaternion representing an identity rotation.

        Args:
            batch_size: the batch size of the underlying data.
            device: device to place the result on.
            dtype: dtype of the result.

        Example:
            >>> q = Quaternion.identity()
            >>> q.data
            tensor([1., 0., 0., 0.])

        )      ?        r   r   r,   Nr   )r   repeat)r   r   r-   r.   r   r&   r&   r'   identity  s   zQuaternion.identityrw   r{   r|   r}   c                 C   s   | t ||||gS )a  Create a quaternion from the data coefficients.

        Args:
            w: a float representing the :math:`q_w` component.
            x: a float representing the :math:`q_x` component.
            y: a float representing the :math:`q_y` component.
            z: a float representing the :math:`q_z` component.

        Example:
            >>> q = Quaternion.from_coeffs(1., 0., 0., 0.)
            >>> q.data
            tensor([1., 0., 0., 0.])

        )r   )r   rw   r{   r|   r}   r&   r&   r'   from_coeffs  s   zQuaternion.from_coeffsc                 C   s   |dur|fnd}t dg|R ||d\}}}d|  dt |   }d|  dt |   }	| dt |   }
| dt |   }| t||	|
|fdS )a  Create a random unit quaternion of shape :math:`(B, 4)`.

        Uniformly distributed across the rotation space as per: http://planning.cs.uiuc.edu/node198.html

        Args:
            batch_size: the batch size of the underlying data.
            device: device to place the result on.
            dtype: dtype of the result.

        Example:
            >>> q = Quaternion.random()
            >>> q = Quaternion.random(batch_size=2)

        Nr&   r0   r,   r   r   r/   )r   sqrtr   rs   rr   r   )r   r   r-   r.   
rand_shaper1r2r3q1q2q3q4r&   r&   r'   random  s   zQuaternion.randomr   c                 C   s.   t |t |  }| }|| | |  S )a  Return a unit quaternion spherically interpolated between quaternions self.q and q1.

        See more: https://en.wikipedia.org/wiki/Slerp

        Args:
            q1: second quaternion to be interpolated between.
            t: interpolation ratio, range [0-1]

        Example:
            >>> q0 = Quaternion.identity()
            >>> q1 = Quaternion(torch.tensor([1., .5, 0., 0.]))
            >>> q2 = q0.slerp(q1, .3)

        )r   r   	normalizer`   )r#   r   rn   q0r&   r&   r'   slerp&  s   
zQuaternion.slerpFro   c                 C   s   | j dd|S )a8  Compute the norm (magnitude) of the quaternion.

        Args:
            keepdim: whether to retain the last dimension.

        Returns:
            The norm of the quaternion(s) as a tensor.

        Example:
            >>> q = Quaternion.identity()
            >>> q.norm()
            tensor(1.)

        r   r/   )r   rq   )r#   ro   r&   r&   r'   rq   :  s   zQuaternion.normc                 C   s   t t| jS )zReturn a normalized (unit) quaternion.

        Returns:
            The normalized quaternion.

        Example:
            >>> q = Quaternion(tensor([2., 1., 0., 0.]))
            >>> q_norm = q.normalize()

        )r   r   r   rJ   r&   r&   r'   r   L  s   zQuaternion.normalizec                 C   s   t t| jd | j fdS )zCompute the conjugate of the quaternion.

        Returns:
            The conjugate quaternion, with the vector part negated.

        Example:
            >>> q = Quaternion(tensor([1., 2., 3., 4.]))
            >>> q_conj = q.conj()

        rU   r/   )r   r   r7   rV   rJ   r&   r&   r'   conjY  s   zQuaternion.conjc                 C   s   |   |   S )zCompute the inverse of the quaternion.

        Returns:
            The inverse quaternion.

        Example:
            >>> q = Quaternion.identity()
            >>> q_inv = q.inv()

        )r   squared_normrJ   r&   r&   r'   r`   f  s   zQuaternion.invc                 C   s   t | j| j| jd  S )a  Compute the squared norm (magnitude) of the quaternion.

        Returns:
            The squared norm of the quaternion(s) as a tensor.

        Example:
            >>> q = Quaternion.identity()
            >>> q.squared_norm()
            tensor(1.)

        r   )r   rV   r7   rJ   r&   r&   r'   r   s  s   zQuaternion.squared_norm)r   r   )NNN)F)A__name__
__module____qualname____doc__r   r   r
   __annotations__r   r   r*   r4   rG   strrK   r3   slicerN   rO   rR   rT   r[   r^   rc   re   rh   rj   rl   rm   ry   propertyr   r   r~   r7   rV   r   r   rw   r{   r|   r}   r8   rp   r   classmethodr   r   r   r   r   r   r   r   r   r   r   r   boolrq   r   r   r`   r   __classcell__r&   r&   r$   r'   r   )   s   
 #(
 


r   Qrw   r   c                 C   s   | j }t| t |jd }|du r|j| | }n+|j|j|jd}| |kr4t	d|  d| ||
  }|jt| | }tj|\}}|ddt|f }||  }t|dS )aW  Compute (weighted) average of multiple quaternions.

    Args:
        Q (Quaternion): quaternion object containing data of shape (M, 4).
        w (torch.Tensor, optional): Weights of shape (M,). If None, uniform weights are used.


    Returns:
        Quaternion: averaged quaternion (shape (4,)), wrapped back in the Quaternion class.
    r   N)r.   zweights length z" must match number of quaternions )r   r   r   r8   Tr*   r-   r.   numelr:   sumr5   diagrW   eighargmaxrq   r?   )r   rw   r   MAeigenvalueseigenvectorsq_avgr&   r&   r'   average_quaternions  s   

r   rH   )!mathr   typingr   r   r   r   r5   kornia.corer   r   r	   r
   r   r   r   r   r   r   kornia.core.checkr   kornia.geometry.conversionsr   r   r   r   r   r   r   kornia.geometry.linalgr   r   r   r&   r&   r&   r'   <module>   s   0$	    "]