code: def forward(
      self,
      data: Tensor,
      feats: Optional[Tensor],
      nan_mask: Optional[BoolTensor],
      length: Optional[LongTensor],
  ) -> Tuple[
      Tensor, Tensor, Tensor, Optional[BoolTensor], Optional[BoolTensor]
  ]:
      batch_size = data.size(0)
      full_length = data.size(1)
  
      # merge padding mask and missing mask
      if length is None:
          pad_mask = None
      else:
          # in our impl, padings are on left side
          pad_mask = pt.gt(
              pt.arange(full_length, 0, -1).to(length).unsqueeze(dim=0),
              length.unsqueeze(dim=1),
          )
      if (pad_mask is None) and (nan_mask is None):
          mask = None
      else:
          if pad_mask is None:
              pad_mask = pt.zeros_like(nan_mask)
          if nan_mask is None:
              nan_mask = pt.zeros_like(pad_mask)
          mask = pt.logical_or(pad_mask, nan_mask)
  
      # compute acutal horizons
      if isinstance(self.horizon, int):
          horizon = (
              data.new_ones(batch_size, dtype=pt.long) * self.horizon
          )
      else:
          if length is None:
              horizon = data.new_ones(batch_size, dtype=pt.long) * int(
                  full_length * self.horizon
              )
          else:
              horizon = (
                  length.to(pt.float).mul(self.horizon).to(pt.long)
              )
      max_horizon = horizon.max().item()
  
      # split data into condition (input) and (forecasting) target
      if self.training:
          target = data[:, full_length - max_horizon : full_length]
          if mask is not None:
              bc_mask = mask
              fc_mask = pt.gt(
                  pt.arange(max_horizon, 0, -1)
                  .to(horizon)
                  .unsqueeze(dim=0),
                  horizon.unsqueeze(dim=1),
              )
              fc_mask = pt.logical_or(
                  fc_mask,
                  mask[:, full_length - max_horizon : full_length],
              )
          else:
              bc_mask = fc_mask = None
          residue, forecast = self._rollout(data, feats, bc_mask)
          forecast = forecast[:, -(max_horizon + 1) : -1]
      else:
          if length is not None:
              # move part of paddings from left to right
              # pad_size = max_horizon - horizon
              start_index = pt.unsqueeze(max_horizon - horizon, dim=1)
              selected_index = (
                  pt.arange(full_length).to(horizon).unsqueeze(dim=0)
                  + start_index
              )
              selected_index = pt.where(
                  selected_index.lt(full_length),
                  selected_index,
                  selected_index - full_length,
              )
              data = pt.gather(
                  data,
                  dim=1,
                  index=selected_index.unsqueeze(dim=2).expand_as(data),
              )
              if feats is not None:
                  feats = pt.gather(
                      feats,
                      dim=1,
                      index=selected_index.unsqueeze(dim=2).expand_as(
                          feats
                      ),
                  )
              mask = pt.gather(
                  mask,
                  dim=1,
                  index=selected_index,
              )
  
          # split condition and target
          cond_length = full_length - max_horizon
          data, target = pt.split(
              data, [cond_length, max_horizon], dim=1
          )
          # features are not split for predictions
          if mask is not None:
              bc_mask, fc_mask = pt.split(
                  mask, [cond_length, max_horizon], dim=1
              )
          else:
              bc_mask = fc_mask = None
          residue, forecast = self._rollout(
              data, feats, bc_mask, max_horizon
          )
  
      return residue, forecast, target, bc_mask, fc_mask
  
imports: from typing import Optional, List, Tuple, Union, Iterator, Callable
  from itertools import product, chain
  from copy import deepcopy
  import warnings
  import math
  import torch
  from torch import Tensor, LongTensor, BoolTensor
  from torch import nn
  from torch.nn import Parameter, functional
  from tslib.nn.activations import PositiveSoftplus
  from ..network import AttentionKernel, ExpKernel, RBFKernel, EncoderModule, DecoderModule, AttentionBlock, AdversarialBlock, SimpleDiscriminator
  from .hooks import ZScoreNormalizer, LossFunction, MSELoss, MAELoss
label_desc: Use image node_id 9 for calling the AttentionEstimator obj's underlying member method code with example usage: obj.forward(data, feats, nan_mask, length) and returns: residue,  forecast,  target,  bc_mask,  fc_mask