class_docstr: An implementation of the Attention Based Spatial-Temporal Graph Convolutional Cell.
  For details see this paper: `"Attention Based Spatial-Temporal Graph Convolutional
  Networks for Traffic Flow Forecasting." <https://ojs.aaai.org/index.php/AAAI/article/view/3881>`_
  
  Args:
      nb_block (int): Number of ASTGCN blocks in the model.
      in_channels (int): Number of input features.
      K (int): Order of Chebyshev polynomials. Degree is K-1.
      nb_chev_filters (int): Number of Chebyshev filters.
      nb_time_filters (int): Number of time filters.
      time_strides (int): Time strides during temporal convolution.
      edge_index (array): edge indices.
      num_for_predict (int): Number of predictions to make in the future.
      len_input (int): Length of the input sequence.
      num_of_vertices (int): Number of vertices in the graph.
      normalization (str, optional): The normalization scheme for the graph
          Laplacian (default: :obj:`"sym"`):
          1. :obj:`None`: No normalization
          :math:`\mathbf{L} = \mathbf{D} - \mathbf{A}`
          2. :obj:`"sym"`: Symmetric normalization
          :math:`\mathbf{L} = \mathbf{I} - \mathbf{D}^{-1/2} \mathbf{A}
          \mathbf{D}^{-1/2}`
          3. :obj:`"rw"`: Random-walk normalization
          :math:`\mathbf{L} = \mathbf{I} - \mathbf{D}^{-1} \mathbf{A}`
          You need to pass :obj:`lambda_max` to the :meth:`forward` method of
          this operator in case the normalization is non-symmetric.
          :obj:`\lambda_max` should be a :class:`torch.Tensor` of size
          :obj:`[num_graphs]` in a mini-batch scenario and a
          scalar/zero-dimensional tensor when operating on single graphs.
          You can pre-compute :obj:`lambda_max` via the
          :class:`torch_geometric.transforms.LaplacianLambdaMax` transform.
      bias (bool, optional): If set to :obj:`False`, the layer will not learn
          an additive bias. (default: :obj:`True`)
docstr: Making a forward pass.
  
  Arg types:
      * **X** (PyTorch FloatTensor) - Node features for T time periods, with shape (B, N_nodes, F_in, T_in).
      * **edge_index** (PyTorch LongTensor): Edge indices, can be an array of a list of Tensor arrays, depending on whether edges change over time.
  
  Return types:
      * **X** (PyTorch FloatTensor)* - Hidden state tensor for all nodes, with shape (B, N_nodes, T_out).
code: def forward(
      self, X: torch.FloatTensor, edge_index: torch.LongTensor
  ) -> torch.FloatTensor:
      """
      Making a forward pass.
  
      Arg types:
          * **X** (PyTorch FloatTensor) - Node features for T time periods, with shape (B, N_nodes, F_in, T_in).
          * **edge_index** (PyTorch LongTensor): Edge indices, can be an array of a list of Tensor arrays, depending on whether edges change over time.
  
      Return types:
          * **X** (PyTorch FloatTensor)* - Hidden state tensor for all nodes, with shape (B, N_nodes, T_out).
      """
      for block in self._blocklist:
          # original x is (B,N,F_in,T) will give (B,N,F_out,T) for example (32, 307, 1, 12) -> (32, 307, 64, 12)
          X = block(X, edge_index)
  
      # (b,N,F,T)->(b,T,N,F)-conv<1,F>->(b,c_out*T,N,1)
      # for example (32, 307, 64, 12) -permute-> (32, 12, 307,64) -final_conv-> (32, 12, 307, 1)
      X = self._final_conv(X.permute(0, 3, 1, 2))
      # (b,c_out*T,N)->(b,N,T)
      X = X[:, :, :, -1]  # (b,c_out*T,N) for example (32, 12, 307)
      X = X.permute(0, 2, 1)  # (b,T,N)-> (b,N,T)
      return X
  
imports: import math
  from typing import Optional, List, Union
  import torch
  import torch.nn
  from torch.nn import Parameter
  import torch.nn.functional
  from torch_geometric.data import Data
  from torch_geometric.typing import OptTensor
  from torch_geometric.nn.conv import MessagePassing
  from torch_geometric.transforms import LaplacianLambdaMax
  from torch_geometric.utils import remove_self_loops, add_self_loops, get_laplacian
label_desc: Use image node_id 3 for calling the ASTGCN obj's underlying member method code with example usage: obj.forward(X, edge_index) and returns: X