class_docstr: Fixed-epsilon attacks try to find adversarials whose perturbation sizes
  are limited by a fixed epsilon
class_docstr_tok: ["Fixed-epsilon","attacks","try","to","find","adversarials","whose","perturbation","sizes","are","limited","by","a","fixed","epsilon"]
code: def __call__(
      self,
      model: Model,
      inputs: T,
      criterion: Any,
      *,
      epsilons: Union[Sequence[Union[float, None]], float, None],
      **kwargs: Any,
  ) -> Union[Tuple[List[T], List[T], T], Tuple[T, T, T]]:
      x, restore_type = ep.astensor_(inputs)
      del inputs
  
      verify_input_bounds(x, model)
  
      criterion = get_criterion(criterion)
      is_adversarial = get_is_adversarial(criterion, model)
  
      was_iterable = True
      if not isinstance(epsilons, Iterable):
          epsilons = [epsilons]
          was_iterable = False
  
      N = len(x)
      K = len(epsilons)
  
      # None means: just minimize, no early stopping, no limit on the perturbation size
      if any(eps is None for eps in epsilons):
          # TODO: implement a binary search
          raise NotImplementedError(
              "FixedEpsilonAttack subclasses do not yet support None in epsilons"
          )
      real_epsilons = [eps for eps in epsilons if eps is not None]
      del epsilons
  
      xps = []
      xpcs = []
      success = []
      for epsilon in real_epsilons:
          xp = self.run(model, x, criterion, epsilon=epsilon, **kwargs)
  
          # clip to epsilon because we don't really know what the attack returns;
          # alternatively, we could check if the perturbation is at most epsilon,
          # but then we would need to handle numerical violations;
          xpc = self.distance.clip_perturbation(x, xp, epsilon)
          is_adv = is_adversarial(xpc)
  
          xps.append(xp)
          xpcs.append(xpc)
          success.append(is_adv)
  
      # # TODO: the correction we apply here should make sure that the limits
      # # are not violated, but this is a hack and we need a better solution
      # # Alternatively, maybe can just enforce the limits in __call__
      # xps = [
      #     self.run(model, x, criterion, epsilon=epsilon, **kwargs)
      #     for epsilon in real_epsilons
      # ]
  
      # is_adv = ep.stack([is_adversarial(xp) for xp in xps])
      # assert is_adv.shape == (K, N)
  
      # in_limits = ep.stack(
      #     [
      #         self.distance(x, xp) <= epsilon
      #         for xp, epsilon in zip(xps, real_epsilons)
      #     ],
      # )
      # assert in_limits.shape == (K, N)
  
      # if not in_limits.all():
      #     # TODO handle (numerical) violations
      #     # warn user if run() violated the epsilon constraint
      #     import pdb
  
      #     pdb.set_trace()
  
      # success = ep.logical_and(in_limits, is_adv)
      # assert success.shape == (K, N)
  
      success_ = ep.stack(success)
      assert success_.shape == (K, N)
  
      xps_ = [restore_type(xp) for xp in xps]
      xpcs_ = [restore_type(xpc) for xpc in xpcs]
  
      if was_iterable:
          return xps_, xpcs_, restore_type(success_)
      else:
          assert len(xps_) == 1
          assert len(xpcs_) == 1
          return (
              xps_[0],
              xpcs_[0],
              restore_type(success_.squeeze(axis=0)),
          )
  
imports: from typing import Callable, TypeVar, Any, Union, Optional, Sequence, List, Tuple, Dict
  from typing_extensions import final, overload
  from abc import ABC, abstractmethod
  from collections.abc import Iterable
  import eagerpy
  from ..models import Model
  from ..criteria import Criterion
  from ..criteria import Misclassification
  from ..devutils import atleast_kd
  from ..distances import Distance
label_desc: Use image node_id 4 for calling the FixedEpsilonAttack obj's underlying member method code with example usage: obj.__call__(model, inputs, criterion) and returns: xps_,  xpcs_,  restore_type, xps_,  xpcs_,  restore_type