class_docstr: GATv2 from `How Attentive are Graph Attention Networks?
  <https://arxiv.org/pdf/2105.14491.pdf>`__, with the sparse aggregation
  accelerated by cugraph-ops.
  
  Parameters
  ----------
  in_feats : int, or pair of ints
      Input feature size; i.e, the number of dimensions of :math:`h_i^{(l)}`.
      If the layer is to be applied to a unidirectional bipartite graph, `in_feats`
      specifies the input feature size on both the source and destination nodes.
      If a scalar is given, the source and destination node feature size
      would take the same value.
  out_feats : int
      Output feature size; i.e, the number of dimensions of :math:`h_i^{(l+1)}`.
  num_heads : int
      Number of heads in Multi-Head Attention.
  feat_drop : float, optional
      Dropout rate on feature. Defaults: ``0``.
  concat : bool, optional
      If False, the multi-head attentions are averaged instead of concatenated.
      Default: ``True``.
  edge_feats : int, optional
      Edge feature size. Default: ``None``.
  negative_slope : float, optional
      LeakyReLU angle of negative slope. Defaults: ``0.2``.
  residual : bool, optional
      If True, use residual connection. Defaults: ``False``.
  allow_zero_in_degree : bool, optional
      If there are 0-in-degree nodes in the graph, output for those nodes will
      be invalid since no message will be passed to those nodes. This is
      harmful for some applications causing silent performance regression.
      This module will raise a DGLError if it detects 0-in-degree nodes in
      input graph. By setting ``True``, it will suppress the check and let the
      users handle it by themselves. Defaults: ``False``.
  bias : bool, optional
      If set to :obj:`False`, the layer will not learn
      an additive bias. (default: :obj:`True`)
  share_weights : bool, optional
      If set to :obj:`True`, the same matrix for :math:`W_{left}` and
      :math:`W_{right}` in the above equations, will be applied to the source
      and the target node of every edge. (default: :obj:`False`)
docstr: Forward computation.
  
  Parameters
  ----------
  graph : DGLGraph or SparseGraph
      The graph.
  nfeat : torch.Tensor
      Input features of shape :math:`(N, D_{in})`.
  efeat: torch.Tensor, optional
      Optional edge features.
  max_in_degree : int
      Maximum in-degree of destination nodes. When :attr:`g` is generated
      from a neighbor sampler, the value should be set to the corresponding
      :attr:`fanout`. This option is used to invoke the MFG-variant of
      cugraph-ops kernel.
  
  Returns
  -------
  torch.Tensor
      The output feature of shape :math:`(N, H, D_{out})` where
      :math:`H` is the number of heads, and :math:`D_{out}` is size of
      output feature.
docstr_tok: ["Forward","computation",".","Parameters","--","--","--","--","--","graph",":","DGLGraph","or","SparseGraph","The","graph",".","nfeat",":","torch.Tensor","Input","features","of","shape",":","math",":","`","(","N",",","D_","{","in","}",")","`",".","efeat",":","torch.Tensor",",","optional","Optional","edge","features",".","max_in_degree",":","int","Maximum","in-degree","of","destination","nodes",".","When",":","attr",":","`","g","`","is","generated","from","a","neighbor","sampler",",","the","value","should","be","set","to","the","corresponding",":","attr",":","`","fanout","`",".","This","option","is","used","to","invoke","the","MFG-variant","of","cugraph-ops","kernel",".","Returns","--","--","--","-","torch.Tensor","The","output","feature","of","shape",":","math",":","`","(","N",",","H",",","D_","{","out","}",")","`","where",":","math",":","`","H","`","is","the","number","of","heads",",","and",":","math",":","`","D_","{","out","}","`","is","size","of","output","feature","."]
code: def forward(
      self,
      g: Union[SparseGraph, dgl.DGLHeteroGraph],
      nfeat: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]],
      efeat: Optional[torch.Tensor] = None,
      max_in_degree: Optional[int] = None,
  ) -> torch.Tensor:
      r"""Forward computation.
  
      Parameters
      ----------
      graph : DGLGraph or SparseGraph
          The graph.
      nfeat : torch.Tensor
          Input features of shape :math:`(N, D_{in})`.
      efeat: torch.Tensor, optional
          Optional edge features.
      max_in_degree : int
          Maximum in-degree of destination nodes. When :attr:`g` is generated
          from a neighbor sampler, the value should be set to the corresponding
          :attr:`fanout`. This option is used to invoke the MFG-variant of
          cugraph-ops kernel.
  
      Returns
      -------
      torch.Tensor
          The output feature of shape :math:`(N, H, D_{out})` where
          :math:`H` is the number of heads, and :math:`D_{out}` is size of
          output feature.
      """
  
      if isinstance(g, dgl.DGLHeteroGraph):
          if not self.allow_zero_in_degree:
              if (g.in_degrees() == 0).any():
                  raise dgl.base.DGLError(
                      "There are 0-in-degree nodes in the graph, "
                      "output for those nodes will be invalid. "
                      "This is harmful for some applications, "
                      "causing silent performance regression. "
                      "Adding self-loop on the input graph by "
                      "calling `g = dgl.add_self_loop(g)` will resolve "
                      "the issue. Setting ``allow_zero_in_degree`` "
                      "to be `True` when constructing this module will "
                      "suppress the check and let the code run."
                  )
  
      nfeat_bipartite = isinstance(nfeat, (list, tuple))
      graph_bipartite = nfeat_bipartite or self.share_weights is False
  
      _graph = self.get_cugraph_ops_CSC(
          g, is_bipartite=graph_bipartite, max_in_degree=max_in_degree
      )
  
      if nfeat_bipartite:
          nfeat = (self.feat_drop(nfeat[0]), self.feat_drop(nfeat[1]))
          nfeat_dst_orig = nfeat[1]
      else:
          nfeat = self.feat_drop(nfeat)
          nfeat_dst_orig = nfeat[: g.num_dst_nodes()]
  
      if efeat is not None:
          if self.lin_edge is None:
              raise RuntimeError(
                  f"{self.__class__.__name__}.edge_feats must be set to "
                  f"accept edge features."
              )
          efeat = self.lin_edge(efeat)
  
      if nfeat_bipartite:
          nfeat = (self.lin_src(nfeat[0]), self.lin_dst(nfeat[1]))
      elif graph_bipartite:
          nfeat = (
              self.lin_src(nfeat),
              self.lin_dst(nfeat[: g.num_dst_nodes()]),
          )
      else:
          nfeat = self.lin_src(nfeat)
  
      out = ops_torch.operators.mha_gat_v2_n2n(
          nfeat,
          self.attn,
          _graph,
          num_heads=self.num_heads,
          activation="LeakyReLU",
          negative_slope=self.negative_slope,
          concat_heads=self.concat,
          edge_feat=efeat,
      )[: g.num_dst_nodes()]
  
      if self.concat:
          out = out.view(-1, self.num_heads, self.out_feats)
  
      if self.residual:
          res = self.lin_res(nfeat_dst_orig).view(
              -1, self.num_heads, self.out_feats
          )
          if not self.concat:
              res = res.mean(dim=1)
          out = out + res
  
      if self.bias is not None:
          out = out + self.bias
  
      return out
  
imports: from typing import Optional, Tuple, Union
  from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph
  from cugraph.utilities.utils import import_optional
label_desc: Use image node_id 3 for calling the GATv2Conv obj's underlying member method code with example usage: obj.forward(g, nfeat, efeat, max_in_degree) and returns: out