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):
early_stop = None
else:
early_stop = min(epsilons)
limit_epsilons = [eps if eps is not None else ep.inf for eps in epsilons]
del epsilons
// run the actual attack
xp = self.run(model, x, criterion, early_stop=early_stop, **kwargs)
// TODO: optionally improve using a binary search?
// TODO: optionally reduce size to the different epsilons and recompute is_adv
is_adv = is_adversarial(xp)
assert is_adv.shape == (N,)
distances = self.distance(x, xp)
assert distances.shape == (N,)
in_limits = ep.stack(
[distances <= epsilon for epsilon in limit_epsilons], axis=0
)
assert in_limits.shape == (K, N)
success = ep.logical_and(in_limits, is_adv)
assert success.shape == (K, N)
xp_ = restore_type(xp)
After Change
xpcs.append(xpc)
success.append(is_adv)
success_ = ep.stack(success)
assert success_.shape == (K, N)
xp_ = restore_type(xp)
xpcs_ = [restore_type(xpc) for xpc in xpcs]