docstr: Find PN for an input instance input_image e.g. celebA is shape (1, 224, 224, 3)
  
  Input:
      imgs (numpy.ndarry): images to be explained, of shape (num_images, size, size, channels)
      labs: one hot encoded vectors of target labels for PN, i.e. which labels
          to explain with an image of a different class
      latent (numpy.ndarry): image to be explained, of shape (1, size, size, channels)
          in the latent space
          
  Output: 
      adv_img (numpy.ndarry): the pertinent negative image
code: def attack(self, imgs, labs, latent):
      """
      Find PN for an input instance input_image e.g. celebA is shape (1, 224, 224, 3)
  
      Input:
          imgs (numpy.ndarry): images to be explained, of shape (num_images, size, size, channels)
          labs: one hot encoded vectors of target labels for PN, i.e. which labels
              to explain with an image of a different class
          latent (numpy.ndarry): image to be explained, of shape (1, size, size, channels)
              in the latent space
  
      Output:
          adv_img (numpy.ndarry): the pertinent negative image
      """
  
      def compare(x, y):
          if not isinstance(x, (float, int, np.int64)):
              x = np.copy(x)
              # x[y] -= self.kappa if self.PP else -self.kappa
              if self.mode == "PP":
                  x[y] -= self.kappa
              elif self.mode == "PN":
                  x[y] += self.kappa
              x = np.argmax(x)
          if self.mode == "PP":
              return x == y
          else:
              return x != y
  
      batch_size = self.batch_size
  
      # set the lower and upper bounds accordingly
      Const_LB = np.zeros(batch_size)
      CONST = np.ones(batch_size) * self.init_const
      Const_UB = np.ones(batch_size) * 1e10
      # the best l2, score, and image attack
      overall_best_dist = [1e10] * batch_size
      overall_best_attack = [np.zeros(imgs[0].shape)] * batch_size
  
      for binary_search_steps_idx in range(self.BINARY_SEARCH_STEPS):
          # completely reset adam's internal state.
          self.sess.run(self.init)
          img_batch = imgs[:batch_size]
          label_batch = labs[:batch_size]
  
          current_step_best_dist = [1e10] * batch_size
          current_step_best_score = [-1] * batch_size
  
          # set the variables so that we don't have to send them over again
          self.sess.run(
              self.setup,
              {
                  self.assign_orig_img: img_batch,
                  self.assign_target_lab: label_batch,
                  self.assign_const: CONST,
                  self.assign_adv_latent: latent,
                  self.assign_orig_latent: latent,
              },
          )
  
          for iteration in range(self.MAX_ITERATIONS):
              # perform the attack
  
              self.sess.run([self.train])
              temp_adv_latent = self.sess.run(self.adv_latent)
              self.sess.run(
                  self.adv_updater,
                  feed_dict={self.assign_adv_latent: temp_adv_latent},
              )
  
              Loss_Overall, OutputScore, adv_img = self.sess.run(
                  [
                      self.Loss_Overall,
                      self.ImgToEnforceLabel_Score,
                      self.adv_img,
                  ]
              )
              (
                  Loss_Attack,
                  Loss_Latent_L2Dist,
                  Loss_Img_L2Dist,
                  Loss_attr,
              ) = self.sess.run(
                  [
                      self.Loss_Attack,
                      self.Loss_Latent_L2Dist,
                      self.Loss_Img_L2Dist,
                      self.attr_score,
                  ]
              )
              (
                  target_lab_score,
                  max_nontarget_lab_score_s,
              ) = self.sess.run(
                  [self.target_lab_score, self.max_nontarget_lab_score]
              )
  
              # if iteration%(self.MAX_ITERATIONS//10) == 0:
              if iteration % 10 == 0:
                  print("iter:{} const:{}".format(iteration, CONST))
                  print(
                      "Loss_Overall:{:.4f}, Loss_Attack:{:.4f}, Loss_attr:{:.4f}".format(
                          Loss_Overall, Loss_Attack, Loss_attr
                      )
                  )
                  print(
                      "Loss_Latent_L2Dist:{:.4f}, Loss_Img_L2Dist:{:.4f}".format(
                          Loss_Latent_L2Dist, Loss_Img_L2Dist
                      )
                  )
                  print(
                      "target_lab_score:{:.4f}, max_nontarget_lab_score:{:.4f}".format(
                          target_lab_score[0],
                          max_nontarget_lab_score_s[0],
                      )
                  )
                  print("")
                  sys.stdout.flush()
  
              for batch_idx, (
                  the_dist,
                  the_score,
                  the_adv_img,
              ) in enumerate(zip([Loss_Overall], OutputScore, adv_img)):
                  if the_dist < current_step_best_dist[
                      batch_idx
                  ] and compare(
                      the_score, np.argmax(label_batch[batch_idx])
                  ):
                      current_step_best_dist[batch_idx] = the_dist
                      current_step_best_score[batch_idx] = np.argmax(
                          the_score
                      )
                  if the_dist < overall_best_dist[
                      batch_idx
                  ] and compare(
                      the_score, np.argmax(label_batch[batch_idx])
                  ):
                      overall_best_dist[batch_idx] = the_dist
                      overall_best_attack[batch_idx] = the_adv_img
  
          # adjust the constant as needed
          for batch_idx in range(batch_size):
              if (
                  compare(
                      current_step_best_score[batch_idx],
                      np.argmax(label_batch[batch_idx]),
                  )
                  and current_step_best_score[batch_idx] != -1
              ):
                  # success, divide const by two
                  Const_UB[batch_idx] = min(
                      Const_UB[batch_idx], CONST[batch_idx]
                  )
                  if Const_UB[batch_idx] < 1e9:
                      CONST[batch_idx] = (
                          Const_LB[batch_idx] + Const_UB[batch_idx]
                      ) / 2
              else:
                  # failure, either multiply by 10 if no solution found yet
                  #          or do binary search with the known upper bound
                  Const_LB[batch_idx] = max(
                      Const_LB[batch_idx], CONST[batch_idx]
                  )
                  if Const_UB[batch_idx] < 1e9:
                      CONST[batch_idx] = (
                          Const_LB[batch_idx] + Const_UB[batch_idx]
                      ) / 2
                  else:
                      CONST[batch_idx] *= 10
  
      # return the best solution found
      overall_best_attack = overall_best_attack[0]
      return overall_best_attack.reshape(
          (1,) + overall_best_attack.shape
      )
  
file_path: AIX360/aix360/algorithms/contrastive/CEM_MAF_aen_PN.py
imports: import sys
  import tensorflow
  import numpy
  import pickle
  from tensorflow.contrib.keras.api.keras.models import Model, Sequential, model_from_json
  from tensorflow.contrib.keras.api.keras.callbacks import ModelCheckpoint
  import os
label_desc: Use image node_id 2 for calling the AEADEN obj's underlying member method code with example usage: obj.attack(imgs, labs, latent) and returns: overall_best_attack, unknown, unknown