class_docstr: Multi-layer Graph Attention Network (GAT).
  Support both `unidirectional GAT
  <https://arxiv.org/abs/1710.10903>`__ and bidirectional versions
  including `GAT-BiSep <https://arxiv.org/abs/1808.07624>`__ and
  `GAT-BiFuse <https://arxiv.org/abs/1908.04942>`__.
  
  .. math::
      h_i^{(l+1)} = \sum_{j\in \mathcal{N}(i)} \alpha_{i,j} W^{(l)} h_j^{(l)}
  where :math:`\alpha_{ij}` is the attention score bewteen node :math:`i` and
  node :math:`j`:
  
  .. math::
      \alpha_{ij}^{l} & = \mathrm{softmax_i} (e_{ij}^{l})
      e_{ij}^{l} & = \mathrm{LeakyReLU}\left(\vec{a}^T [W h_{i} \| W h_{j}]\right)
  
  Parameters
  ----------
  num_layers: int
      Number of GAT layers.
  input_size : int, or pair of ints
      Input feature size.
      If the layer is to be applied to a unidirectional bipartite graph, ``input_size``
      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.
  hidden_size: int or list of int
      Hidden size per GAT layer. If ``int`` is given, all layers are forced to have
      the same hidden size.
  output_size : int
      Output feature size.
  heads : int or list of int
      Number of heads per GAT layer. If ``int`` is given, all layers are forced to
      have the same number of heads.
  direction_option: str
      Whether to use unidirectional (i.e., regular "undirected") or bidirectional (i.e., "bi_sep"
      and "bi_fuse") versions.
      Default : ``'bi_sep'``.
  feat_drop : float, optional
      Dropout rate on feature, default: ``0``.
  attn_drop : float, optional
      Dropout rate on attention weight, default: ``0``.
  negative_slope : float, optional
      LeakyReLU angle of negative slope, default: ``0.2``.
  residual : bool, optional
      If True, use residual connection.
      Default: ``False``.
  activation : callable activation function/layer or None, optional.
      If not None, applies an activation function to the updated node features.
      Default: ``None``.
  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``.
docstr: Compute multi-layer graph attention network.
  
  Parameters
  ----------
  graph : GraphData
      The graph data containing topology and features.
  
  Returns
  -------
  GraphData
      The output graph data containing updated embeddings.
docstr_tok: ["Compute","multi-layer","graph","attention","network",".","Parameters","--","--","--","--","--","graph",":","GraphData","The","graph","data","containing","topology","and","features",".","Returns","--","--","--","-","GraphData","The","output","graph","data","containing","updated","embeddings","."]
code: def forward(self, graph):
      r"""Compute multi-layer graph attention network.
  
      Parameters
      ----------
      graph : GraphData
          The graph data containing topology and features.
  
      Returns
      -------
      GraphData
          The output graph data containing updated embeddings.
      """
      feat = graph.node_features["node_feat"]
      dgl_graph = graph.to_dgl()
  
      if self.direction_option == "bi_sep":
          h = [feat, feat]
      else:
          h = feat
  
      for l in range(self.num_layers - 1):
          h = self.gat_layers[l](dgl_graph, h)
          if self.direction_option == "bi_sep":
              h = [each.flatten(1) for each in h]
          else:
              h = h.flatten(1)
  
      # output projection
      logits = self.gat_layers[-1](dgl_graph, h)
  
      if self.direction_option == "bi_sep":
          logits = [each.mean(1) for each in logits]
          logits = torch.cat(logits, -1)
      else:
          logits = logits.mean(1)
  
      graph.node_features["node_emb"] = logits
  
      return graph
  
imports: import dgl.function
  import torch
  import torch.nn
  from dgl.base import DGLError
  from dgl.nn.pytorch import GATConv
  from dgl.nn.pytorch.softmax import edge_softmax
  from dgl.utils import expand_as_pair
  from ..utils.generic_utils import Identity
  from .base import GNNBase, GNNLayerBase
label_desc: Use image node_id 2 for calling the GAT obj's underlying member method code with example usage: obj.forward(graph) and returns: graph