class_docstr: A block containing multiple subsequent graph (multi-head) attention stacks.
  
  One convolution stack consists of one graph multi-head attention in addition to an activation layer.
  The block expects the input tensors to have the form:
  
  - Feature matrix: first in_key: (batch-dim, num-of-nodes, feature-dim)
  - Adjacency matrix: second in_key: (batch-dim, num-of-nodes, num-of-nodes) (also symmetric)
  
  And returns a tensor of the form (batch-dim, num-of-nodes, feature-out-dim).
  
  :param in_keys: Two keys identifying the feature matrix and adjacency matrix respectively.
  :param out_keys: One key identifying the output tensors.
  :param in_shapes: List of input shapes.
  :param hidden_features: List containing the number of hidden features for hidden layers.
  :param non_lins: The non-linearity/ies to apply after each layer (the same in all layers, or a list corresponding
      to each layer).
  :param n_heads: The number of heads each stack should have. (default suggestion 8)
  :param attention_alpha: Specify the negative slope of the leakyReLU in each of the attention layers.
      parameter with init value :param node_self_importance. (default suggestion 0.2)
  :param avg_last_head_attentions: Specify whether to average the outputs from the attention head in the last layer
      of the attention stack. (default suggestion True or n_heads=0 in the last layer)
  :param attention_dropout: Specify the dropout to be within the layers applied on the computed attention.
class_docstr_tok: ["A","block","containing","multiple","subsequent","graph","(","multi-head",")","attention","stacks",".","One","convolution","stack","consists","of","one","graph","multi-head","attention","in","addition","to","an","activation","layer",".","The","block","expects","the","input","tensors","to","have","the","form",":","-","Feature","matrix",":","first","in_key",":","(","batch-dim",",","num-of-nodes",",","feature-dim",")","-","Adjacency","matrix",":","second","in_key",":","(","batch-dim",",","num-of-nodes",",","num-of-nodes",")","(","also","symmetric",")","And","returns","a","tensor","of","the","form","(","batch-dim",",","num-of-nodes",",","feature-out-dim",")",".",":","param","in_keys",":","Two","keys","identifying","the","feature","matrix","and","adjacency","matrix","respectively",".",":","param","out_keys",":","One","key","identifying","the","output","tensors",".",":","param","in_shapes",":","List","of","input","shapes",".",":","param","hidden_features",":","List","containing","the","number","of","hidden","features","for","hidden","layers",".",":","param","non_lins",":","The","non-linearity\/ies","to","apply","after","each","layer","(","the","same","in","all","layers",",","or","a","list","corresponding","to","each","layer",")",".",":","param","n_heads",":","The","number","of","heads","each","stack","should","have",".","(","default","suggestion","8",")",":","param","attention_alpha",":","Specify","the","negative","slope","of","the","leakyReLU","in","each","of","the","attention","layers",".","parameter","with","init","value",":","param","node_self_importance",".","(","default","suggestion","0.2",")",":","param","avg_last_head_attentions",":","Specify","whether","to","average","the","outputs","from","the","attention","head","in","the","last","layer","of","the","attention","stack",".","(","default","suggestion","True","or","n_heads=0","in","the","last","layer",")",":","param","attention_dropout",":","Specify","the","dropout","to","be","within","the","layers","applied","on","the","computed","attention","."]
code: def __init__(
      self,
      in_keys: Union[str, List[str]],
      out_keys: Union[str, List[str]],
      in_shapes: Union[Sequence[int], List[Sequence[int]]],
      hidden_features: List[int],
      non_lins: Union[
          str, type(nn.Module), List[str], List[type(nn.Module)]
      ],
      n_heads: Union[int, List[int]],
      attention_alpha: Union[List[float], float],
      avg_last_head_attentions: bool,
      attention_dropout: Union[float, List[float]],
  ):
      super().__init__(
          in_keys=in_keys,
          out_keys=out_keys,
          in_shapes=in_shapes,
          in_num_dims=[3, 3],
          out_num_dims=3,
      )
  
      # Assertions
      assert (
          len(self.in_keys) == 2
      ), "There should be two input keys, feature matrix + adjacency matrix"
      assert self.in_shapes[0][-2] == self.in_shapes[1][-1], (
          "The node dimension of the feature matrix should be "
          "the same as the adjacency matrix's rows and "
          f"columns {self.in_shapes}"
      )
      assert (
          self.in_shapes[1][-1] == self.in_shapes[1][-2]
      ), "The adjacency matrix has to be a square matrix"
      self.avg_last_head_attentions = avg_last_head_attentions
  
      # Specify dummy dict creation function for adjacency matrix:
      self.dummy_dict_creators[
          1
      ] = self._dummy_symmetric_adj_tensor_factory(self.in_shapes[1])
  
      # Init class objects
      self.input_features = self.in_shapes[0][-1]
      self.hidden_features = hidden_features
  
      # Create list of heads for each layer
      self.n_heads: List[int] = (
          n_heads
          if isinstance(n_heads, list)
          else [n_heads] * len(self.hidden_features)
      )
  
      # The output features of this block are equivalent to the specified last hidden features if
      #   :param avg_last_head_attention is set to true, otherwise the last output will be concatenated and as such is
      #   equivalent to the number of last hidden features times the last specified number of heads
      self.output_features = (
          self.hidden_features[-1]
          if self.avg_last_head_attentions
          else self.hidden_features[-1] * self.n_heads[-1]
      )
  
      # Create list of non-linearity's for each layer
      non_lins = (
          non_lins
          if isinstance(non_lins, list)
          else [non_lins] * len(self.hidden_features)
      )
      self.non_lins: List[type(nn.Module)] = [
          Factory(base_type=nn.Module).type_from_name(non_lin)
          for non_lin in non_lins
      ]
  
      # Create list of dropout for each layer
      self.attention_dropout = (
          attention_dropout
          if isinstance(attention_dropout, list)
          else [attention_dropout] * len(self.hidden_features)
      )
  
      # Create list of alpha for each layer
      self.attention_alpha = (
          attention_alpha
          if isinstance(attention_alpha, list)
          else [attention_alpha] * len(self.hidden_features)
      )
  
      # compile layer dictionary
      layer_dict = self.build_layer_dict()
  
      # compile network
      self.net = nn.Sequential(layer_dict)
  
imports: from collections import OrderedDict
  from typing import Union, List, Sequence, Dict
  import numpy
  import torch
  import torch.nn.functional
  from torch import nn
  from maze.core.annotations import override
  from maze.core.utils.factory import Factory
  from maze.perception.blocks.feed_forward.graph_conv import GraphAdjacencyMethods
  from maze.perception.blocks.shape_normalization import ShapeNormalizationBlock
label_desc: Use image node_id 1 to create a new GraphAttentionBlock object from inherited base classes: ShapeNormalizationBlock, GraphAdjacencyMethods with example: obj = GraphAttentionBlock(in_keys, out_keys, in_shapes, hidden_features, non_lins, n_heads, attention_alpha, avg_last_head_attentions, attention_dropout)