class_docstr: An RGCN layer from [schlichtkrull2018]_ updated to match the official implementation.
  
  This layer uses separate decompositions for forward and backward edges (i.e., "normal" and implicitly created
  inverse relations), as well as a separate transformation for self-loops.
  
  Ignoring dropouts, decomposition and normalization, it can be written as
  
  .. math ::
      y_i = \sigma(
          W^s x_i
          + \sum_{(e_j, r, e_i) \in \mathcal{T}} W^f_r x_j
          + \sum_{(e_i, r, e_j) \in \mathcal{T}} W^b_r x_j
          + b
      )
  
  where $b, W^s, W^f_r, W^b_r$ are trainable weights. $W^f_r, W^b_r$ are relation-specific, and commonly enmploy a
  weight-sharing mechanism, cf. Decomposition. $\sigma$ is an activation function. The individual terms in both sums
  are typically weighted. This is implemented by EdgeWeighting. Moreover, RGCN employs an edge-dropout, however,
  this needs to be done outside of an individual layer, since the same edges are dropped across all layers. In
  contrast, the self-loop dropout is layer-specific.
class_docstr_tok: ["An","RGCN","layer","from","[","schlichtkrull2018","]","_","updated","to","match","the","official","implementation",".","This","layer","uses","separate","decompositions","for","forward","and","backward","edges","(","i.e.",",","``","normal","''","and","implicitly","created","inverse","relations",")",",","as","well","as","a","separate","transformation","for","self-loops",".","Ignoring","dropouts",",","decomposition","and","normalization",",","it","can","be","written","as","..","math",":",":","y_i","=","\\sigma","(","W^s","x_i","+","\\sum_","{","(","e_j",",","r",",","e_i",")","\\in","\\mathcal","{","T","}","}","W^f_r","x_j","+","\\sum_","{","(","e_i",",","r",",","e_j",")","\\in","\\mathcal","{","T","}","}","W^b_r","x_j","+","b",")","where","$","b",",","W^s",",","W^f_r",",","W^b_r","$","are","trainable","weights",".","$","W^f_r",",","W^b_r","$","are","relation-specific",",","and","commonly","enmploy","a","weight-sharing","mechanism",",","cf",".","Decomposition",".","$","\\sigma","$","is","an","activation","function",".","The","individual","terms","in","both","sums","are","typically","weighted",".","This","is","implemented","by","EdgeWeighting",".","Moreover",",","RGCN","employs","an","edge-dropout",",","however",",","this","needs","to","be","done","outside","of","an","individual","layer",",","since","the","same","edges","are","dropped","across","all","layers",".","In","contrast",",","the","self-loop","dropout","is","layer-specific","."]
docstr: Calculate enriched entity representations.
  
  :param x: shape: (num_entities, input_dim)
      The input entity representations.
  :param source: shape: (num_triples,)
      The indices of the source entity per triple.
  :param target: shape: (num_triples,)
      The indices of the target entity per triple.
  :param edge_type: shape: (num_triples,)
      The relation type per triple.
  :param edge_weights: shape: (num_triples,)
      Scalar edge weights per triple.
  
  :return: shape: (num_entities, output_dim)
      Enriched entity representations.
code: def forward(
      self,
      x: torch.FloatTensor,
      source: torch.LongTensor,
      target: torch.LongTensor,
      edge_type: torch.LongTensor,
      edge_weights: Optional[torch.FloatTensor] = None,
  ) -> torch.FloatTensor:
      """
      Calculate enriched entity representations.
  
      :param x: shape: (num_entities, input_dim)
          The input entity representations.
      :param source: shape: (num_triples,)
          The indices of the source entity per triple.
      :param target: shape: (num_triples,)
          The indices of the target entity per triple.
      :param edge_type: shape: (num_triples,)
          The relation type per triple.
      :param edge_weights: shape: (num_triples,)
          Scalar edge weights per triple.
  
      :return: shape: (num_entities, output_dim)
          Enriched entity representations.
      """
      # TODO: we could cache the stacked adjacency matrices
      # self-loop
      y = self.dropout(self.self_loop(x))
      # forward messages
      y = self.fwd(
          x=x,
          source=source,
          target=target,
          edge_type=edge_type,
          edge_weights=edge_weights,
          accumulator=y,
      )
      # backward messages
      y = self.bwd(
          x=x,
          source=target,
          target=source,
          edge_type=edge_type,
          edge_weights=edge_weights,
          accumulator=y,
      )
      # activation
      if self.activation is not None:
          y = self.activation(y)
      return y
  
imports: import logging
  import math
  from abc import ABC, abstractmethod
  from typing import Any, Iterable, Mapping, Optional, Sequence
  import torch
  from class_resolver import ClassResolver, Hint, HintOrType, OptionalKwargs
  from class_resolver.contrib.torch import activation_resolver
  from docdata import parse_docdata
  from torch import nn
  from .init import uniform_norm_p1_, xavier_normal_
  from .representation import LowRankRepresentation, Representation
  from .utils import ShapeError, adjacency_tensor_to_stacked_matrix, use_horizontal_stacking
  from .weighting import EdgeWeighting, edge_weight_resolver
  from ..triples import CoreTriplesFactory
  from ..utils import ExtraReprMixin, einsum
label_desc: Use image node_id 2 for calling the RGCNLayer obj's underlying member method code with example usage: obj.forward(x, source, target, edge_type, edge_weights) and returns: y