class_docstr: SubgraphX from `On Explainability of Graph Neural Networks via Subgraph
  Explorations <https://arxiv.org/abs/2102.05152>`__, adapted for heterogeneous graphs
  
  It identifies the most important subgraph from the original graph that
  plays a critical role in GNN-based graph classification.
  
  It employs Monte Carlo tree search (MCTS) in efficiently exploring
  different subgraphs for explanation and uses Shapley values as the measure
  of subgraph importance.
  
  Parameters
  ----------
  model : nn.Module
      The GNN model to explain that tackles multiclass graph classification
  
      * Its forward function must have the form
        :attr:`forward(self, graph, nfeat)`.
      * The output of its forward function is the logits.
  num_hops : int
      Number of message passing layers in the model
  coef : float, optional
      This hyperparameter controls the trade-off between exploration and
      exploitation. A higher value encourages the algorithm to explore
      relatively unvisited nodes. Default: 10.0
  high2low : bool, optional
      If True, it will use the "High2low" strategy for pruning actions,
      expanding children nodes from high degree to low degree when extending
      the children nodes in the search tree. Otherwise, it will use the
      "Low2high" strategy. Default: True
  num_child : int, optional
      This is the number of children nodes to expand when extending the
      children nodes in the search tree. Default: 12
  num_rollouts : int, optional
      This is the number of rollouts for MCTS. Default: 20
  node_min : int, optional
      This is the threshold to define a leaf node based on the number of
      nodes in a subgraph. Default: 3
  shapley_steps : int, optional
      This is the number of steps for Monte Carlo sampling in estimating
      Shapley values. Default: 100
  log : bool, optional
      If True, it will log the progress. Default: False
class_docstr_tok: ["SubgraphX","from","`","On","Explainability","of","Graph","Neural","Networks","via","Subgraph","Explorations","<","https",":","\/\/arxiv.org\/abs\/2102.05152",">","`","__",",","adapted","for","heterogeneous","graphs","It","identifies","the","most","important","subgraph","from","the","original","graph","that","plays","a","critical","role","in","GNN-based","graph","classification",".","It","employs","Monte","Carlo","tree","search","(","MCTS",")","in","efficiently","exploring","different","subgraphs","for","explanation","and","uses","Shapley","values","as","the","measure","of","subgraph","importance",".","Parameters","--","--","--","--","--","model",":","nn.Module","The","GNN","model","to","explain","that","tackles","multiclass","graph","classification","*","Its","forward","function","must","have","the","form",":","attr",":","`","forward","(","self",",","graph",",","nfeat",")","`",".","*","The","output","of","its","forward","function","is","the","logits",".","num_hops",":","int","Number","of","message","passing","layers","in","the","model","coef",":","float",",","optional","This","hyperparameter","controls","the","trade-off","between","exploration","and","exploitation",".","A","higher","value","encourages","the","algorithm","to","explore","relatively","unvisited","nodes",".","Default",":","10.0","high2low",":","bool",",","optional","If","True",",","it","will","use","the","``","High2low","''","strategy","for","pruning","actions",",","expanding","children","nodes","from","high","degree","to","low","degree","when","extending","the","children","nodes","in","the","search","tree",".","Otherwise",",","it","will","use","the","``","Low2high","''","strategy",".","Default",":","True","num_child",":","int",",","optional","This","is","the","number","of","children","nodes","to","expand","when","extending","the","children","nodes","in","the","search","tree",".","Default",":","12","num_rollouts",":","int",",","optional","This","is","the","number","of","rollouts","for","MCTS",".","Default",":","20","node_min",":","int",",","optional","This","is","the","threshold","to","define","a","leaf","node","based","on","the","number","of","nodes","in","a","subgraph",".","Default",":","3","shapley_steps",":","int",",","optional","This","is","the","number","of","steps","for","Monte","Carlo","sampling","in","estimating","Shapley","values",".","Default",":","100","log",":","bool",",","optional","If","True",",","it","will","log","the","progress",".","Default",":","False"]
docstr: Compute Shapley value with Monte Carlo approximation.
  
  Parameters
  ----------
  subgraph_nodes : dict[str, Tensor]
      subgraph_nodes[nty] gives the tensor node IDs of node type nty
      in the subgraph, which are associated with this tree node
  
  Returns
  -------
  float
      Shapley value
