class_docstr: FastAP - autograd function definition
  
  This class implements the FastAP loss from the following paper:
  "Deep Metric Learning to Rank",
  F. Cakir, K. He, X. Xia, B. Kulis, S. Sclaroff. CVPR 2019
  
  NOTE:
      Given a input batch, FastAP does not sample triplets from it as it's not
      a triplet-based method. Therefore, FastAP does not take a Sampler as input.
      Rather, we specify how the input batch is selected.
class_docstr_tok: ["FastAP","-","autograd","function","definition","This","class","implements","the","FastAP","loss","from","the","following","paper",":","''","Deep","Metric","Learning","to","Rank","''",",","F.","Cakir",",","K.","He",",","X.","Xia",",","B.","Kulis",",","S.","Sclaroff",".","CVPR","2019","NOTE",":","Given","a","input","batch",",","FastAP","does","not","sample","triplets","from","it","as","it","'s","not","a","triplet-based","method",".","Therefore",",","FastAP","does","not","take","a","Sampler","as","input",".","Rather",",","we","specify","how","the","input","batch","is","selected","."]
code: def backward(ctx, grad_output):
      input, target = ctx.saved_tensors
  
      Z = Variable(ctx.Z, requires_grad=False)
      Delta = Variable(ctx.Delta, requires_grad=False)
      dist2 = Variable(ctx.dist2, requires_grad=False)
      I_pos = Variable(ctx.I_pos, requires_grad=False)
      I_neg = Variable(ctx.I_neg, requires_grad=False)
      h = Variable(ctx.h, requires_grad=False)  # noqa: F841
      H = Variable(ctx.H, requires_grad=False)
      h_pos = Variable(ctx.h_pos, requires_grad=False)
      h_neg = Variable(ctx.h_neg, requires_grad=False)  # noqa: F841
      H_pos = Variable(ctx.H_pos, requires_grad=False)
      N_pos = Variable(ctx.N_pos, requires_grad=False)
  
      L = Z.size()[0]
      H2 = torch.pow(H, 2)
      H_neg = H - H_pos
  
      # 1. d(FastAP)/d(h+)
      LTM1 = torch.tril(torch.ones(L, L), -1)  # lower traingular matrix
      tmp1 = h_pos * H_neg / H2
      tmp1[torch.isnan(tmp1)] = 0
  
      d_AP_h_pos = (H_pos * H + h_pos * H_neg) / H2
      d_AP_h_pos = d_AP_h_pos + torch.mm(tmp1, LTM1.cuda())
      d_AP_h_pos = d_AP_h_pos / N_pos.repeat(L, 1).t()
      d_AP_h_pos[torch.isnan(d_AP_h_pos) | torch.isinf(d_AP_h_pos)] = 0
  
      # 2. d(FastAP)/d(h-)
      LTM0 = torch.tril(torch.ones(L, L), 0)  # lower triangular matrix
      tmp2 = -h_pos * H_pos / H2
      tmp2[torch.isnan(tmp2)] = 0
  
      d_AP_h_neg = torch.mm(tmp2, LTM0.cuda())
      d_AP_h_neg = d_AP_h_neg / N_pos.repeat(L, 1).t()
      d_AP_h_neg[torch.isnan(d_AP_h_neg) | torch.isinf(d_AP_h_neg)] = 0
  
      # 3. d(FastAP)/d(embedding)
      d_AP_x = 0
      for l in range(L):
          dpulse = dSoftBinning(dist2, Z[l], Delta)
          dpulse[torch.isnan(dpulse) | torch.isinf(dpulse)] = 0
          ddp = dpulse * I_pos
          ddn = dpulse * I_neg
  
          alpha_p = torch.diag(d_AP_h_pos[:, l])  # N*N
          alpha_n = torch.diag(d_AP_h_neg[:, l])
          Ap = torch.mm(ddp, alpha_p) + torch.mm(alpha_p, ddp)
          An = torch.mm(ddn, alpha_n) + torch.mm(alpha_n, ddn)
  
          # accumulate gradient
          d_AP_x = d_AP_x - torch.mm(input.t(), (Ap + An))
  
      grad_input = -d_AP_x
      return grad_input.t(), None, None
  
file_path: pytorch-metric-learning/tests/losses/test_fastap_loss.py
imports: import torch
  from torch.autograd import Variable
  import unittest
  from pytorch_metric_learning.losses import FastAPLoss
  from ..None import TEST_DEVICE, TEST_DTYPES
  from ..zzz_testing_utils.testing_utils import angle_to_coord
label_desc: Use image node_id 2 for calling the OriginalImplementationFastAP obj's underlying member method code with example usage: obj.backward(ctx, grad_output) and returns: grad_input,  None,  None