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}^{(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}^{(l)}W^{(l)}_{\dashv})
  
      r_{i}^{l} &= \sigma (W_{f}[h_{i, \vdash}^{l};h_{i, \dashv}^{l};
              h_{i, \vdash}^{l}*h_{i, \dashv}^{l};
              h_{i, \vdash}^{l}-h_{i, \dashv}^{l}])
  
  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``.
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","}","^","{","(","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","}","^","{","(","l",")","}","W^","{","(","l",")","}","_","{","\\dashv","}",")","r_","{","i","}","^","{","l","}","&","=","\\sigma","(","W_","{","f","}","[","h_","{","i",",","\\vdash","}","^","{","l","}",";","h_","{","i",",","\\dashv","}","^","{","l","}",";","h_","{","i",",","\\vdash","}","^","{","l","}","*","h_","{","i",",","\\dashv","}","^","{","l","}",";","h_","{","i",",","\\vdash","}","^","{","l","}","-h_","{","i",",","\\dashv","}","^","{","l","}","]",")","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","``","."]
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 = self._feat_drop(feat)
      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
  
          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 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"]
              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
  
          if self._activation is not None:
              rst_bw = self._activation(rst_bw)
  
      fuse_vector = torch.cat(
          [rst_fw, rst_bw, rst_fw * rst_bw, rst_fw - rst_bw], dim=-1
      )
      fuse_gate_vector = torch.sigmoid(self.fuse_linear(fuse_vector))
      rst = fuse_gate_vector * rst_fw + (1 - fuse_gate_vector) * rst_bw
  
      if self.res_fc is not None:
          h_dst = feat
          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 BiFuseGCNLayerConv obj's underlying member method code with example usage: obj.forward(graph, feat, weight, edge_weight, reverse_edge_weight) and returns: rst