We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CycleMotif lack of label, therefore do not support GNNExplainer.
### 🐛 Describe the bug

when running ./examples/explain/gnn_explainer_ba_shapes.py, when I replace the dataset:
```
dataset = ExplainerDataset(
    graph_generator=BAGraph(num_nodes=300, num_edges=5),
    motif_generator='house',
    num_motifs=80,
    transform=T.Constant(),
) 
```
with
```
dataset = ExplainerDataset(
    graph_generator=BAGraph(num_nodes=300, num_edges=5),
    motif_generator=CycleMotif(num_nodes=6), 
    num_motifs=80,
    transform=T.Constant(),
)
```
There is an error:
```
Traceback (most recent call last):
  File "/home/stt/py_github_repo_read/pytorch_geometric/examples/explain/gnn_explainer_ba_shapes.py", line 46, in <module>
    out_channels=dataset.num_classes).to(device)
  File "/home/stt/py_github_repo_read/pytorch_geometric/torch_geometric/data/in_memory_dataset.py", line 90, in num_classes
    return super().num_classes
  File "/home/stt/py_github_repo_read/pytorch_geometric/torch_geometric/data/dataset.py", line 173, in num_classes
    y = torch.cat([data.y for data in data_list if 'y' in data], dim=0)
RuntimeError: torch.cat(): expected a non-empty list of Tensors
```
The reason behind locate at line 23 in `./torch_geometric/datasets/motif_generator/cycle.py`
```
structure = Data(
            num_nodes=num_nodes,
            edge_index=torch.stack([row, col], dim=0),
# TODO: lack of y label
        )
```
lack of y label as in `./torch_geometric/datasets/motif_generator/house.py`
```
structure = Data(
            num_nodes=5,
            edge_index=torch.tensor([
                [0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4],
                [1, 3, 4, 4, 2, 0, 1, 3, 2, 0, 0, 1],
            ]),
            y=torch.tensor([0, 0, 1, 1, 2]),
        )
```
According to GNNExplainer original repository, for the cycle motif, the node labels are the same. Therefore, we only need to add `y=torch.tensor([0]*num_nodes)`

### Versions

PyTorch version: 2.1.0+cu121
Is debug build: False
CUDA used to build PyTorch: 12.1
ROCM used to build PyTorch: N/A

OS: Ubuntu 22.04.3 LTS (x86_64)
GCC version: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Clang version: Could not collect
CMake version: version 3.22.1
Libc version: glibc-2.35

Python version: 3.9.18 | packaged by conda-forge | (main, Aug 30 2023, 03:49:32)  [GCC 12.3.0] (64-bit runtime)
Python platform: Linux-6.2.0-34-generic-x86_64-with-glibc2.35
Is CUDA available: True
CUDA runtime version: 11.8.89
CUDA_MODULE_LOADING set to: LAZY
GPU models and configuration: 
GPU 0: NVIDIA GeForce RTX 3090
GPU 1: NVIDIA GeForce RTX 3090

Nvidia driver version: 535.113.01
cuDNN version: Could not collect
HIP runtime version: N/A
MIOpen runtime version: N/A
Is XNNPACK available: True

CPU:
架构：                              x86_64
CPU 运行模式：                      32-bit, 64-bit
Address sizes:                      39 bits physical, 48 bits virtual
字节序：                            Little Endian
CPU:                                24
在线 CPU 列表：                     0-23
厂商 ID：                           GenuineIntel
型号名称：                          13th Gen Intel(R) Core(TM) i7-13700KF
CPU 系列：                          6
型号：                              183
每个核的线程数：                    2
每个座的核数：                      16
座：                                1
步进：                              1
CPU 最大 MHz：                      5400.0000
CPU 最小 MHz：                      800.0000
BogoMIPS：                          6835.20
标记：                              fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi umip pku ospke waitpkg gfni vaes vpclmulqdq rdpid movdiri movdir64b fsrm md_clear serialize arch_lbr ibt flush_l1d arch_capabilities
虚拟化：                            VT-x
L1d 缓存：                          640 KiB (16 instances)
L1i 缓存：                          768 KiB (16 instances)
L2 缓存：                           24 MiB (10 instances)
L3 缓存：                           30 MiB (1 instance)
NUMA 节点：                         1
NUMA 节点0 CPU：                    0-23
Vulnerability Gather data sampling: Not affected
Vulnerability Itlb multihit:        Not affected
Vulnerability L1tf:                 Not affected
Vulnerability Mds:                  Not affected
Vulnerability Meltdown:             Not affected
Vulnerability Mmio stale data:      Not affected
Vulnerability Retbleed:             Not affected
Vulnerability Spec rstack overflow: Not affected
Vulnerability Spec store bypass:    Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:           Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:           Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence
Vulnerability Srbds:                Not affected
Vulnerability Tsx async abort:      Not affected

