class_docstr: Postprocessor for SDMGR. It converts the node and edge scores into
  labels and edge labels. If the link_type is not "none", it reconstructs the
  edge labels with different strategies specified by ``link_type``, which is
  generally known as the "openset" mode. In "openset" mode, only the edges
  connecting from "key" to "value" nodes will be constructed.
  
  Args:
      link_type (str): The type of link to be constructed.
          Defaults to 'none'. Options are:
  
          - 'none': The simplest link type involving no edge
            postprocessing. The edge prediction will be returned as-is.
          - 'one-to-one': One key node can be connected to one value node.
          - 'one-to-many': One key node can be connected to multiple value
            nodes.
          - 'many-to-one': Multiple key nodes can be connected to one value
            node.
          - 'many-to-many': No restrictions on the number of edges that a
            key/value node can have.
      key_node_idx (int, optional): The label index of the key node. It must
          be specified if ``link_type`` is not "none". Defaults to None.
      value_node_idx (int, optional): The index of the value node. It must be
          specified if ``link_type`` is not "none". Defaults to None.
docstr: Reconstruct the edges and update edge scores according to
  ``link_type``.
  
  Args:
      data_sample (KIEDataSample): A datasample containing prediction
          results.
  
  Returns:
      tuple(Tensor, Tensor):
  
      - edge_scores (Tensor): A float tensor of shape (N, N)
          indicating the confidence scores for edge predictions.
      - edge_labels (Tensor): An integer tensor of shape (N, N)
          indicating the connection between nodes. Options are 0, 1.
docstr_tok: ["Reconstruct","the","edges","and","update","edge","scores","according","to","``","link_type","``",".","Args",":","data_sample","(","KIEDataSample",")",":","A","datasample","containing","prediction","results",".","Returns",":","tuple","(","Tensor",",","Tensor",")",":","-","edge_scores","(","Tensor",")",":","A","float","tensor","of","shape","(","N",",","N",")","indicating","the","confidence","scores","for","edge","predictions",".","-","edge_labels","(","Tensor",")",":","An","integer","tensor","of","shape","(","N",",","N",")","indicating","the","connection","between","nodes",".","Options","are","0",",","1","."]
code: def decode_edges(
      self,
      node_labels: Tensor,
      edge_scores: Tensor,
      edge_labels: Tensor,
  ) -> Tuple[Tensor, Tensor]:
      """Reconstruct the edges and update edge scores according to
      ``link_type``.
  
      Args:
          data_sample (KIEDataSample): A datasample containing prediction
              results.
  
      Returns:
          tuple(Tensor, Tensor):
  
          - edge_scores (Tensor): A float tensor of shape (N, N)
              indicating the confidence scores for edge predictions.
          - edge_labels (Tensor): An integer tensor of shape (N, N)
              indicating the connection between nodes. Options are 0, 1.
      """
      # Obtain the scores of the existence of edges.
      pos_edges_scores = edge_scores.clone()
      edge_labels_mask = edge_labels.bool()
      pos_edges_scores[~edge_labels_mask] = (
          1 - pos_edges_scores[~edge_labels_mask]
      )
  
      # Temporarily convert the directed graph to undirected by adding
      # reversed edges to every pair of nodes if they were already connected
      # by an directed edge before.
      edge_labels = torch.max(edge_labels, edge_labels.T)
  
      # Maximize edge scores
      edge_labels_mask = edge_labels.bool()
      edge_scores[~edge_labels_mask] = pos_edges_scores[
          ~edge_labels_mask
      ]
      new_edge_scores = torch.max(edge_scores, edge_scores.T)
  
      # Only reconstruct the edges from key nodes to value nodes.
      key_nodes_mask = node_labels == self.key_node_idx
      value_nodes_mask = node_labels == self.value_node_idx
      key2value_mask = (
          key_nodes_mask[:, None] * value_nodes_mask[None, :]
      )
  
      if self.link_type == "many-to-many":
          new_edge_labels = (key2value_mask * edge_labels).int()
      else:
          new_edge_labels = torch.zeros_like(edge_labels).int()
  
          tmp_edge_scores = new_edge_scores.clone().cpu()
          tmp_edge_scores[~edge_labels_mask] = -1
          tmp_edge_scores[~key2value_mask] = -1
          # Greedily extract valid edges
          while (tmp_edge_scores > -1).any():
              i, j = np.unravel_index(
                  torch.argmax(tmp_edge_scores), tmp_edge_scores.shape
              )
              new_edge_labels[i, j] = 1
              if self.link_type == "one-to-one":
                  tmp_edge_scores[i, :] = -1
                  tmp_edge_scores[:, j] = -1
              elif self.link_type == "one-to-many":
                  tmp_edge_scores[:, j] = -1
              elif self.link_type == "many-to-one":
                  tmp_edge_scores[i, :] = -1
  
      return new_edge_scores.cpu(), new_edge_labels.cpu()
  
file_path: mmocr/mmocr/models/kie/postprocessors/sdmgr_postprocessor.py
imports: from typing import List, Optional, Tuple
  import numpy
  import torch
  from mmengine.structures import InstanceData
  from torch import Tensor, nn
  from mmocr.registry import MODELS
  from mmocr.structures import KIEDataSample
label_desc: Use image node_id 3 for calling the SDMGRPostProcessor obj's underlying member method code with example usage: obj.decode_edges(node_labels, edge_scores, edge_labels) and returns: new_edge_scores,  new_edge_labels