class_docstr: Defines an edge inference function that takes source, destination node embeddings
  (node features) as input, and returns a numeric vector of output_dim size.
  
  This class takes as input as either:
  
   * A list of two tensors of shape (N, M) being the embeddings for each of the nodes in the link,
     where N is the number of links, and M is the node embedding size.
   * A single tensor of shape (..., N, 2, M) where the axis second from last indexes the nodes
     in the link and N is the number of links and M the embedding size.
  
  Examples:
      Consider two tensors containing the source and destination embeddings of size M::
  
          x_src = tf.constant(x_src, shape=(1, M), dtype="float32")
          x_dst = tf.constant(x_dst, shape=(1, M), dtype="float32")
  
          li = LinkEmbedding(method="ip", activation="sigmoid")([x_src, x_dst])
  
  .. seealso::
  
     Examples using this class:
  
     - `GCN link prediction <https://stellargraph.readthedocs.io/en/stable/demos/link-prediction/gcn-link-prediction.html>`__
     - `comparison of link prediction algorithms <https://stellargraph.readthedocs.io/en/stable/demos/link-prediction/homogeneous-comparison-link-prediction.html>`__
  
     Related functions: :func:`.link_inference`, :func:`.link_classification`, :func:`.link_regression`.
  
  Args:
      axis (int): If a single tensor is supplied this is the axis that indexes the node
          embeddings so that the indices 0 and 1 give the node embeddings to be combined.
          This is ignored if two tensors are supplied as a list.
      activation (str), optional: activation function applied to the output, one of "softmax", "sigmoid", etc.,
          or any activation function supported by Keras, see https://keras.io/activations/ for more information.
      method (str), optional: Name of the method of combining ``(src,dst)`` node features or embeddings into edge embeddings.
          One of:
  
          * ``concat`` -- concatenation,
          * ``ip`` or ``dot`` -- inner product, :math:`ip(u,v) = sum_{i=1..d}{u_i*v_i}`,
          * ``mul`` or ``hadamard`` -- element-wise multiplication, :math:`h(u,v)_i = u_i*v_i`,
          * ``l1`` -- L1 operator, :math:`l_1(u,v)_i = |u_i-v_i|`,
          * ``l2`` -- L2 operator, :math:`l_2(u,v)_i = (u_i-v_i)^2`,
          * ``avg`` -- average, :math:`avg(u,v) = (u+v)/2`.
  
          For all methods except ``ip`` or ``dot`` a dense layer is applied on top of the combined
          edge embedding to transform to a vector of size ``output_dim``.
class_docstr_tok: ["Defines","an","edge","inference","function","that","takes","source",",","destination","node","embeddings","(","node","features",")","as","input",",","and","returns","a","numeric","vector","of","output_dim","size",".","This","class","takes","as","input","as","either",":","*","A","list","of","two","tensors","of","shape","(","N",",","M",")","being","the","embeddings","for","each","of","the","nodes","in","the","link",",","where","N","is","the","number","of","links",",","and","M","is","the","node","embedding","size",".","*","A","single","tensor","of","shape","(","...",",","N",",","2",",","M",")","where","the","axis","second","from","last","indexes","the","nodes","in","the","link","and","N","is","the","number","of","links","and","M","the","embedding","size",".","Examples",":","Consider","two","tensors","containing","the","source","and","destination","embeddings","of","size","M",":",":","x_src","=","tf.constant","(","x_src",",","shape=","(","1",",","M",")",",","dtype=","''","float32","''",")","x_dst","=","tf.constant","(","x_dst",",","shape=","(","1",",","M",")",",","dtype=","''","float32","''",")","li","=","LinkEmbedding","(","method=","''","ip","''",",","activation=","''","sigmoid","''",")","(","[","x_src",",","x_dst","]",")","..","seealso",":",":","Examples","using","this","class",":","-","`","GCN","link","prediction","<","https",":","\/\/stellargraph.readthedocs.io\/en\/stable\/demos\/link-prediction\/gcn-link-prediction.html",">","`","__","-","`","comparison","of","link","prediction","algorithms","<","https",":","\/\/stellargraph.readthedocs.io\/en\/stable\/demos\/link-prediction\/homogeneous-comparison-link-prediction.html",">","`","__","Related","functions",":",":","func",":","`",".link_inference","`",",",":","func",":","`",".link_classification","`",",",":","func",":","`",".link_regression","`",".","Args",":","axis","(","int",")",":","If","a","single","tensor","is","supplied","this","is","the","axis","that","indexes","the","node","embeddings","so","that","the","indices","0","and","1","give","the","node","embeddings","to","be","combined",".","This","is","ignored","if","two","tensors","are","supplied","as","a","list",".","activation","(","str",")",",","optional",":","activation","function","applied","to","the","output",",","one","of","``","softmax","''",",","``","sigmoid","''",",","etc.",",","or","any","activation","function","supported","by","Keras",",","see","https",":","\/\/keras.io\/activations\/","for","more","information",".","method","(","str",")",",","optional",":","Name","of","the","method","of","combining","``","(","src",",","dst",")","``","node","features","or","embeddings","into","edge","embeddings",".","One","of",":","*","``","concat","``","--","concatenation",",","*","``","ip","``","or","``","dot","``","--","inner","product",",",":","math",":","`","ip","(","u",",","v",")","=","sum_","{","i=1","..","d","}","{","u_i","*","v_i","}","`",",","*","``","mul","``","or","``","hadamard","``","--","element-wise","multiplication",",",":","math",":","`","h","(","u",",","v",")","_i","=","u_i","*","v_i","`",",","*","``","l1","``","--","L1","operator",",",":","math",":","`","l_1","(","u",",","v",")","_i","=","|u_i-v_i|","`",",","*","``","l2","``","--","L2","operator",",",":","math",":","`","l_2","(","u",",","v",")","_i","=","(","u_i-v_i",")","^2","`",",","*","``","avg","``","--","average",",",":","math",":","`","avg","(","u",",","v",")","=","(","u+v",")","\/2","`",".","For","all","methods","except","``","ip","``","or","``","dot","``","a","dense","layer","is","applied","on","top","of","the","combined","edge","embedding","to","transform","to","a","vector","of","size","``","output_dim","``","."]
docstr: Apply the layer to the node embeddings in x. These embeddings are either:
  
    * A list of two tensors of shape (N, M) being the embeddings for each of the nodes in the link,
      where N is the number of links, and M is the node embedding size.
    * A single tensor of shape (..., N, 2, M) where the axis second from last indexes the nodes
      in the link and N is the number of links and M the embedding size.
