Skip to content

Commit

Permalink
[Bugfix] edge order is not preserved when converting from edge list (d…
Browse files Browse the repository at this point in the history
  • Loading branch information
jermainewang authored Dec 5, 2018
1 parent a1d50f0 commit 2194b7d
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 10 deletions.
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def pytorch_unit_test(dev) {
def mxnet_unit_test(dev) {
withEnv(["DGL_LIBRARY_PATH=${env.WORKSPACE}/build", "PYTHONPATH=${env.WORKSPACE}/python"]) {
sh "python3 -m nose -v --with-xunit tests/mxnet"
sh "python3 -m nose -v --with-xunit tests/graph_index"
}
}

Expand Down
36 changes: 33 additions & 3 deletions python/dgl/graph_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,25 @@ def from_scipy_sparse_matrix(self, adj):
dst = utils.toindex(adj_coo.col)
self.add_edges(src, dst)

def from_edge_list(self, elist):
"""Convert from an edge list.
Paramters
---------
elist : list
List of (u, v) edge tuple.
"""
self.clear()
src, dst = zip(*elist)
src = np.array(src)
dst = np.array(dst)
num_nodes = max(src.max(), dst.max()) + 1
min_nodes = min(src.min(), dst.min())
if min_nodes != 0:
raise DGLError('Invalid edge list. Nodes must start from 0.')
self.add_nodes(num_nodes)
self.add_edges(utils.toindex(src), utils.toindex(dst))

def line_graph(self, backtracking=True):
"""Return the line graph of this graph.
Expand Down Expand Up @@ -868,7 +887,10 @@ def create_graph_index(graph_data=None, multigraph=False, readonly=False):
return graph_data

if readonly and graph_data is not None:
gi = create_immutable_graph_index(graph_data)
try:
gi = create_immutable_graph_index(graph_data)
except:
gi = None
# If we can't create an immutable graph index, we'll have to fall back.
if gi is not None:
return gi
Expand All @@ -879,19 +901,27 @@ def create_graph_index(graph_data=None, multigraph=False, readonly=False):
if graph_data is None:
return gi

# edge list
if isinstance(graph_data, (list, tuple)):
try:
gi.from_edge_list(graph_data)
return gi
except:
raise DGLError('Graph data is not a valid edge list.')

# scipy format
if isinstance(graph_data, scipy.sparse.spmatrix):
try:
gi.from_scipy_sparse_matrix(graph_data)
return gi
except:
raise Exception('Graph data is not a valid scipy sparse matrix.')
raise DGLError('Graph data is not a valid scipy sparse matrix.')

# networkx - any format
try:
gi.from_networkx(graph_data)
except:
raise Exception('Error while creating graph from input of type "%s".'
raise DGLError('Error while creating graph from input of type "%s".'
% type(graph_data))

return gi
Expand Down
44 changes: 37 additions & 7 deletions python/dgl/immutable_graph_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ._ffi.function import _init_api
from . import backend as F
from . import utils
from .base import ALL, is_all, dgl_warning
from .base import ALL, is_all, dgl_warning, DGLError

class ImmutableGraphIndex(object):
"""Graph index object on immutable graphs.
Expand All @@ -33,7 +33,7 @@ def add_nodes(self, num):
num : int
Number of nodes to be added.
"""
raise Exception('Immutable graph doesn\'t support adding nodes')
raise DGLError('Immutable graph doesn\'t support adding nodes')

def add_edge(self, u, v):
"""Add one edge.
Expand All @@ -45,7 +45,7 @@ def add_edge(self, u, v):
v : int
The dst node.
"""
raise Exception('Immutable graph doesn\'t support adding an edge')
raise DGLError('Immutable graph doesn\'t support adding an edge')

def add_edges(self, u, v):
"""Add many edges.
Expand All @@ -57,11 +57,11 @@ def add_edges(self, u, v):
v : utils.Index
The dst nodes.
"""
raise Exception('Immutable graph doesn\'t support adding edges')
raise DGLError('Immutable graph doesn\'t support adding edges')

def clear(self):
"""Clear the graph."""
raise Exception('Immutable graph doesn\'t support clearing up')
raise DGLError('Immutable graph doesn\'t support clearing up')

def is_multigraph(self):
"""Return whether the graph is a multigraph
Expand Down Expand Up @@ -592,6 +592,8 @@ def from_networkx(self, nx_graph):
def from_scipy_sparse_matrix(self, adj):
"""Convert from scipy sparse matrix.
NOTE: we assume the row is src nodes and the col is dst nodes.
Parameters
----------
adj : scipy sparse matrix
Expand All @@ -601,6 +603,26 @@ def from_scipy_sparse_matrix(self, adj):
out_mat = adj.tocoo()
self._sparse.from_coo_matrix(out_mat)

def from_edge_list(self, elist):
"""Convert from an edge list.
Paramters
---------
elist : list
List of (u, v) edge tuple.
"""
self.clear()
src, dst = zip(*elist)
src = np.array(src)
dst = np.array(dst)
num_nodes = max(src.max(), dst.max()) + 1
min_nodes = min(src.min(), dst.min())
if min_nodes != 0:
raise DGLError('Invalid edge list. Nodes must start from 0.')
data = np.ones((len(src),), dtype=np.int32)
spmat = sp.coo_matrix((data, (src, dst)), shape=(num_nodes, num_nodes))
self._sparse.from_coo_matrix(spmat)

def line_graph(self, backtracking=True):
"""Return the line graph of this graph.
Expand Down Expand Up @@ -728,19 +750,27 @@ def create_immutable_graph_index(graph_data=None):
# Let's create an empty graph index first.
gi = ImmutableGraphIndex(F.create_immutable_graph_index())

# edge list
if isinstance(graph_data, (list, tuple)):
try:
gi.from_edge_list(graph_data)
return gi
except:
raise DGLError('Graph data is not a valid edge list.')

# scipy format
if isinstance(graph_data, sp.spmatrix):
try:
gi.from_scipy_sparse_matrix(graph_data)
return gi
except:
raise Exception('Graph data is not a valid scipy sparse matrix.')
raise DGLError('Graph data is not a valid scipy sparse matrix.')

# networkx - any format
try:
gi.from_networkx(graph_data)
except:
raise Exception('Error while creating graph from input of type "%s".'
raise DGLError('Error while creating graph from input of type "%s".'
% type(graph_data))

return gi
Expand Down
11 changes: 11 additions & 0 deletions tests/graph_index/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,19 @@ def test_predsucc():
assert 2 in succ
assert 0 in succ

def test_create_from_elist():
elist = [(2, 1), (1, 0), (2, 0), (3, 0), (0, 2)]
g = create_graph_index(elist)
for i, (u, v) in enumerate(elist):
assert g.edge_id(u, v)[0] == i
# immutable graph
g = create_graph_index(elist, readonly=True)
for i, (u, v) in enumerate(elist):
print(u, v, g.edge_id(u, v)[0])
assert g.edge_id(u, v)[0] == i

if __name__ == '__main__':
test_edge_id()
test_nx()
test_predsucc()
test_create_from_elist()
12 changes: 12 additions & 0 deletions tests/pytorch/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import math
import numpy as np
import scipy.sparse as sp
import networkx as nx
import torch as th
import dgl
import utils as U
Expand All @@ -26,6 +27,16 @@ def test_graph_creation():
g.ndata['h'] = 3 * th.ones((5, 2))
assert U.allclose(3 * th.ones((5, 2)), g.ndata['h'])

def test_create_from_elist():
elist = [(2, 1), (1, 0), (2, 0), (3, 0), (0, 2)]
g = dgl.DGLGraph(elist)
for i, (u, v) in enumerate(elist):
assert g.edge_id(u, v) == i
# immutable graph
g = dgl.DGLGraph(elist, readonly=True)
for i, (u, v) in enumerate(elist):
assert g.edge_id(u, v) == i

def test_adjmat_speed():
n = 1000
p = 10 * math.log(n) / n
Expand Down Expand Up @@ -87,6 +98,7 @@ def test_incmat_speed():

if __name__ == '__main__':
test_graph_creation()
test_create_from_elist()
test_adjmat_speed()
test_incmat()
test_incmat_speed()

0 comments on commit 2194b7d

Please sign in to comment.