arraybridge 0.2.10__tar.gz → 0.2.11__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.
- arraybridge-0.2.11/.github/badges/coverage.svg +1 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/ADDING_NEW_FRAMEWORKS.md +11 -7
- {arraybridge-0.2.10 → arraybridge-0.2.11}/CONTRIBUTING.md +2 -4
- arraybridge-0.2.11/PKG-INFO +137 -0
- arraybridge-0.2.11/README.md +70 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/SETUP_INSTRUCTIONS.md +4 -4
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/CI_IMPROVEMENTS.md +0 -1
- arraybridge-0.2.11/docs/archive/README.md +5 -0
- arraybridge-0.2.11/docs/ci-cd.md +7 -0
- arraybridge-0.2.11/docs/source/README.md +15 -0
- arraybridge-0.2.11/docs/source/advanced_topics.rst +20 -0
- arraybridge-0.2.11/docs/source/api_reference.rst +38 -0
- arraybridge-0.2.11/docs/source/ci-cd.rst +25 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/docs/source/conf.py +13 -5
- arraybridge-0.2.11/docs/source/contributing.rst +27 -0
- arraybridge-0.2.11/docs/source/converters.rst +36 -0
- arraybridge-0.2.11/docs/source/decorators.rst +40 -0
- arraybridge-0.2.11/docs/source/examples/basic_conversion.rst +16 -0
- arraybridge-0.2.11/docs/source/examples/decorators.rst +18 -0
- arraybridge-0.2.11/docs/source/examples/multi_framework.rst +18 -0
- arraybridge-0.2.11/docs/source/gpu_features.rst +19 -0
- arraybridge-0.2.11/docs/source/index.rst +33 -0
- arraybridge-0.2.11/docs/source/installation.rst +58 -0
- arraybridge-0.2.11/docs/source/quickstart.rst +54 -0
- arraybridge-0.2.11/docs/source/stack_utils.rst +25 -0
- arraybridge-0.2.11/docs/source/user_guide.rst +35 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/pyproject.toml +6 -7
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/__init__.py +1 -1
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/decorators.py +305 -50
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_converters_registry.py +10 -8
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_decorators.py +23 -7
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_registry_integration.py +3 -2
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_types.py +5 -15
- arraybridge-0.2.10/.github/badges/coverage.svg +0 -1
- arraybridge-0.2.10/PKG-INFO +0 -243
- arraybridge-0.2.10/README.md +0 -175
- arraybridge-0.2.10/docs/ci-cd.md +0 -352
- arraybridge-0.2.10/docs/source/README.md +0 -145
- arraybridge-0.2.10/docs/source/advanced_topics.rst +0 -637
- arraybridge-0.2.10/docs/source/api/converters.rst +0 -46
- arraybridge-0.2.10/docs/source/api/decorators.rst +0 -72
- arraybridge-0.2.10/docs/source/api/exceptions.rst +0 -39
- arraybridge-0.2.10/docs/source/api/index.rst +0 -24
- arraybridge-0.2.10/docs/source/api/types.rst +0 -54
- arraybridge-0.2.10/docs/source/api/utils.rst +0 -70
- arraybridge-0.2.10/docs/source/api_reference.rst +0 -629
- arraybridge-0.2.10/docs/source/ci-cd.rst +0 -32
- arraybridge-0.2.10/docs/source/contributing.rst +0 -24
- arraybridge-0.2.10/docs/source/converters.rst +0 -474
- arraybridge-0.2.10/docs/source/decorators.rst +0 -638
- arraybridge-0.2.10/docs/source/examples/basic_conversion.rst +0 -98
- arraybridge-0.2.10/docs/source/examples/decorators.rst +0 -124
- arraybridge-0.2.10/docs/source/examples/multi_framework.rst +0 -132
- arraybridge-0.2.10/docs/source/gpu_features.rst +0 -624
- arraybridge-0.2.10/docs/source/index.rst +0 -96
- arraybridge-0.2.10/docs/source/installation.rst +0 -229
- arraybridge-0.2.10/docs/source/quickstart.rst +0 -281
- arraybridge-0.2.10/docs/source/stack_utils.rst +0 -603
- arraybridge-0.2.10/docs/source/user_guide.rst +0 -568
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.github/workflows/ci.yml +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.github/workflows/coverage-pages.yml +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.github/workflows/gpu-tests.yml +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.github/workflows/publish.yml +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.gitignore +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/.readthedocs.yml +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/LICENSE +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/TESTING.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/docs/Makefile +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/CI_ARTIFACT_UPDATE.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/CI_CUPY_FIX.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/CONFLICT_VISUALIZATION.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/COVERAGE_AUDIT_PLAN.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/DEPLOYMENT_SUMMARY.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/GPU_TESTING_SETUP.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/MERGE_CONFLICT_RESOLUTION.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/MIGRATION_SUMMARY.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/QUICK_FIX_GUIDE.txt +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11/docs/archive}/RESOLVED_FILES.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/docs/source/.gitignore +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/docs/source/Makefile +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/docs/source/examples/index.rst +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/paper.bib +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/paper.md +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/scripts/release.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/scripts/update_and_release.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/scripts/update_version.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/scripts/verify_release_ready.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/converters.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/converters_registry.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/dtype_scaling.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/exceptions.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/framework_config.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/framework_ops.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/gpu_cleanup.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/oom_recovery.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/slice_processing.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/stack_utils.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/types.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/src/arraybridge/utils.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/__init__.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/conftest.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_converters.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_dtype_scaling.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_exceptions.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_framework_config.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_gpu_cleanup.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_integration.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_oom_recovery.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_slice_processing.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_stack_utils.py +0 -0
- {arraybridge-0.2.10 → arraybridge-0.2.11}/tests/test_utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 75.14%"><title>coverage: 75.14%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">75.14%</text><text x="865" y="150" fill-opacity=".3" textLength="430">75.14%</text></g><text x="865" y="140" textLength="430">75.14%</text></g></g></svg>
|
|
@@ -99,13 +99,15 @@ converter = MemoryType.MXNET.converter
|
|
|
99
99
|
class MxnetConverter(MemoryTypeConverter):
|
|
100
100
|
def to_numpy(self, data, gpu_id):
|
|
101
101
|
return data.asnumpy()
|
|
102
|
+
|
|
102
103
|
def from_numpy(self, data, gpu_id):
|
|
103
104
|
return mxnet.nd.array(data)
|
|
105
|
+
|
|
104
106
|
def to_torch(self, data, gpu_id):
|
|
105
|
-
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
106
109
|
def to_cupy(self, data, gpu_id):
|
|
107
|
-
|
|
108
|
-
# ... 6+ more methods
|
|
110
|
+
raise NotImplementedError
|
|
109
111
|
|
|
110
112
|
# Had to manually register
|
|
111
113
|
_CONVERTERS[MemoryType.MXNET] = MxnetConverter()
|
|
@@ -113,8 +115,10 @@ _CONVERTERS[MemoryType.MXNET] = MxnetConverter()
|
|
|
113
115
|
|
|
114
116
|
### After (Auto-registration - New System)
|
|
115
117
|
```python
|
|
116
|
-
#
|
|
117
|
-
MemoryType
|
|
118
|
+
# Add one enum member and one config entry; everything else is automatic.
|
|
119
|
+
class MemoryType(Enum):
|
|
120
|
+
# Existing members omitted.
|
|
121
|
+
MXNET = "mxnet"
|
|
118
122
|
|
|
119
123
|
_FRAMEWORK_CONFIG[MemoryType.MXNET] = {
|
|
120
124
|
'conversion_ops': {
|
|
@@ -160,7 +164,7 @@ def test_convert_numpy_to_mxnet(self, mxnet_available):
|
|
|
160
164
|
|
|
161
165
|
### GPU Framework with DLPack
|
|
162
166
|
```python
|
|
163
|
-
|
|
167
|
+
conversion_ops = {
|
|
164
168
|
'to_numpy': 'data.cpu().numpy()',
|
|
165
169
|
'from_numpy': '{mod}.from_numpy(data).to(device=gpu_id)',
|
|
166
170
|
'from_dlpack': '{mod}.from_dlpack(data)',
|
|
@@ -170,7 +174,7 @@ def test_convert_numpy_to_mxnet(self, mxnet_available):
|
|
|
170
174
|
|
|
171
175
|
### CPU-only Framework
|
|
172
176
|
```python
|
|
173
|
-
|
|
177
|
+
conversion_ops = {
|
|
174
178
|
'to_numpy': 'np.array(data)',
|
|
175
179
|
'from_numpy': '{mod}.array(data)',
|
|
176
180
|
'from_dlpack': None, # Not supported
|
|
@@ -35,7 +35,7 @@ We are committed to providing a welcoming and inclusive environment. Please be r
|
|
|
35
35
|
|
|
36
36
|
### Finding Issues
|
|
37
37
|
|
|
38
|
-
- Check the [issue tracker](https://github.com/
|
|
38
|
+
- Check the [issue tracker](https://github.com/OpenHCSDev/arraybridge/issues)
|
|
39
39
|
- Look for issues labeled `good first issue` or `help wanted`
|
|
40
40
|
- Feel free to create new issues for bugs or feature requests
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ git clone https://github.com/YOUR_USERNAME/arraybridge.git
|
|
|
49
49
|
cd arraybridge
|
|
50
50
|
|
|
51
51
|
# Add upstream remote
|
|
52
|
-
git remote add upstream https://github.com/
|
|
52
|
+
git remote add upstream https://github.com/OpenHCSDev/arraybridge.git
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
### 2. Create Virtual Environment
|
|
@@ -135,13 +135,11 @@ def convert_memory(data: Any, source_type: str, target_type: str, gpu_id: int) -
|
|
|
135
135
|
MemoryConversionError: If conversion fails
|
|
136
136
|
|
|
137
137
|
Example:
|
|
138
|
-
```python
|
|
139
138
|
import numpy as np
|
|
140
139
|
from arraybridge import convert_memory
|
|
141
140
|
|
|
142
141
|
data = np.array([1, 2, 3])
|
|
143
142
|
result = convert_memory(data, "numpy", "torch", gpu_id=0)
|
|
144
|
-
```
|
|
145
143
|
"""
|
|
146
144
|
```
|
|
147
145
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arraybridge
|
|
3
|
+
Version: 0.2.11
|
|
4
|
+
Summary: Unified API for NumPy, CuPy, PyTorch, TensorFlow, JAX, and pyclesperanto with automatic memory type conversion
|
|
5
|
+
Project-URL: Homepage, https://github.com/OpenHCSDev/arraybridge
|
|
6
|
+
Project-URL: Documentation, https://arraybridge.readthedocs.io
|
|
7
|
+
Project-URL: Repository, https://github.com/OpenHCSDev/arraybridge
|
|
8
|
+
Project-URL: Issues, https://github.com/OpenHCSDev/arraybridge/issues
|
|
9
|
+
Author-email: Tristan Simas <tristan.simas@mail.mcgill.ca>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: array,conversion,cupy,gpu,jax,numpy,pytorch,tensor,tensorflow
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: metaclass-registry>=0.1.5
|
|
25
|
+
Requires-Dist: numpy>=1.20
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: cupy>=10.0; extra == 'all'
|
|
28
|
+
Requires-Dist: jax>=0.3; extra == 'all'
|
|
29
|
+
Requires-Dist: jaxlib>=0.3; extra == 'all'
|
|
30
|
+
Requires-Dist: pyclesperanto>=0.10; extra == 'all'
|
|
31
|
+
Requires-Dist: tensorflow>=2.8; extra == 'all'
|
|
32
|
+
Requires-Dist: torch>=1.10; extra == 'all'
|
|
33
|
+
Provides-Extra: cupy
|
|
34
|
+
Requires-Dist: cupy>=10.0; extra == 'cupy'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: black>=23.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
41
|
+
Provides-Extra: docs
|
|
42
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.25; extra == 'docs'
|
|
43
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'docs'
|
|
44
|
+
Requires-Dist: sphinx>=7.0; extra == 'docs'
|
|
45
|
+
Provides-Extra: gpu
|
|
46
|
+
Requires-Dist: cucim-cu12<26.0.0,>=25.6.0; extra == 'gpu'
|
|
47
|
+
Requires-Dist: cupy-cuda12x<14.0.0,>=13.3.0; extra == 'gpu'
|
|
48
|
+
Requires-Dist: jax-cuda12-pjrt<0.6.0,>=0.5.3; extra == 'gpu'
|
|
49
|
+
Requires-Dist: jax-cuda12-plugin<0.6.0,>=0.5.3; extra == 'gpu'
|
|
50
|
+
Requires-Dist: jax<0.6.0,>=0.5.3; extra == 'gpu'
|
|
51
|
+
Requires-Dist: jaxlib<0.6.0,>=0.5.3; extra == 'gpu'
|
|
52
|
+
Requires-Dist: pyclesperanto>=0.17.1; extra == 'gpu'
|
|
53
|
+
Requires-Dist: tensorflow-probability[tf]<0.26.0,>=0.25.0; extra == 'gpu'
|
|
54
|
+
Requires-Dist: tensorflow<2.20.0,>=2.19.0; extra == 'gpu'
|
|
55
|
+
Requires-Dist: torch<2.8.0,>=2.6.0; extra == 'gpu'
|
|
56
|
+
Requires-Dist: torchvision<0.23.0,>=0.21.0; extra == 'gpu'
|
|
57
|
+
Provides-Extra: jax
|
|
58
|
+
Requires-Dist: jax>=0.3; extra == 'jax'
|
|
59
|
+
Requires-Dist: jaxlib>=0.3; extra == 'jax'
|
|
60
|
+
Provides-Extra: pyclesperanto
|
|
61
|
+
Requires-Dist: pyclesperanto>=0.10; extra == 'pyclesperanto'
|
|
62
|
+
Provides-Extra: tensorflow
|
|
63
|
+
Requires-Dist: tensorflow>=2.8; extra == 'tensorflow'
|
|
64
|
+
Provides-Extra: torch
|
|
65
|
+
Requires-Dist: torch>=1.10; extra == 'torch'
|
|
66
|
+
Description-Content-Type: text/markdown
|
|
67
|
+
|
|
68
|
+
# arraybridge
|
|
69
|
+
|
|
70
|
+
ArrayBridge provides explicit conversion and shared lifecycle utilities for
|
|
71
|
+
NumPy, CuPy, PyTorch, TensorFlow, JAX, and pyclesperanto arrays.
|
|
72
|
+
|
|
73
|
+
Core dependencies are NumPy and metaclass-registry. Other frameworks are
|
|
74
|
+
optional.
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import numpy as np
|
|
80
|
+
|
|
81
|
+
from arraybridge import convert_memory, detect_memory_type
|
|
82
|
+
|
|
83
|
+
value = np.arange(6).reshape(2, 3)
|
|
84
|
+
assert detect_memory_type(value) == "numpy"
|
|
85
|
+
|
|
86
|
+
copy = convert_memory(
|
|
87
|
+
value,
|
|
88
|
+
source_type="numpy",
|
|
89
|
+
target_type="numpy",
|
|
90
|
+
gpu_id=0,
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`convert_memory` requires the declared source type, target type, and device id.
|
|
95
|
+
It uses the registered converter for the source framework. The device id is
|
|
96
|
+
required even for CPU conversions so call sites have one stable signature.
|
|
97
|
+
|
|
98
|
+
## Declarative decorators
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from arraybridge import numpy
|
|
102
|
+
|
|
103
|
+
@numpy
|
|
104
|
+
def normalize(image):
|
|
105
|
+
return image / max(float(image.max()), 1.0)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The framework decorators attach `input_memory_type` and `output_memory_type`
|
|
109
|
+
metadata, provide dtype/slice runtime parameters, and add framework-specific
|
|
110
|
+
stream/OOM handling where supported. They do **not** convert inputs or outputs
|
|
111
|
+
between frameworks and do not accept a `gpu_id` argument. A host runtime must
|
|
112
|
+
call `convert_memory` at the boundary it plans.
|
|
113
|
+
|
|
114
|
+
## Stack utilities
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import numpy as np
|
|
118
|
+
|
|
119
|
+
from arraybridge import stack_slices, unstack_slices
|
|
120
|
+
|
|
121
|
+
slices = [np.zeros((8, 8)), np.ones((8, 8))]
|
|
122
|
+
stack = stack_slices(slices, memory_type="numpy", gpu_id=0)
|
|
123
|
+
restored = unstack_slices(stack, memory_type="numpy", gpu_id=0)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`stack_slices` requires non-empty 2D inputs. `unstack_slices` requires a 3D
|
|
127
|
+
array. Both validate shape and use explicit target memory/device declarations.
|
|
128
|
+
|
|
129
|
+
## Installation
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pip install arraybridge
|
|
133
|
+
pip install "arraybridge[torch]"
|
|
134
|
+
pip install "arraybridge[cupy]"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Documentation: <https://arraybridge.readthedocs.io>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# arraybridge
|
|
2
|
+
|
|
3
|
+
ArrayBridge provides explicit conversion and shared lifecycle utilities for
|
|
4
|
+
NumPy, CuPy, PyTorch, TensorFlow, JAX, and pyclesperanto arrays.
|
|
5
|
+
|
|
6
|
+
Core dependencies are NumPy and metaclass-registry. Other frameworks are
|
|
7
|
+
optional.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from arraybridge import convert_memory, detect_memory_type
|
|
15
|
+
|
|
16
|
+
value = np.arange(6).reshape(2, 3)
|
|
17
|
+
assert detect_memory_type(value) == "numpy"
|
|
18
|
+
|
|
19
|
+
copy = convert_memory(
|
|
20
|
+
value,
|
|
21
|
+
source_type="numpy",
|
|
22
|
+
target_type="numpy",
|
|
23
|
+
gpu_id=0,
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`convert_memory` requires the declared source type, target type, and device id.
|
|
28
|
+
It uses the registered converter for the source framework. The device id is
|
|
29
|
+
required even for CPU conversions so call sites have one stable signature.
|
|
30
|
+
|
|
31
|
+
## Declarative decorators
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from arraybridge import numpy
|
|
35
|
+
|
|
36
|
+
@numpy
|
|
37
|
+
def normalize(image):
|
|
38
|
+
return image / max(float(image.max()), 1.0)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The framework decorators attach `input_memory_type` and `output_memory_type`
|
|
42
|
+
metadata, provide dtype/slice runtime parameters, and add framework-specific
|
|
43
|
+
stream/OOM handling where supported. They do **not** convert inputs or outputs
|
|
44
|
+
between frameworks and do not accept a `gpu_id` argument. A host runtime must
|
|
45
|
+
call `convert_memory` at the boundary it plans.
|
|
46
|
+
|
|
47
|
+
## Stack utilities
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import numpy as np
|
|
51
|
+
|
|
52
|
+
from arraybridge import stack_slices, unstack_slices
|
|
53
|
+
|
|
54
|
+
slices = [np.zeros((8, 8)), np.ones((8, 8))]
|
|
55
|
+
stack = stack_slices(slices, memory_type="numpy", gpu_id=0)
|
|
56
|
+
restored = unstack_slices(stack, memory_type="numpy", gpu_id=0)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`stack_slices` requires non-empty 2D inputs. `unstack_slices` requires a 3D
|
|
60
|
+
array. Both validate shape and use explicit target memory/device declarations.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install arraybridge
|
|
66
|
+
pip install "arraybridge[torch]"
|
|
67
|
+
pip install "arraybridge[cupy]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Documentation: <https://arraybridge.readthedocs.io>
|
|
@@ -6,17 +6,17 @@ This document contains the manual steps needed to complete the badge and documen
|
|
|
6
6
|
|
|
7
7
|
To display the coverage reports hosted on GitHub Pages:
|
|
8
8
|
|
|
9
|
-
1. Go to: https://github.com/
|
|
9
|
+
1. Go to: https://github.com/OpenHCSDev/arraybridge/settings/pages
|
|
10
10
|
2. Under "Build and deployment" > "Source", select **"GitHub Actions"**
|
|
11
11
|
3. Save the changes
|
|
12
12
|
|
|
13
|
-
Once enabled, the coverage-pages.yml workflow will deploy coverage reports to https://
|
|
13
|
+
Once enabled, the coverage-pages.yml workflow will deploy coverage reports to https://openhcsdev.github.io/arraybridge/coverage/
|
|
14
14
|
|
|
15
15
|
## 2. Update Repository Description
|
|
16
16
|
|
|
17
17
|
To add the ReadTheDocs link to the repository description:
|
|
18
18
|
|
|
19
|
-
1. Go to: https://github.com/
|
|
19
|
+
1. Go to: https://github.com/OpenHCSDev/arraybridge
|
|
20
20
|
2. Click the gear icon (⚙️) next to "About" on the right side
|
|
21
21
|
3. In the "Website" field, enter: `https://arraybridge.readthedocs.io`
|
|
22
22
|
4. Optionally add topics/tags to improve discoverability
|
|
@@ -55,7 +55,7 @@ After merging this PR to main:
|
|
|
55
55
|
The following badges have been added to README.md:
|
|
56
56
|
|
|
57
57
|
- **ReadTheDocs**: `[](https://arraybridge.readthedocs.io/en/latest/?badge=latest)`
|
|
58
|
-
- **Coverage**: `[](https://openhcsdev.github.io/arraybridge/coverage/)`
|
|
59
59
|
|
|
60
60
|
## Notes
|
|
61
61
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# CI/CD documentation
|
|
2
|
+
|
|
3
|
+
The maintained CI/CD documentation is
|
|
4
|
+
[`source/ci-cd.rst`](source/ci-cd.rst). It deliberately links to the current
|
|
5
|
+
[GitHub Actions workflows](https://github.com/OpenHCSDev/arraybridge/actions)
|
|
6
|
+
instead of duplicating a version matrix or release procedure that can drift
|
|
7
|
+
from the workflow files.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ArrayBridge documentation source
|
|
2
|
+
|
|
3
|
+
The active Sphinx source is this directory.
|
|
4
|
+
|
|
5
|
+
From the repository root:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m pip install -e ".[docs]"
|
|
9
|
+
python -m sphinx -E -W --keep-going -b html docs/source docs/source/_build/html
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Narrative pages document public behavior and ownership. `api_reference.rst`
|
|
13
|
+
tracks the root export surface. Keep code examples valid under
|
|
14
|
+
`scripts/validate_docs.py` in the OpenHCS superproject and verify signatures
|
|
15
|
+
against `src/arraybridge/` when public APIs change.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Advanced extension topics
|
|
2
|
+
=========================
|
|
3
|
+
|
|
4
|
+
Framework behavior is centralized in the registered conversion and framework
|
|
5
|
+
strategy families. Add a new framework by extending those owning declarations:
|
|
6
|
+
|
|
7
|
+
1. add the ``MemoryType`` member;
|
|
8
|
+
2. provide framework configuration and conversion strategies;
|
|
9
|
+
3. define stack/allocation/device behavior where it differs;
|
|
10
|
+
4. add cleanup and OOM behavior only when the framework supports them;
|
|
11
|
+
5. exercise detection and every supported conversion pair.
|
|
12
|
+
|
|
13
|
+
Do not add a parallel framework-name table in a consumer. Generic consumers use
|
|
14
|
+
``MemoryType``, the converter registry, and framework configuration owned by
|
|
15
|
+
ArrayBridge.
|
|
16
|
+
|
|
17
|
+
Conversion caching and batching belong to the caller because value lifetime and
|
|
18
|
+
semantic identity are application concerns. ArrayBridge functions remain
|
|
19
|
+
stateless at the conversion boundary except for declared thread-local GPU
|
|
20
|
+
context.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
API reference
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
Public imports
|
|
5
|
+
--------------
|
|
6
|
+
|
|
7
|
+
``MemoryType``, ``CPU_MEMORY_TYPES``, ``GPU_MEMORY_TYPES``, ``SUPPORTED_MEMORY_TYPES``
|
|
8
|
+
Nominal framework identities.
|
|
9
|
+
|
|
10
|
+
``detect_memory_type`` and ``convert_memory``
|
|
11
|
+
Detection and explicit conversion.
|
|
12
|
+
|
|
13
|
+
``memory_types``, ``numpy``, ``cupy``, ``torch``, ``tensorflow``, ``jax``, ``pyclesperanto``
|
|
14
|
+
Callable memory declarations and wrappers.
|
|
15
|
+
|
|
16
|
+
``DtypeConversion``
|
|
17
|
+
Output dtype policy values.
|
|
18
|
+
|
|
19
|
+
``stack_slices``, ``unstack_slices``, ``process_slices``
|
|
20
|
+
Validated plane/stack operations.
|
|
21
|
+
|
|
22
|
+
``cleanup_all_gpu_frameworks``
|
|
23
|
+
Installed-framework cache cleanup.
|
|
24
|
+
|
|
25
|
+
``MemoryConversionError``
|
|
26
|
+
Conversion failure boundary.
|
|
27
|
+
|
|
28
|
+
The canonical export list is ``arraybridge.__all__``. Names beginning with an
|
|
29
|
+
underscore are implementation surfaces even when temporarily re-exported for a
|
|
30
|
+
host migration.
|
|
31
|
+
|
|
32
|
+
Documented public surface
|
|
33
|
+
-------------------------
|
|
34
|
+
|
|
35
|
+
.. automodule:: arraybridge
|
|
36
|
+
:members:
|
|
37
|
+
:exclude-members: _FRAMEWORK_CONFIG, _FRAMEWORK_OPS, _ensure_module, _execute_with_oom_recovery, _get_device_id, _supports_dlpack
|
|
38
|
+
:member-order: bysource
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Continuous integration
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
The repository workflows are authoritative. The main test matrix currently
|
|
5
|
+
covers Python 3.10 through 3.13 on Linux, Windows, and macOS. Separate jobs
|
|
6
|
+
exercise optional GPU paths and code quality.
|
|
7
|
+
|
|
8
|
+
Before opening a change, run:
|
|
9
|
+
|
|
10
|
+
.. code-block:: bash
|
|
11
|
+
|
|
12
|
+
python -m pytest
|
|
13
|
+
ruff check src tests
|
|
14
|
+
black --check src tests
|
|
15
|
+
mypy src --ignore-missing-imports
|
|
16
|
+
|
|
17
|
+
Documentation changes should also build with warnings as errors:
|
|
18
|
+
|
|
19
|
+
.. code-block:: bash
|
|
20
|
+
|
|
21
|
+
python -m sphinx -E -W --keep-going -b html docs/source docs/source/_build/html
|
|
22
|
+
|
|
23
|
+
See the current `GitHub Actions workflows
|
|
24
|
+
<https://github.com/OpenHCSDev/arraybridge/actions>`_ instead of copying a
|
|
25
|
+
workflow matrix into downstream documentation.
|
|
@@ -5,7 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
PACKAGE_ROOT = Path(__file__).resolve().parents[2]
|
|
11
|
+
EXTERNAL_ROOT = PACKAGE_ROOT.parent
|
|
12
|
+
|
|
13
|
+
# Prefer the sibling owner source when ArrayBridge is documented from the
|
|
14
|
+
# OpenHCS checkout; an installed metaclass-registry remains the standalone
|
|
15
|
+
# fallback when that sibling is absent.
|
|
16
|
+
sys.path.insert(0, str(EXTERNAL_ROOT / "metaclass-registry" / "src"))
|
|
17
|
+
sys.path.insert(0, str(PACKAGE_ROOT / "src"))
|
|
9
18
|
|
|
10
19
|
# -- Project information -----------------------------------------------------
|
|
11
20
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
@@ -13,7 +22,7 @@ sys.path.insert(0, os.path.abspath('../../src'))
|
|
|
13
22
|
project = 'arraybridge'
|
|
14
23
|
copyright = '2025, Tristan Simas'
|
|
15
24
|
author = 'Tristan Simas'
|
|
16
|
-
release = '0.
|
|
25
|
+
release = '0.2.11'
|
|
17
26
|
|
|
18
27
|
# -- General configuration ---------------------------------------------------
|
|
19
28
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
@@ -29,13 +38,13 @@ extensions = [
|
|
|
29
38
|
]
|
|
30
39
|
|
|
31
40
|
templates_path = ['_templates']
|
|
32
|
-
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
41
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'api/**']
|
|
33
42
|
|
|
34
43
|
# -- Options for HTML output -------------------------------------------------
|
|
35
44
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
36
45
|
|
|
37
46
|
html_theme = 'sphinx_rtd_theme'
|
|
38
|
-
html_static_path = [
|
|
47
|
+
html_static_path = []
|
|
39
48
|
|
|
40
49
|
# -- Extension configuration -------------------------------------------------
|
|
41
50
|
|
|
@@ -114,7 +123,6 @@ html_theme_options = {
|
|
|
114
123
|
'sticky_navigation': True,
|
|
115
124
|
'includehidden': True,
|
|
116
125
|
'titles_only': False,
|
|
117
|
-
'display_version': True,
|
|
118
126
|
'prev_next_buttons_location': 'both',
|
|
119
127
|
'style_external_links': False,
|
|
120
128
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Contributing
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
Install the editable development environment and run the same core checks used
|
|
5
|
+
by the repository:
|
|
6
|
+
|
|
7
|
+
.. code-block:: bash
|
|
8
|
+
|
|
9
|
+
python -m pip install -e ".[dev,docs]"
|
|
10
|
+
ruff check src tests
|
|
11
|
+
black --check src tests
|
|
12
|
+
mypy src --ignore-missing-imports
|
|
13
|
+
python -m pytest
|
|
14
|
+
python -m sphinx -E -W --keep-going -b html docs/source docs/source/_build/html
|
|
15
|
+
|
|
16
|
+
Framework extensions
|
|
17
|
+
--------------------
|
|
18
|
+
|
|
19
|
+
A framework extension must update the nominal ``MemoryType`` declaration and
|
|
20
|
+
the owning conversion/framework strategies. Add detection tests, conversion
|
|
21
|
+
pair tests, stack tests, and optional-dependency behavior. Consumers should not
|
|
22
|
+
maintain a second framework-name registry.
|
|
23
|
+
|
|
24
|
+
See :doc:`advanced_topics` for the ownership model and the repository's
|
|
25
|
+
`contribution guide
|
|
26
|
+
<https://github.com/OpenHCSDev/arraybridge/blob/main/CONTRIBUTING.md>`_ for the
|
|
27
|
+
review workflow.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Conversion system
|
|
2
|
+
=================
|
|
3
|
+
|
|
4
|
+
``convert_memory(data, source_type, target_type, gpu_id)`` selects the converter
|
|
5
|
+
registered for ``source_type`` and invokes its target method.
|
|
6
|
+
|
|
7
|
+
.. code-block:: python
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from arraybridge import convert_memory
|
|
12
|
+
|
|
13
|
+
value = np.arange(4)
|
|
14
|
+
result = convert_memory(value, "numpy", "numpy", 0)
|
|
15
|
+
|
|
16
|
+
Call ``detect_memory_type`` when the source type is not already known. Passing a
|
|
17
|
+
wrong source declaration is a caller error; conversion planning should keep the
|
|
18
|
+
declared type aligned with the actual value.
|
|
19
|
+
|
|
20
|
+
Device semantics
|
|
21
|
+
----------------
|
|
22
|
+
|
|
23
|
+
``gpu_id`` is required. GPU converters use it for allocation or device
|
|
24
|
+
selection. CPU conversion accepts the same argument for a uniform API. Moving a
|
|
25
|
+
value between two devices is expressed as conversion with the target device id.
|
|
26
|
+
|
|
27
|
+
Converters prefer framework-native or DLPack paths when the registered strategy
|
|
28
|
+
supports them and otherwise use the strategy's explicit fallback. No zero-copy
|
|
29
|
+
guarantee applies to every pair.
|
|
30
|
+
|
|
31
|
+
Failures
|
|
32
|
+
--------
|
|
33
|
+
|
|
34
|
+
Invalid framework names raise ``ValueError``. Conversion failures raise
|
|
35
|
+
``MemoryConversionError`` with source/target context. Optional frameworks are
|
|
36
|
+
loaded only when their converter is used.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Memory declaration decorators
|
|
2
|
+
=============================
|
|
3
|
+
|
|
4
|
+
``memory_types`` and the six framework helpers declare callable memory
|
|
5
|
+
contracts.
|
|
6
|
+
|
|
7
|
+
.. code-block:: python
|
|
8
|
+
|
|
9
|
+
from arraybridge import memory_types, numpy
|
|
10
|
+
|
|
11
|
+
@memory_types(input_type="numpy", output_type="numpy")
|
|
12
|
+
def identity(image):
|
|
13
|
+
return image
|
|
14
|
+
|
|
15
|
+
@numpy(oom_recovery=False)
|
|
16
|
+
def offset(image, value=1):
|
|
17
|
+
return image + value
|
|
18
|
+
|
|
19
|
+
Framework helpers accept ``input_type``, ``output_type``, ``oom_recovery``, and
|
|
20
|
+
``contract``. They may be used as ``@numpy`` or ``@numpy()``.
|
|
21
|
+
|
|
22
|
+
Direct-call behavior
|
|
23
|
+
--------------------
|
|
24
|
+
|
|
25
|
+
Decorators do not convert the input or output between frameworks. They attach
|
|
26
|
+
metadata, add keyword-only ``slice_by_slice`` and ``dtype_config`` runtime
|
|
27
|
+
parameters, apply the selected dtype policy, and wrap supported GPU frameworks
|
|
28
|
+
with thread-local stream/OOM handling.
|
|
29
|
+
|
|
30
|
+
The decorator does not accept ``gpu_id`` or ``clear_cuda_cache``. Device
|
|
31
|
+
selection belongs to explicit conversion or the host runtime. A non-callable
|
|
32
|
+
``contract`` is stored as declarative metadata; a callable contract validates
|
|
33
|
+
the returned value.
|
|
34
|
+
|
|
35
|
+
Dtype policy
|
|
36
|
+
------------
|
|
37
|
+
|
|
38
|
+
Direct calls default to preserving the input dtype. Hosts can pass an object
|
|
39
|
+
implementing ``DtypeConversionConfig`` to select native or explicit output dtype
|
|
40
|
+
behavior.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Basic conversion
|
|
2
|
+
================
|
|
3
|
+
|
|
4
|
+
.. code-block:: python
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from arraybridge import convert_memory, detect_memory_type
|
|
9
|
+
|
|
10
|
+
source = np.arange(9).reshape(3, 3)
|
|
11
|
+
source_type = detect_memory_type(source)
|
|
12
|
+
result = convert_memory(source, source_type, "numpy", 0)
|
|
13
|
+
|
|
14
|
+
For an optional target such as ``torch`` or ``cupy``, install that framework and
|
|
15
|
+
replace the target string. The returned value is allocated according to the
|
|
16
|
+
registered converter and requested device.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Decorator declarations
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
.. code-block:: python
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from arraybridge import numpy
|
|
9
|
+
|
|
10
|
+
@numpy
|
|
11
|
+
def threshold(image, *, cutoff=0.5):
|
|
12
|
+
return image > cutoff
|
|
13
|
+
|
|
14
|
+
value = threshold(np.array([0.25, 0.75]))
|
|
15
|
+
assert threshold.input_memory_type == "numpy"
|
|
16
|
+
|
|
17
|
+
The caller supplies the NumPy value. A compiler or orchestration layer may use
|
|
18
|
+
the metadata to plan conversion before calling ``threshold``.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Multi-framework boundary
|
|
2
|
+
========================
|
|
3
|
+
|
|
4
|
+
Keep conversion visible at the point that owns the boundary:
|
|
5
|
+
|
|
6
|
+
.. code-block:: python
|
|
7
|
+
|
|
8
|
+
from arraybridge import convert_memory, detect_memory_type
|
|
9
|
+
|
|
10
|
+
def as_framework(value, target, gpu_id):
|
|
11
|
+
source = detect_memory_type(value)
|
|
12
|
+
if source == target:
|
|
13
|
+
return value
|
|
14
|
+
return convert_memory(value, source, target, gpu_id)
|
|
15
|
+
|
|
16
|
+
For a sequence of framework-specific functions, a host compiler should read
|
|
17
|
+
their declared input/output memory types and create one conversion plan. The
|
|
18
|
+
functions themselves should not each guess the incoming framework.
|