class_docstr: GraphSAGE layer from paper `Inductive Representation Learning on
  Large Graphs <https://arxiv.org/pdf/1706.02216.pdf>`__.
  
  .. math::
      h_{\mathcal{N}(i)}^{(l+1)} & = \mathrm{aggregate}
      \left(\{h_{j}^{l}, \forall j \in \mathcal{N}(i) \}\right)
      h_{i}^{(l+1)} & = \sigma \left(W \cdot \mathrm{concat}
      (h_{i}^{l}, h_{\mathcal{N}(i)}^{l+1} + b) \right)
      h_{i}^{(l+1)} & = \mathrm{norm}(h_{i}^{l})
  
  Parameters
  ----------
  input_size : int, or pair of ints
      Input feature size.
      If the layer is to be applied on a unidirectional bipartite graph, ``in_feats``
      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.
      If aggregator type is ``gcn``, the feature size of source and destination nodes
      are required to be the same.
  out_size : int
      Output feature size.
  feat_drop : float
      Dropout rate on features, default: ``0``.
  aggregator_type : str
      Aggregator type to use (``mean``, ``gcn``, ``pool``, ``lstm``).
  bias : bool
      If True, adds a learnable bias to the output. Default: ``True``.
  norm : callable activation function/layer or None, optional
      If not None, applies normalization to the updated node features.
  activation : callable activation function/layer or None, optional
      If not None, applies an activation function to the updated node features.
      Default: ``None``.
class_docstr_tok: ["GraphSAGE","layer","from","paper","`","Inductive","Representation","Learning","on","Large","Graphs","<","https",":","\/\/arxiv.org\/pdf\/1706.02216.pdf",">","`","__",".","..","math",":",":","h_","{","\\mathcal","{","N","}","(","i",")","}","^","{","(","l+1",")","}","&","=","\\mathrm","{","aggregate","}","\\left","(","\\","{","h_","{","j","}","^","{","l","}",",","\\forall","j","\\in","\\mathcal","{","N","}","(","i",")","\\","}","\\right",")","h_","{","i","}","^","{","(","l+1",")","}","&","=","\\sigma","\\left","(","W","\\cdot","\\mathrm","{","concat","}","(","h_","{","i","}","^","{","l","}",",","h_","{","\\mathcal","{","N","}","(","i",")","}","^","{","l+1","}","+","b",")","\\right",")","h_","{","i","}","^","{","(","l+1",")","}","&","=","\\mathrm","{","norm","}","(","h_","{","i","}","^","{","l","}",")","Parameters","--","--","--","--","--","input_size",":","int",",","or","pair","of","ints","Input","feature","size",".","If","the","layer","is","to","be","applied","on","a","unidirectional","bipartite","graph",",","``","in_feats","``","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",".","If","aggregator","type","is","``","gcn","``",",","the","feature","size","of","source","and","destination","nodes","are","required","to","be","the","same",".","out_size",":","int","Output","feature","size",".","feat_drop",":","float","Dropout","rate","on","features",",","default",":","``","0","``",".","aggregator_type",":","str","Aggregator","type","to","use","(","``","mean","``",",","``","gcn","``",",","``","pool","``",",","``","lstm","``",")",".","bias",":","bool","If","True",",","adds","a","learnable","bias","to","the","output",".","Default",":","``","True","``",".","norm",":","callable","activation","function\/layer","or","None",",","optional","If","not","None",",","applies","normalization","to","the","updated","node","features",".","activation",":","callable","activation","function\/layer","or","None",",","optional","If","not","None",",","applies","an","activation","function","to","the","updated","node","features",".","Default",":","``","None","``","."]
docstr: Compute GraphSAGE layer.
  
  Parameters
  ----------
  graph : DGLGraph
      The graph.
  feat : torch.Tensor or pair of torch.Tensor
      If a torch.Tensor is given, the input feature of shape :math:`(N, D_{in})` where
      :math:`D_{in}` is size of input feature, :math:`N` is the number of nodes.
      If a pair of torch.Tensor is given, the pair must contain two tensors of shape
      :math:`(N_{in}, D_{in_{src}})` and :math:`(N_{out}, D_{in_{dst}})`.
  edge_weighht: torch.tensor
         Only needed when consider the edge weights in message passing.
  Returns
  -------
  torch.Tensor
      The output feature of shape :math:`(N, D_{out})` where :math:`D_{out}`
      is size of output feature.