Versions of relevant libraries:
[pip3] numpy==1.26.0
[pip3] torch==2.1.0
[pip3] torchvision==0.16.0
[pip3] triton==2.1.0
[conda] numpy                     1.26.0                   pypi_0    pypi
[conda] torch                     2.1.0                    pypi_0    pypi
[conda] torchvision               0.16.0                   pypi_0    pypi
[conda] triton                    2.1.0                    pypi_0    pypi

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torch_geometric/datasets/explainer_dataset.py`
Content:
```
1 from typing import Any, Callable, Dict, Optional, Union
2 
3 import torch
4 
5 from torch_geometric.data import InMemoryDataset
6 from torch_geometric.datasets.graph_generator import GraphGenerator
7 from torch_geometric.datasets.motif_generator import MotifGenerator
8 from torch_geometric.explain import Explanation
9 
10 
11 class ExplainerDataset(InMemoryDataset):
12     r"""Generates a synthetic dataset for evaluating explainabilty algorithms,
13     as described in the `"GNNExplainer: Generating Explanations for Graph
14     Neural Networks" <https://arxiv.org/abs/1903.03894>`__ paper.
15     The :class:`~torch_geometric.datasets.ExplainerDataset` creates synthetic
16     graphs coming from a
17     :class:`~torch_geometric.datasets.graph_generator.GraphGenerator`, and
18     randomly attaches :obj:`num_motifs` many motifs to it coming from a
19     :class:`~torch_geometric.datasets.graph_generator.MotifGenerator`.
20     Ground-truth node-level and edge-level explainabilty masks are given based
21     on whether nodes and edges are part of a certain motif or not.
22 
23     For example, to generate a random Barabasi-Albert (BA) graph with 300
24     nodes, in which we want to randomly attach 80 :obj:`"house"` motifs, write:
25 
26     .. code-block:: python
27 
28         from torch_geometric.datasets import ExplainerDataset
29         from torch_geometric.datasets.graph_generator import BAGraph
30 
31         dataset = ExplainerDataset(
32             graph_generator=BAGraph(num_nodes=300, num_edges=5),
33             motif_generator='house',
34             num_motifs=80,
35         )
36 
37     .. note::
38 
39         For an example of using :class:`ExplainerDataset`, see
40         `examples/explain/gnn_explainer_ba_shapes.py
41         <https://github.com/pyg-team/pytorch_geometric/blob/master/examples/
42         /explain/gnn_explainer_ba_shapes.py>`_.
43 
44     Args:
45         graph_generator (GraphGenerator or str): The graph generator to be
46             used, *e.g.*,
47             :class:`torch.geometric.datasets.graph_generator.BAGraph`
48             (or any string that automatically resolves to it).
49         motif_generator (MotifGenerator): The motif generator to be used,
50             *e.g.*,
51             :class:`torch_geometric.datasets.motif_generator.HouseMotif`
52             (or any string that automatically resolves to it).
53         num_motifs (int): The number of motifs to attach to the graph.
54         num_graphs (int, optional): The number of graphs to generate.
55             (default: :obj:`1`)
56         graph_generator_kwargs (Dict[str, Any], optional): Arguments passed to
57             the respective graph generator module in case it gets automatically
58             resolved. (default: :obj:`None`)
59         motif_generator_kwargs (Dict[str, Any], optional): Arguments passed to
60             the respective motif generator module in case it gets automatically
61             resolved. (default: :obj:`None`)
62         transform (callable, optional): A function/transform that takes in an
63             :obj:`torch_geometric.data.Data` object and returns a transformed
64             version. The data object will be transformed before every access.
65             (default: :obj:`None`)
66     """
67     def __init__(
68         self,
69         graph_generator: Union[GraphGenerator, str],
70         motif_generator: Union[MotifGenerator, str],
71         num_motifs: int,
72         num_graphs: int = 1,
73         graph_generator_kwargs: Optional[Dict[str, Any]] = None,
74         motif_generator_kwargs: Optional[Dict[str, Any]] = None,
75         transform: Optional[Callable] = None,
76     ):
77         super().__init__(root=None, transform=transform)
78 
79         if num_motifs <= 0:
80             raise ValueError(f"At least one motif needs to be attached to the "
81                              f"graph (got {num_motifs})")
82 
83         self.graph_generator = GraphGenerator.resolve(
84             graph_generator,
85             **(graph_generator_kwargs or {}),
86         )
87         self.motif_generator = MotifGenerator.resolve(
88             motif_generator,
89             **(motif_generator_kwargs or {}),
90         )
91         self.num_motifs = num_motifs
92 
93         # TODO (matthias) support on-the-fly graph generation.
94         data_list = [self.get_graph() for _ in range(num_graphs)]
95         self.data, self.slices = self.collate(data_list)
96 
97     def get_graph(self) -> Explanation:
98         data = self.graph_generator()
99 
100         edge_indices = [data.edge_index]
101         num_nodes = data.num_nodes
102         node_masks = [torch.zeros(data.num_nodes)]
103         edge_masks = [torch.zeros(data.num_edges)]
104         ys = [torch.zeros(num_nodes, dtype=torch.long)]
105 
106         connecting_nodes = torch.randperm(num_nodes)[:self.num_motifs]
107         for i in connecting_nodes.tolist():
108             motif = self.motif_generator()
109 
110             # Add motif to the graph.
111             edge_indices.append(motif.edge_index + num_nodes)
112             node_masks.append(torch.ones(motif.num_nodes))
113             edge_masks.append(torch.ones(motif.num_edges))
114 
115             # Add random motif connection to the graph.
116             j = int(torch.randint(0, motif.num_nodes, (1, ))) + num_nodes
117             edge_indices.append(torch.tensor([[i, j], [j, i]]))
118             edge_masks.append(torch.zeros(2))
119 
120             if 'y' in motif:
121                 ys.append(motif.y + 1 if motif.y.min() == 0 else motif.y)
122 
123             num_nodes += motif.num_nodes
124 
125         return Explanation(
126             edge_index=torch.cat(edge_indices, dim=1),
127             y=torch.cat(ys, dim=0) if len(ys) > 1 else None,
128             edge_mask=torch.cat(edge_masks, dim=0),
129             node_mask=torch.cat(node_masks, dim=0),
130         )
131 
132     def __repr__(self) -> str:
133         return (f'{self.__class__.__name__}({len(self)}, '
134                 f'graph_generator={self.graph_generator}, '
135                 f'motif_generator={self.motif_generator}, '
136                 f'num_motifs={self.num_motifs})')
137
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/torch_geometric/datasets/explainer_dataset.py b/torch_geometric/datasets/explainer_dataset.py
--- a/torch_geometric/datasets/explainer_dataset.py
+++ b/torch_geometric/datasets/explainer_dataset.py
@@ -119,12 +119,14 @@
 
             if 'y' in motif:
                 ys.append(motif.y + 1 if motif.y.min() == 0 else motif.y)
