HeteroGraphConv with custom layers

Hi,
I am new to this library and to the domain of Graph Neural Networks, and I am having trouble with message passing on an Heterograph. I am trying to build a HeteroGraphConv with custom layers, but my dimensions are not matching.

Here is my custom Test_layer:

class Test_layer(nn.Module): #Replaces SAGEConv layer    
    def __init__(self,
             in_feats,
             out_feats):
        super(Test_layer, self).__init__()

        self._in_src_feats, self._in_dst_feats = dgl.utils.expand_as_pair(in_feats)
        self._out_feats = out_feats

        # NN through which the initial 'h' state passes
        self.fc_self = nn.Linear(self._in_dst_feats, out_feats)
        # NN through which the neighborhood messages passes
        self.fc_neigh = nn.Linear(self._in_src_feats, out_feats)            
    
    def forward(self, graph, feat):
               
        h_self = feat
        
        #get input features
        graph.srcdata['h'] = h_self
        
        #update nodes values
        graph.update_all( 
            fn.copy_src('h', 'm'), 
            fn.mean('m', 'neigh')) 
        
        h_neigh = graph.dstdata['neigh'] 
    
        print(h_self.shape)
        print(h_neigh.shape)

        rst = self.fc_self(h_self) + self.fc_neigh(h_neigh)
               
        return rst

Then, I call the HeteroGraph with some user and item features.

layer = dglnn.HeteroGraphConv({'buys':Test_layer(5,100),
                               'bought-by':Test_layer(3,100)},
                               aggregate='sum')

item_feat = torch.randn((g.number_of_nodes('item'), 3))
user_feat = torch.randn((g.number_of_nodes('user'), 5))
features = {'item' : item_feat, 'user' : user_feat }

out = layer(g, features)

I get the following error.
RuntimeError: The size of tensor a (1892) must match the size of tensor b (2492) at non-singleton dimension 0
Which makes sense, since my h_self is of shape torch.Size([1892, 5]) and my h_neigh of size torch.Size([2492, 5]).<

I believe the problem might be related to my h_neigh being dstdata[‘neigh’], but srcdata[‘neigh’] does not work.

How can I arrange this so that my h_neigh becomes the right shape, i.e. 1892 as well? I though that my reduce function fn_mean would do so, but it appears I am not making it work properly.

Thanks in advance for your answers and for helping me understand better heterographs in DGL!

Hi, would you mind providing more information about g (e.g. number of source nodes/destination nodes/edges of each type)?

Hi,
Yes. The graph is built from a adjacency list that was stored in a pandas dataframe.
image
The graph was created using these functions.

df_src = df.ctm_new_id.values
df_dst = df.pdt_new_id.values

g = dgl.heterograph({
          ('user', 'buys', 'item'): list(zip(df_src, df_dst)),
          ('item', 'bought-by', 'user'): list(zip(df_dst, df_src))          
    })

There are 1892 user nodes, 2492 item nodes, 5969 buys edges and 5969 bought-by edges.

I understand that with only this information, I could work with homogeneous graph and achieve the results wanted. However, I plan on adding more types of nodes, and thus would like to understand better the usage of heterographs.

Thanks!

If I understand correctly, your input graph consists of 1892 user nodes and 2492 item nodes, and you would like to compute the new item representation from the old item representation and the average of its neighboring user representations.

The implementation you gave however only has the user representation as input. To achieve your goal above you will actually need a pair of inputs, one for the user representation and another for the item representation, like this:

    def forward(self, graph, feat):
        h_src, h_dst = feat            # <---
        
        #get input features
        graph.srcdata['h'] = h_src     # <---
        
        #update nodes values
        graph.update_all( 
            fn.copy_src('h', 'm'), 
            fn.mean('m', 'neigh')) 
        
        h_neigh = graph.dstdata['neigh'] 

        rst = self.fc_self(h_dst) + self.fc_neigh(h_neigh)    # <---
               
        return rst

Hi @BarclayII,
Thank you for your answer, it worked!