class_docstr: This class redefine the backpropagation of a quaternion linear layer
  (not a spinor layer). By doing so, we can save up to 4x memory, but it
  is also 2x slower than 'quaternion_linear_op'. It should be used
  within speechbrain.nnet.quaternion_networks.linear.QuaternionLinear.
class_docstr_tok: ["This","class","redefine","the","backpropagation","of","a","quaternion","linear","layer","(","not","a","spinor","layer",")",".","By","doing","so",",","we","can","save","up","to","4x","memory",",","but","it","is","also","2x","slower","than","'quaternion_linear_op","'",".","It","should","be","used","within","speechbrain.nnet.quaternion_networks.linear.QuaternionLinear","."]
docstr: Run the backward phase of the forward call defined above. This
  implementation follows the quaternion backpropagation of a quaternion
  layer that can be found in "Quaternion neural networks" - Parcollet T.
  Page 48.
  
  Arguments
  ---------
  input : torch.Tensor
      Quaternion input tensor to be transformed.
  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
docstr_tok: ["Run","the","backward","phase","of","the","forward","call","defined","above",".","This","implementation","follows","the","quaternion","backpropagation","of","a","quaternion","layer","that","can","be","found","in","``","Quaternion","neural","networks","''","-","Parcollet","T.","Page","48",".","Arguments","--","--","--","--","-","input",":","torch.Tensor","Quaternion","input","tensor","to","be","transformed",".","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"]
code: def backward(ctx, grad_output):
      """
      Run the backward phase of the forward call defined above. This
      implementation follows the quaternion backpropagation of a quaternion
      layer that can be found in "Quaternion neural networks" - Parcollet T.
      Page 48.
  
      Arguments
      ---------
      input : torch.Tensor
          Quaternion input tensor to be transformed.
      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
      """
      (
          input,
          r_weight,
          i_weight,
          j_weight,
          k_weight,
          bias,
      ) = ctx.saved_tensors
      grad_input = (
          grad_weight_r
      ) = (
          grad_weight_i
      ) = grad_weight_j = grad_weight_k = grad_bias = None
  
      input_r = torch.cat(
          [r_weight, -i_weight, -j_weight, -k_weight], dim=0
      )
      input_i = torch.cat(
          [i_weight, r_weight, -k_weight, j_weight], dim=0
      )
      input_j = torch.cat(
          [j_weight, k_weight, r_weight, -i_weight], dim=0
      )
      input_k = torch.cat(
          [k_weight, -j_weight, i_weight, r_weight], dim=0
      )
      cat_kernels_4_quaternion_T = Variable(
          torch.cat(
              [input_r, input_i, input_j, input_k], dim=1
          ).permute(1, 0),
          requires_grad=False,
      )
  
      nb_hidden = input.size()[-1]
      r = input.narrow(1, 0, nb_hidden // 4)
      i = input.narrow(1, nb_hidden // 4, nb_hidden // 4)
      j = input.narrow(1, nb_hidden // 2, nb_hidden // 4)
      k = input.narrow(1, nb_hidden - nb_hidden // 4, nb_hidden // 4)
      input_r = torch.cat([r, -i, -j, -k], dim=0)
      input_i = torch.cat([i, r, -k, j], dim=0)
      input_j = torch.cat([j, k, r, -i], dim=0)
      input_k = torch.cat([k, -j, i, r], dim=0)
      input_mat = Variable(
          torch.cat([input_r, input_i, input_j, input_k], dim=1),
          requires_grad=False,
      )
  
      nb_hidden = grad_output.size()[-1]
      r = grad_output.narrow(1, 0, nb_hidden // 4)
      i = grad_output.narrow(1, nb_hidden // 4, nb_hidden // 4)
      j = grad_output.narrow(1, nb_hidden // 2, nb_hidden // 4)
      k = grad_output.narrow(
          1, nb_hidden - nb_hidden // 4, nb_hidden // 4
      )
      input_r = torch.cat([r, i, j, k], dim=1)
      input_i = torch.cat([-i, r, k, -j], dim=1)
      input_j = torch.cat([-j, -k, r, i], dim=1)
      input_k = torch.cat([-k, j, -i, r], dim=1)
      grad_mat = torch.cat([input_r, input_i, input_j, input_k], dim=0)
  
      if ctx.needs_input_grad[0]:
          grad_input = grad_output.mm(cat_kernels_4_quaternion_T)
      if ctx.needs_input_grad[1]:
          grad_weight = (
              grad_mat.permute(1, 0).mm(input_mat).permute(1, 0)
          )
          unit_size_x = r_weight.size(0)
          unit_size_y = r_weight.size(1)
          grad_weight_r = grad_weight.narrow(0, 0, unit_size_x).narrow(
              1, 0, unit_size_y
          )
          grad_weight_i = grad_weight.narrow(0, 0, unit_size_x).narrow(
              1, unit_size_y, unit_size_y
          )
          grad_weight_j = grad_weight.narrow(0, 0, unit_size_x).narrow(
              1, unit_size_y * 2, unit_size_y
          )
          grad_weight_k = grad_weight.narrow(0, 0, unit_size_x).narrow(
              1, unit_size_y * 3, unit_size_y
          )
      if ctx.needs_input_grad[5]:
          grad_bias = grad_output.sum(0).squeeze(0)
  
      return (
          grad_input,
          grad_weight_r,
          grad_weight_i,
          grad_weight_j,
          grad_weight_k,
          grad_bias,
      )
  
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 2 for calling the QuaternionLinearCustomBackward obj's underlying member method code with example usage: obj.backward(ctx, grad_output) and returns: grad_input,  grad_weight_r,  grad_weight_i,  grad_weight_j,  grad_weight_k,  grad_bias