Repository: dmlc/dgl

Issue:
[Bug] `to_homo` does not return correct node/edge mapping
## 🐛 Bug

The `to_homo` API does not return a graph with correct node/edge mappings.

The steps to reproduce the bug:
```python
>>> import dgl
>>> from dgl.data.rdf import MUTAG
>>> dataset = MUTAG()
>>> g = dataset.graph
Graph(num_nodes={'SCHEMA': 3687, '_Literal': 1104, 'bond': 9317, 'd': 9529, 'hasStructure': 3526},
      num_edges={('SCHEMA', 'rdf-schema#subClassOf', 'SCHEMA'): 138, ('SCHEMA', 'rev-rdf-schema#subClassOf', 'SCHEMA'): 138, ('SCHEMA', 'owl#disjointWith', 'SCHEMA'): 6, ('SCHEMA', 'rev-owl#disjointWith', 'SCHEMA'): 6, ('SCHEMA', '22-rdf-syntax-ns#type', 'SCHEMA'): 3526, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'SCHEMA'): 3526, ('SCHEMA', 'rdf-schema#domain', 'SCHEMA'): 19, ('SCHEMA', 'rev-rdf-schema#domain', 'SCHEMA'): 19, ('SCHEMA', 'rdf-schema#range', 'SCHEMA'): 4, ('SCHEMA', 'rev-rdf-schema#range', 'SCHEMA'): 4, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'bond'): 9317, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'd'): 9529, ('bond', 'inBond', 'd'): 18634, ('bond', 'rev-hasBond', 'd'): 9317, ('bond', '22-rdf-syntax-ns#type', 'SCHEMA'): 9317, ('d', 'rev-inBond', 'bond'): 18634, ('d', 'hasBond', 'bond'): 9317, ('d', 'amesTestPositive', '_Literal'): 340, ('d', 'cytogen_ca', '_Literal'): 293, ('d', 'cytogen_sce', '_Literal'): 283, ('d', 'salmonella', '_Literal'): 305, ('d', 'salmonella_n', '_Literal'): 61, ('d', 'drosophila_rt', '_Literal'): 25, ('d', 'drosophila_slrl', '_Literal'): 25, ('d', 'mouse_lymph', '_Literal'): 203, ('d', 'charge', '_Literal'): 9189, ('d', 'chromaberr', '_Literal'): 55, ('d', 'chromex', '_Literal'): 35, ('d', 'micronuc_f', '_Literal'): 13, ('d', 'micronuc_m', '_Literal'): 13, ('d', 'salmonella_reduc', '_Literal'): 5, ('d', 'hasAtom', 'd'): 9189, ('d', 'rev-hasAtom', 'd'): 9189, ('d', 'hasStructure', 'hasStructure'): 3526, ('d', '22-rdf-syntax-ns#type', 'SCHEMA'): 9529, ('_Literal', 'rev-amesTestPositive', 'd'): 340, ('_Literal', 'rev-cytogen_ca', 'd'): 293, ('_Literal', 'rev-cytogen_sce', 'd'): 283, ('_Literal', 'rev-salmonella', 'd'): 305, ('_Literal', 'rev-salmonella_n', 'd'): 61, ('_Literal', 'rev-drosophila_rt', 'd'): 25, ('_Literal', 'rev-drosophila_slrl', 'd'): 25, ('_Literal', 'rev-mouse_lymph', 'd'): 203, ('_Literal', 'rev-charge', 'd'): 9189, ('_Literal', 'rev-chromaberr', 'd'): 55, ('_Literal', 'rev-chromex', 'd'): 35, ('_Literal', 'rev-micronuc_f', 'd'): 13, ('_Literal', 'rev-micronuc_m', 'd'): 13, ('_Literal', 'rev-salmonella_reduc', 'd'): 5, ('hasStructure', 'rev-hasStructure', 'd'): 3526},
      metagraph=[('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'bond'), ('SCHEMA', 'd'), ('bond', 'd'), ('bond', 'd'), ('bond', 'SCHEMA'), ('d', 'bond'), ('d', 'bond'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', 'd'), ('d', 'd'), ('d', 'hasStructure'), ('d', 'SCHEMA'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('hasStructure', 'd')])
>>> g_homo = dgl.to_homo(g)
>>> g_homo
Graph(num_nodes=27163, num_edges=148100,
      ndata_schemes={'_TYPE': Scheme(shape=(), dtype=torch.int64), '_ID': Scheme(shape=(), dtype=torch.int64)}
      edata_schemes={'_TYPE': Scheme(shape=(), dtype=torch.int64), '_ID': Scheme(shape=(), dtype=torch.int64)})
>>> g_homo.ndata['_ID'].max()
tensor(27162)
>>> len(np.unique(g_homo.ndata['_ID']))
27163
>>> g_homo.edata['_ID'].max()
tensor(148099)
>>> len(np.unique(g_homo.edata['_ID']))
148100
```

