resolve-core 0.8.1__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.
- resolve_core-0.8.1/CMakeLists.txt +192 -0
- resolve_core-0.8.1/PKG-INFO +50 -0
- resolve_core-0.8.1/README.md +23 -0
- resolve_core-0.8.1/pyproject.toml +56 -0
- resolve_core-0.8.1/src/bindings.cpp +118 -0
- resolve_core-0.8.1/src/bindings_common.hpp +79 -0
- resolve_core-0.8.1/src/bindings_dataset.cpp +477 -0
- resolve_core-0.8.1/src/bindings_enums.cpp +131 -0
- resolve_core-0.8.1/src/bindings_fuzzy.cpp +140 -0
- resolve_core-0.8.1/src/bindings_metrics.cpp +55 -0
- resolve_core-0.8.1/src/bindings_model.cpp +422 -0
- resolve_core-0.8.1/src/bindings_pretraining.cpp +136 -0
- resolve_core-0.8.1/src/bindings_trainer.cpp +364 -0
- resolve_core-0.8.1/src/bindings_types.cpp +480 -0
- resolve_core-0.8.1/src/resolve_core/__init__.py +367 -0
- resolve_core-0.8.1/src/resolve_core/_from_pandas.py +122 -0
- resolve_core-0.8.1/src/resolve_core/_io_retry.py +75 -0
- resolve_core-0.8.1/src/resolve_core/species_graph.py +205 -0
- resolve_core-0.8.1/src/resolve_core/tabr.py +212 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.18)
|
|
2
|
+
|
|
3
|
+
# Check if we're being included as a subdirectory or built standalone
|
|
4
|
+
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|
5
|
+
# Standalone build - define project and build resolve_core.
|
|
6
|
+
# No VERSION here: the wheel's version comes from pyproject.toml, the
|
|
7
|
+
# engine's from resolve::VERSION, and both are kept in sync with the
|
|
8
|
+
# repo-root VERSION file by tools/version.py. Nothing consumes
|
|
9
|
+
# PROJECT_VERSION, and this directory is also the sdist root, which does
|
|
10
|
+
# not carry the repo-root file.
|
|
11
|
+
project(resolve_python LANGUAGES CXX)
|
|
12
|
+
|
|
13
|
+
# CPU by default: the PyPI wheels link the CPU libtorch the user's `torch`
|
|
14
|
+
# ships and do not bundle CUDA. Pass -DUSE_CUDA=ON for a local CUDA build.
|
|
15
|
+
option(USE_CUDA "Enable CUDA support" OFF)
|
|
16
|
+
|
|
17
|
+
# libtorch 2.13+ requires C++20 (bit-field default initializers in
|
|
18
|
+
# c10/core/AutogradState.h). Match the parent src/core/CMakeLists.txt.
|
|
19
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
20
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
21
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
22
|
+
|
|
23
|
+
# Skip Caffe2's CUDA detection entirely when building CPU-only
|
|
24
|
+
if(NOT USE_CUDA)
|
|
25
|
+
set(CAFFE2_USE_CUDA OFF CACHE BOOL "" FORCE)
|
|
26
|
+
set(USE_CUDA OFF CACHE BOOL "" FORCE)
|
|
27
|
+
set(CAFFE2_USE_CUDNN OFF CACHE BOOL "" FORCE)
|
|
28
|
+
set(USE_CUDNN OFF CACHE BOOL "" FORCE)
|
|
29
|
+
# This is the key: tell Torch not to look for CUDA
|
|
30
|
+
set(TORCH_CUDA_ARCH_LIST "" CACHE STRING "" FORCE)
|
|
31
|
+
set(CMAKE_CUDA_COMPILER "" CACHE FILEPATH "" FORCE)
|
|
32
|
+
else()
|
|
33
|
+
# Local CUDA wheel build: link PyTorch's prebuilt CUDA libs (we do not
|
|
34
|
+
# compile CUDA ourselves except the custom kernels below). cuDNN off to
|
|
35
|
+
# match the engine build.
|
|
36
|
+
set(CAFFE2_USE_CUDA ON)
|
|
37
|
+
set(CAFFE2_USE_CUDNN OFF)
|
|
38
|
+
set(USE_CUDNN OFF)
|
|
39
|
+
endif()
|
|
40
|
+
|
|
41
|
+
# Locate libtorch via the installed `torch` package's CMake prefix when no
|
|
42
|
+
# explicit Torch_DIR / CMAKE_PREFIX_PATH was given. This is how the PyPI
|
|
43
|
+
# wheel and sdist builds find libtorch: `torch` is a build dependency
|
|
44
|
+
# (pyproject.toml), so it is present in the build environment, and the
|
|
45
|
+
# extension links the same libtorch the user's torch ships -- no separate
|
|
46
|
+
# libtorch is downloaded or bundled.
|
|
47
|
+
if(NOT Torch_DIR AND NOT CMAKE_PREFIX_PATH)
|
|
48
|
+
find_package(Python COMPONENTS Interpreter REQUIRED)
|
|
49
|
+
execute_process(
|
|
50
|
+
COMMAND "${Python_EXECUTABLE}" -c "import torch.utils; print(torch.utils.cmake_prefix_path)"
|
|
51
|
+
OUTPUT_VARIABLE TORCH_CMAKE_PREFIX
|
|
52
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
53
|
+
RESULT_VARIABLE TORCH_PROBE_RC
|
|
54
|
+
)
|
|
55
|
+
if(TORCH_PROBE_RC EQUAL 0 AND TORCH_CMAKE_PREFIX)
|
|
56
|
+
list(APPEND CMAKE_PREFIX_PATH "${TORCH_CMAKE_PREFIX}")
|
|
57
|
+
message(STATUS "Using libtorch from the torch package: ${TORCH_CMAKE_PREFIX}")
|
|
58
|
+
endif()
|
|
59
|
+
endif()
|
|
60
|
+
|
|
61
|
+
# Find libtorch (will skip CUDA if the CPU-only vars above are set)
|
|
62
|
+
find_package(Torch REQUIRED)
|
|
63
|
+
|
|
64
|
+
# Core library sources: single source of truth shared with the main engine
|
|
65
|
+
# build via resolve_core_sources.cmake (no drift -- this is why the wheel
|
|
66
|
+
# previously failed to link set_vram_fraction / install_crash_handler).
|
|
67
|
+
set(RESOLVE_CORE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
|
68
|
+
include(${RESOLVE_CORE_DIR}/resolve_warnings.cmake)
|
|
69
|
+
include(${RESOLVE_CORE_DIR}/resolve_core_sources.cmake)
|
|
70
|
+
|
|
71
|
+
add_library(resolve_core STATIC ${RESOLVE_CORE_SOURCES})
|
|
72
|
+
target_include_directories(resolve_core PUBLIC
|
|
73
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
|
|
74
|
+
)
|
|
75
|
+
target_link_libraries(resolve_core PUBLIC ${TORCH_LIBRARIES})
|
|
76
|
+
target_link_libraries(resolve_core PRIVATE resolve_warnings)
|
|
77
|
+
|
|
78
|
+
# Custom CUDA kernels + RESOLVE_HAS_CUDA, shared with the engine build. Under
|
|
79
|
+
# -DUSE_CUDA=ON this defines RESOLVE_HAS_CUDA so set_vram_fraction and the
|
|
80
|
+
# hash kernels are actually compiled in; a no-op on the default CPU wheel.
|
|
81
|
+
include(${RESOLVE_CORE_DIR}/resolve_cuda_kernels.cmake)
|
|
82
|
+
|
|
83
|
+
# OpenMP for fuzzy::query_batch (optional).
|
|
84
|
+
find_package(OpenMP QUIET)
|
|
85
|
+
if(OpenMP_CXX_FOUND)
|
|
86
|
+
target_link_libraries(resolve_core PUBLIC OpenMP::OpenMP_CXX)
|
|
87
|
+
message(STATUS "OpenMP enabled for resolve_core (standalone Python build)")
|
|
88
|
+
else()
|
|
89
|
+
message(STATUS "OpenMP not found; fuzzy::query_batch will run serially")
|
|
90
|
+
endif()
|
|
91
|
+
|
|
92
|
+
set_property(TARGET resolve_core PROPERTY CXX_STANDARD 20)
|
|
93
|
+
endif()
|
|
94
|
+
|
|
95
|
+
# Warning flags for RESOLVE's own targets. Already included by the branch above
|
|
96
|
+
# when this directory configures standalone (the sdist root) and by
|
|
97
|
+
# src/core/CMakeLists.txt when it is a subdirectory; the file guards itself.
|
|
98
|
+
include(${CMAKE_CURRENT_SOURCE_DIR}/../resolve_warnings.cmake)
|
|
99
|
+
|
|
100
|
+
# Python bindings using nanobind
|
|
101
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
102
|
+
|
|
103
|
+
# Fetch nanobind
|
|
104
|
+
include(FetchContent)
|
|
105
|
+
FetchContent_Declare(
|
|
106
|
+
nanobind
|
|
107
|
+
GIT_REPOSITORY https://github.com/wjakob/nanobind.git
|
|
108
|
+
GIT_TAG v2.4.0
|
|
109
|
+
)
|
|
110
|
+
FetchContent_MakeAvailable(nanobind)
|
|
111
|
+
|
|
112
|
+
# Build the extension module
|
|
113
|
+
set(BINDING_SOURCES
|
|
114
|
+
src/bindings.cpp
|
|
115
|
+
src/bindings_enums.cpp
|
|
116
|
+
src/bindings_types.cpp
|
|
117
|
+
src/bindings_dataset.cpp
|
|
118
|
+
src/bindings_model.cpp
|
|
119
|
+
src/bindings_trainer.cpp
|
|
120
|
+
src/bindings_metrics.cpp
|
|
121
|
+
src/bindings_pretraining.cpp
|
|
122
|
+
src/bindings_fuzzy.cpp
|
|
123
|
+
)
|
|
124
|
+
nanobind_add_module(_resolve_core ${BINDING_SOURCES})
|
|
125
|
+
|
|
126
|
+
# Find torch_python library for Python tensor interop (THPVariable_Wrap, etc.)
|
|
127
|
+
get_filename_component(TORCH_LIB_DIR "${TORCH_INSTALL_PREFIX}/lib" ABSOLUTE)
|
|
128
|
+
find_library(TORCH_PYTHON_LIB torch_python PATHS "${TORCH_LIB_DIR}" NO_DEFAULT_PATH)
|
|
129
|
+
if(TORCH_PYTHON_LIB)
|
|
130
|
+
message(STATUS "Found torch_python: ${TORCH_PYTHON_LIB}")
|
|
131
|
+
target_link_libraries(_resolve_core PRIVATE resolve_core ${TORCH_LIBRARIES} ${TORCH_PYTHON_LIB})
|
|
132
|
+
else()
|
|
133
|
+
message(WARNING "torch_python library not found, some features may not work")
|
|
134
|
+
target_link_libraries(_resolve_core PRIVATE resolve_core ${TORCH_LIBRARIES})
|
|
135
|
+
endif()
|
|
136
|
+
|
|
137
|
+
target_link_libraries(_resolve_core PRIVATE resolve_warnings)
|
|
138
|
+
|
|
139
|
+
# nanobind reaches the binding sources through a FetchContent target, which is
|
|
140
|
+
# not IMPORTED, so CMake does not mark its headers SYSTEM the way it does
|
|
141
|
+
# libtorch's. Promote them, otherwise /W4 and -Wall report nanobind's internals
|
|
142
|
+
# instead of our bindings. Which target carries them depends on the
|
|
143
|
+
# nanobind_add_module mode (static / shared / abi3 variants), so read it off
|
|
144
|
+
# the link line rather than hardcoding a name.
|
|
145
|
+
get_target_property(RESOLVE_NB_LINKED _resolve_core LINK_LIBRARIES)
|
|
146
|
+
foreach(RESOLVE_NB_LIB IN LISTS RESOLVE_NB_LINKED)
|
|
147
|
+
if(RESOLVE_NB_LIB MATCHES "^nanobind" AND TARGET ${RESOLVE_NB_LIB})
|
|
148
|
+
get_target_property(RESOLVE_NB_INCS ${RESOLVE_NB_LIB} INTERFACE_INCLUDE_DIRECTORIES)
|
|
149
|
+
if(RESOLVE_NB_INCS)
|
|
150
|
+
set_property(TARGET ${RESOLVE_NB_LIB} APPEND PROPERTY
|
|
151
|
+
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${RESOLVE_NB_INCS})
|
|
152
|
+
endif()
|
|
153
|
+
endif()
|
|
154
|
+
endforeach()
|
|
155
|
+
|
|
156
|
+
target_include_directories(_resolve_core PRIVATE
|
|
157
|
+
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Set output directory for the built module
|
|
161
|
+
set_target_properties(_resolve_core PROPERTIES
|
|
162
|
+
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/resolve_core
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Runtime libtorch resolution. The wheel deliberately does NOT bundle libtorch
|
|
166
|
+
# -- it links the same libs the user's `torch` ships (a separate copy would be
|
|
167
|
+
# huge and risk an ABI clash). resolve_core/__init__.py imports torch before
|
|
168
|
+
# loading this extension, so torch's libs are already in the process; the RPATH
|
|
169
|
+
# below is a belt-and-braces pointer to the installed torch lib dir
|
|
170
|
+
# (site-packages/torch/lib, one level up from resolve_core/). On Windows, torch
|
|
171
|
+
# adds its lib dir to the DLL search at import, so no RPATH/copy is needed.
|
|
172
|
+
if(UNIX AND NOT APPLE)
|
|
173
|
+
set_target_properties(_resolve_core PROPERTIES
|
|
174
|
+
INSTALL_RPATH "$ORIGIN/../torch/lib"
|
|
175
|
+
BUILD_WITH_INSTALL_RPATH TRUE)
|
|
176
|
+
elseif(APPLE)
|
|
177
|
+
set_target_properties(_resolve_core PROPERTIES
|
|
178
|
+
INSTALL_RPATH "@loader_path/../torch/lib"
|
|
179
|
+
BUILD_WITH_INSTALL_RPATH TRUE)
|
|
180
|
+
# Linking against libtorch_python pulls libtorch's bundled pybind11 into the
|
|
181
|
+
# module (via torch/csrc/autograd/python_variable.h in bindings_model.cpp),
|
|
182
|
+
# which references Python C-API symbols (PyInstanceMethod_*, _PyThreadState_
|
|
183
|
+
# UncheckedGet) not in nanobind's restricted macOS symbol list. A Python
|
|
184
|
+
# extension on macOS does not link libpython; those symbols resolve at load
|
|
185
|
+
# time from the host interpreter, so allow undefined-symbol dynamic lookup
|
|
186
|
+
# (the canonical macOS Python-extension link flag). Without it the arm64 link
|
|
187
|
+
# fails with "Undefined symbols for architecture arm64".
|
|
188
|
+
target_link_options(_resolve_core PRIVATE -Wl,-undefined,dynamic_lookup)
|
|
189
|
+
endif()
|
|
190
|
+
|
|
191
|
+
# Install target for scikit-build-core
|
|
192
|
+
install(TARGETS _resolve_core LIBRARY DESTINATION resolve_core)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: resolve-core
|
|
3
|
+
Version: 0.8.1
|
|
4
|
+
Summary: RESOLVE C++ engine: predict sample outcomes from compositional data via learned representations
|
|
5
|
+
Author-Email: Gilles Colling <gilles.colling051@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: C++
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Project-URL: Homepage, https://github.com/gcol33/resolve
|
|
18
|
+
Project-URL: Repository, https://github.com/gcol33/resolve
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Requires-Dist: numpy>=1.20.0
|
|
21
|
+
Requires-Dist: pandas>=1.3.0
|
|
22
|
+
Requires-Dist: torch>=2.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# resolve-core
|
|
29
|
+
|
|
30
|
+
C++ bindings for the RESOLVE compositional-data prediction engine.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install resolve-core
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- Python >= 3.9
|
|
41
|
+
- PyTorch/libtorch
|
|
42
|
+
- CMake >= 3.18
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from resolve_core import Trainer, TrainConfig, ModelConfig
|
|
48
|
+
|
|
49
|
+
# See full package documentation for API details
|
|
50
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# resolve-core
|
|
2
|
+
|
|
3
|
+
C++ bindings for the RESOLVE compositional-data prediction engine.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install resolve-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python >= 3.9
|
|
14
|
+
- PyTorch/libtorch
|
|
15
|
+
- CMake >= 3.18
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from resolve_core import Trainer, TrainConfig, ModelConfig
|
|
21
|
+
|
|
22
|
+
# See full package documentation for API details
|
|
23
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
# torch is a build requirement: the extension links libtorch, located via the
|
|
3
|
+
# installed torch package's CMake prefix (src/core/python/CMakeLists.txt).
|
|
4
|
+
requires = ["scikit-build-core>=0.4.3", "nanobind>=2.0.0", "torch>=2.0"]
|
|
5
|
+
build-backend = "scikit_build_core.build"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "resolve-core"
|
|
9
|
+
version = "0.8.1"
|
|
10
|
+
description = "RESOLVE C++ engine: predict sample outcomes from compositional data via learned representations"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = {text = "MIT"}
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
authors = [
|
|
15
|
+
{name = "Gilles Colling", email = "gilles.colling051@gmail.com"}
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: C++",
|
|
27
|
+
"Topic :: Scientific/Engineering",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"numpy>=1.20.0",
|
|
31
|
+
"pandas>=1.3.0",
|
|
32
|
+
# The extension links libtorch and imports torch before loading; the wheels
|
|
33
|
+
# do not bundle libtorch, so torch must be installed at runtime.
|
|
34
|
+
"torch>=2.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.0.0",
|
|
40
|
+
"pytest-cov>=4.0.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/gcol33/resolve"
|
|
45
|
+
Repository = "https://github.com/gcol33/resolve"
|
|
46
|
+
|
|
47
|
+
[tool.scikit-build]
|
|
48
|
+
# cmake.version, not cmake.minimum-version: scikit-build-core >= 0.8 rejects the
|
|
49
|
+
# old key outright, which fails metadata generation before any build starts.
|
|
50
|
+
cmake.version = ">=3.18"
|
|
51
|
+
cmake.build-type = "Release"
|
|
52
|
+
wheel.packages = ["src/resolve_core"]
|
|
53
|
+
|
|
54
|
+
[tool.scikit-build.cmake.define]
|
|
55
|
+
BUILD_PYTHON = "ON"
|
|
56
|
+
BUILD_TESTS = "OFF"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Main binding entry point - delegates to split modules for maintainability
|
|
2
|
+
//
|
|
3
|
+
// The actual binding code is split into:
|
|
4
|
+
// - bindings_enums.cpp: All enum bindings
|
|
5
|
+
// - bindings_types.cpp: Configuration structs and result types
|
|
6
|
+
// - bindings_dataset.cpp: ResolveDataset and species encoding
|
|
7
|
+
// - bindings_model.cpp: ResolveModel
|
|
8
|
+
// - bindings_trainer.cpp: Trainer and Predictor
|
|
9
|
+
// - bindings_metrics.cpp: Metrics classes
|
|
10
|
+
|
|
11
|
+
#include "bindings_common.hpp"
|
|
12
|
+
|
|
13
|
+
#include <string>
|
|
14
|
+
|
|
15
|
+
NB_MODULE(_resolve_core, m) {
|
|
16
|
+
m.doc() = "RESOLVE C++ core library for species-composition based prediction";
|
|
17
|
+
|
|
18
|
+
// libtorch holds global state (type registrations, dispatch keys) via shared_ptr
|
|
19
|
+
// that outlives nanobind's module cleanup at interpreter shutdown. This causes
|
|
20
|
+
// false-positive "leaked N types / N functions" warnings. Instance leaks are
|
|
21
|
+
// fixed by returning config objects by value (not reference) in property bindings.
|
|
22
|
+
nb::set_leak_warnings(false);
|
|
23
|
+
|
|
24
|
+
// Register all bindings from split modules
|
|
25
|
+
register_enums(m);
|
|
26
|
+
register_types(m);
|
|
27
|
+
register_dataset(m);
|
|
28
|
+
register_model(m);
|
|
29
|
+
register_trainer(m);
|
|
30
|
+
register_metrics(m);
|
|
31
|
+
register_pretraining(m);
|
|
32
|
+
register_fuzzy(m);
|
|
33
|
+
|
|
34
|
+
// Platform-aware PYTORCH_CUDA_ALLOC_CONF setter. The primary surface is
|
|
35
|
+
// resolve_core.configure_cuda_allocator() in the Python __init__ which
|
|
36
|
+
// runs at module import (pre-torch). This C++ entry point is the
|
|
37
|
+
// late-bound fallback for users who imported torch first; in that case
|
|
38
|
+
// the allocator has already initialized and changing the env var no
|
|
39
|
+
// longer affects allocator behavior. Returns the resulting config
|
|
40
|
+
// string for logging.
|
|
41
|
+
m.def(
|
|
42
|
+
"_configure_cuda_allocator_native",
|
|
43
|
+
&resolve::configure_cuda_allocator,
|
|
44
|
+
nb::arg("force") = false,
|
|
45
|
+
"Set PYTORCH_CUDA_ALLOC_CONF if unset (or force-set). Linux/macOS\n"
|
|
46
|
+
"prepend expandable_segments:True; Windows omits it. Returns the\n"
|
|
47
|
+
"active value. Use resolve_core.configure_cuda_allocator() instead;\n"
|
|
48
|
+
"this native shim is late-bound and cannot rescue allocators that\n"
|
|
49
|
+
"have already initialized."
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Top-level helper: cap the PyTorch CUDA caching allocator at a fraction
|
|
53
|
+
// of device VRAM. Standalone counterpart to TrainConfig.vram_fraction for
|
|
54
|
+
// users running Predictor-only workflows or wanting to apply the cap
|
|
55
|
+
// before constructing any RESOLVE object.
|
|
56
|
+
m.def(
|
|
57
|
+
"set_vram_fraction",
|
|
58
|
+
[](double fraction, int device_index) {
|
|
59
|
+
resolve::set_vram_fraction(fraction, device_index);
|
|
60
|
+
},
|
|
61
|
+
nb::arg("fraction"),
|
|
62
|
+
nb::arg("device_index") = -1,
|
|
63
|
+
"Cap the PyTorch CUDA caching allocator at `fraction` of device VRAM.\n"
|
|
64
|
+
"fraction must be in (0, 1]; 1.0 disables the cap. device_index = -1\n"
|
|
65
|
+
"uses the current CUDA device. No-op on CPU-only builds or when no\n"
|
|
66
|
+
"CUDA device is present."
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Pin libtorch's host thread pools. Standalone counterpart used at startup
|
|
70
|
+
// to avoid worker-thread teardown races on Windows (issue #18); harmless
|
|
71
|
+
// elsewhere. <=0 keeps libtorch's default for the corresponding pool.
|
|
72
|
+
m.def(
|
|
73
|
+
"set_thread_pools",
|
|
74
|
+
[](int intraop_threads, int interop_threads) {
|
|
75
|
+
resolve::set_thread_pools(intraop_threads, interop_threads);
|
|
76
|
+
},
|
|
77
|
+
nb::arg("intraop_threads"),
|
|
78
|
+
nb::arg("interop_threads") = -1,
|
|
79
|
+
"Pin libtorch's intra-op / inter-op thread pools (<=0 keeps the\n"
|
|
80
|
+
"default). Best-effort; call at startup before the first op."
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Windows crash hardening (issue #19): convert an unhandled native fault in
|
|
84
|
+
// a headless training worker into an immediate TerminateProcess with the
|
|
85
|
+
// fault's NTSTATUS, instead of an indefinite Windows-Error-Reporting /
|
|
86
|
+
// JIT-debugger (vsjitdebugger) hang that holds the GPU and stalls the
|
|
87
|
+
// batch. No-op off Windows. Auto-installed at import below; also exposed so
|
|
88
|
+
// callers can re-arm or adjust the shutdown exit code explicitly.
|
|
89
|
+
m.def(
|
|
90
|
+
"install_crash_handler",
|
|
91
|
+
[](int shutdown_exit_code) {
|
|
92
|
+
resolve::install_crash_handler(shutdown_exit_code);
|
|
93
|
+
},
|
|
94
|
+
nb::arg("shutdown_exit_code") = 0,
|
|
95
|
+
"Install the Windows unhandled-exception filter that fails fast via\n"
|
|
96
|
+
"TerminateProcess instead of hanging on the JIT debugger. No-op off\n"
|
|
97
|
+
"Windows. Idempotent."
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Internal: flip the crash handler to treat a subsequent native fault as a
|
|
101
|
+
// benign teardown artifact (exit with the shutdown code, not a failure
|
|
102
|
+
// code). Registered with atexit() in resolve_core/__init__.py so a clean
|
|
103
|
+
// interpreter shutdown after a successful run is not misreported as a crash.
|
|
104
|
+
m.def(
|
|
105
|
+
"_signal_work_complete",
|
|
106
|
+
[]() { resolve::signal_work_complete(); },
|
|
107
|
+
"Mark all engine work complete (atexit hook; see __init__.py)."
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Arm the crash handler as soon as resolve_core is imported, so a training
|
|
111
|
+
// worker is hardened before Trainer::fit() ever runs (issue #19). Default
|
|
112
|
+
// shutdown code 0: a clean interpreter shutdown (after _signal_work_complete
|
|
113
|
+
// via atexit) exits 0; a mid-run native fault exits with its NTSTATUS.
|
|
114
|
+
resolve::install_crash_handler(0);
|
|
115
|
+
|
|
116
|
+
// Version
|
|
117
|
+
m.attr("__version__") = resolve::VERSION;
|
|
118
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <nanobind/nanobind.h>
|
|
4
|
+
#include <nanobind/stl/string.h>
|
|
5
|
+
#include <nanobind/stl/vector.h>
|
|
6
|
+
#include <nanobind/stl/pair.h>
|
|
7
|
+
#include <nanobind/stl/unordered_map.h>
|
|
8
|
+
#include <nanobind/stl/optional.h>
|
|
9
|
+
#include <torch/torch.h>
|
|
10
|
+
#include <torch/csrc/autograd/python_variable.h> // For THPVariable_Wrap/Unpack
|
|
11
|
+
|
|
12
|
+
#include "resolve/resolve.hpp"
|
|
13
|
+
#include "resolve/role_mapping.hpp"
|
|
14
|
+
#include "resolve/dataset.hpp"
|
|
15
|
+
|
|
16
|
+
namespace nb = nanobind;
|
|
17
|
+
|
|
18
|
+
// Unpack an optional tensor argument. Returns an undefined tensor for None;
|
|
19
|
+
// THPVariable_Unpack on Py_None reinterprets the None singleton as a
|
|
20
|
+
// THPVariable and reads out of bounds (UB), so callers passing None for an
|
|
21
|
+
// unused input (e.g. genus_ids in hash mode) must be guarded here.
|
|
22
|
+
inline at::Tensor unpack_optional_tensor(const nb::object& obj) {
|
|
23
|
+
if (!obj.is_valid() || obj.is_none()) return at::Tensor();
|
|
24
|
+
return THPVariable_Unpack(obj.ptr());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Unpack a required tensor argument. Raises a clear Python-visible error for
|
|
28
|
+
// None or a non-tensor instead of the UB THPVariable_Unpack would hit.
|
|
29
|
+
inline at::Tensor unpack_required_tensor(const nb::object& obj, const char* name) {
|
|
30
|
+
PyObject* p = obj.ptr();
|
|
31
|
+
if (obj.is_none() || !THPVariable_Check(p)) {
|
|
32
|
+
throw std::invalid_argument(std::string(name) + " must be a tensor");
|
|
33
|
+
}
|
|
34
|
+
return THPVariable_Unpack(p);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper to convert Python dict to unordered_map of tensors
|
|
38
|
+
inline std::unordered_map<std::string, torch::Tensor> dict_to_tensor_map(const nb::dict& d) {
|
|
39
|
+
std::unordered_map<std::string, torch::Tensor> result;
|
|
40
|
+
for (auto item : d) {
|
|
41
|
+
// Use THPVariable_Unpack to convert Python tensor to C++ tensor.
|
|
42
|
+
// Reject a non-tensor value loudly instead of silently dropping it,
|
|
43
|
+
// which would make a mistyped targets/inputs entry vanish.
|
|
44
|
+
PyObject* py_tensor = item.second.ptr();
|
|
45
|
+
auto key = nb::cast<std::string>(item.first);
|
|
46
|
+
if (!THPVariable_Check(py_tensor)) {
|
|
47
|
+
throw std::runtime_error(
|
|
48
|
+
"dict_to_tensor_map: value for key '" + key +
|
|
49
|
+
"' is not a torch.Tensor");
|
|
50
|
+
}
|
|
51
|
+
result[key] = THPVariable_Unpack(py_tensor);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Helper to convert unordered_map of tensors to Python dict
|
|
57
|
+
inline nb::object tensor_map_to_dict(const std::unordered_map<std::string, torch::Tensor>& m) {
|
|
58
|
+
PyObject* py_dict = PyDict_New();
|
|
59
|
+
for (const auto& [key, value] : m) {
|
|
60
|
+
if (value.defined()) {
|
|
61
|
+
// Move tensor to CPU and make contiguous for Python interop
|
|
62
|
+
auto cpu_tensor = value.detach().cpu().contiguous();
|
|
63
|
+
PyObject* py_tensor = THPVariable_Wrap(cpu_tensor);
|
|
64
|
+
PyDict_SetItemString(py_dict, key.c_str(), py_tensor);
|
|
65
|
+
Py_DECREF(py_tensor); // PyDict_SetItemString increments refcount
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return nb::steal(py_dict);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Forward declarations for binding registration functions
|
|
72
|
+
void register_enums(nb::module_& m);
|
|
73
|
+
void register_types(nb::module_& m);
|
|
74
|
+
void register_dataset(nb::module_& m);
|
|
75
|
+
void register_model(nb::module_& m);
|
|
76
|
+
void register_trainer(nb::module_& m);
|
|
77
|
+
void register_metrics(nb::module_& m);
|
|
78
|
+
void register_pretraining(nb::module_& m);
|
|
79
|
+
void register_fuzzy(nb::module_& m);
|