class_docstr: One layer of GAS.
class_docstr_tok: ["One","layer","of","GAS","."]
code: def forward(self, g, f_feat, b_feat, u_feat, v_feat):
      g.srcnodes["u"].data["h"] = u_feat
      g.srcnodes["v"].data["h"] = v_feat
      g.dstnodes["u"].data["h"] = u_feat[
          : g.number_of_dst_nodes(ntype="u")
      ]
      g.dstnodes["v"].data["h"] = v_feat[
          : g.number_of_dst_nodes(ntype="v")
      ]
      g.edges["forward"].data["h"] = f_feat
      g.edges["backward"].data["h"] = b_feat
  
      # formula 3 and 4 (optimized implementation to save memory)
      g.srcnodes["u"].data.update(
          {"he_u": self.u_linear(g.srcnodes["u"].data["h"])}
      )
      g.srcnodes["v"].data.update(
          {"he_v": self.v_linear(g.srcnodes["v"].data["h"])}
      )
      g.dstnodes["u"].data.update(
          {"he_u": self.u_linear(g.dstnodes["u"].data["h"])}
      )
      g.dstnodes["v"].data.update(
          {"he_v": self.v_linear(g.dstnodes["v"].data["h"])}
      )
      g.edges["forward"].data.update({"he_e": self.e_linear(f_feat)})
      g.edges["backward"].data.update({"he_e": self.e_linear(b_feat)})
      g.apply_edges(
          lambda edges: {
              "he": edges.data["he_e"]
              + edges.dst["he_u"]
              + edges.src["he_v"]
          },
          etype="backward",
      )
      g.apply_edges(
          lambda edges: {
              "he": edges.data["he_e"]
              + edges.src["he_u"]
              + edges.dst["he_v"]
          },
          etype="forward",
      )
      hf = g.edges["forward"].data["he"]
      hb = g.edges["backward"].data["he"]
      if self.activation is not None:
          hf = self.activation(hf)
          hb = self.activation(hb)
  
      # formula 6
      g.apply_edges(
          lambda edges: {
              "h_ve": th.cat([edges.src["h"], edges.data["h"]], -1)
          },
          etype="backward",
      )
      g.apply_edges(
          lambda edges: {
              "h_ue": th.cat([edges.src["h"], edges.data["h"]], -1)
          },
          etype="forward",
      )
  
      # formula 7, self-attention
      g.srcnodes["u"].data["h_att_u"] = self.W_ATTN_u(
          g.srcnodes["u"].data["h"]
      )
      g.srcnodes["v"].data["h_att_v"] = self.W_ATTN_v(
          g.srcnodes["v"].data["h"]
      )
      g.dstnodes["u"].data["h_att_u"] = self.W_ATTN_u(
          g.dstnodes["u"].data["h"]
      )
      g.dstnodes["v"].data["h_att_v"] = self.W_ATTN_v(
          g.dstnodes["v"].data["h"]
      )
  
      # Step 1: dot product
      g.apply_edges(
          fn.e_dot_v("h_ve", "h_att_u", "edotv"), etype="backward"
      )
      g.apply_edges(
          fn.e_dot_v("h_ue", "h_att_v", "edotv"), etype="forward"
      )
  
      # Step 2. softmax
      g.edges["backward"].data["sfm"] = edge_softmax(
          g["backward"], g.edges["backward"].data["edotv"]
      )
      g.edges["forward"].data["sfm"] = edge_softmax(
          g["forward"], g.edges["forward"].data["edotv"]
      )
  
      # Step 3. Broadcast softmax value to each edge, and then attention is done
      g.apply_edges(
          lambda edges: {
              "attn": edges.data["h_ve"] * edges.data["sfm"]
          },
          etype="backward",
      )
      g.apply_edges(
          lambda edges: {
              "attn": edges.data["h_ue"] * edges.data["sfm"]
          },
          etype="forward",
      )
  
      # Step 4. Aggregate attention to dst,user nodes, so formula 7 is done
      g.update_all(
          fn.copy_e("attn", "m"), fn.sum("m", "agg_u"), etype="backward"
      )
      g.update_all(
          fn.copy_e("attn", "m"), fn.sum("m", "agg_v"), etype="forward"
      )
  
      # formula 5
      h_nu = self.W_u(g.dstnodes["u"].data["agg_u"])
      h_nv = self.W_v(g.dstnodes["v"].data["agg_v"])
      if self.activation is not None:
          h_nu = self.activation(h_nu)
          h_nv = self.activation(h_nv)
  
      # Dropout
      hf = self.dropout(hf)
      hb = self.dropout(hb)
      h_nu = self.dropout(h_nu)
      h_nv = self.dropout(h_nv)
  
      # formula 8
      hu = th.cat([self.Vu(g.dstnodes["u"].data["h"]), h_nu], -1)
      hv = th.cat([self.Vv(g.dstnodes["v"].data["h"]), h_nv], -1)
  
      return hf, hb, hu, hv
  
file_path: dgl/examples/pytorch/gas/model_sampling.py
imports: import dgl.function
  import torch
  import torch.nn
  from dgl.nn.functional import edge_softmax
label_desc: Use image node_id 2 for calling the GASConv obj's underlying member method code with example usage: obj.forward(g, f_feat, b_feat, u_feat, v_feat) and returns: hf,  hb,  hu,  hv