docstr_tok: ["Apply","the","layer","to","the","node","embeddings","in","x",".","These","embeddings","are","either",":","*","A","list","of","two","tensors","of","shape","(","N",",","M",")","being","the","embeddings","for","each","of","the","nodes","in","the","link",",","where","N","is","the","number","of","links",",","and","M","is","the","node","embedding","size",".","*","A","single","tensor","of","shape","(","...",",","N",",","2",",","M",")","where","the","axis","second","from","last","indexes","the","nodes","in","the","link","and","N","is","the","number","of","links","and","M","the","embedding","size","."]
code: def call(self, x):
      """
      Apply the layer to the node embeddings in x. These embeddings are either:
  
        * A list of two tensors of shape (N, M) being the embeddings for each of the nodes in the link,
          where N is the number of links, and M is the node embedding size.
        * A single tensor of shape (..., N, 2, M) where the axis second from last indexes the nodes
          in the link and N is the number of links and M the embedding size.
  
      """
      # Currently GraphSAGE & HinSage output a list of two tensors being the embeddings
      # for each of the nodes in the link. However, GCN, GAT & other full-batch methods
      # return a tensor of shape (1, N, 2, M).
      # Detect and support both inputs
      if isinstance(x, (list, tuple)):
          if len(x) != 2:
              raise ValueError(
                  "Expecting a list of length 2 for link embedding"
              )
          x0, x1 = x
      elif isinstance(x, tf.Tensor):
          if int(x.shape[self.axis]) != 2:
              raise ValueError(
                  "Expecting a tensor of shape 2 along specified axis for link embedding"
              )
          x0, x1 = tf.unstack(x, axis=self.axis)
      else:
          raise TypeError("Expected a list, tuple, or Tensor as input")
  
      # Apply different ways to combine the node embeddings to a link embedding.
      if self.method in ["ip", "dot"]:
          out = tf.reduce_sum(x0 * x1, axis=-1, keepdims=True)
  
      elif self.method == "l1":
          # l1(u,v)_i = |u_i - v_i| - vector of the same size as u,v
          out = tf.abs(x0 - x1)
  
      elif self.method == "l2":
          # l2(u,v)_i = (u_i - v_i)^2 - vector of the same size as u,v
          out = tf.square(x0 - x1)
  
      elif self.method in ["mul", "hadamard"]:
          out = tf.multiply(x0, x1)
  
      elif self.method == "concat":
          out = Concatenate()([x0, x1])
  
      elif self.method == "avg":
          out = Average()([x0, x1])
  
      else:
          raise NotImplementedError(
              "{}: the requested method '{}' is not known/not implemented".format(
                  name, edge_embedding_method
              )
          )
  
      # Apply activation function
      out = self.activation(out)
  
      return out
  
file_path: stellargraph/stellargraph/layer/link_inference.py
imports: from typing import AnyStr, Optional, List, Tuple
  import tensorflow
  from tensorflow.keras.layers import Layer, Concatenate, Dense, Lambda, Multiply, Average, Reshape, Activation
  from tensorflow.keras import backend
  import warnings
label_desc: Use image node_id 3 for calling the LinkEmbedding obj's underlying member method code with example usage: obj.call(x) and returns: out