class_docstr: GNN layer for SDMGR.
  
  Args:
      node_dim (int): Dimension of node embedding. Defaults to 256.
      edge_dim (int): Dimension of edge embedding. Defaults to 256.
docstr: Forward function.
  
  Args:
      nodes (Tensor): Concatenated node embeddings.
      edges (Tensor): Concatenated edge embeddings.
      nums (List[int]): List of number of nodes in each batch.
  
  Returns:
      tuple(Tensor, Tensor):
  
      - nodes (Tensor): New node embeddings.
      - edges (Tensor): New edge embeddings.
docstr_tok: ["Forward","function",".","Args",":","nodes","(","Tensor",")",":","Concatenated","node","embeddings",".","edges","(","Tensor",")",":","Concatenated","edge","embeddings",".","nums","(","List","[","int","]",")",":","List","of","number","of","nodes","in","each","batch",".","Returns",":","tuple","(","Tensor",",","Tensor",")",":","-","nodes","(","Tensor",")",":","New","node","embeddings",".","-","edges","(","Tensor",")",":","New","edge","embeddings","."]
code: def forward(
      self, nodes: Tensor, edges: Tensor, nums: List[int]
  ) -> Tuple[Tensor, Tensor]:
      """Forward function.
  
      Args:
          nodes (Tensor): Concatenated node embeddings.
          edges (Tensor): Concatenated edge embeddings.
          nums (List[int]): List of number of nodes in each batch.
  
      Returns:
          tuple(Tensor, Tensor):
  
          - nodes (Tensor): New node embeddings.
          - edges (Tensor): New edge embeddings.
      """
      start, cat_nodes = 0, []
      for num in nums:
          sample_nodes = nodes[start : start + num]
          cat_nodes.append(
              torch.cat(
                  [
                      sample_nodes.unsqueeze(1).expand(-1, num, -1),
                      sample_nodes.unsqueeze(0).expand(num, -1, -1),
                  ],
                  -1,
              ).view(num**2, -1)
          )
          start += num
      cat_nodes = torch.cat([torch.cat(cat_nodes), edges], -1)
      cat_nodes = self.relu(self.in_fc(cat_nodes))
      coefs = self.coef_fc(cat_nodes)
  
      start, residuals = 0, []
      for num in nums:
          residual = F.softmax(
              -torch.eye(num).to(coefs.device).unsqueeze(-1) * 1e9
              + coefs[start : start + num**2].view(num, num, -1),
              1,
          )
          residuals.append(
              (
                  residual
                  * cat_nodes[start : start + num**2].view(
                      num, num, -1
                  )
              ).sum(1)
          )
          start += num**2
  
      nodes += self.relu(self.out_fc(torch.cat(residuals)))
      return nodes, cat_nodes
  
imports: from typing import Dict, List, Optional, Tuple, Union
  import torch
  from mmengine.model import BaseModule
  from torch import Tensor, nn
  from torch.nn import functional
  from mmocr.models.common.dictionary import Dictionary
  from mmocr.registry import MODELS, TASK_UTILS
  from mmocr.structures import KIEDataSample
label_desc: Use image node_id 2 for calling the GNNLayer obj's underlying member method code with example usage: obj.forward(nodes, edges, nums) and returns: nodes,  cat_nodes