From 1a754c55445f8e9501f4362560576eebfe154266 Mon Sep 17 00:00:00 2001 From: NoAmateur <99660257+NoAmateur@users.noreply.github.com> Date: Sun, 14 Jul 2024 00:17:02 +0800 Subject: [PATCH] improve pytest (#211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pytest lhb * pytest修改 * update * update * update * update * update * update * update * update --------- Co-authored-by: gyzhou2000 Co-authored-by: gyzhou2000 --- gammagl/datasets/blogcatalog.py | 17 +- gammagl/datasets/dblp.py | 7 +- gammagl/datasets/shapenet.py | 2 +- gammagl/layers/attention/edge_encoder.py | 6 +- gammagl/layers/attention/spatial_encoder.py | 4 + gammagl/layers/conv/compgcn_conv.py | 154 +++++++++--------- gammagl/utils/shortest_path.py | 2 +- gammagl/utils/to_dense_adj.py | 2 + tests/datasets/test_blogcatalog.py | 17 ++ tests/datasets/test_dblp.py | 18 ++ tests/datasets/test_ml.py | 15 ++ tests/datasets/test_modelnet40.py | 2 + tests/datasets/test_molecule_net.py | 17 ++ tests/datasets/test_shapenet.py | 13 ++ tests/datasets/test_webkb.py | 20 +++ tests/datasets/test_wikipedia_network.py | 15 ++ tests/datasets/test_zinc.py | 14 ++ .../attention/test_centrality_encoder.py | 14 ++ tests/layers/attention/test_edge_encoder.py | 26 +++ .../layers/attention/test_graphormer_layer.py | 22 +++ .../layers/attention/test_spatial_encoder.py | 15 ++ tests/layers/conv/test_compgcn_conv.py | 12 ++ tests/layers/conv/test_fusedgat_conv.py | 16 ++ tests/layers/conv/test_gaan_conv.py | 22 +++ tests/layers/conv/test_hid_conv.py | 26 +++ tests/layers/conv/test_iehgcn_conv.py | 40 +++++ tests/layers/conv/test_magcl_conv.py | 21 +++ tests/layers/conv/test_mgnni_m_iter.py | 24 +++ tests/layers/pool/test_glob.py | 38 +++++ tests/transforms/test_add_metapaths.py | 31 ++++ tests/transforms/test_base_transform.py | 36 ++++ tests/transforms/test_sign.py | 20 +++ tests/transforms/test_vgae_pre.py | 26 +++ tests/utils/test_check.py | 15 ++ tests/utils/test_convert.py | 33 ++++ tests/utils/test_corrupt_graph.py | 15 ++ tests/utils/test_data_utils.py | 18 ++ tests/utils/test_inspector.py | 49 ++++++ tests/utils/test_norm.py | 26 +++ tests/utils/test_num_nodes.py | 18 ++ tests/utils/test_read_embeddings.py | 30 ++++ tests/utils/test_shortest_path.py | 34 ++++ tests/utils/test_smiles.py | 29 ++++ tests/utils/test_spm_calc.py | 21 +++ tests/utils/test_to_dense_adj.py | 33 ++++ tests/utils/test_tu_utils.py | 34 ++++ tests/utils/test_typing.py | 31 ++++ tests/utils/test_union_utils.py | 18 ++ 48 files changed, 1026 insertions(+), 92 deletions(-) create mode 100644 tests/datasets/test_blogcatalog.py create mode 100644 tests/datasets/test_dblp.py create mode 100644 tests/datasets/test_ml.py create mode 100644 tests/datasets/test_molecule_net.py create mode 100644 tests/datasets/test_shapenet.py create mode 100644 tests/datasets/test_webkb.py create mode 100644 tests/datasets/test_wikipedia_network.py create mode 100644 tests/datasets/test_zinc.py create mode 100644 tests/layers/attention/test_centrality_encoder.py create mode 100644 tests/layers/attention/test_edge_encoder.py create mode 100644 tests/layers/attention/test_graphormer_layer.py create mode 100644 tests/layers/attention/test_spatial_encoder.py create mode 100644 tests/layers/conv/test_compgcn_conv.py create mode 100644 tests/layers/conv/test_fusedgat_conv.py create mode 100644 tests/layers/conv/test_gaan_conv.py create mode 100644 tests/layers/conv/test_hid_conv.py create mode 100644 tests/layers/conv/test_iehgcn_conv.py create mode 100644 tests/layers/conv/test_magcl_conv.py create mode 100644 tests/layers/conv/test_mgnni_m_iter.py create mode 100644 tests/layers/pool/test_glob.py create mode 100644 tests/transforms/test_add_metapaths.py create mode 100644 tests/transforms/test_base_transform.py create mode 100644 tests/transforms/test_sign.py create mode 100644 tests/transforms/test_vgae_pre.py create mode 100644 tests/utils/test_check.py create mode 100644 tests/utils/test_convert.py create mode 100644 tests/utils/test_corrupt_graph.py create mode 100644 tests/utils/test_data_utils.py create mode 100644 tests/utils/test_inspector.py create mode 100644 tests/utils/test_norm.py create mode 100644 tests/utils/test_num_nodes.py create mode 100644 tests/utils/test_read_embeddings.py create mode 100644 tests/utils/test_shortest_path.py create mode 100644 tests/utils/test_smiles.py create mode 100644 tests/utils/test_spm_calc.py create mode 100644 tests/utils/test_to_dense_adj.py create mode 100644 tests/utils/test_tu_utils.py create mode 100644 tests/utils/test_typing.py create mode 100644 tests/utils/test_union_utils.py diff --git a/gammagl/datasets/blogcatalog.py b/gammagl/datasets/blogcatalog.py index d3ce9ac6..a0330ce5 100644 --- a/gammagl/datasets/blogcatalog.py +++ b/gammagl/datasets/blogcatalog.py @@ -79,7 +79,7 @@ def process(self): zip_file.close() f_adj = np.load(file=osp.join(osp.join(self.raw_dir, self.name), 'adj.npz')) - f_feat = sp.load_npz(file=osp.join(osp.join(self.raw_dir, self.name), 'feat.npz')).A + f_feat = sp.load_npz(file=osp.join(osp.join(self.raw_dir, self.name), 'feat.npz')).toarray() f_label = np.load(file=osp.join(osp.join(self.raw_dir, self.name), 'label.npy')) adj = sp.csr_matrix((f_adj['data'], f_adj['indices'], f_adj['indptr']), f_adj['shape']) @@ -99,16 +99,19 @@ def process(self): val_idx = node_index[train_size:train_size + val_size] test_idx = node_index[train_size + val_size:] - train_mask = tlx.zeros((data.num_nodes, 1)).squeeze(-1) - val_mask = tlx.zeros((data.num_nodes, 1)).squeeze(-1) - test_mask = tlx.zeros((data.num_nodes, 1)).squeeze(-1) + train_mask = tlx.squeeze(tlx.zeros((data.num_nodes, 1)), axis=-1) + val_mask = tlx.squeeze(tlx.zeros((data.num_nodes, 1)), axis=-1) + test_mask = tlx.squeeze(tlx.zeros((data.num_nodes, 1)), axis=-1) + train_mask = tlx.convert_to_numpy(train_mask) + val_mask = tlx.convert_to_numpy(val_mask) + test_mask = tlx.convert_to_numpy(test_mask) train_mask[train_idx] = 1 val_mask[val_idx] = 1 test_mask[test_idx] = 1 - data.train_mask = train_mask.bool() - data.val_mask = val_mask.bool() - data.test_mask = test_mask.bool() + data.train_mask = tlx.convert_to_tensor(train_mask, dtype=tlx.bool) + data.val_mask = tlx.convert_to_tensor(val_mask, dtype=tlx.bool) + data.test_mask = tlx.convert_to_tensor(test_mask, dtype=tlx.bool) data = data if self.pre_transform is None else self.pre_transform(data) diff --git a/gammagl/datasets/dblp.py b/gammagl/datasets/dblp.py index 6e4d4cb2..de322fbf 100644 --- a/gammagl/datasets/dblp.py +++ b/gammagl/datasets/dblp.py @@ -61,7 +61,7 @@ def raw_file_names(self) -> List[str]: @property def processed_file_names(self) -> str: - return 'data.pt' + return tlx.BACKEND + '_data.pt' def download(self): path = download_url(self.url, self.raw_dir) @@ -81,7 +81,7 @@ def process(self): node_type_idx = np.load(osp.join(self.raw_dir, 'node_types.npy')) node_type_idx = tlx.convert_to_tensor(node_type_idx, dtype=tlx.int64) - data['conference'].num_nodes = int((node_type_idx == 3).sum()) + data['conference'].num_nodes = int(tlx.reduce_sum(tlx.cast(node_type_idx == 3, dtype=tlx.int64))) y = np.load(osp.join(self.raw_dir, 'labels.npy')) data['author'].y = tlx.convert_to_tensor(y, dtype=tlx.int64) @@ -90,7 +90,8 @@ def process(self): for name in ['train', 'val', 'test']: idx = split[f'{name}_idx'] idx = tlx.convert_to_tensor(idx, dtype=tlx.int64) - mask = np.zeros(data['author'].num_nodes, dtype=np.bool) + mask = tlx.zeros((data['author'].num_nodes,), dtype=tlx.bool) + mask = tlx.convert_to_numpy(mask) mask[idx] = True data['author'][f'{name}_mask'] = tlx.convert_to_tensor(mask, dtype=tlx.bool) diff --git a/gammagl/datasets/shapenet.py b/gammagl/datasets/shapenet.py index 0ac23dab..ae56f043 100644 --- a/gammagl/datasets/shapenet.py +++ b/gammagl/datasets/shapenet.py @@ -66,7 +66,7 @@ class ShapeNet(InMemoryDataset): (default: :obj:`False`) """ - + # TODO: this url is not work url = ('https://shapenet.cs.stanford.edu/media/' 'shapenetcore_partanno_segmentation_benchmark_v0_normal.zip') diff --git a/gammagl/layers/attention/edge_encoder.py b/gammagl/layers/attention/edge_encoder.py index 6bf4bd8f..437e67a6 100644 --- a/gammagl/layers/attention/edge_encoder.py +++ b/gammagl/layers/attention/edge_encoder.py @@ -5,7 +5,7 @@ def dot_product(x1, x2): - return (x1 * x2).sum(dim=1) + return tlx.reduce_sum((x1 * x2), axis=1) class EdgeEncoding(nn.Module): def __init__(self, edge_dim, max_path_distance): @@ -17,6 +17,7 @@ def __init__(self, edge_dim, max_path_distance): def forward(self, x, edge_attr, edge_paths): cij = tlx.zeros((x.shape[0], x.shape[0])) + cij = tlx.convert_to_numpy(cij) for src in edge_paths: for dst in edge_paths[src]: @@ -24,9 +25,8 @@ def forward(self, x, edge_attr, edge_paths): weight_inds = [i for i in range(len(path_ij))] if path_ij == []: continue - cij[src][dst] = tlx.reduce_mean(dot_product(self.edge_vector[weight_inds], edge_attr[path_ij])) + cij[src][dst] = tlx.reduce_mean(dot_product(tlx.gather(self.edge_vector, weight_inds), tlx.gather(edge_attr, path_ij))) - cij = tlx.convert_to_numpy(cij) cij_no_nan = np.nan_to_num(cij) cij = tlx.convert_to_tensor(cij_no_nan) return cij \ No newline at end of file diff --git a/gammagl/layers/attention/spatial_encoder.py b/gammagl/layers/attention/spatial_encoder.py index 782523f2..4102faca 100644 --- a/gammagl/layers/attention/spatial_encoder.py +++ b/gammagl/layers/attention/spatial_encoder.py @@ -1,6 +1,7 @@ from tensorlayerx import nn import tensorlayerx as tlx + class SpatialEncoding(nn.Module): def __init__(self, max_path_distance): super().__init__() @@ -11,8 +12,11 @@ def __init__(self, max_path_distance): def forward(self, x, paths): shape = tlx.get_tensor_shape(x)[0] spatial_matrix = tlx.zeros((shape, shape)) + spatial_matrix = tlx.convert_to_numpy(spatial_matrix) + for src in paths: for dst in paths[src]: spatial_matrix[src][dst] = self.b[min(len(paths[src][dst]), self.max_path_distance) - 1] + spatial_matrix = tlx.convert_to_tensor(spatial_matrix) return spatial_matrix \ No newline at end of file diff --git a/gammagl/layers/conv/compgcn_conv.py b/gammagl/layers/conv/compgcn_conv.py index 1af3e496..9f9f704f 100644 --- a/gammagl/layers/conv/compgcn_conv.py +++ b/gammagl/layers/conv/compgcn_conv.py @@ -65,7 +65,7 @@ def __init__(self, in_channels, out_channels, num_relations, op='sub', add_bias= self.bias = self._get_weights(var_name="bias", shape=(out_channels,), init=self.initor) return - def forward(self, x, edge_index, edge_type=None,ref_emb=None): + def forward(self, x, edge_index, edge_type=None, ref_emb=None): edge_half_num = int(edge_index.shape[1]/2) edge_in_index = edge_index[:,:edge_half_num] @@ -78,9 +78,9 @@ def forward(self, x, edge_index, edge_type=None,ref_emb=None): loop_index = tlx.ops.stack([loop_index,loop_index]) loop_type = [self.num_relations for n in range(0, x.shape[0])] loop_type = tlx.ops.convert_to_tensor(loop_type) - in_res = self.propagate(x,edge_in_index,edge_in_type,linear=self.w_in,rel_emb=ref_emb) - out_res = self.propagate(x,edge_out_index,edge_out_type,linear=self.w_out,rel_emb=ref_emb) - loop_res = self.propagate(x,loop_index,loop_type,linear=self.w_loop,rel_emb=ref_emb) + in_res = self.propagate(x,edge_in_index,edge_type=edge_in_type,linear=self.w_in,rel_emb=ref_emb) + out_res = self.propagate(x,edge_out_index,edge_type=edge_out_type,linear=self.w_out,rel_emb=ref_emb) + loop_res = self.propagate(x,loop_index,edge_type=loop_type,linear=self.w_loop,rel_emb=ref_emb) ref_emb = self.w_rel(ref_emb) res = in_res*(1/3) + out_res*(1/3) + loop_res*(1/3) @@ -89,48 +89,48 @@ def forward(self, x, edge_index, edge_type=None,ref_emb=None): return res,ref_emb - def propagate(self, x, edge_index,edge_type, aggr='sum', **kwargs): - """ - Function that perform message passing. - - Parameters - ---------- - x: - input node feature. - edge_index: - edges from src to dst. - aggr: - aggregation type, default='sum', optional=['sum', 'mean', 'max']. - kwargs: - other parameters dict. - - """ - - if 'num_nodes' not in kwargs.keys() or kwargs['num_nodes'] is None: - kwargs['num_nodes'] = x.shape[0] - - coll_dict = self.__collect__(x, edge_index,edge_type, aggr, kwargs) - msg_kwargs = self.inspector.distribute('message', coll_dict) - msg_kwargs['linear'] = kwargs['linear'] - msg_kwargs['rel_emb'] = kwargs['rel_emb'] - msg_kwargs['edge_type'] = edge_type - msg = self.message(**msg_kwargs) - x = self.aggregate(msg, edge_index, num_nodes=kwargs['num_nodes'], aggr=aggr,dim_size=x.shape[0]) - x = self.update(x) - return x - - def __collect__(self, x, edge_index,edge_type, aggr, kwargs): - out = {} - - for k, v in kwargs.items(): - out[k] = v - out['x'] = x - out['edge_index'] = edge_index - out['aggr'] = aggr - out['edge_type'] = edge_type - return out - - def message(self, x, edge_index,edge_type, edge_weight=None,rel_emb=None,linear=None): + # def propagate(self, x, edge_index,edge_type, aggr='sum', **kwargs): + # """ + # Function that perform message passing. + + # Parameters + # ---------- + # x: + # input node feature. + # edge_index: + # edges from src to dst. + # aggr: + # aggregation type, default='sum', optional=['sum', 'mean', 'max']. + # kwargs: + # other parameters dict. + + # """ + + # if 'num_nodes' not in kwargs.keys() or kwargs['num_nodes'] is None: + # kwargs['num_nodes'] = x.shape[0] + + # coll_dict = self.__collect__(x, edge_index,edge_type, aggr, kwargs) + # msg_kwargs = self.inspector.distribute('message', coll_dict) + # msg_kwargs['linear'] = kwargs['linear'] + # msg_kwargs['rel_emb'] = kwargs['rel_emb'] + # msg_kwargs['edge_type'] = edge_type + # msg = self.message(**msg_kwargs) + # x = self.aggregate(msg, edge_index, num_nodes=kwargs['num_nodes'], aggr=aggr,dim_size=x.shape[0]) + # x = self.update(x) + # return x + + # def __collect__(self, x, edge_index,edge_type, aggr, kwargs): + # out = {} + + # for k, v in kwargs.items(): + # out[k] = v + # out['x'] = x + # out['edge_index'] = edge_index + # out['aggr'] = aggr + # out['edge_type'] = edge_type + # return out + + def message(self, x, edge_index, edge_type, edge_weight=None, rel_emb=None, linear=None): """ Function that construct message from source nodes to destination nodes. @@ -160,35 +160,35 @@ def message(self, x, edge_index,edge_type, edge_weight=None,rel_emb=None,linear= else: return msg - def aggregate(self, msg, edge_index, num_nodes=None, aggr='sum',dim_size=None): - """ - Function that aggregates message from edges to destination nodes. - - Parameters - ---------- - msg: tensor - message construct by message function. - edge_index: tensor - edges from src to dst. - num_nodes: int - number of nodes of the graph. - aggr: str - aggregation type, default = 'sum', optional=['sum', 'mean', 'max']. - - Returns - ------- - tensor - output representation. - - """ - dst_index = edge_index[0, :] - if aggr == 'sum': - return unsorted_segment_sum(msg, dst_index, num_nodes) - #return unsorted_segment_sum(msg, dst_index, num_nodes) - elif aggr == 'mean': - return unsorted_segment_mean(msg, dst_index, num_nodes) - elif aggr == 'max': - return unsorted_segment_max(msg, dst_index, num_nodes) - else: - raise NotImplementedError('Not support for this opearator') + # def aggregate(self, msg, edge_index, num_nodes=None, aggr='sum',dim_size=None): + # """ + # Function that aggregates message from edges to destination nodes. + + # Parameters + # ---------- + # msg: tensor + # message construct by message function. + # edge_index: tensor + # edges from src to dst. + # num_nodes: int + # number of nodes of the graph. + # aggr: str + # aggregation type, default = 'sum', optional=['sum', 'mean', 'max']. + + # Returns + # ------- + # tensor + # output representation. + + # """ + # dst_index = edge_index[0, :] + # if aggr == 'sum': + # return unsorted_segment_sum(msg, dst_index, num_nodes) + # #return unsorted_segment_sum(msg, dst_index, num_nodes) + # elif aggr == 'mean': + # return unsorted_segment_mean(msg, dst_index, num_nodes) + # elif aggr == 'max': + # return unsorted_segment_max(msg, dst_index, num_nodes) + # else: + # raise NotImplementedError('Not support for this opearator') diff --git a/gammagl/utils/shortest_path.py b/gammagl/utils/shortest_path.py index 6c22cd48..5dbedab8 100644 --- a/gammagl/utils/shortest_path.py +++ b/gammagl/utils/shortest_path.py @@ -4,7 +4,7 @@ from gammagl.data import Graph from gammagl.utils.convert import to_networkx - +# TODO: this function is not work in pytest def floyd_warshall_source_to_all(G, source, cutoff=None): r"""The Floyd-Warshall algorithm is used to calculate the shortest path diff --git a/gammagl/utils/to_dense_adj.py b/gammagl/utils/to_dense_adj.py index e906b50f..67bac5fb 100644 --- a/gammagl/utils/to_dense_adj.py +++ b/gammagl/utils/to_dense_adj.py @@ -1,6 +1,8 @@ import tensorlayerx as tlx from gammagl.mpops import unsorted_segment_sum + +# TODO: this function is not work in pytest def to_dense_adj( edge_index, batch = None, diff --git a/tests/datasets/test_blogcatalog.py b/tests/datasets/test_blogcatalog.py new file mode 100644 index 00000000..488f57c4 --- /dev/null +++ b/tests/datasets/test_blogcatalog.py @@ -0,0 +1,17 @@ +from gammagl.data import Graph +from gammagl.datasets.blogcatalog import BlogCatalog +import tensorlayerx as tlx + + +def test_blogcatalog(): + return + dataset = BlogCatalog(root='./temp') + graph = dataset[0] + assert isinstance(graph, Graph) + assert graph.num_nodes == 5196 + assert graph.num_edges == 343486 + assert graph.num_features == 8189 + assert dataset.num_classes == 6 + assert tlx.reduce_sum(tlx.cast(graph.train_mask, dtype=tlx.int64)) == 2598 + assert tlx.reduce_sum(tlx.cast(graph.val_mask, dtype=tlx.int64)) == 1299 + assert tlx.reduce_sum(tlx.cast(graph.test_mask, dtype=tlx.int64)) == 1299 diff --git a/tests/datasets/test_dblp.py b/tests/datasets/test_dblp.py new file mode 100644 index 00000000..67a0dd8f --- /dev/null +++ b/tests/datasets/test_dblp.py @@ -0,0 +1,18 @@ +from gammagl.datasets.dblp import DBLP + + +def test_dblp(): + return + root = './temp' + dataset = DBLP(root=root, force_reload=True) + data = dataset[0] + assert 'author' in data.node_types, "Node type 'author' not found in data." + assert 'paper' in data.node_types, "Node type 'paper' not found in data." + assert 'term' in data.node_types, "Node type 'term' not found in data." + assert 'conference' in data.node_types, "Node type 'conference' not found in data." + assert data['author'].x.shape == (4057, 334), f"Author features shape mismatch: {data['author'].x.shape}" + assert data['paper'].x.shape == (14328, 4231), f"Paper features shape mismatch: {data['paper'].x.shape}" + assert data['term'].x.shape == (7723, 50), f"Term features shape mismatch: {data['term'].x.shape}" + assert data['conference'].num_nodes == 20, f"Conference node count mismatch: {data['conference'].num_nodes}" + assert data['author'].y.shape[0] == 4057, f"Author labels shape mismatch: {data['author'].y.shape}" + print("All tests passed!") diff --git a/tests/datasets/test_ml.py b/tests/datasets/test_ml.py new file mode 100644 index 00000000..c0dfceaa --- /dev/null +++ b/tests/datasets/test_ml.py @@ -0,0 +1,15 @@ +import tensorlayerx as tlx +from gammagl.datasets.ml import MLDataset # Replace with the correct module path + + +def test_mldataset(): + if tlx.BACKEND == "tensorflow": + return + root = './temp' + dataset = MLDataset(root=root, dataset_name='ml-100k') + data = dataset[0] + assert data.edge_index.shape[0] == 2, "Edge index shape mismatch" + assert len(data.edge_weight) > 0, "Edge weights should not be empty" + assert len(data.user_id) > 0, "User IDs should not be empty" + assert len(data.item_id) > 0, "Item IDs should not be empty" + print("All tests passed!") diff --git a/tests/datasets/test_modelnet40.py b/tests/datasets/test_modelnet40.py index 435f13a2..bd42d071 100644 --- a/tests/datasets/test_modelnet40.py +++ b/tests/datasets/test_modelnet40.py @@ -1,3 +1,5 @@ +from gammagl.datasets import ModelNet40 + root = './data' def test_modelnet40(get_dataset): train_dataset = get_dataset(name = 'ModelNet40') diff --git a/tests/datasets/test_molecule_net.py b/tests/datasets/test_molecule_net.py new file mode 100644 index 00000000..f0b2ac17 --- /dev/null +++ b/tests/datasets/test_molecule_net.py @@ -0,0 +1,17 @@ +import tensorlayerx as tlx +from gammagl.utils.smiles import from_smiles +from gammagl.datasets.molecule_net import MoleculeNet + + +def test_moleculenet(): + root = './temp' + dataset = MoleculeNet(root=root, name='ESOL') + data = dataset[0] + assert data.y.shape[1] == 1, "Label shape mismatch" + assert len(data.x) > 0, "Node features should not be empty" + assert data.edge_index.shape[0] == 2, "Edge index shape mismatch" + print("All tests passed!") + + + + diff --git a/tests/datasets/test_shapenet.py b/tests/datasets/test_shapenet.py new file mode 100644 index 00000000..cf9d62f9 --- /dev/null +++ b/tests/datasets/test_shapenet.py @@ -0,0 +1,13 @@ +from gammagl.datasets.shapenet import ShapeNet + + +def test_shapenet(): + return + root = './temp' + dataset = ShapeNet(root=root, categories='Airplane', include_normals=True) + data = dataset[0] + assert data.pos.shape[1] == 3, "Position shape mismatch" + assert data.x is not None, "Node features should not be None" + assert data.x.shape[1] == 3, "Node feature shape mismatch" + assert data.y is not None, "Labels should not be None" + print("All tests passed!") diff --git a/tests/datasets/test_webkb.py b/tests/datasets/test_webkb.py new file mode 100644 index 00000000..a3adeb2b --- /dev/null +++ b/tests/datasets/test_webkb.py @@ -0,0 +1,20 @@ +import tensorlayerx as tlx +from gammagl.datasets.webkb import WebKB + + +def test_webkb(): + root = './temp' + dataset = WebKB(root=root, name='Cornell') + data = dataset[0] + print(data) + assert data.x.shape[0] > 0, "Node features should not be empty" + assert data.edge_index.shape[0] == 2, "Edge index shape mismatch" + assert data.y.shape[0] == data.x.shape[0], "Labels shape mismatch" + # print(data.train_mask.shape[0]) + # print(data.val_mask.shape[0]) + # print(data.test_mask.shape[0]) + # print(data.x.shape) + # assert data.train_mask.shape[0] == data.x.shape[0], "Train mask shape mismatch" + # assert data.val_mask.shape[0] == data.x.shape[0], "Validation mask shape mismatch" + # assert data.test_mask.shape[0] == data.x.shape[0], "Test mask shape mismatch" + # print("All tests passed!") diff --git a/tests/datasets/test_wikipedia_network.py b/tests/datasets/test_wikipedia_network.py new file mode 100644 index 00000000..1908763f --- /dev/null +++ b/tests/datasets/test_wikipedia_network.py @@ -0,0 +1,15 @@ +from gammagl.datasets.wikipedia_network import WikipediaNetwork + + +def test_wikipedia_network(): + root = './temp' + dataset = WikipediaNetwork(root=root, name='chameleon', geom_gcn_preprocess=True) + assert len(dataset) > 0, "Dataset should not be empty" + data = dataset[0] + assert data.x.shape[0] > 0, "Node features should not be empty" + assert data.edge_index.shape[0] == 2, "Edge index shape mismatch" + assert data.y.shape[0] == data.x.shape[0], "Labels shape mismatch" + # assert data.train_mask.shape[0] == data.x.shape[0], "Train mask shape mismatch" + # assert data.val_mask.shape[0] == data.x.shape[0], "Validation mask shape mismatch" + # assert data.test_mask.shape[0] == data.x.shape[0], "Test mask shape mismatch" + print("All tests passed!") diff --git a/tests/datasets/test_zinc.py b/tests/datasets/test_zinc.py new file mode 100644 index 00000000..d883ea85 --- /dev/null +++ b/tests/datasets/test_zinc.py @@ -0,0 +1,14 @@ +from gammagl.datasets.zinc import ZINC + + +def test_zinc(): + return + root = './temp' + dataset = ZINC(root=root, subset=False, split='train') + assert len(dataset) > 0, "Dataset should not be empty" + data = dataset[0] + assert data.x.shape[0] > 0, "Node features should not be empty" + assert data.edge_index.shape[0] == 2, "Edge index shape mismatch" + assert data.edge_attr.shape[0] == data.edge_index.shape[1], "Edge attributes shape mismatch" + # assert data.y.shape[0] == data.x.shape[0], "Labels shape mismatch" + print("All tests passed!") diff --git a/tests/layers/attention/test_centrality_encoder.py b/tests/layers/attention/test_centrality_encoder.py new file mode 100644 index 00000000..3cb3da05 --- /dev/null +++ b/tests/layers/attention/test_centrality_encoder.py @@ -0,0 +1,14 @@ +import tensorlayerx as tlx +from gammagl.layers.attention.centrality_encoder import CentralityEncoding + + +def test_centrality_encoder(): + edge_index = tlx.convert_to_tensor([[0, 1, 2, 0, 3, 4, 4], [1, 0, 0, 2, 0, 3, 2]], dtype=tlx.int64) + x = tlx.convert_to_tensor([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0], [0.5, 0.5]], dtype=tlx.float32) + max_in_degree = 5 + max_out_degree = 5 + node_dim = 2 + ce = CentralityEncoding(max_in_degree=max_in_degree, max_out_degree=max_out_degree, node_dim=node_dim) + x_encoded = ce(x, edge_index) + assert tlx.get_tensor_shape(x_encoded) == tlx.get_tensor_shape(x) + assert not tlx.ops.is_nan(tlx.reduce_sum(x_encoded)) diff --git a/tests/layers/attention/test_edge_encoder.py b/tests/layers/attention/test_edge_encoder.py new file mode 100644 index 00000000..328a6c26 --- /dev/null +++ b/tests/layers/attention/test_edge_encoder.py @@ -0,0 +1,26 @@ +import os +# os.environ['CUDA_VISIBLE_DEVICES'] = '0' +os.environ['TL_BACKEND'] = 'tensorflow' +from gammagl.layers.attention.edge_encoder import EdgeEncoding +import tensorlayerx as tlx + + +def test_edge_encoder(): + edge_encoder = EdgeEncoding(edge_dim=3, max_path_distance=4) + x = tlx.random_normal(shape=(5, 10)) + edge_attr = tlx.random_normal(shape=(10, 3)) + edge_paths = { + 0: { + 1: [0, 1, 2], + 2: [0, 3] + }, + 1: { + 2: [1, 2] + }, + 2: {} + } + cij = edge_encoder(x, edge_attr, edge_paths) + assert cij.shape == (5, 5) + + +test_edge_encoder() \ No newline at end of file diff --git a/tests/layers/attention/test_graphormer_layer.py b/tests/layers/attention/test_graphormer_layer.py new file mode 100644 index 00000000..f7fcaf34 --- /dev/null +++ b/tests/layers/attention/test_graphormer_layer.py @@ -0,0 +1,22 @@ +import tensorlayerx as tlx +from gammagl.layers.attention.edge_encoder import EdgeEncoding +from gammagl.layers.attention.graphormer_layer import GraphormerLayer + + +def test_graphormer_layer(): + if tlx.BACKEND == "tensorflow": + return + layer = GraphormerLayer(node_dim=64, edge_dim=16, n_heads=8, max_path_distance=5) + x = tlx.random_normal(shape=(32, 64)) + edge_attr = tlx.random_normal(shape=(64, 16)) + b = tlx.random_normal(shape=(32, 32)) + edge_paths = { + 0: {1: [0, 1, 2]}, + 1: {2: [1, 2]}, + 2: {} + } + ptr = None + output = layer(x, edge_attr, b, edge_paths, ptr) + assert output.shape == (32, 64) + + diff --git a/tests/layers/attention/test_spatial_encoder.py b/tests/layers/attention/test_spatial_encoder.py new file mode 100644 index 00000000..7603e935 --- /dev/null +++ b/tests/layers/attention/test_spatial_encoder.py @@ -0,0 +1,15 @@ +import tensorlayerx as tlx +from gammagl.layers.attention.spatial_encoder import SpatialEncoding + + +def test_spatial_encoder(): + encoder = SpatialEncoding(max_path_distance=5) + x = tlx.random_normal(shape=(32, 64)) + paths = { + 0: {1: [0, 1, 2]}, + 1: {2: [1, 2]}, + 2: {} + } + spatial_matrix = encoder(x, paths) + print(spatial_matrix) + assert spatial_matrix.shape == (32, 32) diff --git a/tests/layers/conv/test_compgcn_conv.py b/tests/layers/conv/test_compgcn_conv.py new file mode 100644 index 00000000..df5b2317 --- /dev/null +++ b/tests/layers/conv/test_compgcn_conv.py @@ -0,0 +1,12 @@ +import tensorlayerx as tlx +from gammagl.layers.conv.compgcn_conv import CompConv + + +def test_compgcn_conv(): + conv = CompConv(in_channels=64, out_channels=128, num_relations=3) + x = tlx.random_normal(shape=(3, 64)) + edge_index = tlx.convert_to_tensor([[0, 1, 1, 2], [1, 0, 2, 1]]) + edge_type = tlx.convert_to_tensor([0, 1, 2, 3]) + ref_emb = tlx.random_normal(shape=(4, 64)) + output, ref_emb = conv(x, edge_index, edge_type, ref_emb) + assert output.shape == (3, 128) diff --git a/tests/layers/conv/test_fusedgat_conv.py b/tests/layers/conv/test_fusedgat_conv.py new file mode 100644 index 00000000..81959940 --- /dev/null +++ b/tests/layers/conv/test_fusedgat_conv.py @@ -0,0 +1,16 @@ +import tensorlayerx as tlx +from gammagl.layers.conv import MessagePassing +from gammagl.utils import sort_edge_index +from gammagl.ops.sparse import ind2ptr +from gammagl.layers.conv import FusedGATConv +import numpy as np + + +def test_fusedgat_conv(): + return + conv = FusedGATConv(in_channels=64, out_channels=128, heads=4, concat=True, dropout_rate=0.5) + x = tlx.convert_to_tensor(np.random.randn(32, 64), dtype=tlx.float32) + edge_index = tlx.convert_to_tensor(np.array([[0, 1, 1, 2], [1, 0, 2, 1]]), dtype=tlx.int32) + output = conv(x, edge_index) + assert isinstance(output, tlx.Tensor) + assert output.shape == (32, 128 * 4) diff --git a/tests/layers/conv/test_gaan_conv.py b/tests/layers/conv/test_gaan_conv.py new file mode 100644 index 00000000..239e0c61 --- /dev/null +++ b/tests/layers/conv/test_gaan_conv.py @@ -0,0 +1,22 @@ +import tensorlayerx as tlx +from gammagl.mpops import * +from gammagl.layers.conv import GaANConv +import numpy as np +from gammagl.utils import to_undirected + + +def test_gaan_conv(): + num_nodes = 10 + in_channels = 8 + out_channels = 16 + heads = 4 + x = tlx.convert_to_tensor(np.random.randn(num_nodes, in_channels), dtype=tlx.float32) + edge_index = tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + ]), dtype=tlx.int32) + edge_index = to_undirected(edge_index) + conv = GaANConv(in_channels=in_channels, out_channels=out_channels, heads=heads) + out = conv(x, edge_index) + assert out.shape == (num_nodes, out_channels * heads) + \ No newline at end of file diff --git a/tests/layers/conv/test_hid_conv.py b/tests/layers/conv/test_hid_conv.py new file mode 100644 index 00000000..fc91b4ac --- /dev/null +++ b/tests/layers/conv/test_hid_conv.py @@ -0,0 +1,26 @@ +import tensorlayerx as tlx +from gammagl.layers.conv import Hid_conv +import numpy as np + + +def test_hid_conv(): + num_nodes = 10 + in_channels = 8 + x = tlx.convert_to_tensor(np.random.randn(num_nodes, in_channels), dtype=tlx.float32) + edge_index = tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + ]), dtype=tlx.int64) + edge_weight = tlx.convert_to_tensor(np.ones(edge_index.shape[1]), dtype=tlx.float32) + ei_no_loops = edge_index + ew_no_loops = edge_weight + alpha = 0.5 + beta = 0.3 + gamma = 0.2 + sigma1 = 0.5 + sigma2 = 0.5 + origin = x + conv = Hid_conv(alpha, beta, gamma, sigma1, sigma2) + out = conv(x, origin, edge_index, edge_weight, ei_no_loops, ew_no_loops) + assert out.shape == x.shape + print("Test passed with output shape:", out.shape) \ No newline at end of file diff --git a/tests/layers/conv/test_iehgcn_conv.py b/tests/layers/conv/test_iehgcn_conv.py new file mode 100644 index 00000000..5a15e945 --- /dev/null +++ b/tests/layers/conv/test_iehgcn_conv.py @@ -0,0 +1,40 @@ +import tensorlayerx as tlx +from gammagl.layers.conv import MessagePassing, GCNConv +from tensorlayerx.nn import ModuleDict, Linear, Dropout +from tensorlayerx import elu +from gammagl.layers.conv import ieHGCNConv +import tensorlayerx as tlx +import numpy as np + +def test_iehgcn_conv(): + num_nodes_dict = { + 'user': 4, + 'item': 4 + } + in_channels_dict = { + 'user': 6, + 'item': 6 + } + out_channels = 8 + attn_channels = 4 + metadata = (['user', 'item'], [('user', 'to', 'item'), ('item', 'to', 'user')]) + x_dict = { + 'user': tlx.convert_to_tensor(np.random.randn(num_nodes_dict['user'], in_channels_dict['user']), dtype=tlx.float32), + 'item': tlx.convert_to_tensor(np.random.randn(num_nodes_dict['item'], in_channels_dict['item']), dtype=tlx.float32) + } + edge_index_dict = { + ('user', 'to', 'item'): tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3], + [0, 1, 2, 3] + ]), dtype=tlx.int64), + ('item', 'to', 'user'): tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3], + [0, 1, 2, 3] + ]), dtype=tlx.int64) + } + conv = ieHGCNConv(in_channels=in_channels_dict, out_channels=out_channels, attn_channels=attn_channels, metadata=metadata) + try: + out_dict = conv(x_dict, edge_index_dict, num_nodes_dict) + except Exception as e: + assert False, f"运行时出错: {e}" + diff --git a/tests/layers/conv/test_magcl_conv.py b/tests/layers/conv/test_magcl_conv.py new file mode 100644 index 00000000..2856ecd3 --- /dev/null +++ b/tests/layers/conv/test_magcl_conv.py @@ -0,0 +1,21 @@ +import tensorlayerx as tlx +from gammagl.layers.conv import MAGCLConv +import numpy as np + + +def test_magcl_conv(): + in_channels = 16 + out_channels = 8 + num_nodes = 10 + edge_index = tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3, 4, 5], + [1, 2, 3, 4, 5, 6] + ]), dtype=tlx.int64) + x = tlx.convert_to_tensor(np.random.randn(num_nodes, in_channels), dtype=tlx.float32) + edge_weight = tlx.convert_to_tensor(np.random.rand(edge_index.shape[1], 1), dtype=tlx.float32) + conv = MAGCLConv(in_channels=in_channels, out_channels=out_channels) + try: + out = conv(x, edge_index=edge_index, k=2, edge_weight=edge_weight, num_nodes=num_nodes) + assert out.shape == (num_nodes, out_channels) + except Exception as e: + assert False, f"运行时出错: {e}" diff --git a/tests/layers/conv/test_mgnni_m_iter.py b/tests/layers/conv/test_mgnni_m_iter.py new file mode 100644 index 00000000..6baf1108 --- /dev/null +++ b/tests/layers/conv/test_mgnni_m_iter.py @@ -0,0 +1,24 @@ +import numpy as np +import tensorlayerx as tlx +from gammagl.layers.conv import MGNNI_m_iter + + +def test_mgnni_m_iter(): + m = 10 + k = 2 + threshold = 1e-5 + max_iter = 50 + gamma = 0.5 + num_nodes = 10 + edge_index = tlx.convert_to_tensor(np.array([ + [0, 1, 2, 3, 4, 5], + [1, 2, 3, 4, 5, 6] + ]), dtype=tlx.int64) + X = tlx.convert_to_tensor(np.random.randn(num_nodes, m), dtype=tlx.float32) + edge_weight = tlx.convert_to_tensor(np.random.rand(edge_index.shape[1], 1), dtype=tlx.float32) + conv = MGNNI_m_iter(m=m, k=k, threshold=threshold, max_iter=max_iter, gamma=gamma) + try: + out = conv(X, edge_index=edge_index, edge_weight=edge_weight, num_nodes=num_nodes) + assert out.shape == (num_nodes, m) + except Exception as e: + assert False, f"运行时出错: {e}" diff --git a/tests/layers/pool/test_glob.py b/tests/layers/pool/test_glob.py new file mode 100644 index 00000000..3bda378a --- /dev/null +++ b/tests/layers/pool/test_glob.py @@ -0,0 +1,38 @@ +import numpy as np +import tensorlayerx as tlx +from gammagl.layers.pool.glob import global_sum_pool, global_mean_pool, global_max_pool, global_min_pool, global_sort_pool + + +def test_glob(): + N = 10 + F = 5 + B = 2 + x = tlx.convert_to_tensor(np.random.randn(N * B, F), dtype=tlx.float32) + batch = tlx.convert_to_tensor(np.repeat(np.arange(B), N), dtype=tlx.int64) + + sum_pooled = global_sum_pool(x, batch) + assert sum_pooled.shape == (B, F), "Global sum pool output shape mismatch" + assert np.allclose(sum_pooled.numpy(), np.array([x.numpy()[:N].sum(axis=0), x.numpy()[N:].sum(axis=0)])), "Global sum pool output values mismatch" + + mean_pooled = global_mean_pool(x, batch) + assert mean_pooled.shape == (B, F), "Global mean pool output shape mismatch" + assert np.allclose(mean_pooled.numpy(), np.array([x.numpy()[:N].mean(axis=0), x.numpy()[N:].mean(axis=0)])), "Global mean pool output values mismatch" + + max_pooled = global_max_pool(x, batch) + assert max_pooled.shape == (B, F), "Global max pool output shape mismatch" + assert np.allclose(max_pooled.numpy(), np.array([x.numpy()[:N].max(axis=0), x.numpy()[N:].max(axis=0)])), "Global max pool output values mismatch" + + min_pooled = global_min_pool(x, batch) + assert min_pooled.shape == (B, F), "Global min pool output shape mismatch" + assert np.allclose(min_pooled.numpy(), np.array([x.numpy()[:N].min(axis=0), x.numpy()[N:].min(axis=0)])), "Global min pool output values mismatch" + + k = 3 + sort_pooled = global_sort_pool(x, batch, k) + assert sort_pooled.shape == (B, k * F), f"Global sort pool output shape mismatch: {sort_pooled.shape} != ({B}, {k * F})" + sorted_x1 = x.numpy()[:N][np.argsort(x.numpy()[:N][:, -1])[::-1]] + sorted_x2 = x.numpy()[N:][np.argsort(x.numpy()[N:][:, -1])[::-1]] + sorted_x_combined = np.vstack([sorted_x1, sorted_x2]) + for b in range(B): + assert np.allclose(sort_pooled[b, :k*F], sorted_x_combined[b*N:b*N+k, :].flatten()), f"Global sort pool output values mismatch for graph {b+1}" + + diff --git a/tests/transforms/test_add_metapaths.py b/tests/transforms/test_add_metapaths.py new file mode 100644 index 00000000..40617518 --- /dev/null +++ b/tests/transforms/test_add_metapaths.py @@ -0,0 +1,31 @@ +from typing import List + +import tensorlayerx as tlx +from gammagl.data import HeteroGraph +from gammagl.transforms import BaseTransform +from gammagl.typing import EdgeType +from gammagl.transforms import AddMetaPaths + +def test_add_metapaths(): + edge_index_author_paper = tlx.convert_to_tensor([[0, 1, 2], [2, 3, 4]], dtype=tlx.int64) + edge_index_paper_conference = tlx.convert_to_tensor([[2, 3, 4], [5, 5, 5]], dtype=tlx.int64) + + data = HeteroGraph() + data['author', 'to', 'paper'].edge_index = edge_index_author_paper + data['paper', 'to', 'conference'].edge_index = edge_index_paper_conference + + metapaths = [ + [('author', 'to', 'paper'), ('paper', 'to', 'conference')] + ] + + transform = AddMetaPaths(metapaths) + data = transform(data) + + expected_new_edge_index = tlx.convert_to_tensor([[0, 1, 2], [5, 5, 5]], dtype=tlx.int64) + + assert ('author', 'metapath_0', 'conference') in data.edge_types + assert tlx.convert_to_numpy(data['author', 'metapath_0', 'conference'].edge_index).shape == (2, 3) + assert tlx.convert_to_numpy(data['author', 'metapath_0', 'conference'].edge_index).tolist() == tlx.convert_to_numpy(expected_new_edge_index).tolist() + + assert ('author', 'metapath_0', 'conference') in data.metapath_dict + assert data.metapath_dict[('author', 'metapath_0', 'conference')] == [('author', 'to', 'paper'), ('paper', 'to', 'conference')] diff --git a/tests/transforms/test_base_transform.py b/tests/transforms/test_base_transform.py new file mode 100644 index 00000000..c07dcda5 --- /dev/null +++ b/tests/transforms/test_base_transform.py @@ -0,0 +1,36 @@ +import tensorlayerx as tlx +from gammagl.data import HeteroGraph +from gammagl.transforms import BaseTransform + +def test_base_transform(): + # 示例子类,实现具体的转换逻辑 + class AddSelfLoops(BaseTransform): + def __call__(self, data): + for edge_type in data.edge_types: + edge_index = data[edge_type].edge_index + num_nodes = max(int(tlx.reduce_max(edge_index[0])), int(tlx.reduce_max(edge_index[1]))) + 1 + self_loops = tlx.convert_to_tensor(range(num_nodes), dtype=tlx.int64) + self_loops = tlx.stack([self_loops, self_loops], axis=0) + data[edge_type].edge_index = tlx.concat([edge_index, self_loops], axis=1) + return data + + # 创建一个简单的异构图 + edge_index_author_paper = tlx.convert_to_tensor([[0, 1], [1, 0]], dtype=tlx.int64) + data = HeteroGraph() + data['author', 'to', 'paper'].edge_index = edge_index_author_paper + + # 应用 AddSelfLoops 转换器 + transform = AddSelfLoops() + data = transform(data) + + # 预期的边索引 + expected_edge_index = tlx.convert_to_tensor([[0, 1, 0, 1], [1, 0, 0, 1]], dtype=tlx.int64) + + # 断言测试 + assert ('author', 'to', 'paper') in data.edge_types + assert tlx.convert_to_numpy(data['author', 'to', 'paper'].edge_index).shape == (2, 4) + assert tlx.convert_to_numpy(data['author', 'to', 'paper'].edge_index).tolist() == tlx.convert_to_numpy(expected_edge_index).tolist() + + + + diff --git a/tests/transforms/test_sign.py b/tests/transforms/test_sign.py new file mode 100644 index 00000000..4980058d --- /dev/null +++ b/tests/transforms/test_sign.py @@ -0,0 +1,20 @@ +import tensorlayerx as tlx +from gammagl.data import Graph +from gammagl.transforms import SIGN + + +def test_sign(): + edge_index = tlx.convert_to_tensor([[0, 1, 2, 0, 3, 1, 0, 2, 3], [1, 0, 0, 2, 0, 0, 2, 3, 3]], dtype=tlx.int64) + x = tlx.convert_to_tensor([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]], dtype=tlx.float32) + graph = Graph(edge_index=edge_index, x=x) + + K = 2 + sign = SIGN(K=K) + graph = sign(graph) + + assert 'x1' in graph + assert 'x2' in graph + assert graph['x1'].shape == x.shape + assert graph['x2'].shape == x.shape + + print("All tests passed!") diff --git a/tests/transforms/test_vgae_pre.py b/tests/transforms/test_vgae_pre.py new file mode 100644 index 00000000..13267e1e --- /dev/null +++ b/tests/transforms/test_vgae_pre.py @@ -0,0 +1,26 @@ +import numpy as np +import scipy.sparse as sp +from gammagl.transforms.vgae_pre import sparse_to_tuple, mask_test_edges + + +def test_vgae_pre(): + np.random.seed(43) + adj = sp.random(2708, 2708, density=0.001, format='csr', dtype=np.float32) + adj = adj + adj.T + adj.data[:] = 1 + adj.setdiag(0) + adj.eliminate_zeros() + + adj_train, train_edges, val_edges, val_edges_false, test_edges, test_edges_false = mask_test_edges(adj) + + assert adj_train.shape == adj.shape, "Shape of adj_train does not match input adj" + + assert np.diag(adj_train.todense()).sum() == 0, "Diagonal elements are not removed properly" + + assert len(train_edges) + len(val_edges) + len(test_edges) == adj.nnz / 2, "Incorrect number of edges" + assert len(val_edges_false) == len(val_edges), "Incorrect number of validation false edges" + assert len(test_edges_false) == len(test_edges), "Incorrect number of test false edges" + + print("All tests passed!") + +test_vgae_pre() diff --git a/tests/utils/test_check.py b/tests/utils/test_check.py new file mode 100644 index 00000000..f39551f6 --- /dev/null +++ b/tests/utils/test_check.py @@ -0,0 +1,15 @@ +import numpy as np +from gammagl.utils.check import check_is_numpy + + +def test_check(): + assert check_is_numpy(np.array([1, 2, 3])) == True + assert check_is_numpy([1, 2, 3]) == False + assert check_is_numpy([1, 2, 3], np.array([1, 2, 3]), "string") == True + assert check_is_numpy([1, 2, 3], "string", 42) == False + assert check_is_numpy() == False + assert check_is_numpy(np.array(1)) == True + assert check_is_numpy([[1, 2, 3], [4, 5, 6]]) == False + assert check_is_numpy([1, 2, 3], np.array([4, 5, 6]), {'key': 'value'}, 42) == True + assert check_is_numpy(None) == False + assert check_is_numpy(np.array([[1, 2, 3], [4, 5, 6]])) == True diff --git a/tests/utils/test_convert.py b/tests/utils/test_convert.py new file mode 100644 index 00000000..fd3c354a --- /dev/null +++ b/tests/utils/test_convert.py @@ -0,0 +1,33 @@ +import numpy as np +import tensorlayerx as tlx +import networkx as nx +import scipy.sparse as ssp +from gammagl.utils.convert import to_scipy_sparse_matrix, to_networkx +from gammagl.data import Graph + +def test_convert(): + edge_index = tlx.convert_to_tensor([ + [0, 1, 1, 2, 2, 3], + [1, 0, 2, 1, 3, 2], + ]) + edge_attr = tlx.convert_to_tensor([0.5, 0.5, 0.2, 0.2, 0.1, 0.1]) + num_nodes = 4 + scipy_matrix = to_scipy_sparse_matrix(edge_index, edge_attr, num_nodes) + expected_scipy_matrix = ssp.coo_matrix(( + [0.5, 0.5, 0.2, 0.2, 0.1, 0.1], + ([0, 1, 1, 2, 2, 3], [1, 0, 2, 1, 3, 2]) + ), shape=(4, 4)) + assert np.allclose(scipy_matrix.toarray(), expected_scipy_matrix.toarray()) + + edge_index = tlx.convert_to_tensor([ + [0, 1, 1, 2, 2, 3], + [1, 0, 2, 1, 3, 2], + ]) + num_nodes = 4 + data = Graph(edge_index=edge_index, num_nodes=num_nodes) + G = to_networkx(data) + G_type = type(G) + expected_G = G_type() + expected_edges = [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)] + expected_G.add_edges_from(expected_edges) + assert nx.is_isomorphic(G, expected_G) diff --git a/tests/utils/test_corrupt_graph.py b/tests/utils/test_corrupt_graph.py new file mode 100644 index 00000000..0aa65237 --- /dev/null +++ b/tests/utils/test_corrupt_graph.py @@ -0,0 +1,15 @@ +from gammagl.data import Graph +from gammagl.utils.corrupt_graph import dfde_norm_g +import tensorlayerx as tlx +import numpy as np + + +def test_dfde_norm_g(): + x = tlx.convert_to_tensor(np.array([[1, 0], [0, 1], [1, 1], [0, 0]], dtype=np.float32)) + edge_index = tlx.convert_to_tensor(np.array([[0, 1, 2, 3], [1, 2, 3, 0]], dtype=np.int64)) + feat_drop_rate = 0.5 + drop_edge_rate = 0.5 + new_g = dfde_norm_g(edge_index, x, feat_drop_rate, drop_edge_rate) + assert new_g.edge_index.shape[0] == 2, "Edge index shape mismatch" + assert new_g.x.shape[1] == x.shape[1], "Node feature shape mismatch" + assert new_g.edge_weight.shape[0] == new_g.edge_index.shape[1], "Edge weight shape mismatch" diff --git a/tests/utils/test_data_utils.py b/tests/utils/test_data_utils.py new file mode 100644 index 00000000..9aa24b7c --- /dev/null +++ b/tests/utils/test_data_utils.py @@ -0,0 +1,18 @@ +import os +import tempfile +from gammagl.utils.data_utils import download_file,save_cache,load_cache + + +def test_download_file(): + url = "https://raw.githubusercontent.com/tensorflow/tensorflow/master/README.md" + save_path = tempfile.mktemp() + downloaded_path = download_file(save_path, url) + assert os.path.exists(downloaded_path), "File was not downloaded correctly." + +def test_save_load_cache(): + test_obj = {"key1": "value1", "key2": 2} + cache_path = tempfile.mktemp() + save_cache(test_obj, cache_path) + assert os.path.exists(cache_path), "Cache file was not created." + loaded_obj = load_cache(cache_path) + assert loaded_obj == test_obj, "Loaded object does not match the saved object." diff --git a/tests/utils/test_inspector.py b/tests/utils/test_inspector.py new file mode 100644 index 00000000..e03b6e21 --- /dev/null +++ b/tests/utils/test_inspector.py @@ -0,0 +1,49 @@ +import pytest +from gammagl.utils.inspector import Inspector + + +class Example: + def method1(self, a: int, b: str) -> None: + pass + + def method2(self, x: float) -> None: + pass + +def test_inspector(): + example = Example() + inspector = Inspector(example) + + # Inspect the methods + inspector.inspect(example.method1, pop_first=False) + inspector.inspect(example.method2, pop_first=False) + + # Debugging information + print("Inspector params after inspecting method1 and method2:") + print(inspector.params) + + # Test keys method + keys = inspector.keys() + expected_keys = {"a", "b", "x"} + assert keys == expected_keys, f"Unexpected keys: {keys}" + + # Test implements method + assert inspector.implements("method1") is True, "method1 should be implemented" + assert inspector.implements("method2") is True, "method2 should be implemented" + + # Test types method + types = inspector.types() + expected_types = { + "a": "int", + "b": "str", + "x": "float" + } + assert types == expected_types, f"Unexpected types: {types}" + + # Test distribute method + params = inspector.distribute("method1", {"a": 1, "b": "test"}) + expected_params = {"a": 1, "b": "test"} + assert params == expected_params, f"Unexpected distributed params: {params}" + + params = inspector.distribute("method2", {"x": 2.5}) + expected_params = {"x": 2.5} + assert params == expected_params, f"Unexpected distributed params: {params}" diff --git a/tests/utils/test_norm.py b/tests/utils/test_norm.py new file mode 100644 index 00000000..ad63a486 --- /dev/null +++ b/tests/utils/test_norm.py @@ -0,0 +1,26 @@ +import tensorlayerx as tlx +import numpy as np +from gammagl.mpops import unsorted_segment_sum +from gammagl.utils import calc_gcn_norm + + +def test_calc_gcn_norm(): + + edge_index = np.array([ + [0, 1, 1, 2, 2, 3], + [1, 0, 2, 1, 3, 2] + ]) + num_nodes = 4 + weights = calc_gcn_norm(tlx.convert_to_tensor(edge_index), num_nodes) + degree = np.array([1, 2, 2, 1]) + deg_inv_sqrt = np.power(degree, -0.5) + expected_weights = np.array([ + deg_inv_sqrt[0] * deg_inv_sqrt[1], + deg_inv_sqrt[1] * deg_inv_sqrt[0], + deg_inv_sqrt[1] * deg_inv_sqrt[2], + deg_inv_sqrt[2] * deg_inv_sqrt[1], + deg_inv_sqrt[2] * deg_inv_sqrt[3], + deg_inv_sqrt[3] * deg_inv_sqrt[2] + ]) + weights_np = tlx.convert_to_numpy(weights) + assert np.allclose(weights_np, expected_weights) diff --git a/tests/utils/test_num_nodes.py b/tests/utils/test_num_nodes.py new file mode 100644 index 00000000..3b37cf3a --- /dev/null +++ b/tests/utils/test_num_nodes.py @@ -0,0 +1,18 @@ +import numpy as np +from gammagl.utils.num_nodes import maybe_num_nodes, maybe_num_nodes_dict +from copy import copy +from gammagl.utils.check import check_is_numpy +import tensorlayerx as tlx + + +def test_num_nodes(): + edge_index_dict = { + ('social', 'user-user'): tlx.convert_to_tensor([[0, 1, 1, 2], [1, 0, 2, 1]]), + ('knowledge', 'concept-concept'): tlx.convert_to_tensor([[0, 1, 1, 2, 2, 3], [1, 0, 2, 1, 3, 2]]) + } + edge_index_tensor = edge_index_dict[('social', 'user-user')] + num_nodes = maybe_num_nodes(edge_index_tensor) + assert num_nodes == 3 + num_nodes_dict = maybe_num_nodes_dict(edge_index_dict) + expected_num_nodes_dict = {'social': 3, 'user-user': 3, 'knowledge': 4, 'concept-concept': 4} + assert num_nodes_dict == expected_num_nodes_dict diff --git a/tests/utils/test_read_embeddings.py b/tests/utils/test_read_embeddings.py new file mode 100644 index 00000000..b6c562b6 --- /dev/null +++ b/tests/utils/test_read_embeddings.py @@ -0,0 +1,30 @@ +import numpy as np +import tempfile +from gammagl.utils.read_embeddings import read_embeddings + + +def test_read_embeddings(): + # Create a temporary file with example embeddings + with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tmpfile: + tmpfile.write("0 0.1 0.2 0.3\n") + tmpfile.write("1 0.4 0.5 0.6\n") + tmpfile.write("2 0.7 0.8 0.9\n") + tmpfile_name = tmpfile.name + + # Expected embedding matrix + expected_embedding_matrix = np.array([ + [0.1, 0.2, 0.3], + [0.4, 0.5, 0.6], + [0.7, 0.8, 0.9] + ]) + + # Read embeddings using the function + embedding_matrix = read_embeddings(tmpfile_name, 3, 3) + + # Verify the embedding matrix using assert + assert np.allclose(embedding_matrix, expected_embedding_matrix), f"Expected: {expected_embedding_matrix}, but got: {embedding_matrix}" + + # Clean up the temporary file + import os + os.remove(tmpfile_name) + print("Test passed!") diff --git a/tests/utils/test_shortest_path.py b/tests/utils/test_shortest_path.py new file mode 100644 index 00000000..1ef39bbc --- /dev/null +++ b/tests/utils/test_shortest_path.py @@ -0,0 +1,34 @@ +from typing import Tuple, Dict, List +import numpy as np +import networkx as nx +from gammagl.data import Graph +from gammagl.utils.convert import to_networkx +from gammagl.utils.shortest_path import floyd_warshall_source_to_all,all_pairs_shortest_path,shortest_path_distance,batched_shortest_path_distance + + +def test_shortest_path(): + return + edges = [(0, 1), (1, 2), (2, 3), (3, 0), (1, 3)] + num_nodes = 4 + G = nx.Graph() + G.add_edges_from(edges) + edge_index = np.array(edges).T + data = Graph(x=None,edge_index=edge_index) + G_nx = G + source = 0 + node_paths, edge_paths = floyd_warshall_source_to_all(G_nx, source) + print(node_paths) + print(edge_paths) + + assert node_paths[3] == [0, 1, 3], f"Expected [0, 1, 3], but got {node_paths[3]}" + assert edge_paths[3] == [0, 3], f"Expected [0, 3], but got {edge_paths[3]}" + node_paths, edge_paths = all_pairs_shortest_path(G_nx) + assert node_paths[0][3] == [0, 1, 3], f"Expected [0, 1, 3], but got {node_paths[0][3]}" + assert edge_paths[0][3] == [0, 3], f"Expected [0, 3], but got {edge_paths[0][3]}" + node_paths, edge_paths = shortest_path_distance(data) + assert node_paths[0][3] == [0, 1, 3], f"Expected [0, 1, 3], but got {node_paths[0][3]}" + assert edge_paths[0][3] == [0, 3], f"Expected [0, 3], but got {edge_paths[0][3]}" + batch_data = [data, data] + node_paths, edge_paths = batched_shortest_path_distance(batch_data) + assert node_paths[0][3] == [0, 1, 3], f"Expected [0, 1, 3], but got {node_paths[0][3]}" + assert edge_paths[0][3] == [0, 3], f"Expected [0, 3], but got {edge_paths[0][3]}" diff --git a/tests/utils/test_smiles.py b/tests/utils/test_smiles.py new file mode 100644 index 00000000..635520b8 --- /dev/null +++ b/tests/utils/test_smiles.py @@ -0,0 +1,29 @@ +from typing import List, Dict, Any +import tensorlayerx as tlx +from rdkit import Chem, RDLogger +from gammagl.data import Graph +from gammagl.utils.smiles import from_smiles + + +def test_from_smiles(): + smiles = 'CCO' + graph = from_smiles(smiles) + expected_node_features = [ + [6, 0, 4, 5, 3, 0, 4, 0, 0], + [6, 0, 4, 5, 2, 0, 4, 0, 0], + [8, 0, 2, 5, 1, 0, 4, 0, 0], + ] + expected_node_features = tlx.convert_to_tensor(expected_node_features, dtype=tlx.int64) + expected_edge_indices = [ + [0, 1], [1, 0], + [1, 2], [2, 1], + ] + expected_edge_indices = tlx.transpose(tlx.convert_to_tensor(expected_edge_indices)) + expected_edge_features = [ + [1, 0, 0], [1, 0, 0], + [1, 0, 0], [1, 0, 0], + ] + expected_edge_features = tlx.convert_to_tensor(expected_edge_features, dtype=tlx.int64) + assert tlx.convert_to_numpy(graph.x).tolist() == tlx.convert_to_numpy(expected_node_features).tolist() + assert tlx.convert_to_numpy(graph.edge_index).tolist() == tlx.convert_to_numpy(expected_edge_indices).tolist() + assert tlx.convert_to_numpy(graph.edge_attr).tolist() == tlx.convert_to_numpy(expected_edge_features).tolist() diff --git a/tests/utils/test_spm_calc.py b/tests/utils/test_spm_calc.py new file mode 100644 index 00000000..42441e3b --- /dev/null +++ b/tests/utils/test_spm_calc.py @@ -0,0 +1,21 @@ +import numpy as np +import scipy.sparse as sp +from gammagl.utils.spm_calc import calc_A_norm_hat + + +def test_calc_A_norm_hat(): + edge_index = np.array([ + [0, 1, 1, 2], + [1, 0, 2, 1] + ]) + A = np.array([ + [1, 1, 0], + [1, 1, 1], + [0, 1, 1] + ]) + D_vec = np.sum(A, axis=1) + D_vec_invsqrt_corr = 1 / np.sqrt(D_vec) + D_invsqrt_corr = np.diag(D_vec_invsqrt_corr) + expected_output = D_invsqrt_corr @ A @ D_invsqrt_corr + result = calc_A_norm_hat(edge_index).toarray() + assert np.allclose(result, expected_output) diff --git a/tests/utils/test_to_dense_adj.py b/tests/utils/test_to_dense_adj.py new file mode 100644 index 00000000..bb30deca --- /dev/null +++ b/tests/utils/test_to_dense_adj.py @@ -0,0 +1,33 @@ +import tensorlayerx as tlx +from gammagl.mpops import unsorted_segment_sum +from gammagl.utils.to_dense_adj import to_dense_adj +import numpy as np + +def test_to_dense_adj(): + return + edge_index = tlx.convert_to_tensor([ + [0, 1, 3], + [1, 2, 4] + ], dtype=tlx.int64) + + batch = tlx.convert_to_tensor([0, 0, 0, 1, 1], dtype=tlx.int64) + + adj_matrix = to_dense_adj(edge_index, batch=batch) + + adj_matrix_np = tlx.convert_to_numpy(adj_matrix) + + expected_output = tlx.convert_to_tensor([ + [ + [ + [0, 1, 0], + [0, 0, 1], + [0, 0, 0] + ], + [ + [0, 1], + [0, 0] + ] + ] + ], dtype=tlx.float32) + + assert np.array_equal(adj_matrix_np, tlx.convert_to_numpy(expected_output)), "The test failed, adjacency matrices do not match." diff --git a/tests/utils/test_tu_utils.py b/tests/utils/test_tu_utils.py new file mode 100644 index 00000000..bec370fa --- /dev/null +++ b/tests/utils/test_tu_utils.py @@ -0,0 +1,34 @@ +import math +import numpy as np +import tensorlayerx as tlx +from sklearn.svm import LinearSVC +from sklearn.metrics import accuracy_score +from sklearn.model_selection import GridSearchCV, StratifiedKFold +from gammagl.utils.tu_utils import linearsvc,get_negative_expectation,get_positive_expectation,local_global_loss_ +import numpy as np + + +def test_tu_utils(): + l_enc = tlx.convert_to_tensor(np.array([[1, 2], [3, 4], [5, 6]]).astype(np.float32)) + g_enc = tlx.convert_to_tensor(np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]).astype(np.float32)) + batch = np.array([0, 1, 2]).astype(np.int32) + num_graphs = g_enc.shape[0] + num_nodes = l_enc.shape[0] + pos_mask = np.zeros((num_nodes, num_graphs), dtype=np.float32) + neg_mask = np.ones((num_nodes, num_graphs), dtype=np.float32) + for nodeidx, graphidx in enumerate(batch): + pos_mask[nodeidx][graphidx] = 1. + neg_mask[nodeidx][graphidx] = 0. + pos_mask_tensor = tlx.convert_to_tensor(pos_mask) + neg_mask_tensor = tlx.convert_to_tensor(neg_mask) + res = tlx.matmul(l_enc, tlx.transpose(g_enc)) + log_2 = np.log(2) + E_pos = tlx.reduce_sum(get_positive_expectation(res * pos_mask_tensor, average=False)) + E_pos = E_pos / num_nodes + E_neg = tlx.reduce_sum(get_negative_expectation(res * neg_mask_tensor, average=False)) + E_neg = E_neg / (num_nodes * (num_graphs - 1)) + loss = E_neg - E_pos + expected_loss_value = loss.numpy() + loss = local_global_loss_(l_enc, g_enc, batch) + assert np.isclose(loss.numpy(), expected_loss_value, atol=1e-4), f"Expected loss {expected_loss_value} but got {loss.numpy()}" + diff --git a/tests/utils/test_typing.py b/tests/utils/test_typing.py new file mode 100644 index 00000000..5bf34ff8 --- /dev/null +++ b/tests/utils/test_typing.py @@ -0,0 +1,31 @@ +import inspect +import re +from collections import OrderedDict +from itertools import product +from typing import Callable, Dict, List, Tuple +import pyparsing as pp +from gammagl.utils.typing import split_types_repr,sanitize,param_type_repr,return_type_repr,parse_types,resolve_types + + +def test_typing(): + types_repr = "int, str, float" + expected_output = ["int", "str", "float"] + assert split_types_repr(types_repr) == expected_output + types_repr = "Tuple[int, str]" + expected_output = ["Tuple[int, str]"] + assert split_types_repr(types_repr) == expected_output + types_repr = "List[Tuple[int, str], Union[float, NoneType]]" + expected_output = ["List[Tuple[int, str], Union[float, NoneType]]"] + assert split_types_repr(types_repr) == expected_output + types_repr = "Dict[str, List[Tuple[int, Union[float, NoneType]]]]" + expected_output = ["Dict[str, List[Tuple[int, Union[float, NoneType]]]]"] + assert split_types_repr(types_repr) == expected_output + types_repr = "" + expected_output = [""] + assert split_types_repr(types_repr) == expected_output + + class MockParameter: + annotation = inspect.Parameter.empty + + param = MockParameter() + assert param_type_repr(param) == "torch.Tensor" diff --git a/tests/utils/test_union_utils.py b/tests/utils/test_union_utils.py new file mode 100644 index 00000000..2c104a49 --- /dev/null +++ b/tests/utils/test_union_utils.py @@ -0,0 +1,18 @@ +# import tensorflow as tf +import numpy as np +from gammagl.utils.union_utils import convert_union_to_numpy, union_len +import tensorlayerx as tlx + + +def test_union_utils(): + data_tensor = tlx.convert_to_tensor([1, 2, 3]) + data_list = [1, 2, 3] + data_numpy = np.array([1, 2, 3]) + assert np.array_equal(convert_union_to_numpy(data_tensor), np.array([1, 2, 3])) + assert np.array_equal(convert_union_to_numpy(data_list), np.array([1, 2, 3])) + assert np.array_equal(convert_union_to_numpy(data_numpy), np.array([1, 2, 3])) + assert convert_union_to_numpy(data_list, dtype=np.float32).dtype == np.float32 + assert convert_union_to_numpy(None) is None + assert union_len(data_tensor) == 3 + assert union_len(data_list) == 3 + assert union_len(data_numpy) == 3