class_docstr: Fast Adaptive Boundary Attack (Linf, L2, L1)
  https://arxiv.org/abs/1907.02044
  
  :param predict:       forward pass function
  :param norm:          Lp-norm to minimize ('Linf', 'L2', 'L1' supported)
  :param n_restarts:    number of random restarts
  :param n_iter:        number of iterations
  :param eps:           epsilon for the random restarts
  :param alpha_max:     alpha_max
  :param eta:           overshooting
  :param beta:          backward step
  :param device:        device to use ('cuda' or 'cpu')
code: def projection_l2(
      self, points_to_project, w_hyperplane, b_hyperplane
  ):
      t = points_to_project.clone()
      w = w_hyperplane.clone()
      b = b_hyperplane.clone()
  
      c = (w * t).sum(1) - b
      ind2 = (c < 0).nonzero().squeeze()
      ind2 = self.check_shape(ind2)
      w[ind2] *= -1
      c[ind2] *= -1
  
      u = torch.arange(0, w.shape[0]).unsqueeze(1)
  
      r = torch.max(t / w, (t - 1) / w)
      u2 = torch.ones(r.shape).to(self.device)
      r = torch.min(r, 1e12 * u2)
      r = torch.max(r, -1e12 * u2)
      r[w.abs() < 1e-8] = 1e12
      r[r == -1e12] = -r[r == -1e12]
      rs, indr = torch.sort(r, dim=1)
      rs2 = torch.cat(
          (rs[:, 1:], torch.zeros(rs.shape[0], 1).to(self.device)), 1
      )
      rs[rs == 1e12] = 0
      rs2[rs2 == 1e12] = 0
  
      w3 = w**2
      w3s = w3[u, indr]
      w5 = w3s.sum(dim=1, keepdim=True)
      ws = w5 - torch.cumsum(w3s, dim=1)
      d = -(r * w).clone()
      d = d * (w.abs() > 1e-8).float()
      s = torch.cat(
          (
              (-w5.squeeze() * rs[:, 0]).unsqueeze(1),
              torch.cumsum((-rs2 + rs) * ws, dim=1)
              - w5 * rs[:, 0].unsqueeze(-1),
          ),
          1,
      )
  
      c4 = s[:, 0] + c < 0
      c3 = (d * w).sum(dim=1) + c > 0
      c6 = c4.nonzero().squeeze()
      c2 = ((1 - c4.float()) * (1 - c3.float())).nonzero().squeeze()
      c6 = self.check_shape(c6)
      c2 = self.check_shape(c2)
  
      counter = 0
      lb = torch.zeros(c2.shape[0])
      ub = torch.ones(c2.shape[0]) * (w.shape[1] - 1)
      nitermax = torch.ceil(
          torch.log2(torch.tensor(w.shape[1]).float())
      )
      counter2 = torch.zeros(lb.shape).long()
  
      while counter < nitermax:
          counter4 = torch.floor((lb + ub) / 2)
          counter2 = counter4.long()
          c3 = s[c2, counter2] + c[c2] > 0
          ind3 = c3.nonzero().squeeze()
          ind32 = (~c3).nonzero().squeeze()
          ind3 = self.check_shape(ind3)
          ind32 = self.check_shape(ind32)
          lb[ind3] = counter4[ind3]
          ub[ind32] = counter4[ind32]
          counter += 1
  
      lb = lb.long()
      alpha = torch.zeros([1])
  
      if c6.nelement() != 0:
          alpha = c[c6] / w5[c6].squeeze(-1)
          d[c6] = -alpha.unsqueeze(-1) * w[c6]
  
      if c2.nelement() != 0:
          alpha = (s[c2, lb] + c[c2]) / ws[c2, lb] + rs[c2, lb]
          if torch.sum(ws[c2, lb] == 0) > 0:
              ind = (ws[c2, lb] == 0).nonzero().squeeze().long()
              ind = self.check_shape(ind)
              alpha[ind] = 0
          c5 = (alpha.unsqueeze(-1) > r[c2]).float()
          d[c2] = d[c2] * c5 - alpha.unsqueeze(-1) * w[c2] * (1 - c5)
  
      return d * (w.abs() > 1e-8).float()
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  import torch
  import time
  from advertorch.utils import replicate_input
  from advertorch.attacks.utils import zero_gradients
  from .base import Attack
  from .base import LabelMixin
label_desc: Use image node_id 5 for calling the FABAttack obj's underlying member method code with example usage: obj.projection_l2(points_to_project, w_hyperplane, b_hyperplane) and returns: unknown