docstr_tok: ["Compute","Shapley","value","with","Monte","Carlo","approximation",".","Parameters","--","--","--","--","--","subgraph_nodes",":","dict","[","str",",","Tensor","]","subgraph_nodes","[","nty","]","gives","the","tensor","node","IDs","of","node","type","nty","in","the","subgraph",",","which","are","associated","with","this","tree","node","Returns","--","--","--","-","float","Shapley","value"]
code: def shapley(self, subgraph_nodes):
      r"""Compute Shapley value with Monte Carlo approximation.
  
      Parameters
      ----------
      subgraph_nodes : dict[str, Tensor]
          subgraph_nodes[nty] gives the tensor node IDs of node type nty
          in the subgraph, which are associated with this tree node
  
      Returns
      -------
      float
          Shapley value
      """
      # Obtain neighboring nodes of the subgraph g_i, P'.
      local_regions = {
          ntype: nodes.tolist()
          for ntype, nodes in subgraph_nodes.items()
      }
      for _ in range(self.num_hops - 1):
          for c_etype in self.graph.canonical_etypes:
              src_ntype, _, dst_ntype = c_etype
              if (
                  src_ntype not in local_regions
                  or dst_ntype not in local_regions
              ):
                  continue
  
              in_neighbors, _ = self.graph.in_edges(
                  local_regions[dst_ntype], etype=c_etype
              )
              _, out_neighbors = self.graph.out_edges(
                  local_regions[src_ntype], etype=c_etype
              )
              local_regions[src_ntype] = list(
                  set(local_regions[src_ntype] + in_neighbors.tolist())
              )
              local_regions[dst_ntype] = list(
                  set(local_regions[dst_ntype] + out_neighbors.tolist())
              )
  
      split_point = self.graph.num_nodes()
      coalition_space = {
          ntype: list(
              set(local_regions[ntype])
              - set(subgraph_nodes[ntype].tolist())
          )
          + [split_point]
          for ntype in subgraph_nodes.keys()
      }
  
      marginal_contributions = []
      for _ in range(self.shapley_steps):
          selected_node_map = dict()
          for ntype, nodes in coalition_space.items():
              permuted_space = np.random.permutation(nodes)
              split_idx = int(
                  np.where(permuted_space == split_point)[0]
              )
              selected_node_map[ntype] = permuted_space[:split_idx]
  
          # Mask for coalition set S_i
          exclude_mask = {
              ntype: torch.ones(self.graph.num_nodes(ntype))
              for ntype in self.graph.ntypes
          }
          for ntype, region in local_regions.items():
              exclude_mask[ntype][region] = 0.0
          for ntype, selected_nodes in selected_node_map.items():
              exclude_mask[ntype][selected_nodes] = 1.0
  
          # Mask for set S_i and g_i
          include_mask = {
              ntype: exclude_mask[ntype].clone()
              for ntype in self.graph.ntypes
          }
          for ntype, subgn in subgraph_nodes.items():
              exclude_mask[ntype][subgn] = 1.0
  
          exclude_feat = {
              ntype: self.feat[ntype]
              * exclude_mask[ntype]
              .unsqueeze(1)
              .to(self.feat[ntype].device)
              for ntype in self.graph.ntypes
          }
          include_feat = {
              ntype: self.feat[ntype]
              * include_mask[ntype]
              .unsqueeze(1)
              .to(self.feat[ntype].device)
              for ntype in self.graph.ntypes
          }
  
          with torch.no_grad():
              exclude_probs = self.model(
                  self.graph, exclude_feat, **self.kwargs
              ).softmax(dim=-1)
              exclude_value = exclude_probs[:, self.target_class]
              include_probs = self.model(
                  self.graph, include_feat, **self.kwargs
              ).softmax(dim=-1)
              include_value = include_probs[:, self.target_class]
          marginal_contributions.append(include_value - exclude_value)
  
      return torch.cat(marginal_contributions).mean().item()
  
imports: import math
  import networkx
  import numpy
  import torch
  import torch.nn
  from ....None import to_heterogeneous, to_homogeneous
  from ....base import NID
  from ....convert import to_networkx
  from ....subgraph import node_subgraph
  from ....transforms.functional import remove_nodes
label_desc: Use image node_id 2 for calling the HeteroSubgraphX obj's underlying member method code with example usage: obj.shapley(subgraph_nodes) and returns: torch