class_docstr: Bidirection version GCN layer from paper `GCN <https://arxiv.org/abs/1609.02907>`__.
  
  .. math::
      h_{i, \vdash}^{(l+1)} = \sigma(b^{(l)}_{\vdash} + \sum_{j\in\mathcal{N}_{\vdash}(i)}
      \frac{1}{c_{ij}}h_{j, \vdash}^{(l)}W^{(l)}_{\vdash})
  
      h_{i, \dashv}^{(l+1)} = \sigma(b^{(l)}_{\dashv} + \sum_{j\in\mathcal{N}_{\dashv}(i)}
      \frac{1}{c_{ij}}h_{j, \dashv}^{(l)}W^{(l)}_{\dashv})
class_docstr_tok: ["Bidirection","version","GCN","layer","from","paper","`","GCN","<","https",":","\/\/arxiv.org\/abs\/1609.02907",">","`","__",".","..","math",":",":","h_","{","i",",","\\vdash","}","^","{","(","l+1",")","}","=","\\sigma","(","b^","{","(","l",")","}","_","{","\\vdash","}","+","\\sum_","{","j\\in\\mathcal","{","N","}","_","{","\\vdash","}","(","i",")","}","\\frac","{","1","}","{","c_","{","ij","}","}","h_","{","j",",","\\vdash","}","^","{","(","l",")","}","W^","{","(","l",")","}","_","{","\\vdash","}",")","h_","{","i",",","\\dashv","}","^","{","(","l+1",")","}","=","\\sigma","(","b^","{","(","l",")","}","_","{","\\dashv","}","+","\\sum_","{","j\\in\\mathcal","{","N","}","_","{","\\dashv","}","(","i",")","}","\\frac","{","1","}","{","c_","{","ij","}","}","h_","{","j",",","\\dashv","}","^","{","(","l",")","}","W^","{","(","l",")","}","_","{","\\dashv","}",")"]
docstr: Description
  -----------
  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)`.
  
  Returns
  -------
  torch.Tensor
      The output feature
docstr_tok: ["Description","--","--","--","--","--","-","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",")","`",".","Returns","--","--","--","-","torch.Tensor","The","output","feature"]
code: def forward(
      self,
      graph,
      feat,
      weight=None,
      edge_weight=None,
      reverse_edge_weight=None,
  ):
      r"""
  
      Description
      -----------
      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)`.
  
      Returns
      -------
      torch.Tensor
          The output feature
      """
      feat_fw, feat_bw = feat
      feat_fw = self._feat_drop(feat_fw)
      feat_bw = self._feat_drop(feat_bw)
      if isinstance(weight, tuple):
          weight_fw, weight_bw = weight
      else:
          weight_fw = weight_bw = weight
  
      # forward direction
      with graph.local_scope():
          graph = graph.local_var()
  
          if self._gcn_norm == "both":
              degs = (
                  graph.out_degrees()
                  .to(feat_fw.device)
                  .float()
                  .clamp(min=1)
              )
              gcn_norm = torch.pow(degs, -0.5)
              shp = gcn_norm.shape + (1,) * (feat_fw.dim() - 1)
              gcn_norm = torch.reshape(gcn_norm, shp)
              feat_fw = feat_fw * gcn_norm
  
          if weight_fw is not None:
              if self.weight_fw 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_fw = self.weight_fw
  
          if self._input_size > self._output_size:
              # mult W first to reduce the feature size for aggregation.
              if weight_fw is not None:
                  feat_fw = torch.matmul(feat_fw, weight_fw)
              graph.srcdata["h"] = feat_fw
              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_fw = graph.dstdata["h"]
          else:
              # aggregate first then mult W
              graph.srcdata["h"] = feat_fw
              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_fw = graph.dstdata["h"]
              if weight_fw is not None:
                  rst_fw = torch.matmul(rst_fw, weight_fw)
  
          if self._gcn_norm != "none":
              degs = (
                  graph.in_degrees()
                  .to(feat_fw.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_fw.dim() - 1)
              gcn_norm = torch.reshape(gcn_norm, shp)
              rst_fw = rst_fw * gcn_norm
  
          if self.bias_fw is not None:
              rst_fw = rst_fw + self.bias_fw
  
          # residual
          if self.res_fc_fw is not None:
              h_dst_fw = feat[0]
              resval_fw = self.res_fc_fw(h_dst_fw).view(
                  h_dst_fw.shape[0], self._output_size
              )
              # .view(h_dst.shape[0], self._output_size)
              rst_fw = rst_fw + resval_fw
  
          if self._activation is not None:
              rst_fw = self._activation(rst_fw)
  
      # backward direction
      graph = graph.reverse()
      with graph.local_scope():
          graph = graph.local_var()
  
          if self._gcn_norm == "both":
              degs = (
                  graph.out_degrees()
                  .to(feat_bw.device)
                  .float()
                  .clamp(min=1)
              )
              gcn_norm = torch.pow(degs, -0.5)
              shp = gcn_norm.shape + (1,) * (feat_bw.dim() - 1)
              gcn_norm = torch.reshape(gcn_norm, shp)
              feat_bw = feat_bw * gcn_norm
  
          if weight_bw is not None:
              if self.weight_bw 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_bw = self.weight_bw
  
          if self._input_size > self._output_size:
              # mult W first to reduce the feature size for aggregation.
              if weight_bw is not None:
                  feat_bw = torch.matmul(feat_bw, weight_bw)
              graph.srcdata["h"] = feat_bw
              if reverse_edge_weight is None:
                  graph.update_all(
                      fn.copy_src(src="h", out="m"),
                      fn.sum(msg="m", out="h"),
                  )
              else:
                  graph.edata[
                      "reverse_edge_weight"
                  ] = reverse_edge_weight
                  graph.update_all(
                      fn.u_mul_e("h", "reverse_edge_weight", "m"),
                      fn.sum("m", "h"),
                  )
              rst_bw = graph.dstdata["h"]
          else:
              # aggregate first then mult W
              graph.srcdata["h"] = feat_bw
              if reverse_edge_weight is None:
                  graph.update_all(
                      fn.copy_src(src="h", out="m"),
                      fn.sum(msg="m", out="h"),
                  )
              else:
                  graph.edata["reverse_edge_weight"] = edge_weight
                  graph.update_all(
                      fn.u_mul_e("h", "reverse_edge_weight", "m"),
                      fn.sum("m", "h"),
                  )
              rst_bw = graph.dstdata["h"]
              if weight_bw is not None:
                  rst_bw = torch.matmul(rst_bw, weight_bw)
  
          if self._gcn_norm != "none":
              degs = (
                  graph.in_degrees()
                  .to(feat_bw.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_bw.dim() - 1)
              gcn_norm = torch.reshape(gcn_norm, shp)
              rst_bw = rst_bw * gcn_norm
  
          if self.bias_bw is not None:
              rst_bw = rst_bw + self.bias_bw
  
          # residual
          if self.res_fc_bw is not None:
              h_dst_bw = feat[1]
              resval_bw = self.res_fc_bw(h_dst_bw).view(
                  h_dst_bw.shape[0], self._output_size
              )
              rst_bw = rst_bw + resval_bw
  
          if self._activation is not None:
              rst_bw = self._activation(rst_bw)
  
      return [rst_fw, rst_bw]
  
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 BiSepGCNLayerConv obj's underlying member method code with example usage: obj.forward(graph, feat, weight, edge_weight, reverse_edge_weight) and returns: list