class_docstr: An implementation of the Multi-Component Spatial-Temporal Graph
  Convolution block from this paper: `"Attention Based Spatial-Temporal
  Graph Convolutional Networks for Traffic Flow Forecasting."
  <https://ojs.aaai.org/index.php/AAAI/article/view/3881>`_
  
  Args:
      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.
docstr: Making a forward pass with a single MSTGCN block.
  
  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, nb_time_filter, T_out).
docstr_tok: ["Making","a","forward","pass","with","a","single","MSTGCN","block",".","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",",","nb_time_filter",",","T_out",")","."]
code: def forward(
      self, X: torch.FloatTensor, edge_index: torch.LongTensor
  ) -> torch.FloatTensor:
      """
      Making a forward pass with a single MSTGCN block.
  
      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, nb_time_filter, T_out).
      """
  
      (
          batch_size,
          num_of_vertices,
          in_channels,
          num_of_timesteps,
      ) = X.shape
  
      if not isinstance(edge_index, list):
          lambda_max = LaplacianLambdaMax()(
              Data(
                  edge_index=edge_index,
                  edge_attr=None,
                  num_nodes=num_of_vertices,
              )
          ).lambda_max
  
          X_tilde = X.permute(2, 0, 1, 3)
          X_tilde = X_tilde.reshape(
              num_of_vertices,
              in_channels,
              num_of_timesteps * batch_size,
          )
          X_tilde = X_tilde.permute(2, 0, 1)
          X_tilde = F.relu(
              self._cheb_conv(
                  x=X_tilde,
                  edge_index=edge_index,
                  lambda_max=lambda_max,
              )
          )
          X_tilde = X_tilde.permute(1, 2, 0)
          X_tilde = X_tilde.reshape(
              num_of_vertices,
              self.nb_time_filter,
              batch_size,
              num_of_timesteps,
          )
          X_tilde = X_tilde.permute(2, 0, 1, 3)
  
      else:
          X_tilde = []
          for t in range(num_of_timesteps):
              lambda_max = LaplacianLambdaMax()(
                  Data(
                      edge_index=edge_index[t],
                      edge_attr=None,
                      num_nodes=num_of_vertices,
                  )
              ).lambda_max
              X_tilde.append(
                  torch.unsqueeze(
                      self._cheb_conv(
                          X[:, :, :, t],
                          edge_index[t],
                          lambda_max=lambda_max,
                      ),
                      -1,
                  )
              )
          X_tilde = F.relu(torch.cat(X_tilde, dim=-1))
  
      X_tilde = self._time_conv(X_tilde.permute(0, 2, 1, 3))
      X = self._residual_conv(X.permute(0, 2, 1, 3))
      X = self._layer_norm(F.relu(X + X_tilde).permute(0, 3, 2, 1))
      X = X.permute(0, 2, 3, 1)
      return X
  
file_path: pytorch_geometric_temporal/torch_geometric_temporal/nn/attention/mstgcn.py
imports: import torch
  import torch.nn
  import torch.nn.functional
  from torch_geometric.data import Data
  from torch_geometric.nn import ChebConv
  from torch_geometric.transforms import LaplacianLambdaMax
label_desc: Use image node_id 3 for calling the MSTGCNBlock obj's underlying member method code with example usage: obj.forward(X, edge_index) and returns: X