def _generate_batch(self, x: "tf.Tensor", y: "tf.Tensor") -> "tf.Tensor":
        """
        Generate adversarial batch.

        :param x: Source samples.
        :param y: Guide samples.
        :return: Batch of adversarial examples.
        """
        import tensorflow as tf

        def loss_fn(source_orig, source_adv, guide):
            representation_loss = tf.zeros(shape=(source_orig.shape[0],), dtype=tf.float32)
            for layer_i in self.layer:
                adv_representation = self.estimator.get_activations(source_adv, layer_i, self.batch_size, True)
                guide_representation = self.estimator.get_activations(guide, layer_i, self.batch_size, True)

                axis = tuple(range(1, len(source_adv.shape)))
                soft_constraint = tf.cast(tf.math.reduce_max(tf.abs(source_adv - source_orig), axis=axis), tf.float32)

                axis = tuple(range(1, len(adv_representation.shape)))
                representation_loss += tf.reduce_sum(tf.square(adv_representation - guide_representation), axis=axis)

            loss = tf.math.reduce_mean(representation_loss + self.lambda_ * soft_constraint)
            return loss

        adv = tf.identity(x)
        if self.random_start:
            # Starting at a uniformly random point
            adv = adv + tf.random.uniform(x.shape, -self.delta, self.delta)
            if self.estimator.clip_values is not None:
                adv = tf.clip_by_value(adv, *self.estimator.clip_values)

        if self.optimizer is None:
            # run a plain-vanilla PGD
            for _ in trange(self.max_iter, desc="Feature Adversaries TensorFlow v2", disable=not self.verbose):
                with tf.GradientTape(watch_accessed_variables=False) as tape:
                    tape.watch(adv)
                    loss = loss_fn(x, adv, y)
                gradient = tape.gradient(loss, adv)

                # pgd step
                adv = adv - tf.math.sign(gradient) * self.step_size
                perturbation = tf.clip_by_value(adv - x, -self.delta, self.delta)
                adv = x + perturbation
                if self.estimator.clip_values is not None:
                    adv = tf.clip_by_value(x + perturbation, *self.estimator.clip_values)
        else:
            # optimize soft constraint problem with chosen optimizer
            opt = self.optimizer(**self._optimizer_kwargs)  # type: ignore
            perturbation = tf.Variable(
                tf.zeros_like(adv),
                trainable=True,
                constraint=lambda x: tf.clip_by_value(x, -self.delta, self.delta),
            )

            for _ in trange(self.max_iter, desc="Feature Adversaries TensorFlow v2", disable=not self.verbose):
                with tf.GradientTape(watch_accessed_variables=False) as tape:
                    tape.watch(perturbation)
                    adv = x + perturbation
                    loss = loss_fn(x, adv, y)
                gradient = tape.gradient(loss, perturbation)

                opt.apply_gradients([(gradient, perturbation)])

                if self.estimator.clip_values is not None:
                    adv = tf.clip_by_value(x + perturbation, *self.estimator.clip_values)
                perturbation.assign(adv - x)
        return adv

        Generate adversarial batch.

        :param x: Source samples.
        :param y: Guide samples.
        :return: Batch of adversarial examples.
        
art/attacks/evasion/feature_adversaries/feature_adversaries_tensorflow.py