class_docstr: Description
  -----------
  Graph convolution was introduced in `GCN <https://arxiv.org/abs/1609.02907>`__
  and mathematically is defined as follows:
  
  .. math::
    h_i^{(l+1)} = \sigma(b^{(l)} + \sum_{j\in\mathcal{N}(i)}\frac{1}{c_{ij}}h_j^{(l)}W^{(l)})
  
  where :math:`\mathcal{N}(i)` is the set of neighbors of node :math:`i`,
  :math:`c_{ij}` is the product of the square root of node degrees
  (i.e.,  :math:`c_{ij} = \sqrt{|\mathcal{N}(i)|}\sqrt{|\mathcal{N}(j)|}`),
  and :math:`\sigma` is an activation function.
  
  Parameters
  ----------
  input_size : int
      Input feature size; i.e, the number of dimensions of :math:`h_j^{(l)}`.
  output_size : int
      Output feature size; i.e., the number of dimensions of :math:`h_i^{(l+1)}`.
  gcn_norm : str, optional
      How to apply the normalizer. If is `'right'`, divide the aggregated messages
      by each node's in-degrees, which is equivalent to averaging the received messages.
      If is `'none'`, no normalization is applied. Default is `'both'`,
      where the :math:`c_{ij}` in the paper is applied.
  weight : bool, optional
      If True, apply a linear layer. Otherwise, aggregating the messages
      without a weight matrix.
  bias : bool, optional
      If True, adds a learnable bias to the output. Default: ``True``.
  activation : callable activation function/layer or None, optional
      If not None, applies an activation function to the updated node features.
      Default: ``None``.
  allow_zero_in_degree : bool, optional
      If there are 0-in-degree nodes in the graph, output for those nodes will be invalid
      since no message will be passed to those nodes. This is harmful for some applications
      causing silent performance regression. This module will raise a DGLError if it detects
      0-in-degree nodes in input graph. By setting ``True``, it will suppress the check
      and let the users handle it by themselves. Default: ``False``.
  
  Attributes
  ----------
  weight : torch.Tensor
      The learnable weight tensor.
  bias : torch.Tensor
      The learnable bias tensor.
class_docstr_tok: ["Description","--","--","--","--","--","-","Graph","convolution","was","introduced","in","`","GCN","<","https",":","\/\/arxiv.org\/abs\/1609.02907",">","`","__","and","mathematically","is","defined","as","follows",":","..","math",":",":","h_i^","{","(","l+1",")","}","=","\\sigma","(","b^","{","(","l",")","}","+","\\sum_","{","j\\in\\mathcal","{","N","}","(","i",")","}","\\frac","{","1","}","{","c_","{","ij","}","}","h_j^","{","(","l",")","}","W^","{","(","l",")","}",")","where",":","math",":","`","\\mathcal","{","N","}","(","i",")","`","is","the","set","of","neighbors","of","node",":","math",":","`","i","`",",",":","math",":","`","c_","{","ij","}","`","is","the","product","of","the","square","root","of","node","degrees","(","i.e.",",",":","math",":","`","c_","{","ij","}","=","\\sqrt","{","|\\mathcal","{","N","}","(","i",")","|","}","\\sqrt","{","|\\mathcal","{","N","}","(","j",")","|","}","`",")",",","and",":","math",":","`","\\sigma","`","is","an","activation","function",".","Parameters","--","--","--","--","--","input_size",":","int","Input","feature","size",";","i.e",",","the","number","of","dimensions","of",":","math",":","`","h_j^","{","(","l",")","}","`",".","output_size",":","int","Output","feature","size",";","i.e.",",","the","number","of","dimensions","of",":","math",":","`","h_i^","{","(","l+1",")","}","`",".","gcn_norm",":","str",",","optional","How","to","apply","the","normalizer",".","If","is","`","'right","'","`",",","divide","the","aggregated","messages","by","each","node","'s","in-degrees",",","which","is","equivalent","to","averaging","the","received","messages",".","If","is","`","'none","'","`",",","no","normalization","is","applied",".","Default","is","`","'both","'","`",",","where","the",":","math",":","`","c_","{","ij","}","`","in","the","paper","is","applied",".","weight",":","bool",",","optional","If","True",",","apply","a","linear","layer",".","Otherwise",",","aggregating","the","messages","without","a","weight","matrix",".","bias",":","bool",",","optional","If","True",",","adds","a","learnable","bias","to","the","output",".","Default",":","``","True","``",".","activation",":","callable","activation","function\/layer","or","None",",","optional","If","not","None",",","applies","an","activation","function","to","the","updated","node","features",".","Default",":","``","None","``",".","allow_zero_in_degree",":","bool",",","optional","If","there","are","0-in-degree","nodes","in","the","graph",",","output","for","those","nodes","will","be","invalid","since","no","message","will","be","passed","to","those","nodes",".","This","is","harmful","for","some","applications","causing","silent","performance","regression",".","This","module","will","raise","a","DGLError","if","it","detects","0-in-degree","nodes","in","input","graph",".","By","setting","``","True","``",",","it","will","suppress","the","check","and","let","the","users","handle","it","by","themselves",".","Default",":","``","False","``",".","Attributes","--","--","--","--","--","weight",":","torch.Tensor","The","learnable","weight","tensor",".","bias",":","torch.Tensor","The","learnable","bias","tensor","."]
docstr: Compute graph convolution.
  
  Parameters
  ----------
  graph: DGLGraph
      The graph.
  
  feat: 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.
  
  weight: torch.Tensor, optional
      Optional external weight tensor.
  
  edge_weight: torch.Tensor
      Optional edge weight. edge_weight shape: "math:`(\text{num_edge}, 1)`.
  
  reverse_edge_weight: torch.Tensor
      Optional reverse edge weight. reverse_edge_weight shape: "math:`(\text{num_edge}, 1)`.
      For undirected GCN layer, reverse_edge_weight must be `None`.
  
  Returns
  -------
  torch.Tensor
      The output feature
