BUCToolkit 1.0b0__tar.gz → 1.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- buctoolkit-1.0b2/BUCToolkit/BatchMD/constrained_md/README.md +4 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchStructures/BatchStructuresBase.py +7 -3
- buctoolkit-1.0b2/BUCToolkit/BatchStructures/README.md +14 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchStructures/__init__.py +7 -0
- buctoolkit-1.0b2/BUCToolkit/BatchStructures/batch.py +256 -0
- buctoolkit-1.0b2/BUCToolkit/BatchStructures/data.py +440 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/preprocessing.py +5 -4
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/StandardTemplate.py +1 -1
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/ConstrainedMolecularDynamics.py +3 -1
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/DataLoaders.py +2 -1
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/NEB.py +3 -1
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/StructureOptimization.py +5 -5
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/_io.py +18 -12
- buctoolkit-1.0b2/BUCToolkit/cli/convert_data.py +56 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/main.py +31 -13
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/PKG-INFO +2 -2
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/SOURCES.txt +6 -0
- buctoolkit-1.0b2/MANIFEST.in +1 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/PKG-INFO +2 -2
- buctoolkit-1.0b2/README.md +545 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/setup.py +5 -2
- buctoolkit-1.0b0/README.md +0 -23
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchGenerate/CheckStructures.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchGenerate/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchGenerate/coords_interp.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchGenerate/initial_ads_structures.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchGenerate/random_replace.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMC/MetropolisMC.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMC/_BaseMC.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMC/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/BiasedMD.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/NVE.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/NVT.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/_BaseMD.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/BiasedMD.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/ConstrNVE.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/ConstrNVT.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/NVE.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/NVT.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/_BaseMD.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/_ConstrBaseMD.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchMD/constrained_md/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/TS/CI_NEB.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/TS/Dimer.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/TS/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/_BaseOpt.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/_utils/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/_utils/_line_search.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/_utils/_warnings.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/frequency.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/minimize/CG.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/minimize/FIRE.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/minimize/QN.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchOptim/minimize/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/BatchStructures/StructuresIO.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/load_files.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/sampling.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/supercells.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/Preprocessing/write_files.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/_version.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/Losses.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/Metrics.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/ModelOptims.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/MolecularDynamics.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/MonteCarlo.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/Predictor.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/Trainer.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/VibrationAnalysis.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/api/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/cli.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/input_stub.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/print_logo.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/cli/template_model.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/io.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/AtomSelector.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/AtomicNumber2Properties.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/ElemListReduce.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/Ewald.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/IrregularTensorReformat.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/_CheckModules.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/_Element_info.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/__init__.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/_para_flatt_list.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/_print_formatter.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/check_structures.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/function_utils.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/grad_functions.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/index_ops.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit/utils/setup_loggers.py +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/dependency_links.txt +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/entry_points.txt +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/requires.txt +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/BUCToolkit.egg-info/top_level.txt +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/LICENSE +0 -0
- {buctoolkit-1.0b0 → buctoolkit-1.0b2}/setup.cfg +0 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
### Here algorithms require all input batch are regular, i.e., in one batch, all structures have the same atom numbers.
|
|
2
|
+
* Typically, they are the same chemical systems with different configurations.
|
|
3
|
+
* It is useful for independent repeated MD, blue-moon ensembles, or application for Jarzynski identity.
|
|
4
|
+
* It is more efficiency than the irregular batch version algorithms.
|
|
@@ -423,13 +423,15 @@ class BatchStructures(object):
|
|
|
423
423
|
_STANDARD_FILE_LIST = set(list(self._ATTR_NAMES) + ['head', ])
|
|
424
424
|
if mode not in {'w', 'a'}:
|
|
425
425
|
raise ValueError(f'Unknown `mode` value {mode}. It must be "w" or "a".')
|
|
426
|
-
if not os.path.
|
|
426
|
+
if not os.path.exists(path): # path do not exist
|
|
427
427
|
mode = 'w'
|
|
428
428
|
os.makedirs(path, )
|
|
429
|
+
elif os.path.isfile(path):
|
|
430
|
+
raise OSError(f"The file `{path}` already exists.")
|
|
429
431
|
elif len(os.listdir(path)) > 0:
|
|
430
432
|
if mode == 'a':
|
|
431
433
|
file_set = set(os.listdir(path))
|
|
432
|
-
|
|
434
|
+
if file_set != _STANDARD_FILE_LIST: raise RuntimeError('Existing files do not match the saving data.')
|
|
433
435
|
else:
|
|
434
436
|
for ff in os.listdir(path):
|
|
435
437
|
if ff not in _STANDARD_FILE_LIST:
|
|
@@ -724,8 +726,10 @@ class BatchStructures(object):
|
|
|
724
726
|
is_convert = True
|
|
725
727
|
else:
|
|
726
728
|
is_convert = False
|
|
727
|
-
if not os.path.
|
|
729
|
+
if not os.path.exists(output_path):
|
|
728
730
|
os.makedirs(output_path)
|
|
731
|
+
elif os.path.isfile(output_path):
|
|
732
|
+
raise OSError(f"There is already a file named '{output_path}'.")
|
|
729
733
|
|
|
730
734
|
try:
|
|
731
735
|
file_name_list = [f'{_}' for _ in file_name_list]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Here `data.py` and `batch.py` are a subset from `pytorch_geometric`
|
|
2
|
+
|
|
3
|
+
BUCToolkit uses the most basic graph data structures of
|
|
4
|
+
[`pytorch_geometric`](https://pytorch-geometric.readthedocs.io/en/latest/) [1, 2] framework as a
|
|
5
|
+
basic standard input data format.
|
|
6
|
+
|
|
7
|
+
Thus, one needs not install the whole `pytorch_geometric` with heavy dependencies to run our methods.
|
|
8
|
+
|
|
9
|
+
We follow the same approach to NequIP (https://github.com/mir-group/nequip/tree/main/nequip) and
|
|
10
|
+
mace (https://github.com/ACEsuit/mace), and copy their code of `data.py` and `batch.py` here.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
[1] Fey, M., & Lenssen, J. E. (2019). Fast Graph Representation Learning with PyTorch Geometric (Version 2.0.1) [Computer software]. https://github.com/pyg-team/pytorch_geometric <br>
|
|
14
|
+
[2] https://arxiv.org/abs/1903.02428
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
# File: __init__.py
|
|
5
5
|
# Environment: Python 3.12
|
|
6
6
|
|
|
7
|
+
from BUCToolkit.BatchStructures.BatchStructuresBase import BatchStructures
|
|
7
8
|
from BUCToolkit.BatchStructures.StructuresIO import read_opt_structures, read_md_traj, read_mc_traj, ArrayDumper, ArrayDumpReader
|
|
9
|
+
from BUCToolkit.BatchStructures.batch import Batch
|
|
10
|
+
from BUCToolkit.BatchStructures.data import Data
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
__all__ = [
|
|
10
14
|
"read_opt_structures",
|
|
@@ -12,4 +16,7 @@ __all__ = [
|
|
|
12
16
|
"read_mc_traj",
|
|
13
17
|
"ArrayDumper",
|
|
14
18
|
"ArrayDumpReader",
|
|
19
|
+
'Batch',
|
|
20
|
+
'Data',
|
|
21
|
+
'BatchStructures'
|
|
15
22
|
]
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import torch
|
|
6
|
+
from torch import Tensor
|
|
7
|
+
|
|
8
|
+
from .data import Data
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Batch(Data):
|
|
12
|
+
r"""A plain old python object modeling a batch of graphs as one big
|
|
13
|
+
(disconnected) graph. With :class:`torch_geometric.data.Data` being the
|
|
14
|
+
base class, all its methods can also be used here.
|
|
15
|
+
In addition, single graphs can be reconstructed via the assignment vector
|
|
16
|
+
:obj:`batch`, which maps each node to its respective graph identifier.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, batch=None, ptr=None, **kwargs):
|
|
20
|
+
super(Batch, self).__init__(**kwargs)
|
|
21
|
+
|
|
22
|
+
for key, item in kwargs.items():
|
|
23
|
+
if key == "num_nodes":
|
|
24
|
+
self.__num_nodes__ = item
|
|
25
|
+
else:
|
|
26
|
+
self[key] = item
|
|
27
|
+
|
|
28
|
+
self.batch = batch
|
|
29
|
+
self.ptr = ptr
|
|
30
|
+
self.__data_class__ = Data
|
|
31
|
+
self.__slices__ = None
|
|
32
|
+
self.__cumsum__ = None
|
|
33
|
+
self.__cat_dims__ = None
|
|
34
|
+
self.__num_nodes_list__ = None
|
|
35
|
+
self.__num_graphs__ = None
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_data_list(cls, data_list, follow_batch=[], exclude_keys=[]):
|
|
39
|
+
r"""Constructs a batch object from a python list holding
|
|
40
|
+
:class:`torch_geometric.data.Data` objects.
|
|
41
|
+
The assignment vector :obj:`batch` is created on the fly.
|
|
42
|
+
Additionally, creates assignment batch vectors for each key in
|
|
43
|
+
:obj:`follow_batch`.
|
|
44
|
+
Will exclude any keys given in :obj:`exclude_keys`."""
|
|
45
|
+
|
|
46
|
+
keys = list(set(data_list[0].keys) - set(exclude_keys))
|
|
47
|
+
assert "batch" not in keys and "ptr" not in keys
|
|
48
|
+
|
|
49
|
+
batch = cls()
|
|
50
|
+
for key in data_list[0].__dict__.keys():
|
|
51
|
+
if key[:2] != "__" and key[-2:] != "__":
|
|
52
|
+
batch[key] = None
|
|
53
|
+
|
|
54
|
+
batch.__num_graphs__ = len(data_list)
|
|
55
|
+
batch.__data_class__ = data_list[0].__class__
|
|
56
|
+
for key in keys + ["batch"]:
|
|
57
|
+
batch[key] = []
|
|
58
|
+
batch["ptr"] = [0]
|
|
59
|
+
|
|
60
|
+
device = None
|
|
61
|
+
slices = {key: [0] for key in keys}
|
|
62
|
+
cumsum = {key: [0] for key in keys}
|
|
63
|
+
cat_dims = {}
|
|
64
|
+
num_nodes_list = []
|
|
65
|
+
for i, data in enumerate(data_list):
|
|
66
|
+
for key in keys:
|
|
67
|
+
item = data[key]
|
|
68
|
+
|
|
69
|
+
# Increase values by `cumsum` value.
|
|
70
|
+
cum = cumsum[key][-1]
|
|
71
|
+
if isinstance(item, Tensor) and item.dtype != torch.bool:
|
|
72
|
+
if not isinstance(cum, int) or cum != 0:
|
|
73
|
+
item = item + cum
|
|
74
|
+
elif isinstance(item, (int, float)):
|
|
75
|
+
item = item + cum
|
|
76
|
+
|
|
77
|
+
# Gather the size of the `cat` dimension.
|
|
78
|
+
size = 1
|
|
79
|
+
cat_dim = data.__cat_dim__(key, data[key])
|
|
80
|
+
# 0-dimensional tensors have no dimension along which to
|
|
81
|
+
# concatenate, so we set `cat_dim` to `None`.
|
|
82
|
+
if isinstance(item, Tensor) and item.dim() == 0:
|
|
83
|
+
cat_dim = None
|
|
84
|
+
cat_dims[key] = cat_dim
|
|
85
|
+
|
|
86
|
+
# Add a batch dimension to items whose `cat_dim` is `None`:
|
|
87
|
+
if isinstance(item, Tensor) and cat_dim is None:
|
|
88
|
+
cat_dim = 0 # Concatenate along this new batch dimension.
|
|
89
|
+
item = item.unsqueeze(0)
|
|
90
|
+
device = item.device
|
|
91
|
+
elif isinstance(item, Tensor):
|
|
92
|
+
size = item.size(cat_dim)
|
|
93
|
+
device = item.device
|
|
94
|
+
|
|
95
|
+
batch[key].append(item) # Append item to the attribute list.
|
|
96
|
+
|
|
97
|
+
slices[key].append(size + slices[key][-1])
|
|
98
|
+
inc = data.__inc__(key, item)
|
|
99
|
+
if isinstance(inc, (tuple, list)):
|
|
100
|
+
inc = torch.tensor(inc)
|
|
101
|
+
cumsum[key].append(inc + cumsum[key][-1])
|
|
102
|
+
|
|
103
|
+
if key in follow_batch:
|
|
104
|
+
if isinstance(size, Tensor):
|
|
105
|
+
for j, size in enumerate(size.tolist()):
|
|
106
|
+
tmp = f"{key}_{j}_batch"
|
|
107
|
+
batch[tmp] = [] if i == 0 else batch[tmp]
|
|
108
|
+
batch[tmp].append(
|
|
109
|
+
torch.full((size,), i, dtype=torch.long, device=device)
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
tmp = f"{key}_batch"
|
|
113
|
+
batch[tmp] = [] if i == 0 else batch[tmp]
|
|
114
|
+
batch[tmp].append(
|
|
115
|
+
torch.full((size,), i, dtype=torch.long, device=device)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if hasattr(data, "__num_nodes__"):
|
|
119
|
+
num_nodes_list.append(data.__num_nodes__)
|
|
120
|
+
else:
|
|
121
|
+
num_nodes_list.append(None)
|
|
122
|
+
|
|
123
|
+
num_nodes = data.num_nodes
|
|
124
|
+
if num_nodes is not None:
|
|
125
|
+
item = torch.full((num_nodes,), i, dtype=torch.long, device=device)
|
|
126
|
+
batch.batch.append(item)
|
|
127
|
+
batch.ptr.append(batch.ptr[-1] + num_nodes)
|
|
128
|
+
|
|
129
|
+
batch.batch = None if len(batch.batch) == 0 else batch.batch
|
|
130
|
+
batch.ptr = None if len(batch.ptr) == 1 else batch.ptr
|
|
131
|
+
batch.__slices__ = slices
|
|
132
|
+
batch.__cumsum__ = cumsum
|
|
133
|
+
batch.__cat_dims__ = cat_dims
|
|
134
|
+
batch.__num_nodes_list__ = num_nodes_list
|
|
135
|
+
|
|
136
|
+
ref_data = data_list[0]
|
|
137
|
+
for key in batch.keys:
|
|
138
|
+
items = batch[key]
|
|
139
|
+
item = items[0]
|
|
140
|
+
cat_dim = ref_data.__cat_dim__(key, item)
|
|
141
|
+
cat_dim = 0 if cat_dim is None else cat_dim
|
|
142
|
+
if isinstance(item, Tensor):
|
|
143
|
+
batch[key] = torch.cat(items, cat_dim)
|
|
144
|
+
elif isinstance(item, (int, float)):
|
|
145
|
+
batch[key] = torch.tensor(items)
|
|
146
|
+
|
|
147
|
+
# if torch_geometric.is_debug_enabled():
|
|
148
|
+
# batch.debug()
|
|
149
|
+
|
|
150
|
+
return batch.contiguous()
|
|
151
|
+
|
|
152
|
+
def get_example(self, idx: int) -> Data:
|
|
153
|
+
r"""Reconstructs the :class:`torch_geometric.data.Data` object at index
|
|
154
|
+
:obj:`idx` from the batch object.
|
|
155
|
+
The batch object must have been created via :meth:`from_data_list` in
|
|
156
|
+
order to be able to reconstruct the initial objects."""
|
|
157
|
+
|
|
158
|
+
if self.__slices__ is None:
|
|
159
|
+
raise RuntimeError(
|
|
160
|
+
(
|
|
161
|
+
"Cannot reconstruct data list from batch because the batch "
|
|
162
|
+
"object was not created using `Batch.from_data_list()`."
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
data = self.__data_class__()
|
|
167
|
+
idx = self.num_graphs + idx if idx < 0 else idx
|
|
168
|
+
|
|
169
|
+
for key in self.__slices__.keys():
|
|
170
|
+
item = self[key]
|
|
171
|
+
if self.__cat_dims__[key] is None:
|
|
172
|
+
# The item was concatenated along a new batch dimension,
|
|
173
|
+
# so just index in that dimension:
|
|
174
|
+
item = item[idx]
|
|
175
|
+
else:
|
|
176
|
+
# Narrow the item based on the values in `__slices__`.
|
|
177
|
+
if isinstance(item, Tensor):
|
|
178
|
+
dim = self.__cat_dims__[key]
|
|
179
|
+
start = self.__slices__[key][idx]
|
|
180
|
+
end = self.__slices__[key][idx + 1]
|
|
181
|
+
item = item.narrow(dim, start, end - start)
|
|
182
|
+
else:
|
|
183
|
+
start = self.__slices__[key][idx]
|
|
184
|
+
end = self.__slices__[key][idx + 1]
|
|
185
|
+
item = item[start:end]
|
|
186
|
+
item = item[0] if len(item) == 1 else item
|
|
187
|
+
|
|
188
|
+
# Decrease its value by `cumsum` value:
|
|
189
|
+
cum = self.__cumsum__[key][idx]
|
|
190
|
+
if isinstance(item, Tensor):
|
|
191
|
+
if not isinstance(cum, int) or cum != 0:
|
|
192
|
+
item = item - cum
|
|
193
|
+
elif isinstance(item, (int, float)):
|
|
194
|
+
item = item - cum
|
|
195
|
+
|
|
196
|
+
data[key] = item
|
|
197
|
+
|
|
198
|
+
if self.__num_nodes_list__[idx] is not None:
|
|
199
|
+
data.num_nodes = self.__num_nodes_list__[idx]
|
|
200
|
+
|
|
201
|
+
return data
|
|
202
|
+
|
|
203
|
+
def index_select(self, idx: List) -> List[Data]:
|
|
204
|
+
if isinstance(idx, slice):
|
|
205
|
+
idx = list(range(self.num_graphs)[idx])
|
|
206
|
+
|
|
207
|
+
elif isinstance(idx, Tensor) and idx.dtype == torch.long:
|
|
208
|
+
idx = idx.flatten().tolist()
|
|
209
|
+
|
|
210
|
+
elif isinstance(idx, Tensor) and idx.dtype == torch.bool:
|
|
211
|
+
idx = idx.flatten().nonzero(as_tuple=False).flatten().tolist()
|
|
212
|
+
|
|
213
|
+
elif isinstance(idx, np.ndarray) and idx.dtype == np.int64:
|
|
214
|
+
idx = idx.flatten().tolist()
|
|
215
|
+
|
|
216
|
+
elif isinstance(idx, np.ndarray) and idx.dtype == np.bool:
|
|
217
|
+
idx = idx.flatten().nonzero()[0].flatten().tolist()
|
|
218
|
+
|
|
219
|
+
elif isinstance(idx, Sequence) and not isinstance(idx, str):
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
else:
|
|
223
|
+
raise IndexError(
|
|
224
|
+
f"Only integers, slices (':'), list, tuples, torch.tensor and "
|
|
225
|
+
f"np.ndarray of dtype long or bool are valid indices (got "
|
|
226
|
+
f"'{type(idx).__name__}')"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return [self.get_example(i) for i in idx]
|
|
230
|
+
|
|
231
|
+
def __getitem__(self, idx):
|
|
232
|
+
if isinstance(idx, str):
|
|
233
|
+
return super(Batch, self).__getitem__(idx)
|
|
234
|
+
elif isinstance(idx, (int, np.integer)):
|
|
235
|
+
return self.get_example(idx)
|
|
236
|
+
else:
|
|
237
|
+
return self.index_select(idx)
|
|
238
|
+
|
|
239
|
+
def to_data_list(self) -> List[Data]:
|
|
240
|
+
r"""Reconstructs the list of :class:`torch_geometric.data.Data` objects
|
|
241
|
+
from the batch object.
|
|
242
|
+
The batch object must have been created via :meth:`from_data_list` in
|
|
243
|
+
order to be able to reconstruct the initial objects."""
|
|
244
|
+
return [self.get_example(i) for i in range(self.num_graphs)]
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def num_graphs(self) -> int:
|
|
248
|
+
"""Returns the number of graphs in the batch."""
|
|
249
|
+
if self.__num_graphs__ is not None:
|
|
250
|
+
return self.__num_graphs__
|
|
251
|
+
elif self.ptr is not None:
|
|
252
|
+
return self.ptr.numel() - 1
|
|
253
|
+
elif self.batch is not None:
|
|
254
|
+
return int(self.batch.max()) + 1
|
|
255
|
+
else:
|
|
256
|
+
raise ValueError
|