docstr_tok: ["Compute","GraphSAGE","layer",".","Parameters","--","--","--","--","--","graph",":","DGLGraph","The","graph",".","feat",":","torch.Tensor","or","pair","of","torch.Tensor","If","a","torch.Tensor","is","given",",","the","input","feature","of","shape",":","math",":","`","(","N",",","D_","{","in","}",")","`","where",":","math",":","`","D_","{","in","}","`","is","size","of","input","feature",",",":","math",":","`","N","`","is","the","number","of","nodes",".","If","a","pair","of","torch.Tensor","is","given",",","the","pair","must","contain","two","tensors","of","shape",":","math",":","`","(","N_","{","in","}",",","D_","{","in_","{","src","}","}",")","`","and",":","math",":","`","(","N_","{","out","}",",","D_","{","in_","{","dst","}","}",")","`",".","edge_weighht",":","torch.tensor","Only","needed","when","consider","the","edge","weights","in","message","passing",".","Returns","--","--","--","-","torch.Tensor","The","output","feature","of","shape",":","math",":","`","(","N",",","D_","{","out","}",")","`","where",":","math",":","`","D_","{","out","}","`","is","size","of","output","feature","."]
code: def forward(
      self, graph, feat, edge_weight=None, reverse_edge_weight=None
  ):
      r"""Compute GraphSAGE layer.
  
      Parameters
      ----------
      graph : DGLGraph
          The graph.
      feat : torch.Tensor or pair of torch.Tensor
          If a torch.Tensor is given, the input feature of shape :math:`(N, D_{in})` where
          :math:`D_{in}` is size of input feature, :math:`N` is the number of nodes.
          If a pair of torch.Tensor is given, the pair must contain two tensors of shape
          :math:`(N_{in}, D_{in_{src}})` and :math:`(N_{out}, D_{in_{dst}})`.
      edge_weighht: torch.tensor
             Only needed when consider the edge weights in message passing.
      Returns
      -------
      torch.Tensor
          The output feature of shape :math:`(N, D_{out})` where :math:`D_{out}`
          is size of output feature.
      """
      graph = graph.local_var()
  
      if isinstance(feat, tuple):
          feat_src = self.feat_drop(feat[0])
          feat_dst = self.feat_drop(feat[1])
      else:
          feat_src = feat_dst = self.feat_drop(feat)
  
      h_self = feat_dst
  
      if self._aggre_type == "mean":
          graph.srcdata["h"] = feat_src
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src("h", "m"), fn.mean("m", "neigh")
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"),
                  fn.mean("m", "neigh"),
              )
          h_neigh = graph.dstdata["neigh"]
      elif self._aggre_type == "gcn":
          check_eq_shape(feat)
          graph.srcdata["h"] = feat_src
          graph.dstdata["h"] = feat_dst  # same as above if homogeneous
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src("h", "m"), fn.sum("m", "neigh")
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"),
                  fn.sum("m", "neigh"),
              )
          # divide in_degrees
          degs = graph.in_degrees().to(feat_dst)
          h_neigh = (graph.dstdata["neigh"] + graph.dstdata["h"]) / (
              degs.unsqueeze(-1) + 1
          )
      elif self._aggre_type == "pool":
          graph.srcdata["h"] = F.relu(self.fc_pool(feat_src))
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src("h", "m"), fn.max("m", "neigh")
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"),
                  fn.max("m", "neigh"),
              )
          h_neigh = graph.dstdata["neigh"]
      elif self._aggre_type == "lstm":
          graph.srcdata["h"] = feat_src
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src("h", "m"), self._lstm_reducer
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"),
                  self._lstm_reducer,
              )
          h_neigh = graph.dstdata["neigh"]
      else:
          raise KeyError(
              "Aggregator type {} not recognized.".format(
                  self._aggre_type
              )
          )
  
      # GraphSAGE GCN does not require fc_self.
      if self._aggre_type == "gcn":
          rst = self.fc_neigh(h_neigh)
      else:
          rst = self.fc_self(h_self) + self.fc_neigh(h_neigh)
      # activation
      if self.activation is not None:
          rst = self.activation(rst)
      # normalization
      if self.norm is not None:
          rst = self.norm(rst)
      return rst
  
imports: import warnings
  import dgl.function
  import torch
  import torch.nn
  import torch.nn.functional
  from dgl.utils import check_eq_shape, expand_as_pair
  from .base import GNNBase, GNNLayerBase
label_desc: Use image node_id 4 for calling the UndirectedGraphSAGELayerConv obj's underlying member method code with example usage: obj.forward(graph, feat, edge_weight, reverse_edge_weight) and returns: rst