GraphAtoms 0.0.2__tar.gz → 0.0.3__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.
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/abc.py +109 -2
- graphatoms-0.0.3/GraphAtoms/common/string.py +138 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms.egg-info/PKG-INFO +20 -11
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms.egg-info/SOURCES.txt +1 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms.egg-info/requires.txt +22 -9
- {graphatoms-0.0.2 → graphatoms-0.0.3}/PKG-INFO +20 -11
- {graphatoms-0.0.2 → graphatoms-0.0.3}/pyproject.toml +29 -11
- {graphatoms-0.0.2 → graphatoms-0.0.3}/tests/test_abc.py +35 -2
- graphatoms-0.0.3/tests/test_rotation.py +44 -0
- graphatoms-0.0.2/GraphAtoms/common/string.py +0 -33
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/__init__.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/__init__.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/error.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/geometry.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/ndarray.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/ndarray.pyi +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/common/rotation.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/__init__.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_atomic.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_bonded.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_graph.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mConnComp.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mDataPyG.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mFreeE.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mIGraph.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mNetworKit.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mNetworkX.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mRDKit.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_mRustworkX.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_sysCluster.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_sysGas.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms/containner/_system.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms.egg-info/dependency_links.txt +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/GraphAtoms.egg-info/top_level.txt +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/LICENSE +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/README.md +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/setup.cfg +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/tests/test_container.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/tests/test_mRDKit.py +0 -0
- {graphatoms-0.0.2 → graphatoms-0.0.3}/tests/test_utils.py +0 -0
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
# ruff: noqa: D100 D102
|
|
2
|
+
import sys
|
|
2
3
|
from collections.abc import Sequence
|
|
3
|
-
from
|
|
4
|
+
from pickle import dumps, loads
|
|
5
|
+
from typing import Annotated, override
|
|
4
6
|
|
|
5
7
|
import numpy as np
|
|
6
8
|
import pydantic
|
|
9
|
+
import tomli_w as toml_w
|
|
10
|
+
import yaml
|
|
7
11
|
from joblib import dump, load
|
|
8
12
|
from numpy.typing import ArrayLike
|
|
9
13
|
from typing_extensions import Self
|
|
10
14
|
|
|
15
|
+
from GraphAtoms.common.string import (
|
|
16
|
+
SUPPORTED_COMPRESS_FORMATS,
|
|
17
|
+
compress,
|
|
18
|
+
decompress,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if sys.version_info < (3, 11):
|
|
22
|
+
import tomli as tomllib
|
|
23
|
+
else:
|
|
24
|
+
import tomllib
|
|
11
25
|
__all__ = [
|
|
12
26
|
"BaseModel",
|
|
13
27
|
"NpzPklBaseModel",
|
|
@@ -15,6 +29,8 @@ __all__ = [
|
|
|
15
29
|
|
|
16
30
|
|
|
17
31
|
class BaseMixin:
|
|
32
|
+
"""The base mixin class which provides some useful classmethods."""
|
|
33
|
+
|
|
18
34
|
@staticmethod
|
|
19
35
|
def get_mask_or_index(k: ArrayLike, n: int) -> np.ndarray:
|
|
20
36
|
if np.isscalar(k):
|
|
@@ -89,6 +105,35 @@ class BaseModel(pydantic.BaseModel):
|
|
|
89
105
|
f = getattr(self, f"write_{self.__get_format(fname)}")
|
|
90
106
|
return f(fname, **kwargs)
|
|
91
107
|
|
|
108
|
+
@pydantic.validate_call
|
|
109
|
+
def as_bytes(
|
|
110
|
+
self,
|
|
111
|
+
compressformat: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
112
|
+
compresslevel: Annotated[int, pydantic.Field(ge=0, le=9)] = 0,
|
|
113
|
+
) -> bytes:
|
|
114
|
+
"""Return the json bytes of this object."""
|
|
115
|
+
return compress(
|
|
116
|
+
dumps(self.model_dump(exclude_none=True)),
|
|
117
|
+
format=compressformat, # type: ignore
|
|
118
|
+
compresslevel=compresslevel,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
@pydantic.validate_call
|
|
123
|
+
def from_bytes(
|
|
124
|
+
cls,
|
|
125
|
+
data: bytes,
|
|
126
|
+
compressformat: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
127
|
+
) -> Self:
|
|
128
|
+
return cls.model_validate(
|
|
129
|
+
loads(
|
|
130
|
+
decompress(
|
|
131
|
+
data,
|
|
132
|
+
format=compressformat, # type: ignore
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
92
137
|
@pydantic.validate_call
|
|
93
138
|
def write_json(
|
|
94
139
|
self,
|
|
@@ -97,7 +142,11 @@ class BaseModel(pydantic.BaseModel):
|
|
|
97
142
|
**kwargs,
|
|
98
143
|
) -> pydantic.FilePath:
|
|
99
144
|
filename.write_text(
|
|
100
|
-
self.model_dump_json(
|
|
145
|
+
self.model_dump_json(
|
|
146
|
+
indent=indent,
|
|
147
|
+
exclude_none=True,
|
|
148
|
+
**kwargs,
|
|
149
|
+
),
|
|
101
150
|
encoding="utf-8",
|
|
102
151
|
)
|
|
103
152
|
return filename
|
|
@@ -113,6 +162,64 @@ class BaseModel(pydantic.BaseModel):
|
|
|
113
162
|
return f(fname, **kwargs)
|
|
114
163
|
|
|
115
164
|
|
|
165
|
+
class ExtendedBaseModel(BaseModel):
|
|
166
|
+
"""A extended base class for creating Pydantic models.
|
|
167
|
+
|
|
168
|
+
This class support many file formats:
|
|
169
|
+
json by `pydantic`
|
|
170
|
+
yaml/yml by `pyyaml`
|
|
171
|
+
toml by `tomli_w`, `tomli`, & `toml`
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
@pydantic.validate_call
|
|
175
|
+
def write_toml(
|
|
176
|
+
self,
|
|
177
|
+
filename: pydantic.FilePath | pydantic.NewPath,
|
|
178
|
+
indent: int = 4,
|
|
179
|
+
**kwargs,
|
|
180
|
+
) -> pydantic.FilePath:
|
|
181
|
+
kwargs["exclude_none"] = True
|
|
182
|
+
with filename.open("wb") as f:
|
|
183
|
+
toml_w.dump(
|
|
184
|
+
self.model_dump(mode="python", **kwargs),
|
|
185
|
+
f,
|
|
186
|
+
indent=indent,
|
|
187
|
+
multiline_strings=True,
|
|
188
|
+
)
|
|
189
|
+
return filename
|
|
190
|
+
|
|
191
|
+
@pydantic.validate_call
|
|
192
|
+
def write_yaml(
|
|
193
|
+
self,
|
|
194
|
+
filename: pydantic.FilePath | pydantic.NewPath,
|
|
195
|
+
indent: int | None = 2,
|
|
196
|
+
**kwargs,
|
|
197
|
+
) -> pydantic.FilePath:
|
|
198
|
+
with filename.open("w", encoding="utf-8") as f:
|
|
199
|
+
yaml.safe_dump(
|
|
200
|
+
self.model_dump(mode="python", **kwargs),
|
|
201
|
+
f,
|
|
202
|
+
encoding="utf-8",
|
|
203
|
+
default_flow_style=False,
|
|
204
|
+
indent=indent,
|
|
205
|
+
)
|
|
206
|
+
return filename
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
@pydantic.validate_call
|
|
210
|
+
def read_yaml(cls, filename: pydantic.FilePath, **kwargs) -> Self:
|
|
211
|
+
with filename.open(encoding="utf-8") as f:
|
|
212
|
+
data = yaml.safe_load(f)
|
|
213
|
+
return cls.model_validate(data, **kwargs)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
@pydantic.validate_call
|
|
217
|
+
def read_toml(cls, filename: pydantic.FilePath, **kwargs) -> Self:
|
|
218
|
+
with filename.open("rb") as f:
|
|
219
|
+
data = tomllib.load(f)
|
|
220
|
+
return cls.model_validate(data, **kwargs)
|
|
221
|
+
|
|
222
|
+
|
|
116
223
|
class NpzPklBaseModel(BaseModel):
|
|
117
224
|
"""A extended base class for creating Pydantic models.
|
|
118
225
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""The utils for strings."""
|
|
2
|
+
|
|
3
|
+
import bz2 # Added in Python 3.3
|
|
4
|
+
import gzip # Added in Python 3.2
|
|
5
|
+
import zlib # Added in Python 3.0
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from functools import partial
|
|
8
|
+
from hashlib import blake2b
|
|
9
|
+
from random import sample
|
|
10
|
+
from string import ascii_lowercase
|
|
11
|
+
from typing import Annotated, Literal
|
|
12
|
+
|
|
13
|
+
import pydantic
|
|
14
|
+
import snappy
|
|
15
|
+
from ase.db.core import convert_str_to_int_float_bool_or_str
|
|
16
|
+
|
|
17
|
+
SUPPORTED_COMPRESS_FORMATS = Literal["z", "gz", "bz2", "snappy"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CompressorAndDecompressor:
|
|
21
|
+
"""The Compressor and Decompressor."""
|
|
22
|
+
|
|
23
|
+
@pydantic.validate_call
|
|
24
|
+
def __init__( # noqa: D107
|
|
25
|
+
self,
|
|
26
|
+
format: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
27
|
+
compresslevel: Annotated[int, pydantic.Field(ge=0, le=9)] = 0,
|
|
28
|
+
) -> None:
|
|
29
|
+
if format == "z":
|
|
30
|
+
self.__func_compress: Callable[..., bytes] = partial(
|
|
31
|
+
zlib.compress,
|
|
32
|
+
level=compresslevel if compresslevel != 0 else -1,
|
|
33
|
+
)
|
|
34
|
+
self.__func_decompress: Callable[..., bytes] = zlib.decompress
|
|
35
|
+
elif format == "gz":
|
|
36
|
+
self.__func_compress: Callable[..., bytes] = partial(
|
|
37
|
+
gzip.compress,
|
|
38
|
+
compresslevel=compresslevel if compresslevel != 0 else 9,
|
|
39
|
+
)
|
|
40
|
+
self.__func_decompress: Callable[..., bytes] = gzip.decompress
|
|
41
|
+
elif format == "bz2":
|
|
42
|
+
self.__func_compress: Callable[..., bytes] = partial(
|
|
43
|
+
bz2.compress,
|
|
44
|
+
compresslevel=compresslevel if compresslevel != 0 else 9,
|
|
45
|
+
)
|
|
46
|
+
self.__func_decompress: Callable[..., bytes] = bz2.decompress
|
|
47
|
+
elif format == "snappy":
|
|
48
|
+
self.__func_compress: Callable[..., bytes] = snappy.compress
|
|
49
|
+
self.__func_decompress: Callable[..., bytes] = partial(
|
|
50
|
+
snappy.uncompress, decoding=None
|
|
51
|
+
) # type: ignore
|
|
52
|
+
else:
|
|
53
|
+
import lzma
|
|
54
|
+
|
|
55
|
+
self.__func_compress: Callable[..., bytes] = partial(
|
|
56
|
+
lzma.compress,
|
|
57
|
+
format=lzma.FORMAT_XZ if format == "xz" else lzma.FORMAT_ALONE,
|
|
58
|
+
)
|
|
59
|
+
self.__func_decompress: Callable[..., bytes] = lzma.decompress
|
|
60
|
+
|
|
61
|
+
@pydantic.validate_call
|
|
62
|
+
def compress(self, value: bytes) -> bytes:
|
|
63
|
+
"""Return the compressed bytes of the given bytes."""
|
|
64
|
+
return self.__func_compress(value)
|
|
65
|
+
|
|
66
|
+
@pydantic.validate_call
|
|
67
|
+
def decompress(self, value: bytes) -> bytes:
|
|
68
|
+
"""Return the decompressed bytes of the given bytes."""
|
|
69
|
+
return self.__func_decompress(value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def compress(
|
|
73
|
+
value: bytes,
|
|
74
|
+
format: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
75
|
+
compresslevel: int = 0,
|
|
76
|
+
) -> bytes:
|
|
77
|
+
"""Return the compressed bytes of the given bytes."""
|
|
78
|
+
return CompressorAndDecompressor(
|
|
79
|
+
format=format,
|
|
80
|
+
compresslevel=compresslevel,
|
|
81
|
+
).compress(value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def compress_string(
|
|
85
|
+
str_value: str,
|
|
86
|
+
format: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
87
|
+
encoding: str = "utf-8",
|
|
88
|
+
compresslevel: int = 0,
|
|
89
|
+
) -> bytes:
|
|
90
|
+
"""Return the compressed bytes of the given string."""
|
|
91
|
+
return compress(
|
|
92
|
+
str_value.encode(encoding),
|
|
93
|
+
format=format,
|
|
94
|
+
compresslevel=compresslevel,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def decompress(
|
|
99
|
+
value: bytes,
|
|
100
|
+
format: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
101
|
+
) -> bytes:
|
|
102
|
+
"""Return the decompressed bytes of the given bytes."""
|
|
103
|
+
return CompressorAndDecompressor(format=format).decompress(value)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def decompress_string(
|
|
107
|
+
value: bytes,
|
|
108
|
+
format: SUPPORTED_COMPRESS_FORMATS = "snappy",
|
|
109
|
+
encoding: str = "utf-8",
|
|
110
|
+
) -> str:
|
|
111
|
+
"""Return the decompressed string of the given bytes."""
|
|
112
|
+
return decompress(value, format=format).decode(encoding)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def random_string(length: int = 6) -> str:
|
|
116
|
+
"""Return the random string that has the given length."""
|
|
117
|
+
return "".join(sample(ascii_lowercase, int(length)))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _hash(str_value: str, digest_size: int = 6) -> str:
|
|
121
|
+
obj = blake2b(
|
|
122
|
+
str_value.encode("ascii"),
|
|
123
|
+
digest_size=int(digest_size / 2),
|
|
124
|
+
)
|
|
125
|
+
return obj.hexdigest()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def hash_string(str_value: str, digest_size: int = 6) -> str:
|
|
129
|
+
"""Return the hashing string of the given string.
|
|
130
|
+
|
|
131
|
+
Note: ensure the result cannot be convert to bool, int and float.
|
|
132
|
+
"""
|
|
133
|
+
result: str = _hash(str_value, digest_size=digest_size)
|
|
134
|
+
value = convert_str_to_int_float_bool_or_str(result)
|
|
135
|
+
while not isinstance(value, str):
|
|
136
|
+
result = _hash(f"{value}_{result}", digest_size)
|
|
137
|
+
value = convert_str_to_int_float_bool_or_str(result)
|
|
138
|
+
return result
|
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GraphAtoms
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: The Chemical Core Class for Graph Theory Analysis & Graph Neural Network.
|
|
5
5
|
Author-email: LiuGaoyong <liugaoyong_88@163.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: ase
|
|
10
|
+
Requires-Dist: pymatgen>=2025
|
|
11
|
+
Requires-Dist: rdkit>=2025
|
|
12
|
+
Requires-Dist: dscribe>=2.1
|
|
13
|
+
Requires-Dist: vesin
|
|
14
|
+
Requires-Dist: ase-extension>=0.5; sys_platform != "win32"
|
|
15
|
+
Requires-Dist: numba>=0.61
|
|
16
|
+
Requires-Dist: numpy>=2.0
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: scipy
|
|
19
|
+
Requires-Dist: torch
|
|
20
|
+
Requires-Dist: scikit-learn>=1.5
|
|
21
|
+
Requires-Dist: sparse
|
|
22
|
+
Requires-Dist: torch_geometric
|
|
10
23
|
Requires-Dist: igraph>=0.11
|
|
11
24
|
Requires-Dist: networkit==11.1; sys_platform != "linux"
|
|
12
25
|
Requires-Dist: networkit==11.1.post1; sys_platform == "linux"
|
|
13
26
|
Requires-Dist: networkx>=3.3
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist: numba>=0.61
|
|
27
|
+
Requires-Dist: rustworkx>=0.16
|
|
16
28
|
Requires-Dist: numpydantic
|
|
17
|
-
Requires-Dist: pandas
|
|
18
29
|
Requires-Dist: pydantic>=2.11
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist: torch_geometric
|
|
25
|
-
Requires-Dist: torch
|
|
30
|
+
Requires-Dist: python-snappy>=0.7.3
|
|
31
|
+
Requires-Dist: pyyaml
|
|
32
|
+
Requires-Dist: sqlmodel
|
|
33
|
+
Requires-Dist: tomli-w
|
|
34
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
26
35
|
Requires-Dist: typing-extensions
|
|
27
36
|
Dynamic: license-file
|
|
28
37
|
|
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
ase
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
pymatgen>=2025
|
|
3
|
+
rdkit>=2025
|
|
4
|
+
dscribe>=2.1
|
|
5
|
+
vesin
|
|
5
6
|
numba>=0.61
|
|
6
|
-
|
|
7
|
+
numpy>=2.0
|
|
7
8
|
pandas
|
|
8
|
-
pydantic>=2.11
|
|
9
|
-
rdkit>=2024.9
|
|
10
|
-
rustworkx>=0.16
|
|
11
|
-
scikit-learn
|
|
12
9
|
scipy
|
|
10
|
+
torch
|
|
11
|
+
scikit-learn>=1.5
|
|
13
12
|
sparse
|
|
14
13
|
torch_geometric
|
|
15
|
-
|
|
14
|
+
igraph>=0.11
|
|
15
|
+
networkx>=3.3
|
|
16
|
+
rustworkx>=0.16
|
|
17
|
+
numpydantic
|
|
18
|
+
pydantic>=2.11
|
|
19
|
+
python-snappy>=0.7.3
|
|
20
|
+
pyyaml
|
|
21
|
+
sqlmodel
|
|
22
|
+
tomli-w
|
|
16
23
|
typing-extensions
|
|
17
24
|
|
|
25
|
+
[:python_version < "3.11"]
|
|
26
|
+
tomli
|
|
27
|
+
|
|
18
28
|
[:sys_platform != "linux"]
|
|
19
29
|
networkit==11.1
|
|
20
30
|
|
|
31
|
+
[:sys_platform != "win32"]
|
|
32
|
+
ase-extension>=0.5
|
|
33
|
+
|
|
21
34
|
[:sys_platform == "linux"]
|
|
22
35
|
networkit==11.1.post1
|
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GraphAtoms
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: The Chemical Core Class for Graph Theory Analysis & Graph Neural Network.
|
|
5
5
|
Author-email: LiuGaoyong <liugaoyong_88@163.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: ase
|
|
10
|
+
Requires-Dist: pymatgen>=2025
|
|
11
|
+
Requires-Dist: rdkit>=2025
|
|
12
|
+
Requires-Dist: dscribe>=2.1
|
|
13
|
+
Requires-Dist: vesin
|
|
14
|
+
Requires-Dist: ase-extension>=0.5; sys_platform != "win32"
|
|
15
|
+
Requires-Dist: numba>=0.61
|
|
16
|
+
Requires-Dist: numpy>=2.0
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: scipy
|
|
19
|
+
Requires-Dist: torch
|
|
20
|
+
Requires-Dist: scikit-learn>=1.5
|
|
21
|
+
Requires-Dist: sparse
|
|
22
|
+
Requires-Dist: torch_geometric
|
|
10
23
|
Requires-Dist: igraph>=0.11
|
|
11
24
|
Requires-Dist: networkit==11.1; sys_platform != "linux"
|
|
12
25
|
Requires-Dist: networkit==11.1.post1; sys_platform == "linux"
|
|
13
26
|
Requires-Dist: networkx>=3.3
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist: numba>=0.61
|
|
27
|
+
Requires-Dist: rustworkx>=0.16
|
|
16
28
|
Requires-Dist: numpydantic
|
|
17
|
-
Requires-Dist: pandas
|
|
18
29
|
Requires-Dist: pydantic>=2.11
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist: torch_geometric
|
|
25
|
-
Requires-Dist: torch
|
|
30
|
+
Requires-Dist: python-snappy>=0.7.3
|
|
31
|
+
Requires-Dist: pyyaml
|
|
32
|
+
Requires-Dist: sqlmodel
|
|
33
|
+
Requires-Dist: tomli-w
|
|
34
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
26
35
|
Requires-Dist: typing-extensions
|
|
27
36
|
Dynamic: license-file
|
|
28
37
|
|
|
@@ -4,29 +4,46 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GraphAtoms"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.3"
|
|
8
8
|
description = "The Chemical Core Class for Graph Theory Analysis & Graph Neural Network."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "LiuGaoyong", email = "liugaoyong_88@163.com" }]
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
dependencies = [
|
|
13
|
+
|
|
14
|
+
# Chemoinformatics
|
|
13
15
|
"ase",
|
|
16
|
+
"pymatgen>=2025",
|
|
17
|
+
"rdkit>=2025",
|
|
18
|
+
"dscribe>=2.1", # transforming atomic structures into fixed-size numerical fingerprints
|
|
19
|
+
"vesin", # fast neighbor lists for atomistic systems (C++ & TorchScript)
|
|
20
|
+
"ase-extension>=0.5; sys_platform != 'win32'", # fast neighborlist(Rust)
|
|
21
|
+
|
|
22
|
+
# Scientific Computing
|
|
23
|
+
"numba>=0.61",
|
|
24
|
+
"numpy>=2.0",
|
|
25
|
+
"pandas",
|
|
26
|
+
"scipy",
|
|
27
|
+
"torch",
|
|
28
|
+
"scikit-learn>=1.5",
|
|
29
|
+
"sparse",
|
|
30
|
+
|
|
31
|
+
# Graph Theory
|
|
32
|
+
"torch_geometric", # PyTorch Based Graph Neural Network Library
|
|
14
33
|
"igraph>=0.11", # high performance graph library based on C
|
|
15
34
|
"networkit==11.1; sys_platform != 'linux'", # high performance graph library based on C++
|
|
16
35
|
"networkit==11.1.post1; sys_platform == 'linux'", # high performance graph library based on C++
|
|
17
36
|
"networkx>=3.3", # General-purpose graph library by pure Python
|
|
18
|
-
"
|
|
19
|
-
|
|
37
|
+
"rustworkx>=0.16", # NetworkX-like graph library based on Rust
|
|
38
|
+
|
|
39
|
+
# Data Model
|
|
20
40
|
"numpydantic",
|
|
21
|
-
"pandas",
|
|
22
41
|
"pydantic>=2.11",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"torch_geometric",
|
|
29
|
-
"torch",
|
|
42
|
+
"python-snappy>=0.7.3",
|
|
43
|
+
"pyyaml", # YAML parser & writer
|
|
44
|
+
"sqlmodel", # SQL + pydantic
|
|
45
|
+
"tomli-w", # TOML writer
|
|
46
|
+
"tomli; python_version < '3.11'", # use tomllib in python>=3.11
|
|
30
47
|
"typing-extensions",
|
|
31
48
|
]
|
|
32
49
|
[dependency-groups]
|
|
@@ -36,6 +53,7 @@ dev = ["ruff>=0.11", "pytest>=8.3", "pytest-xdist", "ipython"]
|
|
|
36
53
|
[tool.uv]
|
|
37
54
|
package = true
|
|
38
55
|
[[tool.uv.index]]
|
|
56
|
+
name = 'tuna'
|
|
39
57
|
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" # TUNA
|
|
40
58
|
default = true
|
|
41
59
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# ruff: noqa: D100, D101, D102, D103
|
|
2
|
+
import itertools
|
|
2
3
|
import warnings
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from pprint import pprint
|
|
@@ -7,7 +8,7 @@ from tempfile import TemporaryDirectory
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
import pytest
|
|
9
10
|
|
|
10
|
-
from GraphAtoms.common.abc import BaseModel, NpzPklBaseModel
|
|
11
|
+
from GraphAtoms.common.abc import BaseModel, ExtendedBaseModel, NpzPklBaseModel
|
|
11
12
|
from GraphAtoms.common.ndarray import NDArray
|
|
12
13
|
|
|
13
14
|
|
|
@@ -16,6 +17,11 @@ class MockNpzBaseModel(NpzPklBaseModel):
|
|
|
16
17
|
v: float = 5
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
class MockExtendedBaseModel(ExtendedBaseModel):
|
|
21
|
+
arr: list[str] = ["1", "sd"]
|
|
22
|
+
v: float = 5
|
|
23
|
+
|
|
24
|
+
|
|
19
25
|
class Test_ABC_Pydantic_Model:
|
|
20
26
|
def test_json_schema(self):
|
|
21
27
|
print()
|
|
@@ -38,10 +44,37 @@ class Test_ABC_Pydantic_Model:
|
|
|
38
44
|
np.testing.assert_array_equal(obj.arr, new_obj.arr) # type: ignore
|
|
39
45
|
assert new_obj == obj
|
|
40
46
|
|
|
47
|
+
@pytest.mark.parametrize("format", ["yaml", "toml", "json"])
|
|
48
|
+
def test_convert(self, format: str) -> None:
|
|
49
|
+
self.__run(MockExtendedBaseModel(), MockExtendedBaseModel, format)
|
|
50
|
+
|
|
41
51
|
@pytest.mark.parametrize("format", ["pkl", "npz", "json"])
|
|
42
52
|
def test_convert_numpy(self, format: str) -> None:
|
|
43
53
|
self.__run(MockNpzBaseModel(), MockNpzBaseModel, format)
|
|
44
54
|
|
|
45
55
|
|
|
56
|
+
@pytest.mark.parametrize("cls", [MockExtendedBaseModel, MockNpzBaseModel])
|
|
57
|
+
@pytest.mark.parametrize(
|
|
58
|
+
"fmt,level",
|
|
59
|
+
[
|
|
60
|
+
# ("xz", 0),
|
|
61
|
+
# ("lzma", 0),
|
|
62
|
+
("snappy", 0),
|
|
63
|
+
]
|
|
64
|
+
+ sorted(
|
|
65
|
+
itertools.product(
|
|
66
|
+
["z", "gz", "bz2"],
|
|
67
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
68
|
+
)
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
def test_compress(fmt, level: int, cls: type[BaseModel]) -> None:
|
|
72
|
+
obj = cls()
|
|
73
|
+
b = obj.as_bytes(compressformat=fmt, compresslevel=level)
|
|
74
|
+
obj2 = cls.from_bytes(b, compressformat=fmt)
|
|
75
|
+
print(fmt, level, len(b), sep="\t")
|
|
76
|
+
assert obj == obj2
|
|
77
|
+
|
|
78
|
+
|
|
46
79
|
if __name__ == "__main__":
|
|
47
|
-
pytest.main([__file__, "-
|
|
80
|
+
pytest.main([__file__, "-s", "--maxfail=1"])
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
from ase.build import molecule
|
|
4
|
+
|
|
5
|
+
from GraphAtoms.common.rotation import Rot, kabsch, rotate
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_rotation() -> None: # noqa: D103
|
|
9
|
+
ben = molecule("C6H6")
|
|
10
|
+
points = ben.positions + np.random.random(3)
|
|
11
|
+
center = points.mean(axis=0)
|
|
12
|
+
print(f"Points center: {center}")
|
|
13
|
+
|
|
14
|
+
rotation = Rot.random()
|
|
15
|
+
print(f"Random rotation:\n{rotation.as_quat()}")
|
|
16
|
+
|
|
17
|
+
rotated = rotate(points, rotation, center=[0, 0, 0])
|
|
18
|
+
print(f"Rotated points around zero point:\n{rotated}")
|
|
19
|
+
assert pytest.approx(rotated.mean(axis=0)) != center
|
|
20
|
+
|
|
21
|
+
rotated = rotate(points, rotation, center)
|
|
22
|
+
print(f"Rotated points around center point:\n{rotated}")
|
|
23
|
+
assert pytest.approx(rotated.mean(axis=0)) == center
|
|
24
|
+
|
|
25
|
+
# Default center point as the geometry center:
|
|
26
|
+
rotated = rotate(points, rotation)
|
|
27
|
+
print(f"Rotated points around center point:\n{rotated}")
|
|
28
|
+
assert pytest.approx(rotated.mean(axis=0)) == center
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_kabsch() -> None: # noqa: D103
|
|
32
|
+
B = molecule("C6H6").positions + np.random.random(3)
|
|
33
|
+
R, T = Rot.random(), np.random.random(3)
|
|
34
|
+
|
|
35
|
+
A = rotate(B, R) + T
|
|
36
|
+
assert pytest.approx((A - T).mean(axis=0)) == B.mean(axis=0)
|
|
37
|
+
|
|
38
|
+
R0, T0, rmsd = kabsch(A, B)
|
|
39
|
+
assert isinstance(T0, np.ndarray)
|
|
40
|
+
assert pytest.approx(T0) == T
|
|
41
|
+
assert isinstance(R0, Rot)
|
|
42
|
+
assert pytest.approx(R0.as_matrix()) == R.as_matrix()
|
|
43
|
+
assert rmsd <= 1e-5
|
|
44
|
+
assert pytest.approx(rotate(B, R0) + T0) == A
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""The utils for strings."""
|
|
2
|
-
|
|
3
|
-
from hashlib import blake2b
|
|
4
|
-
from random import sample
|
|
5
|
-
from string import ascii_lowercase
|
|
6
|
-
|
|
7
|
-
from ase.db.core import convert_str_to_int_float_bool_or_str
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def random_string(length: int = 6) -> str:
|
|
11
|
-
"""Return the random string that has the given length."""
|
|
12
|
-
return "".join(sample(ascii_lowercase, int(length)))
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _hash(str_value: str, digest_size: int = 6) -> str:
|
|
16
|
-
obj = blake2b(
|
|
17
|
-
str_value.encode("ascii"),
|
|
18
|
-
digest_size=int(digest_size / 2),
|
|
19
|
-
)
|
|
20
|
-
return obj.hexdigest()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def hash_string(str_value: str, digest_size: int = 6) -> str:
|
|
24
|
-
"""Return the hashing string of the given string.
|
|
25
|
-
|
|
26
|
-
Note: ensure the result cannot be convert to bool, int and float.
|
|
27
|
-
"""
|
|
28
|
-
result: str = _hash(str_value, digest_size=digest_size)
|
|
29
|
-
value = convert_str_to_int_float_bool_or_str(result)
|
|
30
|
-
while not isinstance(value, str):
|
|
31
|
-
result = _hash(f"{value}_{result}", digest_size)
|
|
32
|
-
value = convert_str_to_int_float_bool_or_str(result)
|
|
33
|
-
return result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|