@soodoshll found this bug.

## Expected behavior

As described in the docstring of [`to_homo`](https://docs.dgl.ai/generated/dgl.to_homo.html?highlight=to_homo#dgl.to_homo), `g_homo.ndata['_ID']` should store the node mapping from the node index in current graph to their node index in the original graph (which should not be from 0 to 27162), likewise for edata.

## Environment

 - DGL Version (e.g., 1.0): master
 - Backend Library & Version (e.g., PyTorch 0.4.1, MXNet/Gluon 1.3): PyTorch 1.4
 - OS (e.g., Linux): mac
 - How you installed DGL (`conda`, `pip`, source): source
 - Build command you used (if compiling from source): default
 - Python version: 3.7
 - CUDA/cuDNN version (if applicable): nope
 - GPU models and configuration (e.g. V100): nope
 - Any other relevant information: nope

## Additional context

@BarclayII is it a desired behaviour?
[Bug] `to_homo` does not return correct node/edge mapping
## 🐛 Bug

The `to_homo` API does not return a graph with correct node/edge mappings.

The steps to reproduce the bug:
```python
>>> import dgl
>>> from dgl.data.rdf import MUTAG
>>> dataset = MUTAG()
>>> g = dataset.graph
Graph(num_nodes={'SCHEMA': 3687, '_Literal': 1104, 'bond': 9317, 'd': 9529, 'hasStructure': 3526},
      num_edges={('SCHEMA', 'rdf-schema#subClassOf', 'SCHEMA'): 138, ('SCHEMA', 'rev-rdf-schema#subClassOf', 'SCHEMA'): 138, ('SCHEMA', 'owl#disjointWith', 'SCHEMA'): 6, ('SCHEMA', 'rev-owl#disjointWith', 'SCHEMA'): 6, ('SCHEMA', '22-rdf-syntax-ns#type', 'SCHEMA'): 3526, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'SCHEMA'): 3526, ('SCHEMA', 'rdf-schema#domain', 'SCHEMA'): 19, ('SCHEMA', 'rev-rdf-schema#domain', 'SCHEMA'): 19, ('SCHEMA', 'rdf-schema#range', 'SCHEMA'): 4, ('SCHEMA', 'rev-rdf-schema#range', 'SCHEMA'): 4, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'bond'): 9317, ('SCHEMA', 'rev-22-rdf-syntax-ns#type', 'd'): 9529, ('bond', 'inBond', 'd'): 18634, ('bond', 'rev-hasBond', 'd'): 9317, ('bond', '22-rdf-syntax-ns#type', 'SCHEMA'): 9317, ('d', 'rev-inBond', 'bond'): 18634, ('d', 'hasBond', 'bond'): 9317, ('d', 'amesTestPositive', '_Literal'): 340, ('d', 'cytogen_ca', '_Literal'): 293, ('d', 'cytogen_sce', '_Literal'): 283, ('d', 'salmonella', '_Literal'): 305, ('d', 'salmonella_n', '_Literal'): 61, ('d', 'drosophila_rt', '_Literal'): 25, ('d', 'drosophila_slrl', '_Literal'): 25, ('d', 'mouse_lymph', '_Literal'): 203, ('d', 'charge', '_Literal'): 9189, ('d', 'chromaberr', '_Literal'): 55, ('d', 'chromex', '_Literal'): 35, ('d', 'micronuc_f', '_Literal'): 13, ('d', 'micronuc_m', '_Literal'): 13, ('d', 'salmonella_reduc', '_Literal'): 5, ('d', 'hasAtom', 'd'): 9189, ('d', 'rev-hasAtom', 'd'): 9189, ('d', 'hasStructure', 'hasStructure'): 3526, ('d', '22-rdf-syntax-ns#type', 'SCHEMA'): 9529, ('_Literal', 'rev-amesTestPositive', 'd'): 340, ('_Literal', 'rev-cytogen_ca', 'd'): 293, ('_Literal', 'rev-cytogen_sce', 'd'): 283, ('_Literal', 'rev-salmonella', 'd'): 305, ('_Literal', 'rev-salmonella_n', 'd'): 61, ('_Literal', 'rev-drosophila_rt', 'd'): 25, ('_Literal', 'rev-drosophila_slrl', 'd'): 25, ('_Literal', 'rev-mouse_lymph', 'd'): 203, ('_Literal', 'rev-charge', 'd'): 9189, ('_Literal', 'rev-chromaberr', 'd'): 55, ('_Literal', 'rev-chromex', 'd'): 35, ('_Literal', 'rev-micronuc_f', 'd'): 13, ('_Literal', 'rev-micronuc_m', 'd'): 13, ('_Literal', 'rev-salmonella_reduc', 'd'): 5, ('hasStructure', 'rev-hasStructure', 'd'): 3526},
      metagraph=[('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'SCHEMA'), ('SCHEMA', 'bond'), ('SCHEMA', 'd'), ('bond', 'd'), ('bond', 'd'), ('bond', 'SCHEMA'), ('d', 'bond'), ('d', 'bond'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', '_Literal'), ('d', 'd'), ('d', 'd'), ('d', 'hasStructure'), ('d', 'SCHEMA'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('_Literal', 'd'), ('hasStructure', 'd')])
>>> g_homo = dgl.to_homo(g)
>>> g_homo
Graph(num_nodes=27163, num_edges=148100,
      ndata_schemes={'_TYPE': Scheme(shape=(), dtype=torch.int64), '_ID': Scheme(shape=(), dtype=torch.int64)}
      edata_schemes={'_TYPE': Scheme(shape=(), dtype=torch.int64), '_ID': Scheme(shape=(), dtype=torch.int64)})
>>> g_homo.ndata['_ID'].max()
tensor(27162)
>>> len(np.unique(g_homo.ndata['_ID']))
27163
>>> g_homo.edata['_ID'].max()
tensor(148099)
>>> len(np.unique(g_homo.edata['_ID']))
148100
```

@soodoshll found this bug.

## Expected behavior

As described in the docstring of [`to_homo`](https://docs.dgl.ai/generated/dgl.to_homo.html?highlight=to_homo#dgl.to_homo), `g_homo.ndata['_ID']` should store the node mapping from the node index in current graph to their node index in the original graph (which should not be from 0 to 27162), likewise for edata.

## Environment

 - DGL Version (e.g., 1.0): master
 - Backend Library & Version (e.g., PyTorch 0.4.1, MXNet/Gluon 1.3): PyTorch 1.4
 - OS (e.g., Linux): mac
 - How you installed DGL (`conda`, `pip`, source): source
 - Build command you used (if compiling from source): default
 - Python version: 3.7
 - CUDA/cuDNN version (if applicable): nope
 - GPU models and configuration (e.g. V100): nope
 - Any other relevant information: nope

## Additional context

@BarclayII is it a desired behaviour?


Discussion:
I think the problem is that the `_ID` field is not empty in the original heterograph, e.g.
```
>>> g.nodes['SCHEMA']
NodeSpace(data={'_ID': tensor([    0, 25371, 25370,  ...,   178,   664,   189]), '_TYPE': tensor([0, 0, 0,  ..., 0, 0, 0])})
```

and in https://github.com/dmlc/dgl/blob/c2e61ce10b4704ce24133900731787c0bcc5847e/python/dgl/convert.py#L775-L780 the new assigned id mapping would be overwritten.
I think the problem is that the `_ID` field is not empty in the original heterograph, e.g.
```
>>> g.nodes['SCHEMA']
NodeSpace(data={'_ID': tensor([    0, 25371, 25370,  ...,   178,   664,   189]), '_TYPE': tensor([0, 0, 0,  ..., 0, 0, 0])})
```

and in https://github.com/dmlc/dgl/blob/c2e61ce10b4704ce24133900731787c0bcc5847e/python/dgl/convert.py#L775-L780 the new assigned id mapping would be overwritten.

Resolving patch:
diff --git a/python/dgl/convert.py b/python/dgl/convert.py
--- a/python/dgl/convert.py
+++ b/python/dgl/convert.py
@@ -683,11 +683,6 @@ def to_hetero(G, ntypes, etypes, ntype_field=NTYPE, etype_field=ETYPE,
                                    ntypes, ntype_count)})
 
     ntype2ngrp = {ntype : node_groups[ntid] for ntid, ntype in enumerate(ntypes)}
-    for ntid, ntype in enumerate(hg.ntypes):
-        hg._node_frames[ntid][NID] = F.tensor(ntype2ngrp[ntype])
-
-    for etid in range(len(hg.canonical_etypes)):
-        hg._edge_frames[etid][EID] = F.tensor(edge_groups[etid])
 
     # features
     for key, data in G.ndata.items():
@@ -699,6 +694,12 @@ def to_hetero(G, ntypes, etypes, ntype_field=NTYPE, etype_field=ETYPE,
             rows = F.copy_to(F.tensor(edge_groups[etid]), F.context(data))
             hg._edge_frames[etid][key] = F.gather_row(data, rows)
 
+    for ntid, ntype in enumerate(hg.ntypes):
+        hg._node_frames[ntid][NID] = F.tensor(ntype2ngrp[ntype])
+
+    for etid in range(len(hg.canonical_etypes)):
+        hg._edge_frames[etid][EID] = F.tensor(edge_groups[etid])
+
     return hg
 
 def to_homo(G):
@@ -766,12 +767,8 @@ def to_homo(G):
 
     retg = graph((F.cat(srcs, 0), F.cat(dsts, 0)), num_nodes=total_num_nodes,
                  validate=False, index_dtype=G._idtype_str)
-    retg.ndata[NTYPE] = F.cat(ntype_ids, 0)
-    retg.ndata[NID] = F.cat(nids, 0)
-    retg.edata[ETYPE] = F.cat(etype_ids, 0)
-    retg.edata[EID] = F.cat(eids, 0)
 
-    # features
+    # copy features
     comb_nf = combine_frames(G._node_frames, range(len(G.ntypes)))
     comb_ef = combine_frames(G._edge_frames, range(len(G.etypes)))
     if comb_nf is not None:
@@ -779,6 +776,12 @@ def to_homo(G):
     if comb_ef is not None:
         retg.edata.update(comb_ef)
 
+    # assign node type and id mapping field.
+    retg.ndata[NTYPE] = F.cat(ntype_ids, 0)
+    retg.ndata[NID] = F.cat(nids, 0)
+    retg.edata[ETYPE] = F.cat(etype_ids, 0)
+    retg.edata[EID] = F.cat(eids, 0)
+
     return retg
 
 ############################################################


Test patch: