class_docstr: A customizable graph neural network (GNN) block.
  
  :param in_keys: One key identifying the input tensors.
  :param out_keys: One key identifying the output tensors.
  :param in_shapes: List of input shapes.
  :param edges: List of graph edges required for message passing (aggregation).
  :param aggregate: The aggregation function to use (max, mean, sum).
  :param hidden_units: List containing the number of hidden units for hidden layers.
  :param non_lin: The non-linearity to apply after each layer.
  :param with_layer_norm: If True layer normalization is applied.
  :param node2node_aggr: If True node to node message passing is applied.
  :param edge2node_aggr: If True edge to node message passing is applied.
  :param node2edge_aggr: If True node to edge message passing is applied.
  :param edge2edge_aggr: If True edge to edge message passing is applied.
  :param with_node_embedding: If True the node embedding is computed.
  :param with_edge_embedding: If True the edge embedding is computed.
docstr: implementation of :class:`~maze.perception.blocks.shape_normalization.ShapeNormalizationBlock` interface
          
docstr_tok: ["implementation","of",":","class",":","`","~maze.perception.blocks.shape_normalization.ShapeNormalizationBlock","`","interface"]
code: def normalized_forward(
      self, block_input: Dict[str, torch.Tensor]
  ) -> Dict[str, torch.Tensor]:
      """implementation of :class:`~maze.perception.blocks.shape_normalization.ShapeNormalizationBlock` interface"""
  
      # check input tensor
      node_tensor = block_input[self.in_keys[0]]
      edge_tensor = block_input[self.in_keys[1]]
      assert node_tensor.ndim == self.in_num_dims[0]
      assert edge_tensor.ndim == self.in_num_dims[1]
  
      # forward pass
      node_embedding, edge_embedding = self._compute_embeddings(
          node_tensor, edge_tensor
      )
  
      # check output tensor
      assert node_embedding.ndim == self.out_num_dims[0]
      assert edge_embedding.ndim == self.out_num_dims[1]
  
      # prepare layer output
      out_dict = dict()
  
      if self.with_node_embedding:
          out_dict[self.out_keys[0]] = node_embedding
  
      if self.with_edge_embedding:
          idx = 1 if self.with_node_embedding else 0
          out_dict[self.out_keys[idx]] = edge_embedding
  
      return out_dict
  
imports: from typing import Any, List, Tuple, Union, Sequence, Dict
  import numpy
  import torch
  from torch import nn
  from torch_scatter import scatter
  from maze.core.annotations import override
  from maze.core.utils.factory import Factory
  from maze.perception.blocks.shape_normalization import ShapeNormalizationBlock
label_desc: Use image node_id 2 for calling the GNNBlock obj's underlying member method code with example usage: obj.normalized_forward(block_input) and returns: out_dict