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`)
code: def __init__(
      self,
      in_feats: Union[int, Tuple[int, int]],
      out_feats: int,
      num_heads: int,
      feat_drop: float = 0.0,
      concat: bool = True,
      edge_feats: Optional[int] = None,
      negative_slope: float = 0.2,
      residual: bool = False,
      allow_zero_in_degree: bool = False,
      bias: bool = True,
      share_weights: bool = False,
  ):
      super().__init__()
      self.in_feats = in_feats
      self.out_feats = out_feats
      self.in_feats_src, self.in_feats_dst = dgl.utils.expand_as_pair(
          in_feats
      )
      self.num_heads = num_heads
      self.feat_drop = nn.Dropout(feat_drop)
      self.concat = concat
      self.edge_feats = edge_feats
      self.negative_slope = negative_slope
      self.allow_zero_in_degree = allow_zero_in_degree
      self.share_weights = share_weights
  
      self.lin_src = nn.Linear(
          self.in_feats_src, num_heads * out_feats, bias=bias
      )
      if share_weights:
          if self.in_feats_src != self.in_feats_dst:
              raise ValueError(
                  f"Input feature size of source and destination "
                  f"nodes must be identical when share_weights is enabled, "
                  f"but got {self.in_feats_src} and {self.in_feats_dst}."
              )
          self.lin_dst = self.lin_src
      else:
          self.lin_dst = nn.Linear(
              self.in_feats_dst, num_heads * out_feats, bias=bias
          )
  
      self.attn = nn.Parameter(torch.Tensor(num_heads * out_feats))
  
      if edge_feats is not None:
          self.lin_edge = nn.Linear(
              edge_feats, num_heads * out_feats, bias=False
          )
      else:
          self.register_parameter("lin_edge", None)
  
      if bias and concat:
          self.bias = nn.Parameter(torch.Tensor(num_heads, out_feats))
      elif bias and not concat:
          self.bias = nn.Parameter(torch.Tensor(out_feats))
      else:
          self.register_buffer("bias", None)
  
      self.residual = (
          residual and self.in_feats_dst != out_feats * num_heads
      )
      if self.residual:
          self.lin_res = nn.Linear(
              self.in_feats_dst, num_heads * out_feats, bias=bias
          )
      else:
          self.register_buffer("lin_res", None)
  
      self.reset_parameters()
  
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 1 to create a new GATv2Conv object from inherited base classes: BaseConv with example: obj = GATv2Conv(in_feats, out_feats, num_heads, feat_drop, concat, edge_feats, negative_slope, residual, allow_zero_in_degree, bias, share_weights)