docstr_tok: ["Compute","graph","convolution",".","Parameters","--","--","--","--","--","graph",":","DGLGraph","The","graph",".","feat",":","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",".","weight",":","torch.Tensor",",","optional","Optional","external","weight","tensor",".","edge_weight",":","torch.Tensor","Optional","edge","weight",".","edge_weight","shape",":","``","math",":","`","(","\\text","{","num_edge","}",",","1",")","`",".","reverse_edge_weight",":","torch.Tensor","Optional","reverse","edge","weight",".","reverse_edge_weight","shape",":","``","math",":","`","(","\\text","{","num_edge","}",",","1",")","`",".","For","undirected","GCN","layer",",","reverse_edge_weight","must","be","`","None","`",".","Returns","--","--","--","-","torch.Tensor","The","output","feature"]
code: def forward(
      self,
      graph,
      feat,
      weight=None,
      edge_weight=None,
      reverse_edge_weight=None,
  ):
      r"""Compute graph convolution.
  
      Parameters
      ----------
      graph: DGLGraph
          The graph.
  
      feat: 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.
  
      weight: torch.Tensor, optional
          Optional external weight tensor.
  
      edge_weight: torch.Tensor
          Optional edge weight. edge_weight shape: "math:`(\text{num_edge}, 1)`.
  
      reverse_edge_weight: torch.Tensor
          Optional reverse edge weight. reverse_edge_weight shape: "math:`(\text{num_edge}, 1)`.
          For undirected GCN layer, reverse_edge_weight must be `None`.
  
      Returns
      -------
      torch.Tensor
          The output feature
      """
      assert reverse_edge_weight is None
      graph = graph.local_var()
  
      feat_origin = feat
      feat = self._feat_drop(feat)
  
      if self._gcn_norm == "both":
          degs = (
              graph.out_degrees().to(feat.device).float().clamp(min=1)
          )
          gcn_norm = torch.pow(degs, -0.5)
          shp = gcn_norm.shape + (1,) * (feat.dim() - 1)
          gcn_norm = torch.reshape(gcn_norm, shp)
          feat = feat * gcn_norm
  
      if weight is not None:
          if self.weight is not None:
              raise RuntimeError(
                  "External weight is provided while at the same time the"
                  " module has defined its own weight parameter. Please"
                  " create the module with flag weight=False."
              )
      else:
          weight = self.weight
  
      if self._input_size > self._output_size:
          # mult W first to reduce the feature size for aggregation.
          if weight is not None:
              feat = torch.matmul(feat, weight)
          graph.srcdata["h"] = feat
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src(src="h", out="m"),
                  fn.sum(msg="m", out="h"),
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"), fn.sum("m", "h")
              )
          rst = graph.dstdata["h"]
      else:
          # aggregate first then mult W
          graph.srcdata["h"] = feat
          if edge_weight is None:
              graph.update_all(
                  fn.copy_src(src="h", out="m"),
                  fn.sum(msg="m", out="h"),
              )
          else:
              graph.edata["edge_weight"] = edge_weight
              graph.update_all(
                  fn.u_mul_e("h", "edge_weight", "m"), fn.sum("m", "h")
              )
          rst = graph.dstdata["h"]
          if weight is not None:
              rst = torch.matmul(rst, weight)
  
      if self._gcn_norm != "none":
          degs = graph.in_degrees().to(feat.device).float().clamp(min=1)
          if self._gcn_norm == "both":
              gcn_norm = torch.pow(degs, -0.5)
          else:
              gcn_norm = 1.0 / degs
          shp = gcn_norm.shape + (1,) * (feat.dim() - 1)
          gcn_norm = torch.reshape(gcn_norm, shp)
          rst = rst * gcn_norm
  
      if self.bias is not None:
          rst = rst + self.bias
  
      if self.res_fc is not None:
          h_dst = feat_origin
          resval = self.res_fc(h_dst).view(
              h_dst.shape[0], self._output_size
          )
          rst = rst + resval
  
      if self._activation is not None:
          rst = self._activation(rst)
  
      return rst
  
imports: import dgl.function
  import torch
  import torch.nn
  from torch.nn import init
  from .base import GNNBase, GNNLayerBase
label_desc: Use image node_id 4 for calling the UndirectedGCNLayerConv obj's underlying member method code with example usage: obj.forward(graph, feat, weight, edge_weight, reverse_edge_weight) and returns: rst