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_linf(
      self, points_to_project, w_hyperplane, b_hyperplane
  ):
      t = points_to_project.clone()
      w = w_hyperplane.clone()
      b = b_hyperplane.clone()
  
      ind2 = ((w * t).sum(1) - b < 0).nonzero().squeeze()
      ind2 = self.check_shape(ind2)
      w[ind2] *= -1
      b[ind2] *= -1
  
      c5 = (w < 0).float()
      a = torch.ones(t.shape).to(self.device)
      d = (a * c5 - t) * (w != 0).float()
      a -= a * (1 - c5)
  
      p = torch.ones(t.shape).to(self.device) * c5 - t * (2 * c5 - 1)
      _, indp = torch.sort(p, dim=1)
  
      b = b - (w * t).sum(1)
      b0 = (w * d).sum(1)
      b1 = b0.clone()
  
      counter = 0
      indp2 = flip(indp.unsqueeze(-1), dims=(1, 2)).squeeze()
      u = torch.arange(0, w.shape[0])
      ws = w[u.unsqueeze(1), indp2]
      bs2 = -ws * d[u.unsqueeze(1), indp2]
  
      s = torch.cumsum(ws.abs(), dim=1)
      sb = torch.cumsum(bs2, dim=1) + b0.unsqueeze(1)
  
      c = b - b1 > 0
      b2 = sb[u, -1] - s[u, -1] * p[u, indp[u, 0]]
      c_l = (b - b2 > 0).nonzero().squeeze()
      c2 = ((b - b1 > 0) * (b - b2 <= 0)).nonzero().squeeze()
      c_l = self.check_shape(c_l)
      c2 = self.check_shape(c2)
  
      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()
          indcurr = indp[c2, -counter2 - 1]
          b2 = sb[c2, counter2] - s[c2, counter2] * p[c2, indcurr]
          c = b[c2] - b2 > 0
          ind3 = c.nonzero().squeeze()
          ind32 = (~c).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()
      counter2 = 0
  
      if c_l.nelement() != 0:
          lmbd_opt = (
              torch.max(
                  (b[c_l] - sb[c_l, -1]) / (-s[c_l, -1]),
                  torch.zeros(sb[c_l, -1].shape).to(self.device),
              )
          ).unsqueeze(-1)
          d[c_l] = (2 * a[c_l] - 1) * lmbd_opt
  
      lmbd_opt = (
          torch.max(
              (b[c2] - sb[c2, lb]) / (-s[c2, lb]),
              torch.zeros(sb[c2, lb].shape).to(self.device),
          )
      ).unsqueeze(-1)
      d[c2] = torch.min(lmbd_opt, d[c2]) * c5[c2] + torch.max(
          -lmbd_opt, d[c2]
      ) * (1 - c5[c2])
  
      return d * (w != 0).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 4 for calling the FABAttack obj's underlying member method code with example usage: obj.projection_linf(points_to_project, w_hyperplane, b_hyperplane) and returns: unknown