# Buggy code
def loss_gradient(self, x: np.ndarray, y: np.ndarray, **kwargs) -> np.ndarray:
    """
    Compute the gradient of the loss function w.r.t. `x`.

    :param x: Samples of shape (nb_samples, seq_length). Note that, it is allowable that sequences in the batch
              could have different lengths. A possible example of `x` could be:
              `x = np.array([np.array([0.1, 0.2, 0.1, 0.4]), np.array([0.3, 0.1])])`.
    :param y: Target values of shape (nb_samples). Each sample in `y` is a string and it may possess different
              lengths. A possible example of `y` could be: `y = np.array(['SIXTY ONE', 'HELLO'])`.
    :return: Loss gradients of the same shape as `x`.
    """
    from warpctc_pytorch import CTCLoss

    x_ = np.array([x_i for x_i in x] + [np.array([0.1]), np.array([0.1, 0.2])])[:-2]

    # Put the model in the training mode
    self._model.train()

    # Apply preprocessing
    x_preprocessed, y_preprocessed = self._apply_preprocessing(x_, y, fit=False)

    # Transform data into the model input space
    inputs, targets, input_rates, target_sizes, batch_idx = self.transform_model_input(
        x=x_preprocessed, y=y_preprocessed, compute_gradient=True
    )

    # Compute real input sizes
    input_sizes = input_rates.mul_(inputs.size()[-1]).int()

    # Call to DeepSpeech model for prediction
    outputs, output_sizes = self._model(
        inputs.to(self._device), input_sizes.to(self._device)
    )
    outputs = outputs.transpose(0, 1)
    float_outputs = outputs.float()

    # Loss function
    criterion = CTCLoss()
    loss = criterion(float_outputs, targets, output_sizes, target_sizes).to(
        self._device
    )
    loss = loss / inputs.size(0)

    # Compute gradients
    if self._use_amp:
        from apex import amp

        with amp.scale_loss(loss, self._optimizer) as scaled_loss:
            scaled_loss.backward()

    else:
        loss.backward()

    # Get results
    results = []
    for i in range(len(x_preprocessed)):
        results.append(x_preprocessed[i].grad.cpu().numpy().copy())

    results = np.array(results)

    if results.shape[0] == 1:
        results = np.array(
            [results_i for results_i in results]
            + [np.array([0.1]), np.array([0.1, 0.2])],
            dtype="object",
        )[:-2]

    results = self._apply_preprocessing_gradient(x_, results)

    return results


# Traceback (TypeError)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-c849f56466d3> in <module>
3
4 # Generate attack
----> 5 x_adv = attack_pgd.generate(np.array([x2, x3]), y=None, batch_size=2)

/opt/conda/lib/python3.7/site-packages/art/attacks/attack.py in replacement_function(self, *args, **kwargs)
72                 if len(args) > 0:
73                     args = tuple(lst)
---> 74                 return fdict[func_name](self, *args, **kwargs)
75
76             replacement_function.__doc__ = fdict[func_name].__doc__

