def forward(self, inputs):
        """
        Inputs:
            inputs          tuple / list of two elements:
                            1. adj_norm: normalized subgraph adj. Normalization should
                               consider both the node degree and aggregation normalization
                            2. feat_in: 2D matrix of node features input to the layer

        Outputs:
            feat_out        2D matrix of features for output nodes of the layer
            adj_norm        normalized adj same as the input. We have to return it to
                            support nn.Sequential called in models.py
        """
        adj_norm, feat_in = inputs
        feat_in = self.f_dropout(feat_in)
        # compute gate value
        adj_norm_cpu = adj_norm.cpu()
        adj_norm_sp_csr = sp.coo_matrix(
            (
                adj_norm_cpu._values().numpy(),
                (
                    adj_norm_cpu._indices()[0].numpy(),
                    adj_norm_cpu._indices()[1].numpy(),
                ),
            ),
            shape=(adj_norm.shape[0], adj_norm.shape[0]),
        ).tocsr()
        gate_value = self._compute_gate_value(adj_norm, feat_in, adj_norm_sp_csr)
        feat_partial = []
        for i in range(self.order + 1):
            feat_partial.append(self._f_feat_trans(feat_in, self.f_lin[i]))
        for i in range(1, self.order + 1):
            for j in range(i):
                for k in range(self.multi_head):
                    feat_partial[i][k] = self._aggregate_attention(
                        adj_norm,
                        feat_partial[i][k],
                        feat_partial[i - j - 1][k],
                        self.attention[i - 1][k],
                    )
                    feat_partial[i][k] *= gate_value[:, k].unsqueeze(1)
        for i in range(self.order + 1):
            feat_partial[i] = torch.cat(feat_partial[i], 1)
        # if norm before concatenation, gate value vanishes
        if self.bias == "norm":
            feat_partial = self._batch_norm(feat_partial)
        if self.aggr == "mean":
            feat_out = feat_partial[0]
            for i in range(len(feat_partial) - 1):
                feat_out += feat_partial[i + 1]
        elif self.aggr == "concat":
            feat_out = torch.cat(feat_partial, 1)
        else:
            raise NotImplementedError
        return adj_norm, feat_out

        Inputs:
            inputs          tuple / list of two elements:
                            1. adj_norm: normalized subgraph adj. Normalization should
                               consider both the node degree and aggregation normalization
                            2. feat_in: 2D matrix of node features input to the layer

        Outputs:
            feat_out        2D matrix of features for output nodes of the layer
            adj_norm        normalized adj same as the input. We have to return it to
                            support nn.Sequential called in models.py
        
graphsaint/pytorch_version/layers.py