From 5c86c940da48958195cc6b55637b4d1c78cbba25 Mon Sep 17 00:00:00 2001 From: Paul Louis Date: Sat, 11 Feb 2023 00:38:58 -0500 Subject: [PATCH 1/6] Increase Coverage for GCNNorm transform --- test/transforms/test_gcn_norm.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/transforms/test_gcn_norm.py diff --git a/test/transforms/test_gcn_norm.py b/test/transforms/test_gcn_norm.py new file mode 100644 index 000000000000..4613b90075b7 --- /dev/null +++ b/test/transforms/test_gcn_norm.py @@ -0,0 +1,31 @@ +import torch + +from torch_geometric.data import Data +from torch_geometric.transforms import GCNNorm + + +def test_gcn_norm(): + assert GCNNorm().__repr__() == 'GCNNorm()' + + edge_index = torch.tensor([[0, 1, 1, 2], [1, 0, 2, 1]]) + edge_attr = torch.Tensor([1, 1, 1, 1]) + + data = Data(edge_index=edge_index, edge_attr=edge_attr, num_nodes=3) + data = GCNNorm(add_self_loops=False)(data) + assert data.edge_index.tolist() == [[0, 1, 1, 2], [1, 0, 2, 1]] + assert torch.allclose(data.edge_weight, + torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) + + data = Data(edge_index=edge_index, edge_weight=edge_attr, num_nodes=3) + data = GCNNorm(add_self_loops=False)(data) + assert data.edge_index.tolist() == [[0, 1, 1, 2], [1, 0, 2, 1]] + assert torch.allclose(data.edge_weight, + torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) + + data = GCNNorm(add_self_loops=True)(data) + assert data.edge_index.tolist() == [[0, 1, 1, 2, 0, 1, 2], + [1, 0, 2, 1, 0, 1, 2]] + assert torch.allclose( + data.edge_weight, + torch.tensor( + [0.34831, 0.34831, 0.34831, 0.34831, 0.58579, 0.41421, 0.58579])) From 043a66ef105b7553406f49ea95886a17c10cce32 Mon Sep 17 00:00:00 2001 From: Paul Louis Date: Sat, 11 Feb 2023 00:41:44 -0500 Subject: [PATCH 2/6] Update Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9518c826ce4..ecc9a6f79b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,7 +81,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed a bug in `Data.subgraph()` and `HeteroData.subgraph()` ([#6613](https://github.com/pyg-team/pytorch_geometric/pull/6613) - Fixed a bug in `PNAConv` and `DegreeScalerAggregation` to correctly incorporate degree statistics of isolated nodes ([#6609](https://github.com/pyg-team/pytorch_geometric/pull/6609)) -- Improved code coverage ([#6523](https://github.com/pyg-team/pytorch_geometric/pull/6523), [#6538](https://github.com/pyg-team/pytorch_geometric/pull/6538), [#6555](https://github.com/pyg-team/pytorch_geometric/pull/6555), [#6558](https://github.com/pyg-team/pytorch_geometric/pull/6558), [#6568](https://github.com/pyg-team/pytorch_geometric/pull/6568), [#6573](https://github.com/pyg-team/pytorch_geometric/pull/6573), [#6578](https://github.com/pyg-team/pytorch_geometric/pull/6578), [#6597](https://github.com/pyg-team/pytorch_geometric/pull/6597), [#6600](https://github.com/pyg-team/pytorch_geometric/pull/6600), [#6618](https://github.com/pyg-team/pytorch_geometric/pull/6618), [#6619](https://github.com/pyg-team/pytorch_geometric/pull/6619), [#6621](https://github.com/pyg-team/pytorch_geometric/pull/6621), [#6623](https://github.com/pyg-team/pytorch_geometric/pull/6623), [#6637](https://github.com/pyg-team/pytorch_geometric/pull/6637), [#6638](https://github.com/pyg-team/pytorch_geometric/pull/6638), [#6640](https://github.com/pyg-team/pytorch_geometric/pull/6640), [#6645](https://github.com/pyg-team/pytorch_geometric/pull/6645), [#6648](https://github.com/pyg-team/pytorch_geometric/pull/6648), [#6647](https://github.com/pyg-team/pytorch_geometric/pull/6647), [#6657](https://github.com/pyg-team/pytorch_geometric/pull/6657), [#6664](https://github.com/pyg-team/pytorch_geometric/pull/6664)) +- Improved code coverage ([#6523](https://github.com/pyg-team/pytorch_geometric/pull/6523), [#6538](https://github.com/pyg-team/pytorch_geometric/pull/6538), [#6555](https://github.com/pyg-team/pytorch_geometric/pull/6555), [#6558](https://github.com/pyg-team/pytorch_geometric/pull/6558), [#6568](https://github.com/pyg-team/pytorch_geometric/pull/6568), [#6573](https://github.com/pyg-team/pytorch_geometric/pull/6573), [#6578](https://github.com/pyg-team/pytorch_geometric/pull/6578), [#6597](https://github.com/pyg-team/pytorch_geometric/pull/6597), [#6600](https://github.com/pyg-team/pytorch_geometric/pull/6600), [#6618](https://github.com/pyg-team/pytorch_geometric/pull/6618), [#6619](https://github.com/pyg-team/pytorch_geometric/pull/6619), [#6621](https://github.com/pyg-team/pytorch_geometric/pull/6621), [#6623](https://github.com/pyg-team/pytorch_geometric/pull/6623), [#6637](https://github.com/pyg-team/pytorch_geometric/pull/6637), [#6638](https://github.com/pyg-team/pytorch_geometric/pull/6638), [#6640](https://github.com/pyg-team/pytorch_geometric/pull/6640), [#6645](https://github.com/pyg-team/pytorch_geometric/pull/6645), [#6648](https://github.com/pyg-team/pytorch_geometric/pull/6648), [#6647](https://github.com/pyg-team/pytorch_geometric/pull/6647), [#6657](https://github.com/pyg-team/pytorch_geometric/pull/6657), [#6664](https://github.com/pyg-team/pytorch_geometric/pull/6664), [#6673](https://github.com/pyg-team/pytorch_geometric/pull/6673)) - Fixed a bug in which `data.to_heterogeneous()` filtered attributs in the wrong dimension ([#6522](https://github.com/pyg-team/pytorch_geometric/pull/6522)) - Breaking Change: Temporal sampling will now also sample nodes with an equal timestamp to the seed time (requires `pyg-lib>0.1.0`) ([#6517](https://github.com/pyg-team/pytorch_geometric/pull/6517)) - Changed `DataLoader` workers with affinity to start at `cpu0` ([#6512](https://github.com/pyg-team/pytorch_geometric/pull/6512)) From ddf92b2b62dbfae3eee06ede3d4f9dbc037c22a2 Mon Sep 17 00:00:00 2001 From: wsad1 Date: Sat, 11 Feb 2023 12:20:29 +0000 Subject: [PATCH 3/6] modify test --- test/transforms/test_gcn_norm.py | 42 ++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/test/transforms/test_gcn_norm.py b/test/transforms/test_gcn_norm.py index 4613b90075b7..5a77598a61bb 100644 --- a/test/transforms/test_gcn_norm.py +++ b/test/transforms/test_gcn_norm.py @@ -1,31 +1,41 @@ +import pytest import torch +from torch_sparse import SparseTensor from torch_geometric.data import Data from torch_geometric.transforms import GCNNorm -def test_gcn_norm(): +@pytest.mark.parametrize('add_self_loops', [True, False]) +def test_gcn_norm(add_self_loops): assert GCNNorm().__repr__() == 'GCNNorm()' edge_index = torch.tensor([[0, 1, 1, 2], [1, 0, 2, 1]]) edge_attr = torch.Tensor([1, 1, 1, 1]) + if add_self_loops: + expected_edge_index = [[0, 1, 1, 2, 0, 1, 2], [1, 0, 2, 1, 0, 1, 2]] + expected_edge_weight = torch.tensor( + [0.4082, 0.4082, 0.4082, 0.4082, 0.5000, 0.3333, 0.5000]) + else: + expected_edge_index = edge_index.tolist() + expected_edge_weight = torch.tensor( + [0.70711, 0.70711, 0.70711, 0.70711]) + # Test with edge_index as LongTensor: data = Data(edge_index=edge_index, edge_attr=edge_attr, num_nodes=3) - data = GCNNorm(add_self_loops=False)(data) - assert data.edge_index.tolist() == [[0, 1, 1, 2], [1, 0, 2, 1]] - assert torch.allclose(data.edge_weight, - torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) + data = GCNNorm(add_self_loops=add_self_loops)(data) + assert data.edge_index.tolist() == expected_edge_index + assert torch.allclose(data.edge_weight, expected_edge_weight) data = Data(edge_index=edge_index, edge_weight=edge_attr, num_nodes=3) - data = GCNNorm(add_self_loops=False)(data) - assert data.edge_index.tolist() == [[0, 1, 1, 2], [1, 0, 2, 1]] - assert torch.allclose(data.edge_weight, - torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) + data = GCNNorm(add_self_loops=add_self_loops)(data) + assert data.edge_index.tolist() == expected_edge_index + assert torch.allclose(data.edge_weight, expected_edge_weight) - data = GCNNorm(add_self_loops=True)(data) - assert data.edge_index.tolist() == [[0, 1, 1, 2, 0, 1, 2], - [1, 0, 2, 1, 0, 1, 2]] - assert torch.allclose( - data.edge_weight, - torch.tensor( - [0.34831, 0.34831, 0.34831, 0.34831, 0.58579, 0.41421, 0.58579])) + # Test with edge_index as SparseTensor: + adj_t = SparseTensor.from_edge_index(edge_index, sparse_sizes=(3, 3)) + data = Data(adj_t=adj_t) + data = GCNNorm(add_self_loops=add_self_loops)(data) + assert data.adj_t.storage.row().tolist() == expected_edge_index[0] + assert data.adj_t.storage.col().tolist() == expected_edge_index[1] + assert torch.allclose(data.adj_t.storage.value(), expected_edge_weight) From bf0bca227adf91c2127b15bd66b00fd5628ec74c Mon Sep 17 00:00:00 2001 From: wsad1 Date: Sat, 11 Feb 2023 14:18:40 +0000 Subject: [PATCH 4/6] parametrize add_self_loops --- test/transforms/test_gcn_norm.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/transforms/test_gcn_norm.py b/test/transforms/test_gcn_norm.py index 5a77598a61bb..32a23fbd8179 100644 --- a/test/transforms/test_gcn_norm.py +++ b/test/transforms/test_gcn_norm.py @@ -25,17 +25,18 @@ def test_gcn_norm(add_self_loops): data = Data(edge_index=edge_index, edge_attr=edge_attr, num_nodes=3) data = GCNNorm(add_self_loops=add_self_loops)(data) assert data.edge_index.tolist() == expected_edge_index - assert torch.allclose(data.edge_weight, expected_edge_weight) + assert torch.allclose(data.edge_weight, expected_edge_weight, atol=1e-4) data = Data(edge_index=edge_index, edge_weight=edge_attr, num_nodes=3) data = GCNNorm(add_self_loops=add_self_loops)(data) assert data.edge_index.tolist() == expected_edge_index - assert torch.allclose(data.edge_weight, expected_edge_weight) + assert torch.allclose(data.edge_weight, expected_edge_weight, atol=1e-4) # Test with edge_index as SparseTensor: adj_t = SparseTensor.from_edge_index(edge_index, sparse_sizes=(3, 3)) data = Data(adj_t=adj_t) - data = GCNNorm(add_self_loops=add_self_loops)(data) - assert data.adj_t.storage.row().tolist() == expected_edge_index[0] - assert data.adj_t.storage.col().tolist() == expected_edge_index[1] - assert torch.allclose(data.adj_t.storage.value(), expected_edge_weight) + data = GCNNorm(add_self_loops=False)(data) + assert data.adj_t.storage.row().tolist() == edge_index[0].tolist() + assert data.adj_t.storage.col().tolist() == edge_index[1].tolist() + assert torch.allclose(data.adj_t.storage.value(), + torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) From b862379fa3567d8bde4d6babe907981506bebfeb Mon Sep 17 00:00:00 2001 From: rusty1s Date: Sun, 12 Feb 2023 11:01:18 +0100 Subject: [PATCH 5/6] typo --- test/transforms/test_gcn_norm.py | 56 ++++++++++++++------------ torch_geometric/transforms/gcn_norm.py | 9 +++-- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/test/transforms/test_gcn_norm.py b/test/transforms/test_gcn_norm.py index 32a23fbd8179..4e86d84d5516 100644 --- a/test/transforms/test_gcn_norm.py +++ b/test/transforms/test_gcn_norm.py @@ -6,37 +6,41 @@ from torch_geometric.transforms import GCNNorm -@pytest.mark.parametrize('add_self_loops', [True, False]) -def test_gcn_norm(add_self_loops): - assert GCNNorm().__repr__() == 'GCNNorm()' - +def test_gcn_norm(): edge_index = torch.tensor([[0, 1, 1, 2], [1, 0, 2, 1]]) - edge_attr = torch.Tensor([1, 1, 1, 1]) - if add_self_loops: - expected_edge_index = [[0, 1, 1, 2, 0, 1, 2], [1, 0, 2, 1, 0, 1, 2]] - expected_edge_weight = torch.tensor( - [0.4082, 0.4082, 0.4082, 0.4082, 0.5000, 0.3333, 0.5000]) - else: - expected_edge_index = edge_index.tolist() - expected_edge_weight = torch.tensor( - [0.70711, 0.70711, 0.70711, 0.70711]) - - # Test with edge_index as LongTensor: - data = Data(edge_index=edge_index, edge_attr=edge_attr, num_nodes=3) - data = GCNNorm(add_self_loops=add_self_loops)(data) + edge_weight = torch.ones(edge_index.size(1)) + adj_t = SparseTensor.from_edge_index(edge_index, edge_weight).t() + + transform = GCNNorm() + assert str(transform) == 'GCNNorm(add_self_loops=True)' + + expected_edge_index = [[0, 1, 1, 2, 0, 1, 2], [1, 0, 2, 1, 0, 1, 2]] + expected_edge_weight = torch.tensor( + [0.4082, 0.4082, 0.4082, 0.4082, 0.5000, 0.3333, 0.5000]) + + data = Data(edge_index=edge_index, edge_weight=edge_weight, num_nodes=3) + data = transform(data) + assert len(data) == 3 + assert data.num_nodes == 3 assert data.edge_index.tolist() == expected_edge_index assert torch.allclose(data.edge_weight, expected_edge_weight, atol=1e-4) - data = Data(edge_index=edge_index, edge_weight=edge_attr, num_nodes=3) - data = GCNNorm(add_self_loops=add_self_loops)(data) + data = Data(edge_index=edge_index, num_nodes=3) + data = transform(data) + assert len(data) == 3 + assert data.num_nodes == 3 assert data.edge_index.tolist() == expected_edge_index assert torch.allclose(data.edge_weight, expected_edge_weight, atol=1e-4) - # Test with edge_index as SparseTensor: - adj_t = SparseTensor.from_edge_index(edge_index, sparse_sizes=(3, 3)) + # For `SparseTensor`, expected outputs will be sorted: + expected_edge_index = [[0, 0, 1, 1, 1, 2, 2], [0, 1, 0, 1, 2, 1, 2]] + expected_edge_weight = torch.tensor( + [0.500, 0.4082, 0.4082, 0.3333, 0.4082, 0.4082, 0.5000]) + data = Data(adj_t=adj_t) - data = GCNNorm(add_self_loops=False)(data) - assert data.adj_t.storage.row().tolist() == edge_index[0].tolist() - assert data.adj_t.storage.col().tolist() == edge_index[1].tolist() - assert torch.allclose(data.adj_t.storage.value(), - torch.tensor([0.70711, 0.70711, 0.70711, 0.70711])) + data = transform(data) + assert len(data) == 1 + row, col, value = data.adj_t.coo() + assert row.tolist() == expected_edge_index[0] + assert col.tolist() == expected_edge_index[1] + assert torch.allclose(value, expected_edge_weight, atol=1e-4) diff --git a/torch_geometric/transforms/gcn_norm.py b/torch_geometric/transforms/gcn_norm.py index aa52a4f020aa..66ca9e59dac3 100644 --- a/torch_geometric/transforms/gcn_norm.py +++ b/torch_geometric/transforms/gcn_norm.py @@ -24,14 +24,15 @@ def __call__(self, data: Data) -> Data: assert 'edge_index' in data or 'adj_t' in data if 'edge_index' in data: - edge_weight = data.edge_attr - if 'edge_weight' in data: - edge_weight = data.edge_weight data.edge_index, data.edge_weight = gcn_norm( - data.edge_index, edge_weight, data.num_nodes, + data.edge_index, data.edge_weight, data.num_nodes, add_self_loops=self.add_self_loops) else: data.adj_t = gcn_norm(data.adj_t, add_self_loops=self.add_self_loops) return data + + def __repr__(self) -> str: + return (f'{self.__class__.__name__}(' + f'add_self_loops={self.add_self_loops})') From 2890e20954d6a6df180fd602eaf1b5a743bb81da Mon Sep 17 00:00:00 2001 From: rusty1s Date: Sun, 12 Feb 2023 11:14:46 +0100 Subject: [PATCH 6/6] typo --- test/transforms/test_gcn_norm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/transforms/test_gcn_norm.py b/test/transforms/test_gcn_norm.py index 4e86d84d5516..9eb2bd5debc5 100644 --- a/test/transforms/test_gcn_norm.py +++ b/test/transforms/test_gcn_norm.py @@ -1,4 +1,3 @@ -import pytest import torch from torch_sparse import SparseTensor