+            else:
+                ys.append(torch.ones(motif.num_nodes, dtype=torch.long))
 
             num_nodes += motif.num_nodes
 
         return Explanation(
             edge_index=torch.cat(edge_indices, dim=1),
-            y=torch.cat(ys, dim=0) if len(ys) > 1 else None,
+            y=torch.cat(ys, dim=0),
             edge_mask=torch.cat(edge_masks, dim=0),
             node_mask=torch.cat(node_masks, dim=0),
         )

{"golden_diff": "diff --git a/torch_geometric/datasets/explainer_dataset.py b/torch_geometric/datasets/explainer_dataset.py\n--- a/torch_geometric/datasets/explainer_dataset.py\n+++ b/torch_geometric/datasets/explainer_dataset.py\n@@ -119,12 +119,14 @@\n \n             if 'y' in motif:\n                 ys.append(motif.y + 1 if motif.y.min() == 0 else motif.y)\n+            else:\n+                ys.append(torch.ones(motif.num_nodes, dtype=torch.long))\n \n             num_nodes += motif.num_nodes\n \n         return Explanation(\n             edge_index=torch.cat(edge_indices, dim=1),\n-            y=torch.cat(ys, dim=0) if len(ys) > 1 else None,\n+            y=torch.cat(ys, dim=0),\n             edge_mask=torch.cat(edge_masks, dim=0),\n             node_mask=torch.cat(node_masks, dim=0),\n         )\n", "issue": "CycleMotif lack of label, therefore do not support GNNExplainer.\n### \ud83d\udc1b Describe the bug\n\nwhen running ./examples/explain/gnn_explainer_ba_shapes.py, when I replace the dataset:\r\n```\r\ndataset = ExplainerDataset(\r\n    graph_generator=BAGraph(num_nodes=300, num_edges=5),\r\n    motif_generator='house',\r\n    num_motifs=80,\r\n    transform=T.Constant(),\r\n) \r\n```\r\nwith\r\n```\r\ndataset = ExplainerDataset(\r\n    graph_generator=BAGraph(num_nodes=300, num_edges=5),\r\n    motif_generator=CycleMotif(num_nodes=6), \r\n    num_motifs=80,\r\n    transform=T.Constant(),\r\n)\r\n```\r\nThere is an error:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/home/stt/py_github_repo_read/pytorch_geometric/examples/explain/gnn_explainer_ba_shapes.py\", line 46, in <module>\r\n    out_channels=dataset.num_classes).to(device)\r\n  File \"/home/stt/py_github_repo_read/pytorch_geometric/torch_geometric/data/in_memory_dataset.py\", line 90, in num_classes\r\n    return super().num_classes\r\n  File \"/home/stt/py_github_repo_read/pytorch_geometric/torch_geometric/data/dataset.py\", line 173, in num_classes\r\n    y = torch.cat([data.y for data in data_list if 'y' in data], dim=0)\r\nRuntimeError: torch.cat(): expected a non-empty list of Tensors\r\n```\r\nThe reason behind locate at line 23 in `./torch_geometric/datasets/motif_generator/cycle.py`\r\n```\r\nstructure = Data(\r\n            num_nodes=num_nodes,\r\n            edge_index=torch.stack([row, col], dim=0),\r\n# TODO: lack of y label\r\n        )\r\n```\r\nlack of y label as in `./torch_geometric/datasets/motif_generator/house.py`\r\n```\r\nstructure = Data(\r\n            num_nodes=5,\r\n            edge_index=torch.tensor([\r\n                [0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4],\r\n                [1, 3, 4, 4, 2, 0, 1, 3, 2, 0, 0, 1],\r\n            ]),\r\n            y=torch.tensor([0, 0, 1, 1, 2]),\r\n        )\r\n```\r\nAccording to GNNExplainer original repository, for the cycle motif, the node labels are the same. Therefore, we only need to add `y=torch.tensor([0]*num_nodes)`\n\n### Versions\n\nPyTorch version: 2.1.0+cu121\r\nIs debug build: False\r\nCUDA used to build PyTorch: 12.1\r\nROCM used to build PyTorch: N/A\r\n\r\nOS: Ubuntu 22.04.3 LTS (x86_64)\r\nGCC version: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0\r\nClang version: Could not collect\r\nCMake version: version 3.22.1\r\nLibc version: glibc-2.35\r\n\r\nPython version: 3.9.18 | packaged by conda-forge | (main, Aug 30 2023, 03:49:32)  [GCC 12.3.0] (64-bit runtime)\r\nPython platform: Linux-6.2.0-34-generic-x86_64-with-glibc2.35\r\nIs CUDA available: True\r\nCUDA runtime version: 11.8.89\r\nCUDA_MODULE_LOADING set to: LAZY\r\nGPU models and configuration: \r\nGPU 0: NVIDIA GeForce RTX 3090\r\nGPU 1: NVIDIA GeForce RTX 3090\r\n\r\nNvidia driver version: 535.113.01\r\ncuDNN version: Could not collect\r\nHIP runtime version: N/A\r\nMIOpen runtime version: N/A\r\nIs XNNPACK available: True\r\n\r\nCPU:\r\n\u67b6\u6784\uff1a                              x86_64\r\nCPU \u8fd0\u884c\u6a21\u5f0f\uff1a                      32-bit, 64-bit\r\nAddress sizes:                      39 bits physical, 48 bits virtual\r\n\u5b57\u8282\u5e8f\uff1a                            Little Endian\r\nCPU:                                24\r\n\u5728\u7ebf CPU \u5217\u8868\uff1a                     0-23\r\n\u5382\u5546 ID\uff1a                           GenuineIntel\r\n\u578b\u53f7\u540d\u79f0\uff1a                          13th Gen Intel(R) Core(TM) i7-13700KF\r\nCPU \u7cfb\u5217\uff1a                          6\r\n\u578b\u53f7\uff1a                              183\r\n\u6bcf\u4e2a\u6838\u7684\u7ebf\u7a0b\u6570\uff1a                    2\r\n\u6bcf\u4e2a\u5ea7\u7684\u6838\u6570\uff1a                      16\r\n\u5ea7\uff1a                                1\r\n\u6b65\u8fdb\uff1a                              1\r\nCPU \u6700\u5927 MHz\uff1a                      5400.0000\r\nCPU \u6700\u5c0f MHz\uff1a                      800.0000\r\nBogoMIPS\uff1a                          6835.20\r\n\u6807\u8bb0\uff1a                              fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi umip pku ospke waitpkg gfni vaes vpclmulqdq rdpid movdiri movdir64b fsrm md_clear serialize arch_lbr ibt flush_l1d arch_capabilities\r\n\u865a\u62df\u5316\uff1a                            VT-x\r\nL1d \u7f13\u5b58\uff1a                          640 KiB (16 instances)\r\nL1i \u7f13\u5b58\uff1a                          768 KiB (16 instances)\r\nL2 \u7f13\u5b58\uff1a                           24 MiB (10 instances)\r\nL3 \u7f13\u5b58\uff1a                           30 MiB (1 instance)\r\nNUMA \u8282\u70b9\uff1a                         1\r\nNUMA \u8282\u70b90 CPU\uff1a                    0-23\r\nVulnerability Gather data sampling: Not affected\r\nVulnerability Itlb multihit:        Not affected\r\nVulnerability L1tf:                 Not affected\r\nVulnerability Mds:                  Not affected\r\nVulnerability Meltdown:             Not affected\r\nVulnerability Mmio stale data:      Not affected\r\nVulnerability Retbleed:             Not affected\r\nVulnerability Spec rstack overflow: Not affected\r\nVulnerability Spec store bypass:    Mitigation; Speculative Store Bypass disabled via prctl\r\nVulnerability Spectre v1:           Mitigation; usercopy/swapgs barriers and __user pointer sanitization\r\nVulnerability Spectre v2:           Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence\r\nVulnerability Srbds:                Not affected\r\nVulnerability Tsx async abort:      Not affected\r\n\r\nVersions of relevant libraries:\r\n[pip3] numpy==1.26.0\r\n[pip3] torch==2.1.0\r\n[pip3] torchvision==0.16.0\r\n[pip3] triton==2.1.0\r\n[conda] numpy                     1.26.0                   pypi_0    pypi\r\n[conda] torch                     2.1.0                    pypi_0    pypi\r\n[conda] torchvision               0.16.0                   pypi_0    pypi\r\n[conda] triton                    2.1.0                    pypi_0    pypi\n", "before_files": [{"content": "from typing import Any, Callable, Dict, Optional, Union\n\nimport torch\n\nfrom torch_geometric.data import InMemoryDataset\nfrom torch_geometric.datasets.graph_generator import GraphGenerator\nfrom torch_geometric.datasets.motif_generator import MotifGenerator\nfrom torch_geometric.explain import Explanation\n\n\nclass ExplainerDataset(InMemoryDataset):\n    r\"\"\"Generates a synthetic dataset for evaluating explainabilty algorithms,\n    as described in the `\"GNNExplainer: Generating Explanations for Graph\n    Neural Networks\" <https://arxiv.org/abs/1903.03894>`__ paper.\n    The :class:`~torch_geometric.datasets.ExplainerDataset` creates synthetic\n    graphs coming from a\n    :class:`~torch_geometric.datasets.graph_generator.GraphGenerator`, and\n    randomly attaches :obj:`num_motifs` many motifs to it coming from a\n    :class:`~torch_geometric.datasets.graph_generator.MotifGenerator`.\n    Ground-truth node-level and edge-level explainabilty masks are given based\n    on whether nodes and edges are part of a certain motif or not.\n\n    For example, to generate a random Barabasi-Albert (BA) graph with 300\n    nodes, in which we want to randomly attach 80 :obj:`\"house\"` motifs, write:\n\n    .. code-block:: python\n\n        from torch_geometric.datasets import ExplainerDataset\n        from torch_geometric.datasets.graph_generator import BAGraph\n\n        dataset = ExplainerDataset(\n            graph_generator=BAGraph(num_nodes=300, num_edges=5),\n            motif_generator='house',\n            num_motifs=80,\n        )\n\n    .. note::\n\n        For an example of using :class:`ExplainerDataset`, see\n        `examples/explain/gnn_explainer_ba_shapes.py\n        <https://github.com/pyg-team/pytorch_geometric/blob/master/examples/\n        /explain/gnn_explainer_ba_shapes.py>`_.\n\n    Args:\n        graph_generator (GraphGenerator or str): The graph generator to be\n            used, *e.g.*,\n            :class:`torch.geometric.datasets.graph_generator.BAGraph`\n            (or any string that automatically resolves to it).\n        motif_generator (MotifGenerator): The motif generator to be used,\n            *e.g.*,\n            :class:`torch_geometric.datasets.motif_generator.HouseMotif`\n            (or any string that automatically resolves to it).\n        num_motifs (int): The number of motifs to attach to the graph.\n        num_graphs (int, optional): The number of graphs to generate.\n            (default: :obj:`1`)\n        graph_generator_kwargs (Dict[str, Any], optional): Arguments passed to\n            the respective graph generator module in case it gets automatically\n            resolved. (default: :obj:`None`)\n        motif_generator_kwargs (Dict[str, Any], optional): Arguments passed to\n            the respective motif generator module in case it gets automatically\n            resolved. (default: :obj:`None`)\n        transform (callable, optional): A function/transform that takes in an\n            :obj:`torch_geometric.data.Data` object and returns a transformed\n            version. The data object will be transformed before every access.\n            (default: :obj:`None`)\n    \"\"\"\n    def __init__(\n        self,\n        graph_generator: Union[GraphGenerator, str],\n        motif_generator: Union[MotifGenerator, str],\n        num_motifs: int,\n        num_graphs: int = 1,\n        graph_generator_kwargs: Optional[Dict[str, Any]] = None,\n        motif_generator_kwargs: Optional[Dict[str, Any]] = None,\n        transform: Optional[Callable] = None,\n    ):\n        super().__init__(root=None, transform=transform)\n\n        if num_motifs <= 0:\n            raise ValueError(f\"At least one motif needs to be attached to the \"\n                             f\"graph (got {num_motifs})\")\n\n        self.graph_generator = GraphGenerator.resolve(\n            graph_generator,\n            **(graph_generator_kwargs or {}),\n        )\n        self.motif_generator = MotifGenerator.resolve(\n            motif_generator,\n            **(motif_generator_kwargs or {}),\n        )\n        self.num_motifs = num_motifs\n\n        # TODO (matthias) support on-the-fly graph generation.\n        data_list = [self.get_graph() for _ in range(num_graphs)]\n        self.data, self.slices = self.collate(data_list)\n\n    def get_graph(self) -> Explanation:\n        data = self.graph_generator()\n\n        edge_indices = [data.edge_index]\n        num_nodes = data.num_nodes\n        node_masks = [torch.zeros(data.num_nodes)]\n        edge_masks = [torch.zeros(data.num_edges)]\n        ys = [torch.zeros(num_nodes, dtype=torch.long)]\n\n        connecting_nodes = torch.randperm(num_nodes)[:self.num_motifs]\n        for i in connecting_nodes.tolist():\n            motif = self.motif_generator()\n\n            # Add motif to the graph.\n            edge_indices.append(motif.edge_index + num_nodes)\n            node_masks.append(torch.ones(motif.num_nodes))\n            edge_masks.append(torch.ones(motif.num_edges))\n\n            # Add random motif connection to the graph.\n            j = int(torch.randint(0, motif.num_nodes, (1, ))) + num_nodes\n            edge_indices.append(torch.tensor([[i, j], [j, i]]))\n            edge_masks.append(torch.zeros(2))\n\n            if 'y' in motif:\n                ys.append(motif.y + 1 if motif.y.min() == 0 else motif.y)\n\n            num_nodes += motif.num_nodes\n\n        return Explanation(\n            edge_index=torch.cat(edge_indices, dim=1),\n            y=torch.cat(ys, dim=0) if len(ys) > 1 else None,\n            edge_mask=torch.cat(edge_masks, dim=0),\n            node_mask=torch.cat(node_masks, dim=0),\n        )\n\n    def __repr__(self) -> str:\n        return (f'{self.__class__.__name__}({len(self)}, '\n                f'graph_generator={self.graph_generator}, '\n                f'motif_generator={self.motif_generator}, '\n                f'num_motifs={self.num_motifs})')\n", "path": "torch_geometric/datasets/explainer_dataset.py"}], "after_files": [{"content": "from typing import Any, Callable, Dict, Optional, Union\n\nimport torch\n\nfrom torch_geometric.data import InMemoryDataset\nfrom torch_geometric.datasets.graph_generator import GraphGenerator\nfrom torch_geometric.datasets.motif_generator import MotifGenerator\nfrom torch_geometric.explain import Explanation\n\n\nclass ExplainerDataset(InMemoryDataset):\n    r\"\"\"Generates a synthetic dataset for evaluating explainabilty algorithms,\n    as described in the `\"GNNExplainer: Generating Explanations for Graph\n    Neural Networks\" <https://arxiv.org/abs/1903.03894>`__ paper.\n    The :class:`~torch_geometric.datasets.ExplainerDataset` creates synthetic\n    graphs coming from a\n    :class:`~torch_geometric.datasets.graph_generator.GraphGenerator`, and\n    randomly attaches :obj:`num_motifs` many motifs to it coming from a\n    :class:`~torch_geometric.datasets.graph_generator.MotifGenerator`.\n    Ground-truth node-level and edge-level explainabilty masks are given based\n    on whether nodes and edges are part of a certain motif or not.\n\n    For example, to generate a random Barabasi-Albert (BA) graph with 300\n    nodes, in which we want to randomly attach 80 :obj:`\"house\"` motifs, write:\n\n    .. code-block:: python\n\n        from torch_geometric.datasets import ExplainerDataset\n        from torch_geometric.datasets.graph_generator import BAGraph\n\n        dataset = ExplainerDataset(\n            graph_generator=BAGraph(num_nodes=300, num_edges=5),\n            motif_generator='house',\n            num_motifs=80,\n        )\n\n    .. note::\n\n        For an example of using :class:`ExplainerDataset`, see\n        `examples/explain/gnn_explainer_ba_shapes.py\n        <https://github.com/pyg-team/pytorch_geometric/blob/master/examples/\n        /explain/gnn_explainer_ba_shapes.py>`_.\n\n    Args:\n        graph_generator (GraphGenerator or str): The graph generator to be\n            used, *e.g.*,\n            :class:`torch.geometric.datasets.graph_generator.BAGraph`\n            (or any string that automatically resolves to it).\n        motif_generator (MotifGenerator): The motif generator to be used,\n            *e.g.*,\n            :class:`torch_geometric.datasets.motif_generator.HouseMotif`\n            (or any string that automatically resolves to it).\n        num_motifs (int): The number of motifs to attach to the graph.\n        num_graphs (int, optional): The number of graphs to generate.\n            (default: :obj:`1`)\n        graph_generator_kwargs (Dict[str, Any], optional): Arguments passed to\n            the respective graph generator module in case it gets automatically\n            resolved. (default: :obj:`None`)\n        motif_generator_kwargs (Dict[str, Any], optional): Arguments passed to\n            the respective motif generator module in case it gets automatically\n            resolved. (default: :obj:`None`)\n        transform (callable, optional): A function/transform that takes in an\n            :obj:`torch_geometric.data.Data` object and returns a transformed\n            version. The data object will be transformed before every access.\n            (default: :obj:`None`)\n    \"\"\"\n    def __init__(\n        self,\n        graph_generator: Union[GraphGenerator, str],\n        motif_generator: Union[MotifGenerator, str],\n        num_motifs: int,\n        num_graphs: int = 1,\n        graph_generator_kwargs: Optional[Dict[str, Any]] = None,\n        motif_generator_kwargs: Optional[Dict[str, Any]] = None,\n        transform: Optional[Callable] = None,\n    ):\n        super().__init__(root=None, transform=transform)\n\n        if num_motifs <= 0:\n            raise ValueError(f\"At least one motif needs to be attached to the \"\n                             f\"graph (got {num_motifs})\")\n\n        self.graph_generator = GraphGenerator.resolve(\n            graph_generator,\n            **(graph_generator_kwargs or {}),\n        )\n        self.motif_generator = MotifGenerator.resolve(\n            motif_generator,\n            **(motif_generator_kwargs or {}),\n        )\n        self.num_motifs = num_motifs\n\n        # TODO (matthias) support on-the-fly graph generation.\n        data_list = [self.get_graph() for _ in range(num_graphs)]\n        self.data, self.slices = self.collate(data_list)\n\n    def get_graph(self) -> Explanation:\n        data = self.graph_generator()\n\n        edge_indices = [data.edge_index]\n        num_nodes = data.num_nodes\n        node_masks = [torch.zeros(data.num_nodes)]\n        edge_masks = [torch.zeros(data.num_edges)]\n        ys = [torch.zeros(num_nodes, dtype=torch.long)]\n\n        connecting_nodes = torch.randperm(num_nodes)[:self.num_motifs]\n        for i in connecting_nodes.tolist():\n            motif = self.motif_generator()\n\n            # Add motif to the graph.\n            edge_indices.append(motif.edge_index + num_nodes)\n            node_masks.append(torch.ones(motif.num_nodes))\n            edge_masks.append(torch.ones(motif.num_edges))\n\n            # Add random motif connection to the graph.\n            j = int(torch.randint(0, motif.num_nodes, (1, ))) + num_nodes\n            edge_indices.append(torch.tensor([[i, j], [j, i]]))\n            edge_masks.append(torch.zeros(2))\n\n            if 'y' in motif:\n                ys.append(motif.y + 1 if motif.y.min() == 0 else motif.y)\n            else:\n                ys.append(torch.ones(motif.num_nodes, dtype=torch.long))\n\n            num_nodes += motif.num_nodes\n\n        return Explanation(\n            edge_index=torch.cat(edge_indices, dim=1),\n            y=torch.cat(ys, dim=0),\n            edge_mask=torch.cat(edge_masks, dim=0),\n            node_mask=torch.cat(node_masks, dim=0),\n        )\n\n    def __repr__(self) -> str:\n        return (f'{self.__class__.__name__}({len(self)}, '\n                f'graph_generator={self.graph_generator}, '\n                f'motif_generator={self.motif_generator}, '\n                f'num_motifs={self.num_motifs})')\n", "path": "torch_geometric/datasets/explainer_dataset.py"}]}