code: def attack_batch(self, batch, true_future_target):
      res = {}
      batch_size, context_length, dim = batch["past_target_cdf"].shape
      max_norm = self.params.max_norm
  
      with torch.no_grad():
          inputs = {key: batch[key] for key in self.input_names}
          _, sample_mu = forward_model(self.model, inputs)
          future_target = sample_mu
          loss_function = nn.MSELoss(reduction="none")
          best_loss = [0] * len(self.params.sparsity)
  
      for mode in self.params.modes:
          print("Mode ", mode)
  
          # generate target
          adv_target = generate_adv_target(
              mode, future_target, self.params.factor
          )
  
          # traverse over each sparsity
          for i, s in enumerate(self.params.sparsity):
              # Generate sparse attack using sparse layer
              sparse_layer = SparseNet(
                  context_length=context_length,
                  target_dim=dim,
                  target_item=self.params.target_items[0],
                  hidden_dim=40,
                  m=s,
                  max_norm=max_norm,
              ).cuda()
              optimizer = optim.Adam(
                  sparse_layer.parameters(),
                  lr=self.params.learning_rate,
              )
  
              # iteration steps
              for _ in tqdm(range(self.params.n_iterations)):
                  optimizer.zero_grad()
                  sparse_delta = sparse_layer(
                      batch["past_target_cdf"], n_sample=100
                  ).view(batch_size, context_length, dim)
                  perturbed_inputs = dict(
                      [(key, batch[key]) for key in self.input_names]
                  )
                  perturbed_inputs["past_target_cdf"] = batch[
                      "past_target_cdf"
                  ] * (1 + sparse_delta)
                  _, perturbed_mu = forward_model(
                      self.model, perturbed_inputs
                  )
                  loss = loss_function(perturbed_mu, adv_target)[
                      :,
                      self.params.attack_idx,
                      self.params.target_items,
                  ].mean()
                  loss.backward()
                  optimizer.step()
  
              with torch.no_grad():
                  # construct perturbed inputs
                  perturbation = sparse_layer(
                      batch["past_target_cdf"], n_sample=100
                  ).view(batch_size, context_length, dim)
                  perturbed_inputs = {
                      key: batch[key] for key in self.input_names
                  }
                  perturbed_inputs[
                      "past_target_cdf"
                  ] = perturbed_inputs["past_target_cdf"] * (
                      1 + perturbation
                  )
  
                  # get model outputs
                  _, perturbed_mu = forward_model(
                      self.model, perturbed_inputs
                  )
                  # compute loss
                  loss = loss_function(
                      perturbed_mu[
                          :,
                          self.params.attack_idx,
                          self.params.target_items,
                      ],
                      true_future_target[
                          :,
                          self.params.attack_idx,
                          self.params.target_items,
                      ],
                  ).mean()
                  if loss >= best_loss[i]:
                      best_loss[i] = loss
                      res[s] = perturbation.data
  
      return res
  
imports: import torch
  import torch.nn
  import torch.optim
  from tqdm.auto import tqdm
  from typing import List
  from sparse_layer import SparseNet
label_desc: Use image node_id 2 for calling the SparseLayerAttack obj's underlying member method code with example usage: obj.attack_batch(batch, true_future_target) and returns: res