cunumpy 0.1.1__tar.gz → 0.1.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.
- {cunumpy-0.1.1 → cunumpy-0.1.3}/PKG-INFO +27 -14
- cunumpy-0.1.3/README.md +55 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/pyproject.toml +4 -1
- cunumpy-0.1.3/src/cunumpy/__init__.py +49 -0
- cunumpy-0.1.3/src/cunumpy/__init__.pyi +27 -0
- cunumpy-0.1.3/src/cunumpy/xp.py +210 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy.egg-info/PKG-INFO +27 -14
- cunumpy-0.1.1/README.md +0 -42
- cunumpy-0.1.1/src/cunumpy/__init__.py +0 -9
- cunumpy-0.1.1/src/cunumpy/__init__.pyi +0 -7
- cunumpy-0.1.1/src/cunumpy/xp.py +0 -64
- {cunumpy-0.1.1 → cunumpy-0.1.3}/setup.cfg +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy/main.py +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy/py.typed +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy.egg-info/SOURCES.txt +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy.egg-info/dependency_links.txt +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy.egg-info/requires.txt +0 -0
- {cunumpy-0.1.1 → cunumpy-0.1.3}/src/cunumpy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cunumpy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.
|
|
5
5
|
Author: Max
|
|
6
6
|
Project-URL: Source, https://github.com/max-models/cunumpy
|
|
@@ -40,18 +40,8 @@ Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cun
|
|
|
40
40
|
|
|
41
41
|
# Install
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
python -m venv env
|
|
47
|
-
source env/bin/activate
|
|
48
|
-
pip install --upgrade pip
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Install the code and requirements with pip
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
pip install -e .
|
|
43
|
+
```bash
|
|
44
|
+
pip install cunumpy
|
|
55
45
|
```
|
|
56
46
|
|
|
57
47
|
Example usage:
|
|
@@ -62,10 +52,33 @@ export ARRAY_BACKEND=cupy
|
|
|
62
52
|
|
|
63
53
|
```python
|
|
64
54
|
import cunumpy as xp
|
|
65
|
-
|
|
55
|
+
|
|
56
|
+
arr = xp.array([1, 2])
|
|
66
57
|
|
|
67
58
|
print(type(arr))
|
|
68
59
|
print(xp.__version__)
|
|
60
|
+
|
|
61
|
+
# Convert to NumPy
|
|
62
|
+
arr_np = xp.to_numpy(arr)
|
|
63
|
+
|
|
64
|
+
# Convert to active backend
|
|
65
|
+
arr_xp = xp.to_cunumpy(arr)
|
|
66
|
+
|
|
67
|
+
# Inspect backend
|
|
68
|
+
print(xp.get_backend(arr))
|
|
69
|
+
print(xp.is_gpu(arr))
|
|
70
|
+
print(xp.is_cpu(arr))
|
|
71
|
+
|
|
72
|
+
# Temporarily switch backend
|
|
73
|
+
with xp.use_backend("numpy"):
|
|
74
|
+
# This code runs on CPU even if ARRAY_BACKEND=cupy
|
|
75
|
+
arr_cpu = xp.zeros(100)
|
|
76
|
+
|
|
77
|
+
# Set backend globally
|
|
78
|
+
xp.set_backend("cupy")
|
|
79
|
+
|
|
80
|
+
# Synchronize GPU operations (no-op on CPU)
|
|
81
|
+
xp.synchronize()
|
|
69
82
|
```
|
|
70
83
|
|
|
71
84
|
# Build docs
|
cunumpy-0.1.3/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CuNumpy
|
|
2
|
+
|
|
3
|
+
Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.
|
|
4
|
+
|
|
5
|
+
# Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cunumpy
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Example usage:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
export ARRAY_BACKEND=cupy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import cunumpy as xp
|
|
19
|
+
|
|
20
|
+
arr = xp.array([1, 2])
|
|
21
|
+
|
|
22
|
+
print(type(arr))
|
|
23
|
+
print(xp.__version__)
|
|
24
|
+
|
|
25
|
+
# Convert to NumPy
|
|
26
|
+
arr_np = xp.to_numpy(arr)
|
|
27
|
+
|
|
28
|
+
# Convert to active backend
|
|
29
|
+
arr_xp = xp.to_cunumpy(arr)
|
|
30
|
+
|
|
31
|
+
# Inspect backend
|
|
32
|
+
print(xp.get_backend(arr))
|
|
33
|
+
print(xp.is_gpu(arr))
|
|
34
|
+
print(xp.is_cpu(arr))
|
|
35
|
+
|
|
36
|
+
# Temporarily switch backend
|
|
37
|
+
with xp.use_backend("numpy"):
|
|
38
|
+
# This code runs on CPU even if ARRAY_BACKEND=cupy
|
|
39
|
+
arr_cpu = xp.zeros(100)
|
|
40
|
+
|
|
41
|
+
# Set backend globally
|
|
42
|
+
xp.set_backend("cupy")
|
|
43
|
+
|
|
44
|
+
# Synchronize GPU operations (no-op on CPU)
|
|
45
|
+
xp.synchronize()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
# Build docs
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
make html
|
|
53
|
+
cd ../
|
|
54
|
+
open docs/_build/html/index.html
|
|
55
|
+
```
|
|
@@ -5,7 +5,7 @@ requires = [ "setuptools", "wheel" ]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "cunumpy"
|
|
8
|
-
version = "0.1.
|
|
8
|
+
version = "0.1.3"
|
|
9
9
|
description = "Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = [ "python" ]
|
|
@@ -51,3 +51,6 @@ where = [ "src" ]
|
|
|
51
51
|
|
|
52
52
|
[tool.setuptools.package-data]
|
|
53
53
|
cunumpy = [ "py.typed", "*.pyi" ]
|
|
54
|
+
|
|
55
|
+
[tool.isort]
|
|
56
|
+
profile = "black"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# cunumpy/__init__.py
|
|
2
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
3
|
+
|
|
4
|
+
from . import xp
|
|
5
|
+
from .xp import (
|
|
6
|
+
cupy_available,
|
|
7
|
+
get_backend,
|
|
8
|
+
is_cpu,
|
|
9
|
+
is_gpu,
|
|
10
|
+
set_backend,
|
|
11
|
+
set_device,
|
|
12
|
+
synchronize,
|
|
13
|
+
to_cunumpy,
|
|
14
|
+
to_cupy,
|
|
15
|
+
to_numpy,
|
|
16
|
+
use_backend,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
__version__ = version("cunumpy")
|
|
21
|
+
except PackageNotFoundError:
|
|
22
|
+
__version__ = "0.0.0+unknown"
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"__version__",
|
|
26
|
+
"cupy_available",
|
|
27
|
+
"cupy_backend",
|
|
28
|
+
"get_backend",
|
|
29
|
+
"is_cpu",
|
|
30
|
+
"is_gpu",
|
|
31
|
+
"numpy_backend",
|
|
32
|
+
"set_backend",
|
|
33
|
+
"set_device",
|
|
34
|
+
"synchronize",
|
|
35
|
+
"to_cunumpy",
|
|
36
|
+
"to_cupy",
|
|
37
|
+
"to_numpy",
|
|
38
|
+
"use_backend",
|
|
39
|
+
"xp",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __getattr__(name: str):
|
|
44
|
+
"""Set cunumpy.<name> to cunumpy.xp.<name> (NumPy/CuPy)."""
|
|
45
|
+
if name == "numpy_backend":
|
|
46
|
+
return xp.numpy_backend
|
|
47
|
+
if name == "cupy_backend":
|
|
48
|
+
return xp.cupy_backend
|
|
49
|
+
return getattr(xp.xp, name)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Stub file for Pylance/mypy: exposes all numpy symbols so that
|
|
2
|
+
# `import cunumpy as xp` followed by `xp.<Tab>` shows numpy completions.
|
|
3
|
+
# At runtime the real __init__.py dispatches to numpy or cupy via __getattr__.
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Any, Generator
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from numpy import *
|
|
9
|
+
|
|
10
|
+
from . import xp as xp
|
|
11
|
+
|
|
12
|
+
def to_numpy(array: Any) -> np.ndarray: ...
|
|
13
|
+
def to_cupy(array: Any) -> Any: ...
|
|
14
|
+
def to_cunumpy(array: Any) -> Any: ...
|
|
15
|
+
def cupy_available() -> bool: ...
|
|
16
|
+
def get_backend(array: Any) -> str: ...
|
|
17
|
+
def is_gpu(array: Any) -> bool: ...
|
|
18
|
+
def is_cpu(array: Any) -> bool: ...
|
|
19
|
+
@contextmanager
|
|
20
|
+
def use_backend(backend: str) -> Generator[None]: ...
|
|
21
|
+
def set_backend(backend: str) -> None: ...
|
|
22
|
+
def set_device(device_id: int) -> None: ...
|
|
23
|
+
def synchronize() -> None: ...
|
|
24
|
+
|
|
25
|
+
numpy_backend: bool
|
|
26
|
+
cupy_backend: bool
|
|
27
|
+
__version__: str
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import warnings
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generator, Literal
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
BackendType = Literal["numpy", "cupy"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_CUPY_AVAILABLE_CACHE = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cupy_available() -> bool:
|
|
16
|
+
"""Check if CuPy is available and functional."""
|
|
17
|
+
global _CUPY_AVAILABLE_CACHE
|
|
18
|
+
if _CUPY_AVAILABLE_CACHE is not None:
|
|
19
|
+
return _CUPY_AVAILABLE_CACHE
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import cupy as cp
|
|
23
|
+
|
|
24
|
+
# Check if a GPU is available
|
|
25
|
+
_CUPY_AVAILABLE_CACHE = cp.is_available()
|
|
26
|
+
return _CUPY_AVAILABLE_CACHE
|
|
27
|
+
except Exception: # noqa: BLE001 - tolerate any driver/runtime failure
|
|
28
|
+
_CUPY_AVAILABLE_CACHE = False
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ArrayBackend:
|
|
33
|
+
"""Holds the process-wide active backend (NumPy or CuPy).
|
|
34
|
+
|
|
35
|
+
Not thread-safe: `set_backend`/`use_backend` mutate this single shared
|
|
36
|
+
instance in place, so concurrent code (threads, async tasks) switching
|
|
37
|
+
backends independently will race. Safe for the typical single-threaded
|
|
38
|
+
script/notebook usage this library targets.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
backend: BackendType = "numpy",
|
|
44
|
+
verbose: bool = False,
|
|
45
|
+
) -> None:
|
|
46
|
+
if backend.lower() not in ("numpy", "cupy"):
|
|
47
|
+
raise ValueError("Array backend must be either 'numpy' or 'cupy'.")
|
|
48
|
+
|
|
49
|
+
self._backend: BackendType = "cupy" if backend.lower() == "cupy" else "numpy"
|
|
50
|
+
self._xp: ModuleType = np # Placeholder
|
|
51
|
+
|
|
52
|
+
# Import numpy/cupy
|
|
53
|
+
self._xp = self._load_backend(self._backend, verbose)
|
|
54
|
+
|
|
55
|
+
def _load_backend(self, backend: BackendType, verbose: bool = False) -> ModuleType:
|
|
56
|
+
if backend == "cupy":
|
|
57
|
+
if cupy_available():
|
|
58
|
+
import cupy as cp
|
|
59
|
+
|
|
60
|
+
self._backend = "cupy"
|
|
61
|
+
return cp
|
|
62
|
+
else:
|
|
63
|
+
if verbose:
|
|
64
|
+
print(
|
|
65
|
+
"CuPy not available or not functional. Falling back to NumPy."
|
|
66
|
+
)
|
|
67
|
+
self._backend = "numpy"
|
|
68
|
+
return np
|
|
69
|
+
import numpy as np_mod
|
|
70
|
+
|
|
71
|
+
self._backend = "numpy"
|
|
72
|
+
return np_mod
|
|
73
|
+
|
|
74
|
+
def __repr__(self) -> str:
|
|
75
|
+
return f"ArrayBackend(backend={self._backend!r}, module={self._xp.__name__!r})"
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def backend(self) -> BackendType:
|
|
79
|
+
return self._backend
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def xp(self) -> ModuleType:
|
|
83
|
+
return self._xp
|
|
84
|
+
|
|
85
|
+
@contextmanager
|
|
86
|
+
def use_backend(self, backend: BackendType) -> Generator[None, None, None]:
|
|
87
|
+
"""Temporarily change the backend."""
|
|
88
|
+
old_backend = self._backend
|
|
89
|
+
old_xp = self._xp
|
|
90
|
+
|
|
91
|
+
self._backend = backend
|
|
92
|
+
self._xp = self._load_backend(backend)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
yield
|
|
96
|
+
finally:
|
|
97
|
+
self._backend = old_backend
|
|
98
|
+
self._xp = old_xp
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
array_backend = ArrayBackend(
|
|
102
|
+
backend=(
|
|
103
|
+
"cupy" if os.getenv("ARRAY_BACKEND", "numpy").lower() == "cupy" else "numpy"
|
|
104
|
+
),
|
|
105
|
+
verbose=False,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def use_backend(backend: BackendType) -> Generator[None, None, None]:
|
|
110
|
+
"""Temporarily change the backend."""
|
|
111
|
+
return array_backend.use_backend(backend)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def set_backend(backend: BackendType) -> None:
|
|
115
|
+
"""Set the backend globally."""
|
|
116
|
+
array_backend._backend = backend
|
|
117
|
+
array_backend._xp = array_backend._load_backend(backend)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _cupy_backend() -> bool:
|
|
121
|
+
"""Check if the active global backend is CuPy."""
|
|
122
|
+
return array_backend.backend == "cupy"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _numpy_backend() -> bool:
|
|
126
|
+
"""Check if the active global backend is NumPy."""
|
|
127
|
+
return array_backend.backend == "numpy"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def set_device(device_id: int) -> None:
|
|
131
|
+
"""Select the active CUDA device for the current process (no-op on NumPy)."""
|
|
132
|
+
if array_backend.backend == "cupy":
|
|
133
|
+
import cupy as cp
|
|
134
|
+
|
|
135
|
+
cp.cuda.Device(device_id).use()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def synchronize() -> None:
|
|
139
|
+
"""Wait for all kernels in all streams on current device to complete."""
|
|
140
|
+
if array_backend.backend == "cupy":
|
|
141
|
+
try:
|
|
142
|
+
import cupy as cp
|
|
143
|
+
|
|
144
|
+
cp.cuda.Device().synchronize()
|
|
145
|
+
except ImportError:
|
|
146
|
+
pass
|
|
147
|
+
except AttributeError as e:
|
|
148
|
+
warnings.warn(
|
|
149
|
+
f"CuPy synchronize() failed unexpectedly, this may indicate a "
|
|
150
|
+
f"CuPy API mismatch: {e}",
|
|
151
|
+
RuntimeWarning,
|
|
152
|
+
stacklevel=2,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def to_numpy(array: Any) -> np.ndarray:
|
|
157
|
+
"""Convert an array to a NumPy array."""
|
|
158
|
+
if get_backend(array) == "cupy":
|
|
159
|
+
return array.get()
|
|
160
|
+
|
|
161
|
+
return np.asarray(array)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def to_cupy(array: Any) -> Any:
|
|
165
|
+
"""Convert an array to a CuPy array."""
|
|
166
|
+
if not cupy_available():
|
|
167
|
+
raise ImportError("CuPy is not available or not functional.")
|
|
168
|
+
|
|
169
|
+
import cupy as cp
|
|
170
|
+
|
|
171
|
+
return cp.asarray(array)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def to_cunumpy(array: Any) -> Any:
|
|
175
|
+
"""Convert an array to the currently active backend."""
|
|
176
|
+
if array_backend.backend == "cupy" and cupy_available():
|
|
177
|
+
return to_cupy(array)
|
|
178
|
+
return to_numpy(array)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_backend(array: Any) -> BackendType:
|
|
182
|
+
"""Return 'cupy' or 'numpy' depending on the array type."""
|
|
183
|
+
module = getattr(type(array), "__module__", "")
|
|
184
|
+
return "cupy" if "cupy" in module else "numpy"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def is_gpu(array: Any) -> bool:
|
|
188
|
+
"""Check if the array is stored on a GPU (CuPy)."""
|
|
189
|
+
return get_backend(array) == "cupy"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def is_cpu(array: Any) -> bool:
|
|
193
|
+
"""Check if the array is stored on a CPU (NumPy)."""
|
|
194
|
+
return get_backend(array) == "numpy"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
|
|
198
|
+
# This allows us to use autocompletion for xp (i.e., numpy/cupy) as if numpy was imported.
|
|
199
|
+
if TYPE_CHECKING:
|
|
200
|
+
import numpy as xp # noqa: F401 - type-checker-only alias for autocompletion
|
|
201
|
+
else:
|
|
202
|
+
# Use module-level __getattr__ for dynamic xp (Python 3.7+)
|
|
203
|
+
def __getattr__(name):
|
|
204
|
+
if name == "xp":
|
|
205
|
+
return array_backend.xp
|
|
206
|
+
if name == "numpy_backend":
|
|
207
|
+
return _numpy_backend()
|
|
208
|
+
if name == "cupy_backend":
|
|
209
|
+
return _cupy_backend()
|
|
210
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cunumpy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.
|
|
5
5
|
Author: Max
|
|
6
6
|
Project-URL: Source, https://github.com/max-models/cunumpy
|
|
@@ -40,18 +40,8 @@ Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cun
|
|
|
40
40
|
|
|
41
41
|
# Install
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
python -m venv env
|
|
47
|
-
source env/bin/activate
|
|
48
|
-
pip install --upgrade pip
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Install the code and requirements with pip
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
pip install -e .
|
|
43
|
+
```bash
|
|
44
|
+
pip install cunumpy
|
|
55
45
|
```
|
|
56
46
|
|
|
57
47
|
Example usage:
|
|
@@ -62,10 +52,33 @@ export ARRAY_BACKEND=cupy
|
|
|
62
52
|
|
|
63
53
|
```python
|
|
64
54
|
import cunumpy as xp
|
|
65
|
-
|
|
55
|
+
|
|
56
|
+
arr = xp.array([1, 2])
|
|
66
57
|
|
|
67
58
|
print(type(arr))
|
|
68
59
|
print(xp.__version__)
|
|
60
|
+
|
|
61
|
+
# Convert to NumPy
|
|
62
|
+
arr_np = xp.to_numpy(arr)
|
|
63
|
+
|
|
64
|
+
# Convert to active backend
|
|
65
|
+
arr_xp = xp.to_cunumpy(arr)
|
|
66
|
+
|
|
67
|
+
# Inspect backend
|
|
68
|
+
print(xp.get_backend(arr))
|
|
69
|
+
print(xp.is_gpu(arr))
|
|
70
|
+
print(xp.is_cpu(arr))
|
|
71
|
+
|
|
72
|
+
# Temporarily switch backend
|
|
73
|
+
with xp.use_backend("numpy"):
|
|
74
|
+
# This code runs on CPU even if ARRAY_BACKEND=cupy
|
|
75
|
+
arr_cpu = xp.zeros(100)
|
|
76
|
+
|
|
77
|
+
# Set backend globally
|
|
78
|
+
xp.set_backend("cupy")
|
|
79
|
+
|
|
80
|
+
# Synchronize GPU operations (no-op on CPU)
|
|
81
|
+
xp.synchronize()
|
|
69
82
|
```
|
|
70
83
|
|
|
71
84
|
# Build docs
|
cunumpy-0.1.1/README.md
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# CuNumpy
|
|
2
|
-
|
|
3
|
-
Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.
|
|
4
|
-
|
|
5
|
-
# Install
|
|
6
|
-
|
|
7
|
-
Create and activate python environment
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
python -m venv env
|
|
11
|
-
source env/bin/activate
|
|
12
|
-
pip install --upgrade pip
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Install the code and requirements with pip
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
pip install -e .
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Example usage:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
export ARRAY_BACKEND=cupy
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
import cunumpy as xp
|
|
29
|
-
arr = xp.array([1,2])
|
|
30
|
-
|
|
31
|
-
print(type(arr))
|
|
32
|
-
print(xp.__version__)
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
# Build docs
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
make html
|
|
40
|
-
cd ../
|
|
41
|
-
open docs/_build/html/index.html
|
|
42
|
-
```
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Stub file for Pylance/mypy: exposes all numpy symbols so that
|
|
2
|
-
# `import cunumpy as xp` followed by `xp.<Tab>` shows numpy completions.
|
|
3
|
-
# At runtime the real __init__.py dispatches to numpy or cupy via __getattr__.
|
|
4
|
-
from numpy import *
|
|
5
|
-
from numpy import __config__, __version__
|
|
6
|
-
|
|
7
|
-
from . import xp
|
cunumpy-0.1.1/src/cunumpy/xp.py
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from types import ModuleType
|
|
3
|
-
from typing import TYPE_CHECKING, Literal
|
|
4
|
-
|
|
5
|
-
BackendType = Literal["numpy", "cupy"]
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ArrayBackend:
|
|
9
|
-
def __init__(
|
|
10
|
-
self,
|
|
11
|
-
backend: BackendType = "numpy",
|
|
12
|
-
verbose: bool = False,
|
|
13
|
-
) -> None:
|
|
14
|
-
assert backend.lower() in [
|
|
15
|
-
"numpy",
|
|
16
|
-
"cupy",
|
|
17
|
-
], "Array backend must be either 'numpy' or 'cupy'."
|
|
18
|
-
|
|
19
|
-
self._backend: BackendType = "cupy" if backend.lower() == "cupy" else "numpy"
|
|
20
|
-
|
|
21
|
-
# Import numpy/cupy
|
|
22
|
-
if self.backend == "cupy":
|
|
23
|
-
try:
|
|
24
|
-
import cupy as cp
|
|
25
|
-
|
|
26
|
-
self._xp = cp
|
|
27
|
-
except ImportError:
|
|
28
|
-
if verbose:
|
|
29
|
-
print("CuPy not available.")
|
|
30
|
-
self._backend = "numpy"
|
|
31
|
-
|
|
32
|
-
if self.backend == "numpy":
|
|
33
|
-
import numpy as np
|
|
34
|
-
|
|
35
|
-
self._xp = np
|
|
36
|
-
|
|
37
|
-
assert isinstance(self.xp, ModuleType)
|
|
38
|
-
|
|
39
|
-
if verbose:
|
|
40
|
-
print(f"Using {self.xp.__name__} backend.")
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def backend(self) -> BackendType:
|
|
44
|
-
return self._backend
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def xp(self) -> ModuleType:
|
|
48
|
-
return self._xp
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# TODO: Make this configurable via environment variable or config file.
|
|
52
|
-
array_backend = ArrayBackend(
|
|
53
|
-
backend=(
|
|
54
|
-
"cupy" if os.getenv("ARRAY_BACKEND", "numpy").lower() == "cupy" else "numpy"
|
|
55
|
-
),
|
|
56
|
-
verbose=False,
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
|
|
60
|
-
# This allows us to use autocompletion for xp (i.e., numpy/cupy) as if numpy was imported.
|
|
61
|
-
if TYPE_CHECKING:
|
|
62
|
-
import numpy as xp
|
|
63
|
-
else:
|
|
64
|
-
xp = array_backend.xp
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|