/opt/conda/lib/python3.7/site-packages/art/attacks/evasion/projected_gradient_descent/projected_gradient_descent.py in generate(self, x, y, **kwargs)
181         """
182         logger.info("Creating adversarial samples.")
--> 183         return self._attack.generate(x=x, y=y, **kwargs)
184
185     def set_params(self, **kwargs) -> None:

/opt/conda/lib/python3.7/site-packages/art/attacks/attack.py in replacement_function(self, *args, **kwargs)
72                 if len(args) > 0:
73                     args = tuple(lst)
---> 74                 return fdict[func_name](self, *args, **kwargs)
75
76             replacement_function.__doc__ = fdict[func_name].__doc__

/opt/conda/lib/python3.7/site-packages/art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_numpy.py in generate(self, x, y, **kwargs)
330                     self.eps_step,
331                     self._project,
--> 332                     self.num_random_init > 0 and i_max_iter == 0,
333                 )
334

/opt/conda/lib/python3.7/site-packages/art/attacks/evasion/fast_gradient.py in _compute(self, x, x_init, y, mask, eps, eps_step, project, random_init)
366
367             # Get perturbation
--> 368             perturbation = self._compute_perturbation(batch, batch_labels, mask_batch)
369
370             # Apply perturbation and clip

/opt/conda/lib/python3.7/site-packages/art/attacks/evasion/fast_gradient.py in _compute_perturbation(self, batch, batch_labels, mask)
280
281         # Get gradient wrt loss; invert it if attack is targeted
--> 282         grad = self.estimator.loss_gradient(batch, batch_labels) * (1 - 2 * int(self.targeted))
283
284         # Apply norm bound

/opt/conda/lib/python3.7/site-packages/art/estimators/speech_recognition/pytorch_deep_speech.py in loss_gradient(self, x, y, **kwargs)
372         # Transform data into the model input space
373         inputs, targets, input_rates, target_sizes, batch_idx = self.transform_model_input(
--> 374             x=x_preprocessed, y=y_preprocessed, compute_gradient=True
375         )
376

/opt/conda/lib/python3.7/site-packages/art/estimators/speech_recognition/pytorch_deep_speech.py in transform_model_input(self, x, y, compute_gradient, tensor_input, real_lengths)
569                 target = []
570             else:
--> 571                 target = list(filter(None, [label_map.get(letter) for letter in list(y[i])]))
572
573             # Push the sequence to device

/opt/conda/lib/python3.7/site-packages/art/estimators/speech_recognition/pytorch_deep_speech.py in <listcomp>(.0)
569                 target = []
570             else:
--> 571                 target = list(filter(None, [label_map.get(letter) for letter in list(y[i])]))
572
573             # Push the sequence to device

# Fixed code
def loss_gradient(self, x: np.ndarray, y: np.ndarray, **kwargs) -> np.ndarray:
    """
    Compute the gradient of the loss function w.r.t. `x`.

    :param x: Samples of shape (nb_samples, seq_length). Note that, it is allowable that sequences in the batch
              could have different lengths. A possible example of `x` could be:
              `x = np.array([np.array([0.1, 0.2, 0.1, 0.4]), np.array([0.3, 0.1])])`.
    :param y: Target values of shape (nb_samples). Each sample in `y` is a string and it may possess different
              lengths. A possible example of `y` could be: `y = np.array(['SIXTY ONE', 'HELLO'])`.
    :return: Loss gradients of the same shape as `x`.
    """
    from warpctc_pytorch import CTCLoss

    x_ = np.array([x_i for x_i in x] + [np.array([0.1]), np.array([0.1, 0.2])])[:-2]

    # Put the model in the training mode
    self._model.train()

    # Apply preprocessing
    x_preprocessed, y_preprocessed = self._apply_preprocessing(x_, y, fit=False)

    # Transform data into the model input space
    inputs, targets, input_rates, target_sizes, batch_idx = self.transform_model_input(
        x=x_preprocessed, y=y_preprocessed, compute_gradient=True
    )

    # Compute real input sizes
    input_sizes = input_rates.mul_(inputs.size()[-1]).int()

    # Call to DeepSpeech model for prediction
    outputs, output_sizes = self._model(
        inputs.to(self._device), input_sizes.to(self._device)
    )
    outputs = outputs.transpose(0, 1)
    float_outputs = outputs.float()

    # Loss function
    criterion = CTCLoss()
    loss = criterion(float_outputs, targets, output_sizes, target_sizes).to(
        self._device
    )
    loss = loss / inputs.size(0)

    # Compute gradients
    if self._use_amp:
        from apex import amp

        with amp.scale_loss(loss, self._optimizer) as scaled_loss:
            scaled_loss.backward()

    else:
        loss.backward()

    # Get results
    results = []
    for i in range(len(x_preprocessed)):
        results.append(x_preprocessed[i].grad.cpu().numpy().copy())

    results = np.array(results)

    if results.shape[0] == 1:
        results = np.array(
            [results_i for results_i in results]
            + [np.array([0.1]), np.array([0.1, 0.2])],
            dtype="object",
        )[:-2]

    results = self._apply_preprocessing_gradient(x_, results)

    if x.dtype != np.object:
        results = np.array([i for i in results], dtype=x.dtype)
        assert results.shape == x.shape and results.dtype == x.dtype

    return results