Repository: pyg-team/pytorch_geometric

Issue:
Data Batch problem in PyG
### 🐛 Describe the bug

Hi. I am a computational physics researcher and was using PyG very well.
my pyg code was working well a few weeks ago, but now that I run my code, it is not working anymore without any changes.

the problem is like below.
I have many material structures and in my "custom_dataset" class, these are preprocessed and all graph informations (node features, edge features, edge index etc) are inserted into "Data" object in PyTorch geometric.
You can see that each preprocessed sample with index $i$ was printed normal "Data" object in pyg

![캡처2](https://user-images.githubusercontent.com/76824867/175221069-874bf9b1-308b-4d99-8459-b3479307e78a.PNG)

But When I insert my custom dataset class into pyg DataLoader and I did like below,

``` Python
sample = next(iter(train_loader)) # batch sample
```

batch sample is denoted by "DataDataBatch". I didn't see this kind of object name.
and i can't use "sample.x' or "sample.edge_index" command. Instead I need to do like this

![캡처3](https://user-images.githubusercontent.com/76824867/175222628-75eee3a2-869a-4c80-8dd5-5f0f160682f0.PNG)

I want to use expressions like "sample.x", "sample.edge_index" or "sample.edge_attr" as like before. 
I expect your kind explanations. Thank you.


### Environment

* PyG version: `2.0.5`
* PyTorch version: `1.11.0+cu113`
* OS: `GoogleColab Pro Plus`
* Python version: `Python 3.7.13 in colab`
* CUDA/cuDNN version:
* How you installed PyTorch and PyG (`conda`, `pip`, source): 
``` python
# Install required packages.
import os
import torch
os.environ['TORCH'] = torch.__version__
print(torch.__version__)
!pip install -q torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install -q torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install -q git+https://github.com/pyg-team/pytorch_geometric.git
!pip install -q pymatgen==2020.11.11  
```
* Any other relevant information (*e.g.*, version of `torch-scatter`):



Discussion:
`DataDataBatch` indeed suggests that there is something weird going on. I am not able to reproduce this on any of our examples though. Do you have a minimal script to reproduce? This would help us out a lot!
cc @mananshah99 Related to recent changes to dynamic inheritance?
Acknowledged, will take a look shortly.
Here is an image captured in a few weeks ago.
And there was no changes in my code.

The object name is "DataBatch" clearly.

![캡처](https://user-images.githubusercontent.com/76824867/175793387-93f87337-58ba-46cf-8a5d-4cd027d17687.PNG)

Maybe... Should I take down-grade pyg??
I have solved this problem! I just downgraded current version(2.0.5) of PyG to previous version (2.0.4).  
In my opinion, "collate function" or any other components in PyG Dataloader could be the problem. 
Until I put my custom dataset class into the dataloader, it was okay. 
But after the step of dataloader, something changed. 
Or maybe there's some dependency problem in googlecolab..
Anyway, I hope you guys should fix this problem soon.
Thanks for your effort.
The name `DataBatch` is correct. Is this for 2.0.4 or 2.0.5? Can you share an example that reproduces the issue?
I'm having the same problem.
## Example
``` python
from pprint import pprint

import networkx as nx
import torch as th
from torch_geometric.data import InMemoryDataset, LightningDataset
from torch_geometric.transforms import LocalDegreeProfile
from torch_geometric.utils import from_networkx


class PathGraph(InMemoryDataset):
    def __init__(
        self,
        root="data",
        transform=None,
        pre_transform=None,
        pre_filter=None,
    ):
        super().__init__(root, transform, pre_transform, pre_filter)
        self.data, self.slices = th.load(self.processed_paths[0])

    @property
    def raw_file_names(self):
        return [f"path_graph_{i}.gml" for i in range(3, 6)]

    @property
    def processed_file_names(self):
        return ["data.pt"]

    def download(self):
        graphs = (nx.path_graph(i) for i in range(3, 6))
        for g, p in zip(graphs, self.raw_paths):
            nx.write_gml(g, p)

    def process(self):
        graphs = map(nx.read_gml, self.raw_paths)
        graphs = list(graphs)
        data_list = [from_networkx(g) for g in graphs]

        if self.pre_filter is not None:
            data_list = [data for data in data_list if self.pre_filter(data)]

        if self.pre_transform is not None:
            data_list = [self.pre_transform(data) for data in data_list]

        data, slices = self.collate(data_list)
        th.save((data, slices), self.processed_paths[0])


def main():
    data_module = LightningDataset(
        train_dataset=PathGraph(transform=LocalDegreeProfile()),
        batch_size=2,
    )
    batch = next(iter(data_module.train_dataloader()))
    print("batch:")
    pprint(batch)
    print("batch.x:")
    pprint(batch.x)


if __name__ == "__main__":
    main()


```
The output is:
```
batch:
DataDataBatch(
  x={
    edge_index=[2, 10],
    num_nodes=7,
    x=[7, 5],
    batch=[7],
    ptr=[3]
  }
)
batch.x:
{'batch': tensor([0, 0, 0, 0, 1, 1, 1]),
 'edge_index': tensor([[0, 1, 1, 2, 2, 3, 4, 5, 5, 6],
        [1, 0, 2, 1, 3, 2, 5, 4, 6, 5]]),
 'num_nodes': 7,
 'ptr': tensor([0, 4, 7]),
 'x': tensor([[1.0000, 2.0000, 2.0000, 2.0000, 0.0000],
        [2.0000, 1.0000, 2.0000, 1.5000, 0.7071],
        [2.0000, 1.0000, 2.0000, 1.5000, 0.7071],
        [1.0000, 2.0000, 2.0000, 2.0000, 0.0000],
        [1.0000, 2.0000, 2.0000, 2.0000, 0.0000],
        [2.0000, 1.0000, 1.0000, 1.0000, 0.0000],
        [1.0000, 2.0000, 2.0000, 2.0000, 0.0000]])}
```
## Environment

-  PyG version: pyg-nightly=2.0.5.dev20220706
- PyTorch version: 1.12.0
- OS: ArchLinux
- Python version: 3.10.5 (conda_forge)
- CUDA/cuDNN version: 11.6.0
- How you installed PyTorch and PyG (conda, pip, source):
```
mamba install pytorch pytorch-lightning scikit-learn networkx scipy
pip install torch-scatter -f https://data.pyg.org/whl/torch-1.12.0%2Bcu116.html
pip install torch-sparse -f https://data.pyg.org/whl/torch-1.12.0%2Bcu116.html
```




Thanks! I can indeed reproduce. Only happens with PyTorch Lightning and indeed related to the changes of `DynamicInheritance` in the `Batch` class (cc @mananshah99).
I'm glad that the problem will be resolved!!

Resolving patch:
diff --git a/torch_geometric/loader/dataloader.py b/torch_geometric/loader/dataloader.py
--- a/torch_geometric/loader/dataloader.py
+++ b/torch_geometric/loader/dataloader.py
@@ -1,4 +1,5 @@
 from collections.abc import Mapping, Sequence
+from inspect import signature
 from typing import List, Optional, Union
 
 import torch.utils.data
@@ -39,6 +40,28 @@ def collate(self, batch):  # Deprecated...
         return self(batch)
 
 
+# PyG 'Data' objects are subclasses of MutableMapping, which is an
+# instance of collections.abc.Mapping. Currently, PyTorch pin_memory
+# for DataLoaders treats the returned batches as Mapping objects and
+# calls `pin_memory` on each element in `Data.__dict__`, which is not
+# desired behavior if 'Data' has a `pin_memory` function. We patch
+# this behavior here by monkeypatching `pin_memory`, but can hopefully patch
+# this in PyTorch in the future:
+__torch_pin_memory = torch.utils.data._utils.pin_memory.pin_memory
+__torch_pin_memory_params = signature(__torch_pin_memory).parameters
+
+
+def pin_memory(data, device=None):
+    if hasattr(data, "pin_memory"):
+        return data.pin_memory()
+    if len(__torch_pin_memory_params) > 1:
+        return __torch_pin_memory(data, device)
+    return __torch_pin_memory(data)
+
+
+torch.utils.data._utils.pin_memory.pin_memory = pin_memory
+
+
 class DataLoader(torch.utils.data.DataLoader):
     r"""A data loader which merges data objects from a
     :class:`torch_geometric.data.Dataset` to a mini-batch.


Test patch: