code: def quaternion_init(
      in_features, out_features, kernel_size=None, criterion="glorot"
  ):
      """Returns a matrix of quaternion numbers initialized with the method
      described in "Quaternion Recurrent Neural Network " - Parcollt T.
  
      Arguments
      ---------
      in_features : int
          Number of real values of the input layer (quaternion // 4).
      out_features : int
          Number of real values of the output layer (quaternion // 4).
      kernel_size : int
          Kernel_size for convolutional layers (ex: (3,3)).
      criterion : str
          (glorot, he)
      """
  
      # We set the numpy seed equal to the torch seed for reproducibility
      # Indeed we use numpy and scipy here. We need % (2**31-1) or, if the
      # seed hasn't been set by the used in the YAML file, torch will generate
      # a double that would be to big for numpy.
      np.random.seed(seed=torch.initial_seed() % (2**31 - 1))
  
      if kernel_size is not None:
          receptive_field = np.prod(kernel_size)
          fan_in = in_features * receptive_field
          fan_out = out_features * receptive_field
      else:
          fan_in = in_features
          fan_out = out_features
  
      if criterion == "glorot":
          s = 1.0 / np.sqrt(2 * (fan_in + fan_out))
      else:
          s = 1.0 / np.sqrt(2 * fan_in)
  
      # Generating randoms and purely imaginary quaternions :
      if kernel_size is None:
          kernel_shape = (in_features, out_features)
      else:
          if type(kernel_size) is int:
              kernel_shape = (out_features, in_features) + tuple(
                  (kernel_size,)
              )
          else:
              kernel_shape = (out_features, in_features) + (
                  *kernel_size,
              )
  
      modulus = torch.from_numpy(
          chi.rvs(4, loc=0, scale=s, size=kernel_shape)
      )
      number_of_weights = np.prod(kernel_shape)
      v_i = torch.FloatTensor(number_of_weights).uniform_(-1, 1)
      v_j = torch.FloatTensor(number_of_weights).uniform_(-1, 1)
      v_k = torch.FloatTensor(number_of_weights).uniform_(-1, 1)
  
      # Purely imaginary quaternions unitary
      for i in range(0, number_of_weights):
          norm = (
              torch.sqrt(v_i[i] ** 2 + v_j[i] ** 2 + v_k[i] ** 2)
              + 0.0001
          )
          v_i[i] /= norm
          v_j[i] /= norm
          v_k[i] /= norm
      v_i = v_i.reshape(kernel_shape)
      v_j = v_j.reshape(kernel_shape)
      v_k = v_k.reshape(kernel_shape)
  
      phase = torch.rand(kernel_shape).uniform_(-math.pi, math.pi)
  
      weight_r = modulus * torch.cos(phase)
      weight_i = modulus * v_i * torch.sin(phase)
      weight_j = modulus * v_j * torch.sin(phase)
      weight_k = modulus * v_k * torch.sin(phase)
  
      return (weight_r, weight_i, weight_j, weight_k)
  
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 5 for calling a global function with example usage: quaternion_init(in_features, out_features, kernel_size, criterion) and returns: weight_r,  weight_i,  weight_j,  weight_k