code: def fgm(self, x, labels, targeted=False):
      
      # Compute loss
      with tf.GradientTape() as tape:
        # input should be watched because it may be
        # combination of trainable and non-trainable variables
        tape.watch(x)
        loss_obj = LossCrossEntropy(self.model, smoothing=0.)
        loss = loss_obj.fprop(x=x, y=labels)
        if targeted:
          loss = -loss
  
      # Define gradient of loss wrt input
      grad = tape.gradient(loss, x)
      optimal_perturbation = attacks.optimize_linear(grad, self.eps, self.ord)
  
      # Add perturbation to original example to obtain adversarial example
      adv_x = x + optimal_perturbation
  
      # If clipping is needed
      # reset all values outside of [clip_min, clip_max]
      if (self.clip_min is not None) and (self.clip_max is not None):
        adv_x = tf.clip_by_value(adv_x, self.clip_min, self.clip_max)
      return adv_x
docs: TensorFlow Eager implementation of the Fast Gradient Method.
      :param x: the input variable
      :param targeted: Is the attack targeted or untargeted? Untargeted, the
                       default, will try to make the label incorrect.
                       Targeted will instead try to move in the direction
                       of being more like y.
      :return: a tensor for the adversarial example