code: def quaternion_conv_rotation_op(
      input,
      r_weight,
      i_weight,
      j_weight,
      k_weight,
      bias,
      scale,
      zero_kernel,
      stride: int,
      padding: int,
      groups: int,
      dilation: int,
      conv1d: bool,
  ):
      """
      Applies a quaternion rotation transformation to the incoming data:
      The rotation W*x*W^t can be replaced by R*x following:
      https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
      Works for unitary and non-unitary weights (they will be normalized).
      The initial size of the input must be a multiple of 4 with the real part
      equal to zero. Rotations only affect the vector part of a quaternion.
  
      Arguments
      ---------
      input : torch.Tensor
          Quaternion input tensor to be transformed.
      conv1d : bool
          If true, a 1D convolution operation will be applied. Otherwise, a 2D
          convolution is called.
      r_weight : torch.Parameter
          Real part of the quaternion weight matrix of this layer.
      i_weight : torch.Parameter
          First imaginary part of the quaternion weight matrix of this layer.
      j_weight : torch.Parameter
          Second imaginary part of the quaternion weight matrix of this layer.
      k_weight : torch.Parameter
          Third imaginary part of the quaternion weight matrix of this layer.
      bias : torch.Parameter
      scale : torch.Parameter
          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 scale
          parameters are learnable parameters that acts like gates by multiplying
          the output vector with a small trainable parameter.
      zero_kernel : torch.Parameter
          The zero kernel is simply a tensor of zeros with require grad = False.
          Its shape is equivalent to a quaternion component shape. In fact,
          it is only needed to make the dimensions match when using the rotation
          matrix : https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
      """
  
      square_r = r_weight * r_weight
      square_i = i_weight * i_weight
      square_j = j_weight * j_weight
      square_k = k_weight * k_weight
  
      norm = torch.sqrt(
          square_r + square_i + square_j + square_k + 0.0001
      )
  
      r_n_weight = r_weight / norm
      i_n_weight = i_weight / norm
      j_n_weight = j_weight / norm
      k_n_weight = k_weight / norm
  
      norm_factor = 2.0
  
      square_i = norm_factor * (i_n_weight * i_n_weight)
      square_j = norm_factor * (j_n_weight * j_n_weight)
      square_k = norm_factor * (k_n_weight * k_n_weight)
  
      ri = norm_factor * r_n_weight * i_n_weight
      rj = norm_factor * r_n_weight * j_n_weight
      rk = norm_factor * r_n_weight * k_n_weight
  
      ij = norm_factor * i_n_weight * j_n_weight
      ik = norm_factor * i_n_weight * k_n_weight
  
      jk = norm_factor * j_n_weight * k_n_weight
  
      if scale.requires_grad:
          rot_kernel_1 = torch.cat(
              [
                  zero_kernel,
                  scale * (1.0 - (square_j + square_k)),
                  scale * (ij - rk),
                  scale * (ik + rj),
              ],
              dim=1,
          )
          rot_kernel_2 = torch.cat(
              [
                  zero_kernel,
                  scale * (ij + rk),
                  scale * (1.0 - (square_i + square_k)),
                  scale * (jk - ri),
              ],
              dim=1,
          )
          rot_kernel_3 = torch.cat(
              [
                  zero_kernel,
                  scale * (ik - rj),
                  scale * (jk + ri),
                  scale * (1.0 - (square_i + square_j)),
              ],
              dim=1,
          )
      else:
          rot_kernel_1 = torch.cat(
              [
                  zero_kernel,
                  (1.0 - (square_j + square_k)),
                  (ij - rk),
                  (ik + rj),
              ],
              dim=1,
          )
          rot_kernel_2 = torch.cat(
              [
                  zero_kernel,
                  (ij + rk),
                  (1.0 - (square_i + square_k)),
                  (jk - ri),
              ],
              dim=1,
          )
          rot_kernel_3 = torch.cat(
              [
                  zero_kernel,
                  (ik - rj),
                  (jk + ri),
                  (1.0 - (square_i + square_j)),
              ],
              dim=1,
          )
  
      zero_kernel2 = torch.cat(
          [zero_kernel, zero_kernel, zero_kernel, zero_kernel], dim=1
      )
      global_rot_kernel = torch.cat(
          [zero_kernel2, rot_kernel_1, rot_kernel_2, rot_kernel_3],
          dim=0,
      )
  
      if conv1d:
          return F.conv1d(
              input=input,
              weight=global_rot_kernel,
              bias=bias,
              stride=stride,
              padding=padding,
              dilation=dilation,
              groups=groups,
          )
      else:
          return F.conv2d(
              input=input,
              weight=global_rot_kernel,
              bias=bias,
              stride=stride,
              padding=padding,
              dilation=dilation,
              groups=groups,
          )
  
file_path: speechbrain/speechbrain/nnet/quaternion_networks/q_ops.py
imports: import torch
  import math
  import numpy
  import torch.nn.functional
  from scipy.stats import chi
  from torch.autograd import Variable
label_desc: Use image node_id 3 for calling a global function with example usage: quaternion_conv_rotation_op(input, r_weight, i_weight, j_weight, k_weight, bias, scale, zero_kernel, stride, padding, groups, dilation, conv1d) and returns: F, F