def _loss_gradient_masking_threshold_torch(
        self, perturbation: np.ndarray, psd_maximum_stabilized: np.ndarray, masking_threshold_stabilized: np.ndarray
    ) -> tuple[np.ndarray, np.ndarray]:
        """
        Compute loss gradient of the masking threshold loss in PyTorch.

        See also `ImperceptibleASR._loss_gradient_masking_threshold_tf`.
        """
        import torch

        # define tensors

        perturbation_torch = torch.from_numpy(perturbation).to(self.estimator._device)
        masking_threshold_stabilized_torch = torch.from_numpy(masking_threshold_stabilized).to(self.estimator._device)
        psd_maximum_stabilized_torch = torch.from_numpy(psd_maximum_stabilized).to(self.estimator._device)

        # track gradient of perturbation
        perturbation_torch.requires_grad = True

        # calculate approximate power spectral density
        psd_perturbation = self._approximate_power_spectral_density_torch(
            perturbation_torch, psd_maximum_stabilized_torch
        )

        # calculate hinge loss
        loss = torch.mean(  # type: ignore
            torch.nn.functional.relu(psd_perturbation - masking_threshold_stabilized_torch), dim=(1, 2), keepdims=False
        )

        # compute loss gradient
        loss.sum().backward()
        if perturbation_torch.grad is not None:
            loss_gradient = perturbation_torch.grad.cpu().numpy()
        else:
            raise ValueError("Gradient tensor in PyTorch model is `None`.")
        loss_value = loss.detach().cpu().numpy()

        return loss_gradient, loss_value

        Compute loss gradient of the masking threshold loss in PyTorch.

        See also `ImperceptibleASR._loss_gradient_masking_threshold_tf`.
        
art/attacks/evasion/imperceptible_asr/imperceptible_asr.py