Unverified Commit 67cb7a43 authored by xiang song(charlie.song)'s avatar xiang song(charlie.song) Committed by GitHub
Browse files

[Feature] Deprecate multigraph (#1389)



* Deprecate multi-graph

* Handle heterograph and edge_ids

* lint

* Fix

* Remove multigraph in C++ end

* Fix lint

* Add some test and fix something

* Fix

* Fix

* upd

* Fix some test case

* Fix

* Fix
Co-authored-by: default avatarUbuntu <ubuntu@ip-172-31-51-214.ec2.internal>
Co-authored-by: default avatarJinjing Zhou <VoVAllen@users.noreply.github.com>
Co-authored-by: default avatarMinjie Wang <wmjlyjemaine@gmail.com>
parent 3efb5d8e
......@@ -5,6 +5,7 @@ import networkx as nx
import dgl
import backend as F
from dgl import DGLError
import pytest
# graph generation: a random graph with 10 nodes
# and 20 edges.
......@@ -58,7 +59,6 @@ def test_query():
assert g.number_of_nodes() == 10
assert g.number_of_edges() == 20
assert len(g) == 10
assert not g.is_multigraph
for i in range(10):
assert g.has_node(i)
......@@ -131,7 +131,6 @@ def test_query():
assert g.number_of_nodes() == 10
assert g.number_of_edges() == 20
assert len(g) == 10
assert not g.is_multigraph
for i in range(10):
assert g.has_node(i)
......@@ -203,6 +202,25 @@ def test_query():
_test_csr_one(g)
_test_csr_one(g)
def _test_edge_ids():
g = gen_by_mutation()
eids = g.edge_ids([4,0], [4,9])
assert eids.shape[0] == 2
eid = g.edge_id(4, 4)
assert isinstance(eid, int)
with pytest.raises(AssertionError):
eids = g.edge_ids([9,0], [4,9])
with pytest.raises(AssertionError):
eid = g.edge_id(4, 5)
g.add_edge(0, 4)
with pytest.raises(AssertionError):
eids = g.edge_ids([0,0], [4,9])
with pytest.raises(AssertionError):
eid = g.edge_id(0, 4)
_test(gen_by_mutation())
_test(gen_from_data(elist_input(), False, False))
_test(gen_from_data(elist_input(), True, False))
......@@ -214,6 +232,7 @@ def test_query():
_test_csr(gen_from_data(scipy_csr_input(), False, False))
_test_csr(gen_from_data(scipy_csr_input(), True, False))
_test_edge_ids()
def test_mutation():
g = dgl.DGLGraph()
......@@ -382,6 +401,17 @@ def test_find_edges():
finally:
assert fail
def test_ismultigraph():
g = dgl.DGLGraph()
g.add_nodes(10)
assert g.is_multigraph == False
g.add_edges([0], [0])
assert g.is_multigraph == False
g.add_edges([1], [2])
assert g.is_multigraph == False
g.add_edges([0, 2], [0, 3])
assert g.is_multigraph == True
if __name__ == '__main__':
test_query()
test_mutation()
......
......@@ -12,20 +12,20 @@ def generate_from_networkx():
edges = [[2, 3], [2, 5], [3, 0], [1, 0], [4, 3], [4, 5]]
nx_graph = nx.DiGraph()
nx_graph.add_edges_from(edges)
g = create_graph_index(nx_graph, multigraph=False, readonly=False)
ig = create_graph_index(nx_graph, multigraph=False, readonly=True)
g = create_graph_index(nx_graph, readonly=False)
ig = create_graph_index(nx_graph, readonly=True)
return g, ig
def generate_from_edgelist():
edges = [[2, 3], [2, 5], [3, 0], [6, 10], [10, 3], [10, 15]]
g = create_graph_index(edges, multigraph=False, readonly=False)
ig = create_graph_index(edges, multigraph=False, readonly=True)
g = create_graph_index(edges, readonly=False)
ig = create_graph_index(edges, readonly=True)
return g, ig
def generate_rand_graph(n):
arr = (sp.sparse.random(n, n, density=0.1, format='coo') != 0).astype(np.int64)
g = create_graph_index(arr, multigraph=False, readonly=False)
ig = create_graph_index(arr, multigraph=False, readonly=True)
g = create_graph_index(arr, readonly=False)
ig = create_graph_index(arr, readonly=True)
return g, ig
def check_graph_equal(g1, g2):
......@@ -161,7 +161,7 @@ def test_load_csr():
# Load CSR normally.
idx = dgl.graph_index.from_csr(
utils.toindex(csr.indptr), utils.toindex(csr.indices), False, 'out')
utils.toindex(csr.indptr), utils.toindex(csr.indices), 'out')
assert idx.number_of_nodes() == n
assert idx.number_of_edges() == csr.nnz
src, dst, eid = idx.edges()
......@@ -175,7 +175,7 @@ def test_load_csr():
if os.name is not 'nt':
idx = dgl.graph_index.from_csr(
utils.toindex(csr.indptr), utils.toindex(csr.indices),
False, 'out', '/test_graph_struct')
'out', '/test_graph_struct')
assert idx.number_of_nodes() == n
assert idx.number_of_edges() == csr.nnz
src, dst, eid = idx.edges()
......
......@@ -6,7 +6,7 @@ import scipy.sparse as ssp
import itertools
import backend as F
import networkx as nx
import unittest
import unittest, pytest
from dgl import DGLError
def create_test_heterograph():
......@@ -222,9 +222,9 @@ def test_query():
# edge_id & edge_ids
for i, (src, dst) in enumerate(zip(srcs, dsts)):
assert g.edge_id(src, dst, etype=etype) == i
assert F.asnumpy(g.edge_id(src, dst, etype=etype, force_multi=True)).tolist() == [i]
assert F.asnumpy(g.edge_id(src, dst, etype=etype, return_array=True)).tolist() == [i]
assert F.asnumpy(g.edge_ids(srcs, dsts, etype=etype)).tolist() == list(range(n_edges))
u, v, e = g.edge_ids(srcs, dsts, etype=etype, force_multi=True)
u, v, e = g.edge_ids(srcs, dsts, etype=etype, return_uv=True)
assert F.asnumpy(u).tolist() == srcs
assert F.asnumpy(v).tolist() == dsts
assert F.asnumpy(e).tolist() == list(range(n_edges))
......@@ -362,6 +362,31 @@ def test_hypersparse():
assert g.out_degree(N2, 'plays') == 0
assert F.asnumpy(g.out_degrees([0, N2], 'plays')).tolist() == [1, 0]
def test_edge_ids():
N1 = 1 << 50 # should crash if allocated a CSR
N2 = 1 << 48
g = dgl.heterograph({
('user', 'follows', 'user'): [(0, 1)],
('user', 'plays', 'game'): [(0, N2)]},
{'user': N1, 'game': N1})
with pytest.raises(AssertionError):
eids = g.edge_ids(0, 0, etype='follows')
with pytest.raises(AssertionError):
eid = g.edge_id(0, 0, etype='follows')
g2 = dgl.heterograph({
('user', 'follows', 'user'): [(0, 1), (0, 1)],
('user', 'plays', 'game'): [(0, N2)]},
{'user': N1, 'game': N1})
with pytest.raises(AssertionError):
eids = g2.edge_ids(0, 1, etype='follows')
with pytest.raises(AssertionError):
eid = g2.edge_id(0, 1, etype='follows')
def test_adj():
g = create_test_heterograph()
adj = F.sparse_to_numpy(g.adj(etype='follows'))
......@@ -521,9 +546,9 @@ def test_view1():
# edge_id & edge_ids
for i, (src, dst) in enumerate(zip(srcs, dsts)):
assert g.edge_id(src, dst) == i
assert F.asnumpy(g.edge_id(src, dst, force_multi=True)).tolist() == [i]
assert F.asnumpy(g.edge_id(src, dst, return_array=True)).tolist() == [i]
assert F.asnumpy(g.edge_ids(srcs, dsts)).tolist() == list(range(n_edges))
u, v, e = g.edge_ids(srcs, dsts, force_multi=True)
u, v, e = g.edge_ids(srcs, dsts, return_uv=True)
assert F.asnumpy(u).tolist() == srcs
assert F.asnumpy(v).tolist() == dsts
assert F.asnumpy(e).tolist() == list(range(n_edges))
......@@ -1474,6 +1499,24 @@ def test_isolated_ntype():
assert g.number_of_nodes('B') == 4
assert g.number_of_nodes('C') == 4
def test_ismultigraph():
g1 = dgl.bipartite([(0, 1), (0, 2), (1, 5), (2, 5)], 'A', 'AB', 'B', num_nodes=(6, 6))
assert g1.is_multigraph == False
g2 = dgl.bipartite([(0, 1), (0, 1), (0, 2), (1, 5)], 'A', 'AC', 'C', num_nodes=(6, 6))
assert g2.is_multigraph == True
g3 = dgl.graph([(0, 1), (1, 2)], 'A', 'AA', num_nodes=6)
assert g3.is_multigraph == False
g4 = dgl.graph([(0, 1), (0, 1), (1, 2)], 'A', 'AA', num_nodes=6)
assert g4.is_multigraph == True
g = dgl.hetero_from_relations([g1, g3])
assert g.is_multigraph == False
g = dgl.hetero_from_relations([g1, g2])
assert g.is_multigraph == True
g = dgl.hetero_from_relations([g1, g4])
assert g.is_multigraph == True
g = dgl.hetero_from_relations([g2, g4])
assert g.is_multigraph == True
def test_bipartite():
g1 = dgl.bipartite([(0, 1), (0, 2), (1, 5)], 'A', 'AB', 'B')
assert g1.is_unibipartite
......
......@@ -11,7 +11,6 @@ import pickle
import io
def _assert_is_identical(g, g2):
assert g.is_multigraph == g2.is_multigraph
assert g.is_readonly == g2.is_readonly
assert g.number_of_nodes() == g2.number_of_nodes()
src, dst = g.all_edges(order='eid')
......@@ -27,7 +26,6 @@ def _assert_is_identical(g, g2):
assert F.allclose(g.edata[k], g2.edata[k])
def _assert_is_identical_hetero(g, g2):
assert g.is_multigraph == g2.is_multigraph
assert g.is_readonly == g2.is_readonly
assert g.ntypes == g2.ntypes
assert g.canonical_etypes == g2.canonical_etypes
......@@ -53,7 +51,6 @@ def _assert_is_identical_hetero(g, g2):
assert F.allclose(g.edges[etype].data[k], g2.edges[etype].data[k])
def _assert_is_identical_nodeflow(nf1, nf2):
assert nf1.is_multigraph == nf2.is_multigraph
assert nf1.is_readonly == nf2.is_readonly
assert nf1.number_of_nodes() == nf2.number_of_nodes()
src, dst = nf1.all_edges()
......@@ -107,7 +104,7 @@ def test_pickling_index():
_assert_is_identical_index(i, i2)
def test_pickling_graph_index():
gi = create_graph_index(None, False, False)
gi = create_graph_index(None, False)
gi.add_nodes(3)
src_idx = toindex([0, 0])
dst_idx = toindex([1, 2])
......@@ -210,12 +207,12 @@ def test_pickling_graph():
_assert_is_identical(g, new_g)
# multigraph
g = dgl.DGLGraph([(0, 1), (0, 1), (1, 2)], multigraph=True)
g = dgl.DGLGraph([(0, 1), (0, 1), (1, 2)])
new_g = _reconstruct_pickle(g)
_assert_is_identical(g, new_g)
# readonly multigraph
g = dgl.DGLGraph([(0, 1), (0, 1), (1, 2)], multigraph=True, readonly=True)
g = dgl.DGLGraph([(0, 1), (0, 1), (1, 2)], readonly=True)
new_g = _reconstruct_pickle(g)
_assert_is_identical(g, new_g)
......
......@@ -27,7 +27,7 @@ def test_node_removal():
assert F.array_equal(g.ndata['id'], F.tensor([0, 7, 8, 9, 0, 0, 0]))
def test_multigraph_node_removal():
g = dgl.DGLGraph(multigraph=True)
g = dgl.DGLGraph()
g.add_nodes(5)
for i in range(5):
g.add_edge(i, i)
......@@ -53,7 +53,7 @@ def test_multigraph_node_removal():
assert g.number_of_edges() == 6
def test_multigraph_edge_removal():
g = dgl.DGLGraph(multigraph=True)
g = dgl.DGLGraph()
g.add_nodes(5)
for i in range(5):
g.add_edge(i, i)
......
......@@ -202,8 +202,8 @@ def create_large_graph_index(num_nodes):
row = np.random.choice(num_nodes, num_nodes * 10)
col = np.random.choice(num_nodes, num_nodes * 10)
spm = spsp.coo_matrix((np.ones(len(row)), (row, col)))
# It's possible that we generate a multigraph.
return from_scipy_sparse_matrix(spm, True, True)
return from_scipy_sparse_matrix(spm, True)
def get_nodeflow(g, node_ids, num_layers):
batch_size = len(node_ids)
......
......@@ -7,7 +7,7 @@
#include <dgl/graph.h>
TEST(GraphTest, TestNumVertices){
dgl::Graph g(false);
dgl::Graph g;
g.AddVertices(10);
ASSERT_EQ(g.NumVertices(), 10);
};
......@@ -257,9 +257,9 @@ def check_mem(gidx, cond_v, shared_v):
cond_v.release()
gidx1 = dgl.graph_index.from_shared_mem_csr_matrix("test_graph5", gidx.number_of_nodes(),
gidx.number_of_edges(), "in", False)
gidx.number_of_edges(), "in")
gidx2 = dgl.graph_index.from_shared_mem_csr_matrix("test_graph6", gidx.number_of_nodes(),
gidx.number_of_edges(), "out", False)
gidx.number_of_edges(), "out")
in_csr = gidx.adjacency_matrix_scipy(False, "csr")
out_csr = gidx.adjacency_matrix_scipy(True, "csr")
......@@ -288,7 +288,7 @@ def check_mem(gidx, cond_v, shared_v):
def test_copy_shared_mem():
csr = (spsp.random(num_nodes, num_nodes, density=0.1, format='csr') != 0).astype(np.int64)
gidx = dgl.graph_index.create_graph_index(csr, False, True)
gidx = dgl.graph_index.create_graph_index(csr, True)
cond_v = Condition()
shared_v = Value('i', 0)
......
......@@ -7,7 +7,7 @@ from dgl.graph_index import create_graph_index
from dgl.graph_index import from_scipy_sparse_matrix
def test_node_subgraph():
gi = create_graph_index(None, True, False)
gi = create_graph_index(None, False)
gi.add_nodes(4)
gi.add_edge(0, 1)
gi.add_edge(0, 2)
......@@ -22,7 +22,7 @@ def test_node_subgraph():
sgi.induced_nodes[s], sgi.induced_nodes[d])
def test_edge_subgraph():
gi = create_graph_index(None, True, False)
gi = create_graph_index(None, False)
gi.add_nodes(4)
gi.add_edge(0, 1)
gi.add_edge(0, 1)
......@@ -37,7 +37,7 @@ def test_edge_subgraph():
sgi.induced_nodes[s], sgi.induced_nodes[d])
def test_edge_subgraph_preserve_nodes():
gi = create_graph_index(None, True, False)
gi = create_graph_index(None, False)
gi.add_nodes(4)
gi.add_edge(0, 1)
gi.add_edge(0, 1)
......@@ -55,7 +55,7 @@ def test_edge_subgraph_preserve_nodes():
def test_immutable_edge_subgraph():
gi = create_graph_index(None, True, False)
gi = create_graph_index(None, False)
gi.add_nodes(4)
gi.add_edge(0, 1)
gi.add_edge(0, 1)
......@@ -71,7 +71,7 @@ def test_immutable_edge_subgraph():
sgi.induced_nodes[s], sgi.induced_nodes[d])
def test_immutable_edge_subgraph_preserve_nodes():
gi = create_graph_index(None, True, False)
gi = create_graph_index(None, False)
gi.add_nodes(4)
gi.add_edge(0, 1)
gi.add_edge(0, 1)
......@@ -92,8 +92,8 @@ def create_large_graph_index(num_nodes):
row = np.random.choice(num_nodes, num_nodes * 10)
col = np.random.choice(num_nodes, num_nodes * 10)
spm = spsp.coo_matrix((np.ones(len(row)), (row, col)))
# It's possible that we generate a multigraph.
return from_scipy_sparse_matrix(spm, True, True)
return from_scipy_sparse_matrix(spm, True)
def test_node_subgraph_with_halo():
gi = create_large_graph_index(1000)
......
......@@ -165,10 +165,10 @@ g.edata.pop('w')
###############################################################################
# Working with multigraphs
# ~~~~~~~~~~~~~~~~~~~~~~~~
# Many graph applications need parallel edges. To enable this, construct :class:`DGLGraph`
# with ``multigraph=True``.
# Many graph applications need parallel edges,
# which class:DGLGraph supports by default.
g_multi = dgl.DGLGraph(multigraph=True)
g_multi = dgl.DGLGraph()
g_multi.add_nodes(10)
g_multi.ndata['x'] = th.randn(10, 2)
......@@ -184,7 +184,7 @@ print(g_multi.edges())
# An edge in multigraph cannot be uniquely identified by using its incident nodes
# :math:`u` and :math:`v`; query their edge IDs use ``edge_id`` interface.
eid_10 = g_multi.edge_id(1, 0)
eid_10 = g_multi.edge_id(1, 0, return_array=True)
g_multi.edges[eid_10].data['w'] = th.ones(len(eid_10), 2)
print(g_multi.edata['w'])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment