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

        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)
        # generate A^i X
        feat_partial = []
        for o in range(self.order + 1):
            feat_partial.append(self._f_feat_trans(feat_in, self.f_lin[o]))
        for o in range(1,self.order + 1):
            for s in range(o):
                for i in range(self.mulhead):
                    feat_partial[o][i] = self._aggregate_attention(
                        adj_norm,
                        feat_partial[o][i],
                        feat_partial[o - s - 1][i],
                        self.attention[o-1][i],
                    )
        if self.bias == 'norm':
            # normalize per-order, per-head
            for o in range(self.order + 1):
                for i in range(self.mulhead):
                    mean = feat_partial[o][i].mean(dim=1).unsqueeze(1)
                    var = feat_partial[o][i].var(dim=1, unbiased=False).unsqueeze(1) + 1e-9
                    feat_partial[o][i] = (feat_partial[o][i] - mean) \
                                * self.scale[o][i] * torch.rsqrt(var) + self.offset[o][i]

        for o in range(self.order + 1):
            feat_partial[o] = torch.cat(feat_partial[o], 1)
        if self.aggr == 'mean':
            feat_out = feat_partial[0]
            for o in range(len(feat_partial) - 1):
                feat_out += feat_partial[o + 1]
        elif self.aggr == 'concat':
            feat_out = torch.cat(feat_partial, 1)
        else:
            raise NotImplementedError
        if self.bias == 'norm-nn':
            feat_out = self.f_norm(feat_out)
        return adj_norm, feat_out

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

        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