metatomic-torch 0.1.0rc1__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.
- metatomic_torch-0.1.0rc1/AUTHORS +2 -0
- metatomic_torch-0.1.0rc1/CMakeLists.txt +65 -0
- metatomic_torch-0.1.0rc1/LICENSE +28 -0
- metatomic_torch-0.1.0rc1/MANIFEST.in +10 -0
- metatomic_torch-0.1.0rc1/PKG-INFO +40 -0
- metatomic_torch-0.1.0rc1/README.rst +4 -0
- metatomic_torch-0.1.0rc1/build-backend/backend.py +33 -0
- metatomic_torch-0.1.0rc1/git_version_info +2 -0
- metatomic_torch-0.1.0rc1/metatomic/__init__.py +0 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/__init__.py +49 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/_c_lib.py +134 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/_extensions.py +202 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/ase_calculator.py +774 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/documentation.py +523 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/io.py +164 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/model.py +799 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/outputs.py +337 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/systems_to_torch.py +125 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/utils.py +52 -0
- metatomic_torch-0.1.0rc1/metatomic/torch/version.py +4 -0
- metatomic_torch-0.1.0rc1/metatomic-torch-cxx-0.1.0-rc1.tar.gz +0 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/PKG-INFO +40 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/SOURCES.txt +28 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/dependency_links.txt +1 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/not-zip-safe +1 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/requires.txt +4 -0
- metatomic_torch-0.1.0rc1/metatomic_torch.egg-info/top_level.txt +2 -0
- metatomic_torch-0.1.0rc1/pyproject.toml +57 -0
- metatomic_torch-0.1.0rc1/setup.cfg +4 -0
- metatomic_torch-0.1.0rc1/setup.py +343 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# This file allow the python module in metatomic-torch to either use an
|
|
2
|
+
# externally-provided version of the shared metatomic_torch library; or to build
|
|
3
|
+
# the code from source and bundle the shared library inside the wheel.
|
|
4
|
+
#
|
|
5
|
+
# The first case is used when distributing the code in conda (since we have a
|
|
6
|
+
# separate libmetatomic_torch package), the second one is used everywhere else
|
|
7
|
+
# (for local development builds and for the PyPI distribution).
|
|
8
|
+
|
|
9
|
+
cmake_minimum_required(VERSION 3.16)
|
|
10
|
+
project(metatomic-torch-python NONE)
|
|
11
|
+
|
|
12
|
+
option(METATOMIC_TORCH_PYTHON_USE_EXTERNAL_LIB "Force the usage of an external version of metatomic-torch" OFF)
|
|
13
|
+
set(METATOMIC_TORCH_SOURCE_DIR "" CACHE PATH "Path to the sources of metatomic-torch")
|
|
14
|
+
|
|
15
|
+
file(REMOVE ${CMAKE_INSTALL_PREFIX}/_external.py)
|
|
16
|
+
|
|
17
|
+
set(REQUIRED_METATOMIC_TORCH_VERSION "0.0.0")
|
|
18
|
+
if(${METATOMIC_TORCH_PYTHON_USE_EXTERNAL_LIB})
|
|
19
|
+
# when building a source checkout, update version to include git information
|
|
20
|
+
# this will not apply when building a sdist
|
|
21
|
+
if (EXISTS ${CMAKE_SOURCE_DIR}/../../metatomic-torch/cmake/dev-versions.cmake)
|
|
22
|
+
include(${CMAKE_SOURCE_DIR}/../../metatomic-torch/cmake/dev-versions.cmake)
|
|
23
|
+
create_development_version(
|
|
24
|
+
"${REQUIRED_METATOMIC_TORCH_VERSION}"
|
|
25
|
+
REQUIRED_METATOMIC_TORCH_VERSION
|
|
26
|
+
"metatomic-torch-v"
|
|
27
|
+
)
|
|
28
|
+
# strip any -dev/-rc suffix on the version since find_package does not support it
|
|
29
|
+
string(
|
|
30
|
+
REGEX REPLACE "([0-9]*)\\.([0-9]*)\\.([0-9]*).*" "\\1.\\2.\\3"
|
|
31
|
+
REQUIRED_METATOMIC_TORCH_VERSION
|
|
32
|
+
${REQUIRED_METATOMIC_TORCH_VERSION}
|
|
33
|
+
)
|
|
34
|
+
endif()
|
|
35
|
+
|
|
36
|
+
find_package(metatomic_torch ${REQUIRED_METATOMIC_TORCH_VERSION} REQUIRED)
|
|
37
|
+
|
|
38
|
+
get_target_property(METATOMIC_TORCH_LOCATION metatomic_torch LOCATION)
|
|
39
|
+
message(STATUS "Using external metatomic-torch v${metatomic_torch_VERSION} at ${METATOMIC_TORCH_LOCATION}")
|
|
40
|
+
|
|
41
|
+
# Get the prefix to use as cmake_prefix_path when trying to load this
|
|
42
|
+
# version of the library again
|
|
43
|
+
get_filename_component(METATOMIC_TORCH_PREFIX "${METATOMIC_TORCH_LOCATION}" DIRECTORY)
|
|
44
|
+
get_filename_component(METATOMIC_TORCH_PREFIX "${METATOMIC_TORCH_PREFIX}" DIRECTORY)
|
|
45
|
+
|
|
46
|
+
file(WRITE ${CMAKE_INSTALL_PREFIX}/_external.py
|
|
47
|
+
"EXTERNAL_METATOMIC_TORCH_PATH = \"${METATOMIC_TORCH_LOCATION}\"\n\n"
|
|
48
|
+
)
|
|
49
|
+
file(APPEND ${CMAKE_INSTALL_PREFIX}/_external.py
|
|
50
|
+
"EXTERNAL_METATOMIC_TORCH_PREFIX = \"${METATOMIC_TORCH_PREFIX}\"\n"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
install(CODE "message(STATUS \"nothing to install\")")
|
|
54
|
+
else()
|
|
55
|
+
if ("${METATOMIC_TORCH_SOURCE_DIR}" STREQUAL "")
|
|
56
|
+
message(FATAL_ERROR
|
|
57
|
+
"Missing METATOMIC_TORCH_SOURCE_DIR, please specify where to \
|
|
58
|
+
find the source code for metatomic-torch"
|
|
59
|
+
)
|
|
60
|
+
endif()
|
|
61
|
+
|
|
62
|
+
message(STATUS "Using internal metatomic-torch from ${METATOMIC_TORCH_SOURCE_DIR}")
|
|
63
|
+
|
|
64
|
+
add_subdirectory("${METATOMIC_TORCH_SOURCE_DIR}" metatomic-torch)
|
|
65
|
+
endif()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, metatomic developers
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metatomic-torch
|
|
3
|
+
Version: 0.1.0rc1
|
|
4
|
+
Summary: TODO
|
|
5
|
+
Author: Guillaume Fraux, Filippo Bigi
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: homepage, https://docs.metatensor.org/metatomic/
|
|
8
|
+
Project-URL: documentation, https://docs.metatensor.org/metatomic/
|
|
9
|
+
Project-URL: repository, https://github.com/metatensor/metatomic
|
|
10
|
+
Project-URL: changelog, https://docs.metatensor.org/metatomic/latest/torch/CHANGELOG.html
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/x-rst
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
License-File: AUTHORS
|
|
29
|
+
Requires-Dist: torch>=1.12
|
|
30
|
+
Requires-Dist: vesin
|
|
31
|
+
Requires-Dist: metatensor-torch<0.8.0,>=0.7.0
|
|
32
|
+
Requires-Dist: metatensor-operations<0.4.0,>=0.3.0
|
|
33
|
+
Dynamic: author
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
Dynamic: requires-dist
|
|
36
|
+
|
|
37
|
+
metatensor-torch
|
|
38
|
+
================
|
|
39
|
+
|
|
40
|
+
This package contains the TorchScript bindings to the core API of metatensor.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This is a custom Python build backend wrapping setuptool's to only depend on
|
|
2
|
+
# torch/metatensor-torch when building the wheel and not the sdist
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from setuptools import build_meta
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
ROOT = os.path.realpath(os.path.dirname(__file__))
|
|
9
|
+
|
|
10
|
+
FORCED_TORCH_VERSION = os.environ.get("METATOMIC_TORCH_BUILD_WITH_TORCH_VERSION")
|
|
11
|
+
if FORCED_TORCH_VERSION is not None:
|
|
12
|
+
TORCH_DEP = f"torch =={FORCED_TORCH_VERSION}"
|
|
13
|
+
else:
|
|
14
|
+
TORCH_DEP = "torch >=1.12"
|
|
15
|
+
|
|
16
|
+
# ==================================================================================== #
|
|
17
|
+
# Build backend functions definition #
|
|
18
|
+
# ==================================================================================== #
|
|
19
|
+
|
|
20
|
+
# Use the default version of these
|
|
21
|
+
prepare_metadata_for_build_wheel = build_meta.prepare_metadata_for_build_wheel
|
|
22
|
+
get_requires_for_build_sdist = build_meta.get_requires_for_build_sdist
|
|
23
|
+
build_wheel = build_meta.build_wheel
|
|
24
|
+
build_sdist = build_meta.build_sdist
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Special dependencies to build the wheels
|
|
28
|
+
def get_requires_for_build_wheel(config_settings=None):
|
|
29
|
+
defaults = build_meta.get_requires_for_build_wheel(config_settings)
|
|
30
|
+
return defaults + [
|
|
31
|
+
TORCH_DEP,
|
|
32
|
+
"metatensor-torch >=0.7.0,<0.8.0",
|
|
33
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
from ._c_lib import _load_library
|
|
6
|
+
from .version import __version__ # noqa: F401
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if os.environ.get("METATOMIC_IMPORT_FOR_SPHINX", "0") != "0":
|
|
10
|
+
from .documentation import (
|
|
11
|
+
ModelCapabilities,
|
|
12
|
+
ModelEvaluationOptions,
|
|
13
|
+
ModelMetadata,
|
|
14
|
+
ModelOutput,
|
|
15
|
+
NeighborListOptions,
|
|
16
|
+
System,
|
|
17
|
+
check_atomistic_model,
|
|
18
|
+
load_model_extensions,
|
|
19
|
+
read_model_metadata,
|
|
20
|
+
register_autograd_neighbors,
|
|
21
|
+
unit_conversion_factor,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
_load_library()
|
|
26
|
+
|
|
27
|
+
System = torch.classes.metatomic.System
|
|
28
|
+
NeighborListOptions = torch.classes.metatomic.NeighborListOptions
|
|
29
|
+
|
|
30
|
+
ModelOutput = torch.classes.metatomic.ModelOutput
|
|
31
|
+
ModelEvaluationOptions = torch.classes.metatomic.ModelEvaluationOptions
|
|
32
|
+
ModelCapabilities = torch.classes.metatomic.ModelCapabilities
|
|
33
|
+
ModelMetadata = torch.classes.metatomic.ModelMetadata
|
|
34
|
+
|
|
35
|
+
read_model_metadata = torch.ops.metatomic.read_model_metadata
|
|
36
|
+
load_model_extensions = torch.ops.metatomic.load_model_extensions
|
|
37
|
+
check_atomistic_model = torch.ops.metatomic.check_atomistic_model
|
|
38
|
+
|
|
39
|
+
register_autograd_neighbors = torch.ops.metatomic.register_autograd_neighbors
|
|
40
|
+
unit_conversion_factor = torch.ops.metatomic.unit_conversion_factor
|
|
41
|
+
|
|
42
|
+
from .io import load_system, save # noqa: F401
|
|
43
|
+
from .model import ( # noqa: F401
|
|
44
|
+
MetatensorAtomisticModel,
|
|
45
|
+
ModelInterface,
|
|
46
|
+
is_atomistic_model,
|
|
47
|
+
load_atomistic_model, # noqa: F401
|
|
48
|
+
)
|
|
49
|
+
from .systems_to_torch import systems_to_torch # noqa: F401
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import importlib
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import metatensor.torch
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
from .utils import parse_version, version_compatible
|
|
10
|
+
from .version import __version__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_HERE = os.path.realpath(os.path.dirname(__file__))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _lib_path():
|
|
17
|
+
torch_version = parse_version(torch.__version__)
|
|
18
|
+
install_prefix = os.path.join(
|
|
19
|
+
_HERE, f"torch-{torch_version.major}.{torch_version.minor}"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if os.path.exists(install_prefix):
|
|
23
|
+
# check if we are using an externally-provided version of the shared library
|
|
24
|
+
external_path = os.path.join(install_prefix, "_external.py")
|
|
25
|
+
if os.path.exists(external_path):
|
|
26
|
+
spec = importlib.util.spec_from_file_location("_external", external_path)
|
|
27
|
+
module = importlib.util.module_from_spec(spec)
|
|
28
|
+
spec.loader.exec_module(module)
|
|
29
|
+
return module.EXTERNAL_METATOMIC_TORCH_PATH
|
|
30
|
+
|
|
31
|
+
if sys.platform.startswith("darwin"):
|
|
32
|
+
path = os.path.join(install_prefix, "lib", "libmetatomic_torch.dylib")
|
|
33
|
+
windows = False
|
|
34
|
+
elif sys.platform.startswith("linux"):
|
|
35
|
+
path = os.path.join(install_prefix, "lib", "libmetatomic_torch.so")
|
|
36
|
+
windows = False
|
|
37
|
+
elif sys.platform.startswith("win"):
|
|
38
|
+
path = os.path.join(install_prefix, "bin", "metatomic_torch.dll")
|
|
39
|
+
windows = True
|
|
40
|
+
else:
|
|
41
|
+
raise ImportError("Unknown platform. Please edit this file")
|
|
42
|
+
|
|
43
|
+
if os.path.isfile(path):
|
|
44
|
+
if windows:
|
|
45
|
+
_check_dll(path)
|
|
46
|
+
return path
|
|
47
|
+
else:
|
|
48
|
+
raise ImportError(
|
|
49
|
+
"Could not find metatomic_torch shared library at " + path
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# gather which torch version(s) the current install was built
|
|
53
|
+
# with to create the error message
|
|
54
|
+
existing_versions = []
|
|
55
|
+
for prefix in glob.glob(os.path.join(_HERE, "torch-*")):
|
|
56
|
+
existing_versions.append(os.path.basename(prefix)[6:])
|
|
57
|
+
|
|
58
|
+
if len(existing_versions) == 1:
|
|
59
|
+
raise ImportError(
|
|
60
|
+
f"Trying to load metatomic-torch with torch v{torch.__version__}, "
|
|
61
|
+
f"but it was compiled against torch v{existing_versions[0]}, which "
|
|
62
|
+
"is not ABI compatible"
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
all_versions = ", ".join(map(lambda version: f"v{version}", existing_versions))
|
|
66
|
+
raise ImportError(
|
|
67
|
+
f"Trying to load metatomic-torch with torch v{torch.__version__}, "
|
|
68
|
+
f"we found builds for torch {all_versions}; which are not ABI compatible.\n"
|
|
69
|
+
"You can try to re-install from source with "
|
|
70
|
+
"`pip install metatomic-torch --no-binary=metatomic-torch`"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _check_dll(path):
|
|
75
|
+
"""
|
|
76
|
+
Check if the DLL pointer size matches Python (32-bit or 64-bit)
|
|
77
|
+
"""
|
|
78
|
+
import platform
|
|
79
|
+
import struct
|
|
80
|
+
|
|
81
|
+
IMAGE_FILE_MACHINE_I386 = 332
|
|
82
|
+
IMAGE_FILE_MACHINE_AMD64 = 34404
|
|
83
|
+
|
|
84
|
+
machine = None
|
|
85
|
+
with open(path, "rb") as fd:
|
|
86
|
+
header = fd.read(2).decode(encoding="utf-8", errors="strict")
|
|
87
|
+
if header != "MZ":
|
|
88
|
+
raise ImportError(path + " is not a DLL")
|
|
89
|
+
else:
|
|
90
|
+
fd.seek(60)
|
|
91
|
+
header = fd.read(4)
|
|
92
|
+
header_offset = struct.unpack("<L", header)[0]
|
|
93
|
+
fd.seek(header_offset + 4)
|
|
94
|
+
header = fd.read(2)
|
|
95
|
+
machine = struct.unpack("<H", header)[0]
|
|
96
|
+
|
|
97
|
+
arch = platform.architecture()[0]
|
|
98
|
+
if arch == "32bit":
|
|
99
|
+
if machine != IMAGE_FILE_MACHINE_I386:
|
|
100
|
+
raise ImportError("Python is 32-bit, but this DLL is not")
|
|
101
|
+
elif arch == "64bit":
|
|
102
|
+
if machine != IMAGE_FILE_MACHINE_AMD64:
|
|
103
|
+
raise ImportError("Python is 64-bit, but this DLL is not")
|
|
104
|
+
else:
|
|
105
|
+
raise ImportError("Could not determine pointer size of Python")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _load_library():
|
|
109
|
+
# Load metatensor_torch shared library in the process first, to ensure
|
|
110
|
+
# the metatomic_torch shared library can find it
|
|
111
|
+
metatensor.torch._c_lib._load_library()
|
|
112
|
+
|
|
113
|
+
# load the C++ operators and custom classes
|
|
114
|
+
try:
|
|
115
|
+
torch.ops.load_library(_lib_path())
|
|
116
|
+
except Exception as e:
|
|
117
|
+
if "undefined symbol" in str(e):
|
|
118
|
+
file_name = os.path.basename(_lib_path())
|
|
119
|
+
raise ImportError(
|
|
120
|
+
f"{file_name} is not compatible with the current PyTorch "
|
|
121
|
+
"installation.\nThis can happen if PyTorch comes from one source "
|
|
122
|
+
"(pip, conda, custom), but metatomic-torch comes from a different "
|
|
123
|
+
"one.\nIn this case, you can try to compile metatomic-torch yourself "
|
|
124
|
+
"with `pip install --no-binary=metatomic-torch metatomic-torch`"
|
|
125
|
+
) from e
|
|
126
|
+
else:
|
|
127
|
+
raise e
|
|
128
|
+
|
|
129
|
+
lib_version = torch.ops.metatomic.version()
|
|
130
|
+
if not version_compatible(lib_version, __version__):
|
|
131
|
+
raise ImportError(
|
|
132
|
+
f"Trying to load the Python package metatomic-torch v{__version__} "
|
|
133
|
+
f"with the incompatible metatomic-torch C++ library v{lib_version}"
|
|
134
|
+
)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import hashlib
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import site
|
|
6
|
+
import sys
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
import metatensor.torch
|
|
10
|
+
import torch
|
|
11
|
+
|
|
12
|
+
from . import _c_lib
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
METATOMIC_TORCH_LIB_PATH = _c_lib._lib_path()
|
|
16
|
+
METATENSOR_TORCH_LIB_PATH = metatensor.torch._c_lib._lib_path()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _rascaline_lib_path():
|
|
20
|
+
# This is kept for backward compatibility, but rascaline is now named featomic.
|
|
21
|
+
# This code should be removed by the middle of 2025.
|
|
22
|
+
import rascaline
|
|
23
|
+
|
|
24
|
+
return [rascaline._c_lib._lib_path()]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _featomic_deps_path():
|
|
28
|
+
import featomic
|
|
29
|
+
|
|
30
|
+
deps_path = [featomic._c_lib._lib_path()]
|
|
31
|
+
|
|
32
|
+
libgomp_path = _find_openmp_dep("featomic_torch.libs")
|
|
33
|
+
if libgomp_path is not None:
|
|
34
|
+
deps_path.insert(0, libgomp_path)
|
|
35
|
+
|
|
36
|
+
return deps_path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _sphericart_deps_path():
|
|
40
|
+
import sphericart.torch
|
|
41
|
+
|
|
42
|
+
deps_path = []
|
|
43
|
+
|
|
44
|
+
libgomp_path = _find_openmp_dep("sphericart_torch.libs")
|
|
45
|
+
if libgomp_path is not None:
|
|
46
|
+
deps_path.append(libgomp_path)
|
|
47
|
+
|
|
48
|
+
if sys.platform.startswith("linux"):
|
|
49
|
+
# sphericart uses a separate library to get the CUDA stream corresponding to a
|
|
50
|
+
# tensor, see https://github.com/lab-cosmo/sphericart/pull/164
|
|
51
|
+
sphericart_torch_path = sphericart.torch._lib_path()
|
|
52
|
+
lib_dir = os.path.dirname(sphericart_torch_path)
|
|
53
|
+
|
|
54
|
+
cuda_stream_lib = os.path.join(lib_dir, "libsphericart_torch_cuda_stream.so")
|
|
55
|
+
if os.path.exists(cuda_stream_lib):
|
|
56
|
+
deps_path.append(cuda_stream_lib)
|
|
57
|
+
|
|
58
|
+
return deps_path
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _find_openmp_dep(search_dir):
|
|
62
|
+
"""
|
|
63
|
+
When building code that uses OpenMP on linux, we typically dynamically link to
|
|
64
|
+
libgomp. `cibuildwheel` then copies ``libgomp.so`` to
|
|
65
|
+
``<wheel_name>.libs/libgomp-<hash>.so``, so we need to find and add this shared
|
|
66
|
+
library to the extensions dependencies.
|
|
67
|
+
"""
|
|
68
|
+
if sys.platform.startswith("linux"):
|
|
69
|
+
libs_list = []
|
|
70
|
+
|
|
71
|
+
for prefix in site.getsitepackages():
|
|
72
|
+
libs_dir = os.path.join(prefix, search_dir)
|
|
73
|
+
if os.path.exists(libs_dir):
|
|
74
|
+
libs_list = glob.glob(os.path.join(libs_dir, "libgomp-*.so*"))
|
|
75
|
+
if len(libs_list) != 0:
|
|
76
|
+
# found it!
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
if len(libs_list) == 0:
|
|
80
|
+
warnings.warn(
|
|
81
|
+
f"No libgomp shared library found in '{search_dir}'. "
|
|
82
|
+
"This may cause issues when loading and running the model.",
|
|
83
|
+
stacklevel=2,
|
|
84
|
+
)
|
|
85
|
+
elif len(libs_list) > 1:
|
|
86
|
+
raise RuntimeError(
|
|
87
|
+
f"Multiple libgomp shared libraries found in '{search_dir}': "
|
|
88
|
+
f"{libs_list}. Try to re-install in a fresh environment."
|
|
89
|
+
)
|
|
90
|
+
else: # len(libs_list) == 1
|
|
91
|
+
return libs_list[0]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Manual definition of which TorchScript extensions have their own dependencies. The
|
|
95
|
+
# dependencies should be returned in the order they need to be loaded.
|
|
96
|
+
EXTENSIONS_WITH_DEPENDENCIES = {
|
|
97
|
+
"rascaline_torch": _rascaline_lib_path,
|
|
98
|
+
"featomic_torch": _featomic_deps_path,
|
|
99
|
+
"sphericart_torch": _sphericart_deps_path,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _collect_extensions(extensions_path):
|
|
104
|
+
"""
|
|
105
|
+
Record the list of loaded TorchScript extensions (and their dependencies), to check
|
|
106
|
+
that they are also loaded when executing the model.
|
|
107
|
+
"""
|
|
108
|
+
if extensions_path is not None:
|
|
109
|
+
if os.path.exists(extensions_path):
|
|
110
|
+
shutil.rmtree(extensions_path)
|
|
111
|
+
os.makedirs(extensions_path)
|
|
112
|
+
# TODO: the extensions are currently collected in a separate directory,
|
|
113
|
+
# should we store the files directly inside the model file? This would makes
|
|
114
|
+
# the model platform-specific but much more convenient (since the end user
|
|
115
|
+
# does not have to move a model around)
|
|
116
|
+
|
|
117
|
+
extensions = []
|
|
118
|
+
extensions_deps = []
|
|
119
|
+
for library in torch.ops.loaded_libraries:
|
|
120
|
+
assert os.path.exists(library)
|
|
121
|
+
|
|
122
|
+
# these should be provided by the simulation engine
|
|
123
|
+
if os.path.samefile(library, METATENSOR_TORCH_LIB_PATH):
|
|
124
|
+
continue
|
|
125
|
+
if os.path.samefile(library, METATOMIC_TORCH_LIB_PATH):
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
path = _copy_extension(library, extensions_path)
|
|
129
|
+
info = _extension_info(library)
|
|
130
|
+
info["path"] = path
|
|
131
|
+
extensions.append(info)
|
|
132
|
+
|
|
133
|
+
for extra in EXTENSIONS_WITH_DEPENDENCIES.get(info["name"], lambda: [])():
|
|
134
|
+
path = _copy_extension(extra, extensions_path)
|
|
135
|
+
info = _extension_info(extra)
|
|
136
|
+
info["path"] = path
|
|
137
|
+
extensions_deps.append(info)
|
|
138
|
+
|
|
139
|
+
return extensions, extensions_deps
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _copy_extension(full_path, extensions_dir):
|
|
143
|
+
full_path = os.path.realpath(full_path)
|
|
144
|
+
|
|
145
|
+
prefixes = site.getsitepackages()
|
|
146
|
+
if site.ENABLE_USER_SITE:
|
|
147
|
+
prefixes.append(site.getusersitepackages())
|
|
148
|
+
|
|
149
|
+
# this also takes care of extensions installed directly in the same prefix as
|
|
150
|
+
# Python, for example when installing a standalone libmetatensor_torch with conda.
|
|
151
|
+
prefixes.append(sys.prefix)
|
|
152
|
+
|
|
153
|
+
path = full_path
|
|
154
|
+
for prefix in prefixes:
|
|
155
|
+
prefix = os.path.realpath(prefix)
|
|
156
|
+
assert os.path.isabs(prefix)
|
|
157
|
+
|
|
158
|
+
# Remove any local prefix
|
|
159
|
+
if path.startswith(prefix):
|
|
160
|
+
path = os.path.relpath(path, prefix)
|
|
161
|
+
break
|
|
162
|
+
|
|
163
|
+
if extensions_dir is not None:
|
|
164
|
+
collect_path = os.path.realpath(os.path.join(extensions_dir, path))
|
|
165
|
+
if collect_path == path:
|
|
166
|
+
raise RuntimeError(
|
|
167
|
+
f"extensions directory '{extensions_dir}' would overwrite files, "
|
|
168
|
+
"you should set it to a local path instead"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if os.path.exists(collect_path):
|
|
172
|
+
raise RuntimeError(
|
|
173
|
+
f"more than one extension would be collected at {collect_path}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
os.makedirs(os.path.dirname(collect_path), exist_ok=True)
|
|
177
|
+
shutil.copyfile(full_path, collect_path)
|
|
178
|
+
|
|
179
|
+
return path
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _extension_info(path):
|
|
183
|
+
# get the name of the library, excluding any shared object prefix/suffix
|
|
184
|
+
name = os.path.basename(path)
|
|
185
|
+
if name.startswith("lib"):
|
|
186
|
+
name = name[3:]
|
|
187
|
+
|
|
188
|
+
if name.endswith(".so"):
|
|
189
|
+
name = name[:-3]
|
|
190
|
+
|
|
191
|
+
if name.endswith(".dll"):
|
|
192
|
+
name = name[:-4]
|
|
193
|
+
|
|
194
|
+
if name.endswith(".dylib"):
|
|
195
|
+
name = name[:-6]
|
|
196
|
+
|
|
197
|
+
# Collect the hash of the extension shared library. We don't currently use
|
|
198
|
+
# this, but it would allow for binary-level reproducibility later.
|
|
199
|
+
with open(path, "rb") as fd:
|
|
200
|
+
sha256 = hashlib.sha256(fd.read()).hexdigest()
|
|
201
|
+
|
|
202
|
+
return {"name": name, "sha256": sha256}
|