def _get_regularisation_loss_gradients(self, perturbation: np.ndarray) -> np.ndarray:
        """
        Get regularisation loss gradients.

        :param perturbation: The perturbation to be regularised.
        :return: The loss gradients of the perturbation.
        """
        if not self.estimator.channels_first:
            perturbation = perturbation.transpose((0, 3, 1, 2))

        if self.framework == "tensorflow":
            import tensorflow as tf

            if tf.executing_eagerly():
                with tf.GradientTape() as tape:

                    perturbation_t = tf.convert_to_tensor(perturbation)
                    tape.watch(perturbation_t)

                    x_t = perturbation_t[:, :, :, 1:] - perturbation_t[:, :, :, :-1]
                    y_t = perturbation_t[:, :, 1:, :] - perturbation_t[:, :, :-1, :]
                    loss_tv = tf.reduce_sum(x_t * x_t, axis=(1, 2, 3)) + tf.reduce_sum(y_t * y_t, axis=(1, 2, 3))

                    if perturbation_t.shape[1] == 1:
                        loss_s = 0.0
                    elif perturbation_t.shape[1] == 3:
                        loss_s = tf.norm(
                            (perturbation_t[:, 0, :, :] - perturbation_t[:, 1, :, :]) ** 2
                            + (perturbation_t[:, 1, :, :] - perturbation_t[:, 2, :, :]) ** 2
                            + (perturbation_t[:, 0, :, :] - perturbation_t[:, 2, :, :]) ** 2,
                            ord=2,
                            axis=(1, 2),
                        )
                    else:
                        raise ValueError("Value for number of channels in `perturbation_t.shape` not recognized.")

                    loss_c = tf.norm(tf.reduce_mean(tf.abs(perturbation_t), axis=[2, 3]), ord=2, axis=1) ** 2
                    loss = self.lambda_tv * loss_tv + self.lambda_s * loss_s + self.lambda_c * loss_c
                    gradients = tape.gradient(loss, perturbation_t).numpy()

            else:  # pragma: no cover
                raise ValueError("Expecting eager execution.")

        elif self.framework == "pytorch":
            import torch

            perturbation_t = torch.from_numpy(perturbation).to("cpu")
            perturbation_t.requires_grad = True

            x_t = perturbation_t[:, :, :, 1:] - perturbation_t[:, :, :, :-1]
            y_t = perturbation_t[:, :, 1:, :] - perturbation_t[:, :, :-1, :]

            loss_tv = (x_t * x_t).sum(dim=(1, 2, 3)) + (y_t * y_t).sum(dim=(1, 2, 3))

            if perturbation_t.shape[1] == 1:
                loss_s = 0.0
            elif perturbation_t.shape[1] == 3:
                loss_s = (
                    (perturbation_t[:, 0, :, :] - perturbation_t[:, 1, :, :]) ** 2
                    + (perturbation_t[:, 1, :, :] - perturbation_t[:, 2, :, :]) ** 2
                    + (perturbation_t[:, 0, :, :] - perturbation_t[:, 2, :, :]) ** 2
                ).norm(p=2, dim=(1, 2))
            else:
                raise ValueError("Value for number of channels in `perturbation_t.shape` not recognized.")

            loss_c = perturbation_t.abs().mean([2, 3]).norm(dim=1) ** 2
            loss = torch.mean(self.lambda_tv * loss_tv + self.lambda_s * loss_s + self.lambda_c * loss_c)
            loss.backward()
            gradients = perturbation_t.grad.numpy()
        else:
            raise NotImplementedError

        if not self.estimator.channels_first:
            gradients = gradients.transpose(0, 2, 3, 1)

        return gradients

        Get regularisation loss gradients.

        :param perturbation: The perturbation to be regularised.
        :return: The loss gradients of the perturbation.
        
art/attacks/evasion/shadow_attack.py