def generate(self, x: np.ndarray, y: np.ndarray | None = None, **kwargs) -> np.ndarray:
        """
        Generate adversaries for the samples passed in the `x` data matrix, whose targets are specified in `y`,
        one-hot-encoded target matrix. This procedure makes use of the LowProFool algorithm. In the case of failure,
        the resulting array will contain the initial samples on the problematic positions - which otherwise should
        contain the best adversary found in the process.

        :param x: An array with the original inputs to be attacked.
        :param y: One-hot-encoded target classes of shape (nb_samples, nb_classes).
        :param kwargs:
        :return: An array holding the adversarial examples.
        """
        if self.importance_vec is None:
            raise ValueError("No feature importance vector has been provided yet.")
        if y is None:
            raise ValueError("It is required to pass target classes as `y` parameter.")

        # Make sure samples and targets are of type np.ndarray.
        samples = np.array(x, dtype=np.float64)
        targets = np.array(y, dtype=np.float64)

        # Extract the target classes as integers implying their classes' indices.
        targets_integer = np.argmax(y, axis=1)

        if targets.shape[1] != self.n_classes:
            raise ValueError("Targets shape is not compatible with number of classes.")
        if samples.shape[1] != self.n_features:
            raise ValueError("Samples shape is not compatible with number of features.")

        # Initialize perturbation vectors and learning rate.
        perturbations = np.zeros(samples.shape, dtype=np.float64)
        eta = self.eta

        # Initialize 'keep-the-best' variables.
        best_norm_losses = np.inf * np.ones(samples.shape[0], dtype=np.float64)
        best_perturbations = perturbations.copy()

        # Success indicators per sample.
        success_indicators = np.zeros(samples.shape[0], dtype=np.float64)

        # Predicate used to determine whether the target was met based on the given probabilities.
        def met_target(probas, target_class):
            if self.threshold is None:
                return np.argmax(probas) == target_class
            return probas[target_class] > self.threshold

        # Main loop.
        for _ in trange(self.n_steps, desc="LowProFool", disable=not self.verbose):
            # Calculate gradients, apply them to perturbations and clip if needed.
            grad = self.__get_gradients(samples, perturbations, targets)
            perturbations -= eta * grad
            perturbations = self.__apply_clipping(samples, perturbations)

            # Decrease learning rate for the next iteration.
            eta = max(eta * self.eta_decay, self.eta_min)

            # Calculate class-wise probabilities.
            y_probas = self.estimator.predict((samples + perturbations).astype(np.float32))

            # Examine the quality of adversaries in the current step.
            for j, target_int in enumerate(targets_integer):
                # Check for every sample whether the threshold probability is reached.
                if met_target(y_probas[j], target_int):
                    success_indicators[j] = 1.0
                    # Calculate weighted Lp-norm loss.
                    norm_loss = self.__weighted_lp_norm(perturbations[j : j + 1])[0, 0]

                    # Note it, if the adversary improves.
                    if norm_loss < best_norm_losses[j]:
                        best_norm_losses[j] = norm_loss
                        best_perturbations[j] = perturbations[j].copy()

        logger.info(
            "Success rate of LowProFool attack: %.2f}%%", 100 * np.sum(success_indicators) / success_indicators.size
        )

        # The generated adversaries are a sum of initial samples and best perturbation vectors found by the algorithm.
        return samples + best_perturbations

        Generate adversaries for the samples passed in the `x` data matrix, whose targets are specified in `y`,
        one-hot-encoded target matrix. This procedure makes use of the LowProFool algorithm. In the case of failure,
        the resulting array will contain the initial samples on the problematic positions - which otherwise should
        contain the best adversary found in the process.

        :param x: An array with the original inputs to be attacked.
        :param y: One-hot-encoded target classes of shape (nb_samples, nb_classes).
        :param kwargs:
        :return: An array holding the adversarial examples.
        
art/attacks/evasion/lowprofool.py