cunumpy 0.6.1__tar.gz → 0.6.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.
Files changed (70) hide show
  1. cunumpy-0.6.3/LICENSE.txt +21 -0
  2. {cunumpy-0.6.1 → cunumpy-0.6.3}/PKG-INFO +40 -8
  3. {cunumpy-0.6.1 → cunumpy-0.6.3}/README.md +12 -7
  4. {cunumpy-0.6.1 → cunumpy-0.6.3}/pyproject.toml +29 -2
  5. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/LLM_GUIDE.md +24 -4
  6. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/__init__.py +50 -18
  7. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/__init__.pyi +12 -1
  8. cunumpy-0.6.3/src/cunumpy/_algorithms.py +512 -0
  9. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_cuda_kernel.py +802 -592
  10. cunumpy-0.6.3/src/cunumpy/_device.py +435 -0
  11. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_dispatch.py +337 -225
  12. cunumpy-0.6.3/src/cunumpy/_emulation.py +947 -0
  13. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_fake_cupy.py +32 -3
  14. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_fake_cupy_impl.py +15 -0
  15. cunumpy-0.6.3/src/cunumpy/_fake_cupyx.py +381 -0
  16. cunumpy-0.6.3/src/cunumpy/_fusion.py +105 -0
  17. cunumpy-0.6.3/src/cunumpy/_host.py +140 -0
  18. cunumpy-0.6.3/src/cunumpy/_kernel.py +1174 -0
  19. cunumpy-0.6.3/src/cunumpy/_linalg.py +103 -0
  20. cunumpy-0.6.3/src/cunumpy/_metal_kernel.py +279 -0
  21. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_mirror.py +54 -48
  22. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_morton.py +101 -34
  23. cunumpy-0.6.3/src/cunumpy/_mpi.py +582 -0
  24. cunumpy-0.6.3/src/cunumpy/_optimize.py +162 -0
  25. cunumpy-0.6.3/src/cunumpy/_philox.py +242 -0
  26. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_profiling.py +44 -39
  27. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_random_streams.py +142 -46
  28. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_scipy_backend.py +63 -23
  29. cunumpy-0.6.3/src/cunumpy/_scipy_host.py +370 -0
  30. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/_staging.py +195 -48
  31. cunumpy-0.6.3/src/cunumpy/_streams.py +227 -0
  32. cunumpy-0.6.3/src/cunumpy/_transfers.py +667 -0
  33. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/algorithms.py +8 -3
  34. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/arguments.py +1 -4
  35. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/__init__.py +11 -16
  36. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/array_view.cuh +11 -1
  37. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/reduce.cuh +6 -0
  38. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/scan.cuh +1 -0
  39. cunumpy-0.6.3/src/cunumpy/integrate.py +10 -0
  40. cunumpy-0.6.3/src/cunumpy/kernel_testing.py +1154 -0
  41. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/kernels.py +15 -7
  42. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/memory.py +3 -5
  43. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/mpi.py +9 -9
  44. cunumpy-0.6.3/src/cunumpy/optimize.py +19 -0
  45. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/petsc.py +18 -18
  46. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/profiling.py +6 -3
  47. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/rng.py +4 -6
  48. cunumpy-0.6.3/src/cunumpy/xp.py +878 -0
  49. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy.egg-info/PKG-INFO +40 -8
  50. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy.egg-info/SOURCES.txt +9 -0
  51. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy.egg-info/requires.txt +6 -0
  52. cunumpy-0.6.1/src/cunumpy/_algorithms.py +0 -253
  53. cunumpy-0.6.1/src/cunumpy/_device.py +0 -260
  54. cunumpy-0.6.1/src/cunumpy/_emulation.py +0 -446
  55. cunumpy-0.6.1/src/cunumpy/_fusion.py +0 -115
  56. cunumpy-0.6.1/src/cunumpy/_kernel.py +0 -785
  57. cunumpy-0.6.1/src/cunumpy/_mpi.py +0 -383
  58. cunumpy-0.6.1/src/cunumpy/_philox.py +0 -149
  59. cunumpy-0.6.1/src/cunumpy/_streams.py +0 -97
  60. cunumpy-0.6.1/src/cunumpy/_transfers.py +0 -299
  61. cunumpy-0.6.1/src/cunumpy/kernel_testing.py +0 -637
  62. cunumpy-0.6.1/src/cunumpy/xp.py +0 -494
  63. {cunumpy-0.6.1 → cunumpy-0.6.3}/setup.cfg +0 -0
  64. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/atomic.cuh +0 -0
  65. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/index.cuh +0 -0
  66. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/morton.cuh +0 -0
  67. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/cuda/include/cunumpy/random.cuh +0 -0
  68. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy/py.typed +0 -0
  69. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy.egg-info/dependency_links.txt +0 -0
  70. {cunumpy-0.6.1 → cunumpy-0.6.3}/src/cunumpy.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Max Lindqvist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,8 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cunumpy
3
- Version: 0.6.1
3
+ Version: 0.6.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
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Max Lindqvist
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
6
28
  Project-URL: Source, https://github.com/max-models/cunumpy
7
29
  Keywords: python
8
30
  Classifier: Development Status :: 3 - Alpha
@@ -14,6 +36,7 @@ Classifier: Programming Language :: Python :: 3.13
14
36
  Classifier: Programming Language :: Python :: 3.14
15
37
  Requires-Python: >=3.10
16
38
  Description-Content-Type: text/markdown
39
+ License-File: LICENSE.txt
17
40
  Requires-Dist: array-api-compat
18
41
  Requires-Dist: maybempi>=0.1.2
19
42
  Requires-Dist: numpy
@@ -25,11 +48,14 @@ Requires-Dist: ipykernel; extra == "docs"
25
48
  Requires-Dist: myst-parser; extra == "docs"
26
49
  Requires-Dist: nbconvert; extra == "docs"
27
50
  Requires-Dist: nbsphinx; extra == "docs"
51
+ Requires-Dist: numpydoc; extra == "docs"
28
52
  Requires-Dist: jupyterlab; extra == "docs"
29
53
  Requires-Dist: pre-commit; extra == "docs"
30
54
  Requires-Dist: pyproject-fmt; extra == "docs"
31
55
  Requires-Dist: sphinx; extra == "docs"
32
56
  Requires-Dist: sphinx-book-theme; extra == "docs"
57
+ Provides-Extra: metal
58
+ Requires-Dist: mlx; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "metal"
33
59
  Provides-Extra: test
34
60
  Requires-Dist: coverage; extra == "test"
35
61
  Requires-Dist: pytest; extra == "test"
@@ -37,6 +63,7 @@ Requires-Dist: scipy; extra == "test"
37
63
  Provides-Extra: test-compiled
38
64
  Requires-Dist: cunumpy[test]; extra == "test-compiled"
39
65
  Requires-Dist: pyccel; extra == "test-compiled"
66
+ Dynamic: license-file
40
67
 
41
68
  # CuNumpy
42
69
 
@@ -64,7 +91,7 @@ never hide a NumPy name:
64
91
 
65
92
  | Submodule | Contents |
66
93
  |---|---|
67
- | `xp.kernels` | `Kernel`, `KernelCatalog`, `PyccelKernel`, `CudaKernel`, host implementations, `fuse` |
94
+ | `xp.kernels` | `Kernel`, `KernelCatalog`, `PyccelKernel`, `CudaKernel`, `MetalKernel`, host implementations, `fuse` |
68
95
  | `xp.arguments` | CUDA only: `CudaStruct`, `CudaStructArguments`, `CudaArguments` |
69
96
  | `xp.cuda` | CUDA only: devices, streams, debug mode, CUDA headers |
70
97
  | `xp.rng` | `random_streams`, `get_rng`, `philox_*` |
@@ -76,6 +103,8 @@ never hide a NumPy name:
76
103
  | `cunumpy.kernel_testing` | pytest helpers for host/CUDA kernel pairs |
77
104
 
78
105
  Everything except `xp.cuda`, `xp.arguments` and `CudaKernel` works on both backends.
106
+ `MetalKernel` runs Metal kernels on the GPU of an Apple silicon Mac (MLX, float32, NumPy arrays;
107
+ `pip install 'cunumpy[metal]'`, see [the guide](docs/source/kernels/metal-kernel.md)).
79
108
 
80
109
  ## Install
81
110
 
@@ -337,8 +366,9 @@ positional argument 0 is written, avoiding unnecessary copy-back for
337
366
  read-only inputs. For a keyword call, declare the keyword name, such as
338
367
  `outputs=("out",)`. A wrong declaration can leave GPU output values stale.
339
368
  The wrapper can also traverse arrays nested in lists, tuples, dictionaries,
340
- and selected application objects; see the full [API reference](docs/source/api.md)
341
- for `object_modules`, `is_array`, aliasing, and output declarations.
369
+ and selected application objects; see [Host kernels with GPU
370
+ data](docs/source/kernels/pyccel-kernel.md) for `object_modules`, `is_array`,
371
+ aliasing, and output declarations.
342
372
 
343
373
  ## Write CUDA kernels next to host kernels
344
374
 
@@ -473,8 +503,9 @@ Launches can be 1D to 3D (`n_threads=(nx, ny)`, `block_size=(16, 16)`) or use
473
503
  an explicit `grid`, with dynamic shared memory (`shared_mem`) and a `stream`.
474
504
  C++ function templates are instantiated with `template_args`, and
475
505
  `CudaKernelVariants` caches kernels whose source is generated per variant
476
- (e.g. per dimension and dtype). See the [API reference](docs/source/api.md) for
477
- details.
506
+ (e.g. per dimension and dtype). See [Writing CUDA
507
+ kernels](docs/source/kernels/cuda-kernel.md) and [CUDA
508
+ headers](docs/source/kernels/cuda-headers.md) for details.
478
509
 
479
510
  Kernels run asynchronously, so a CUDA error (an illegal memory access, say)
480
511
  normally surfaces at a later `.get()` or MPI call, far from the kernel that
@@ -561,8 +592,9 @@ published at <https://max-models.github.io/cunumpy/>:
561
592
  * [Worked examples](docs/source/examples/index.md),
562
593
  [best practices](docs/source/best-practices.md),
563
594
  [troubleshooting](docs/source/troubleshooting.md),
564
- [Pyodide](docs/source/pyodide.md) and the
565
- [API reference](docs/source/api.md).
595
+ [Pyodide](docs/source/pyodide.md), the
596
+ [API overview](docs/source/api.md) and the
597
+ [API reference](docs/source/reference/index.md).
566
598
 
567
599
  ### For AI coding assistants
568
600
 
@@ -24,7 +24,7 @@ never hide a NumPy name:
24
24
 
25
25
  | Submodule | Contents |
26
26
  |---|---|
27
- | `xp.kernels` | `Kernel`, `KernelCatalog`, `PyccelKernel`, `CudaKernel`, host implementations, `fuse` |
27
+ | `xp.kernels` | `Kernel`, `KernelCatalog`, `PyccelKernel`, `CudaKernel`, `MetalKernel`, host implementations, `fuse` |
28
28
  | `xp.arguments` | CUDA only: `CudaStruct`, `CudaStructArguments`, `CudaArguments` |
29
29
  | `xp.cuda` | CUDA only: devices, streams, debug mode, CUDA headers |
30
30
  | `xp.rng` | `random_streams`, `get_rng`, `philox_*` |
@@ -36,6 +36,8 @@ never hide a NumPy name:
36
36
  | `cunumpy.kernel_testing` | pytest helpers for host/CUDA kernel pairs |
37
37
 
38
38
  Everything except `xp.cuda`, `xp.arguments` and `CudaKernel` works on both backends.
39
+ `MetalKernel` runs Metal kernels on the GPU of an Apple silicon Mac (MLX, float32, NumPy arrays;
40
+ `pip install 'cunumpy[metal]'`, see [the guide](docs/source/kernels/metal-kernel.md)).
39
41
 
40
42
  ## Install
41
43
 
@@ -297,8 +299,9 @@ positional argument 0 is written, avoiding unnecessary copy-back for
297
299
  read-only inputs. For a keyword call, declare the keyword name, such as
298
300
  `outputs=("out",)`. A wrong declaration can leave GPU output values stale.
299
301
  The wrapper can also traverse arrays nested in lists, tuples, dictionaries,
300
- and selected application objects; see the full [API reference](docs/source/api.md)
301
- for `object_modules`, `is_array`, aliasing, and output declarations.
302
+ and selected application objects; see [Host kernels with GPU
303
+ data](docs/source/kernels/pyccel-kernel.md) for `object_modules`, `is_array`,
304
+ aliasing, and output declarations.
302
305
 
303
306
  ## Write CUDA kernels next to host kernels
304
307
 
@@ -433,8 +436,9 @@ Launches can be 1D to 3D (`n_threads=(nx, ny)`, `block_size=(16, 16)`) or use
433
436
  an explicit `grid`, with dynamic shared memory (`shared_mem`) and a `stream`.
434
437
  C++ function templates are instantiated with `template_args`, and
435
438
  `CudaKernelVariants` caches kernels whose source is generated per variant
436
- (e.g. per dimension and dtype). See the [API reference](docs/source/api.md) for
437
- details.
439
+ (e.g. per dimension and dtype). See [Writing CUDA
440
+ kernels](docs/source/kernels/cuda-kernel.md) and [CUDA
441
+ headers](docs/source/kernels/cuda-headers.md) for details.
438
442
 
439
443
  Kernels run asynchronously, so a CUDA error (an illegal memory access, say)
440
444
  normally surfaces at a later `.get()` or MPI call, far from the kernel that
@@ -521,8 +525,9 @@ published at <https://max-models.github.io/cunumpy/>:
521
525
  * [Worked examples](docs/source/examples/index.md),
522
526
  [best practices](docs/source/best-practices.md),
523
527
  [troubleshooting](docs/source/troubleshooting.md),
524
- [Pyodide](docs/source/pyodide.md) and the
525
- [API reference](docs/source/api.md).
528
+ [Pyodide](docs/source/pyodide.md), the
529
+ [API overview](docs/source/api.md) and the
530
+ [API reference](docs/source/reference/index.md).
526
531
 
527
532
  ### For AI coding assistants
528
533
 
@@ -5,7 +5,7 @@ requires = [ "setuptools", "wheel" ]
5
5
 
6
6
  [project]
7
7
  name = "cunumpy"
8
- version = "0.6.1"
8
+ version = "0.6.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" ]
@@ -37,12 +37,14 @@ optional-dependencies.docs = [
37
37
  "myst-parser",
38
38
  "nbconvert",
39
39
  "nbsphinx",
40
+ "numpydoc",
40
41
  "jupyterlab",
41
42
  "pre-commit",
42
43
  "pyproject-fmt",
43
44
  "sphinx",
44
45
  "sphinx-book-theme",
45
46
  ]
47
+ optional-dependencies.metal = [ "mlx; sys_platform == 'darwin' and platform_machine == 'arm64'" ]
46
48
  optional-dependencies.test = [ "coverage", "pytest", "scipy" ]
47
49
  optional-dependencies.test-compiled = [ "cunumpy[test]", "pyccel" ]
48
50
  urls."Source" = "https://github.com/max-models/cunumpy"
@@ -57,8 +59,33 @@ cunumpy = [ "py.typed", "*.pyi", "LLM_GUIDE.md", "cuda/include/cunumpy/*.cuh" ]
57
59
  # agent worktrees and scratch scripts are local copies, not part of the project
58
60
  extend-exclude = [ ".claude" ]
59
61
  # formatting: `ruff format`; import sorting: the isort rules of `ruff check`
60
- lint.extend-select = [ "I", "TID252" ]
62
+ lint.extend-select = [ "D", "I", "TID252" ]
63
+ # D105/D107: magic methods and __init__ (constructor parameters go in the class docstring)
64
+ lint.extend-ignore = [ "D105", "D107" ]
61
65
  lint.isort.known-first-party = [ "cunumpy" ]
66
+ lint.per-file-ignores."_fake_cupy*.py" = [ "D" ]
67
+ lint.per-file-ignores."conftest.py" = [ "D" ]
68
+ lint.per-file-ignores."docs/**" = [ "D" ]
69
+ lint.per-file-ignores."tests/**" = [ "D" ]
70
+ lint.pydocstyle.convention = "numpy"
62
71
 
63
72
  [tool.ruff.lint.flake8-tidy-imports]
64
73
  ban-relative-imports = "all"
74
+
75
+ # `python -m numpydoc lint src/cunumpy/*.py`: the public API (names re-exported by a public
76
+ # module) has full numpydoc sections; private helpers only need a one-line summary.
77
+ [tool.numpydoc_validation]
78
+ checks = [
79
+ "all",
80
+ "ES01", # extended summary is optional (docstrings are kept short)
81
+ "EX01", # examples are required for public functions and classes, not every method
82
+ "SA01", # See Also is optional
83
+ "GL08", # undocumented objects are caught by ruff's D rules instead
84
+ ]
85
+ # one-line docstrings (properties, trivial methods) need no Returns section
86
+ override_RT01 = [ '\A[^\n]*\Z' ]
87
+ exclude = [
88
+ '\._[A-Za-z]\w*$', # private functions, classes and methods
89
+ '\.__\w+__$', # dunder methods
90
+ '_fake_cupy',
91
+ ]
@@ -72,26 +72,37 @@ https://max-models.github.io/cunumpy/ and in `docs/source/` of the repository.
72
72
  | hand data to SciPy/matplotlib/h5py | `xp.to_numpy(a)` |
73
73
  | call an existing NumPy-only kernel with GPU arrays (slow, correct) | `xp.kernels.PyccelKernel(fn, outputs=(...))` |
74
74
  | launch a hand-written CUDA C kernel | `xp.kernels.CudaKernel(source, "name")` / `CudaKernel.from_file(path)` |
75
+ | run a Metal (MSL) kernel on an Apple silicon GPU, NumPy float32 in and out (float64 raises; `float64="cast"` computes in float32); not part of `Kernel` dispatch | `xp.kernels.MetalKernel(body, inputs=[...], outputs=[...])(*args, out=arrays, n_threads=n)`; check `xp.kernels.metal_available()` |
76
+ | call host-only code (SciPy, file readers) with arguments of either backend | `xp.host_call(fun, *args)`; `@xp.evaluate_on_host` on a method; `@xp.setup_on_host` on `__init__` |
77
+ | keep the live rows of particle arrays at the front | `n = xp.algorithms.compact_by_mask(alive, markers, weights)`; component-major `(ncomp, N)`: `axis=-1` |
78
+ | run CUDA kernel launches on the CPU in a test (fake CuPy) | `with kernel_testing.emulated_launches(): ...` (also makes `kernel.compile()`/`compile_all()` build the emulation instead of CUDA); `with kernel_testing.fake_cupy_session(): ...` adds the CuPy backend; `kernel_testing.host_buffer(a)` reads a fake array; struct arguments are read through their fields |
79
+ | run a test on the fake CuPy from a NumPy test process | `kernel_testing.run_in_fake_cupy_subprocess(code)` (serial child, rank 0 only under MPI); `@kernel_testing.requires_device_backend` = a GPU or the fake CuPy |
80
+ | find the arguments a host kernel writes (copy only those back) | `PyccelKernel(fn, outputs=("out",))` (names work positionally); `xp.kernels.outputs_from_annotations(fn)`; `Kernel.from_folder(..., outputs="annotations")` |
75
81
  | host kernel + CUDA port, chosen by backend | `xp.kernels.Kernel(host_fn, cuda_kernel_or_None)` |
76
82
  | many kernels in a package, ported incrementally | `xp.kernels.KernelCatalog.from_package(__name__, missing_cuda="fallback")` |
77
83
  | host kernels compiled at first call (your compile function), NumPy fallback | `from_package(..., host_suffix="_pyccel", compile_host=my_compile, host_fallback={...})` -> `xp.kernels.CompiledHostKernel` |
78
84
  | host arrays reach kernels while CuPy is active | `Kernel(..., dispatch="arrays")` / `from_package(..., dispatch="arrays")`: CUDA only for device arguments |
79
85
  | one kernel folder declares its kernel in its own `__init__.py` | `kernel = xp.kernels.Kernel.from_folder(__name__, host_suffix="_pyccel", compile_host=..., dispatch="arrays")`; `<name>_numba.py`, `<name>_numpy.py` in the folder are further host implementations |
80
- | bring a `dispatch="arrays"` kernel's arguments to the side of the main array | `xp.kernels.as_kernel_array(a, like=grid, dtype=float)`; outputs: `with xp.kernels.kernel_output(out, like=grid, dtype=float) as buf:` |
86
+ | bring a `dispatch="arrays"` kernel's arguments to the side of the main array | `xp.kernels.as_kernel_array(a, like=grid, dtype=float)`; outputs: `with xp.kernels.kernel_output(out, like=grid, dtype=float) as buf:`; `strided=True` passes NumPy views in C order with gaps (`a[:, :n]`) to Pyccel hosts without a copy |
81
87
  | choose the host implementation (pyccel/numba/numpy/python) | `xp.kernels.set_host_kernel_implementation("numpy")`, `with xp.kernels.use_host_kernel_implementation(...)`, `CUNUMPY_HOST_KERNEL_IMPLEMENTATION=numpy`; default: first available of pyccel, numba, numpy; `kernel.selected()` |
82
88
  | require CUDA for device kernel dispatch | `xp.kernels.set_device_kernel_implementation("cuda")`, `get_device_kernel_implementation()`, `with xp.kernels.use_device_kernel_implementation(...)`, `CUNUMPY_DEVICE_KERNEL_IMPLEMENTATION=cuda`; default `None` preserves `missing_cuda` policy; explicit CUDA rejects host fallback |
83
89
  | check host and CUDA kernels take the same parameters | `catalog.check_signatures()` (in a unit test) |
84
- | test a CUDA kernel's arithmetic without a GPU | `cunumpy.kernel_testing.emulate_cuda_kernel(kernel, *numpy_args, n_threads=n)` (C++ compiler; shared memory and __syncthreads ok, no warp ops; `shared_mem=` for extern shared) |
90
+ | test a CUDA kernel's arithmetic without a GPU | `cunumpy.kernel_testing.emulate_cuda_kernel(kernel, *numpy_args, n_threads=n)` (C++ compiler; shared memory and __syncthreads ok, no warp ops; inline asm traps; compiled once per kernel and cached; `shared_mem=` for extern shared) |
85
91
  | shared-memory budget of a block | `xp.cuda.max_shared_memory_per_block()` (48 KiB without a GPU) |
86
92
  | random numbers inside a kernel, equal on the host | `#include <cunumpy/random.cuh>`: `cunumpy_uniform(seed, particle_id, step)`; host: `xp.rng.philox_uniform(seed, ids, step)` |
87
93
  | sort points along a Z-curve / quadtree or octree nodes as contiguous ranges | `keys = xp.algorithms.morton_keys(pos, lower, upper, levels)`, `keys, order, pos = xp.algorithms.sort_by_key(keys, pos)`; in a kernel `#include <cunumpy/morton.cuh>`: `cunumpy_morton_key2(x, y, x0, y0, sx, sy, levels)` with `xp.algorithms.morton_scales(...)` |
88
94
  | one thread per marker without passing n_threads | default: `CudaKernel(...)` uses the first array's row count for 1D launches |
89
95
  | copy device arrays to the host for output without stalling | `xp.memory.HostStaging(shape, dtype)`: `c = staging.copy(a)` ... `c.result()` |
96
+ | read a device scalar without a sync (e.g. a convergence test one iteration late) | `h = xp.to_host_async(x)`; `h.ready()` never waits, `h.result()` |
90
97
  | PIC recipes (compaction, sort by cell, MPI exchange, graphs) | docs guide "Particle codes" |
91
98
  | reproducible random numbers per MPI rank | `xp.rng.random_streams.seed(seed, rank=rank)`, then `xp.rng.random_streams.normal(...)` / `.generator()` |
92
99
  | group arrays/scalars into one kernel argument | `xp.arguments.CudaArguments` (flattened), `xp.arguments.CudaStruct` (C struct), `xp.arguments.CudaStructArguments` (C struct as a class); host kernels take their own argument objects, the caller picks one per backend |
93
100
  | CUDA struct from a Pyccel argument class | `xp.arguments.CudaStruct.from_signature(Cls.__init__, "Name")`, `xp.arguments.write_cuda_header(...)` |
94
101
  | SciPy (sparse, sparse.linalg, fft, special, ndimage, ...) on either backend | `xp.scipy.<subpackage>.<name>` (SciPy or `cupyx.scipy`); `xp.scipy.special.available(name)` |
102
+ | splines on the device | `xp.scipy.interpolate.UnivariateSpline`, `make_interp_spline`, `NdBSpline` (no `RectBivariateSpline`: `make_interp_spline` per axis + `NdBSpline`) |
103
+ | circulant solve (FFT preconditioner) on the device | `xp.scipy.linalg.solve_circulant(c, b)` (cunumpy fill-in on CuPy, SciPy's arguments) |
104
+ | one root/minimization/integral/ODE at setup with an `xp` callback using device data | `xp.optimize.fsolve`/`root`/`minimize`, `xp.integrate.quad`/`odeint` (SciPy on the host, callback args and results on `x0`'s backend) |
105
+ | many independent scalar root problems (one per ray, per marker) | `xp.optimize.newton(func, x0_array, fprime=None)` (SciPy's array Newton/secant, on the device) |
95
106
  | chain of elementwise operations as one GPU kernel | `@xp.kernels.fuse` (`cupy.fuse` for CuPy arrays, plain call otherwise) |
96
107
  | PETSc solve on device arrays without copies | `xp.petsc.petsc_vec(array)` (CUDA/HIP petsc4py for CuPy arrays); `xp.synchronize()` around PETSc calls |
97
108
  | reduction inside a CUDA kernel (energy, max velocity) | `<cunumpy/reduce.cuh>`: `cunumpy_block_sum_to(out, v)`, `cunumpy_block_min/max`, `cunumpy_warp_sum` |
@@ -101,6 +112,7 @@ https://max-models.github.io/cunumpy/ and in `docs/source/` of the repository.
101
112
  | timing GPU code | `with xp.profiling.timed_region("name") as t:` → `t.elapsed` |
102
113
  | profiler markers | `xp.profiling.nvtx_range("name")` (context manager or decorator) |
103
114
  | find transfers | `with xp.profiling.count_transfers() as c: ...; print(c.report())` |
115
+ | check transfers per phase of a time loop | `b = xp.profiling.TransferBudget()`; `b.count("step")(fn)` or `with b.phase("output"):`; `b.require("step", allow={"to_host": {"max_nbytes": 8}})`; `b.check()` |
104
116
  | debug an illegal memory access | `CUNUMPY_CUDA_DEBUG=1`, then `compute-sanitizer` |
105
117
  | test on both backends | `cunumpy.kernel_testing.BACKENDS`, `backend` fixture, `requires_cupy` |
106
118
  | test CUDA vs host kernel | `cunumpy.kernel_testing.assert_kernels_agree(kernel, make_args, n_threads=...)` |
@@ -137,6 +149,9 @@ xp.as_device_array(value, dtype=None, ndim=None, *, name=None)
137
149
  with xp.profiling.count_transfers() as c: ... # c.total, c.to_host, c.to_device,
138
150
  # c.kernel_conversions, c.fallbacks, c.events, c.report()
139
151
  with xp.profiling.assert_no_transfers(): ... # rejects host/device copies and host fallback
152
+ h = xp.to_host_async(a) # non-blocking copy of a device scalar: h.ready(), h.result()
153
+ budget = xp.profiling.TransferBudget() # per-phase counts: budget.phase(name),
154
+ # budget.count(name)(fn), budget.require(...), budget.check()
140
155
  ```
141
156
 
142
157
  Mirror refreshes, MPI/output staging, argument conversion and kernel output
@@ -160,6 +175,9 @@ with xp.mpi.mpi_buffer(a) as buf:
160
175
  with xp.mpi.mpi_buffer(a, send=False, recv=True) as buf:
161
176
  ... # array, or pinned staging copy
162
177
  xp.mpi.set_mpi_cuda_aware(True | False | None), xp.mpi.get_mpi_cuda_aware()
178
+ xp.mpi.exchange(
179
+ comm, sends=[(send, dest, tag)], receives=[(recv, source, tag)]
180
+ ) # waits for all transfers and GPU copy-back; MPI posting/wait errors abort comm
163
181
  xp.algorithms.segment_sum(
164
182
  values, keys, n_segments
165
183
  ) # out[k] = sum(values[keys == k]); keys < 0 dropped
@@ -300,8 +318,10 @@ xp.cuda.cuda_include_dir()
300
318
  * Default `n_threads_from="auto"` infers from the first array, including arrays
301
319
  in supported argument objects: 1D block -> shape[0], 2D -> shape[:2], 3D ->
302
320
  shape[:3] (x, y, z). Per-call block overrides apply. Explicit n_threads/grid
303
- wins; use a callback for flattened `.size`, reversed axes or another work
304
- count, or None to require explicit sizes. Missing arrays/axes raise.
321
+ wins; `"last_axis"` uses the first array's last axis (component-major
322
+ `(ncomp, N)` marker arrays); use a callback for flattened `.size`, reversed
323
+ axes or another work count, or None to require explicit sizes. Missing
324
+ arrays/axes raise.
305
325
 
306
326
  Shipped CUDA headers (always on the include path):
307
327
 
@@ -1,3 +1,16 @@
1
+ """NumPy and CuPy behind one namespace.
2
+
3
+ ``import cunumpy as xp`` gives the functions of the active backend's
4
+ ``array-api-compat`` module (``xp.zeros``, ``xp.linalg``, ...), replaced when
5
+ the backend changes, plus backend selection, array inspection and conversion,
6
+ host execution helpers (:func:`host_call`), and :data:`scipy`. Everything else
7
+ is in a submodule (:mod:`cunumpy.kernels`, :mod:`cunumpy.cuda`, ...). See
8
+ :doc:`/api` for the module layout and :doc:`/guides/backends`.
9
+
10
+ ``xp.numpy_backend`` and ``xp.cupy_backend`` are booleans telling whether the
11
+ active backend is NumPy or CuPy (prefer :func:`get_backend` for the name).
12
+ """
13
+
1
14
  # cunumpy/__init__.py
2
15
  import re as _re
3
16
  from importlib.metadata import PackageNotFoundError, version
@@ -6,20 +19,26 @@ from cunumpy import (
6
19
  algorithms,
7
20
  arguments,
8
21
  cuda,
22
+ integrate,
9
23
  kernels,
10
24
  memory,
11
25
  mpi,
26
+ optimize,
12
27
  petsc,
13
28
  profiling,
14
29
  rng,
15
30
  xp,
16
31
  )
32
+ from cunumpy._host import evaluate_on_host, host_call, setup_on_host
17
33
  from cunumpy._scipy_backend import scipy
34
+ from cunumpy._staging import to_host_async
18
35
  from cunumpy.xp import (
36
+ MIN_CUPY_VERSION,
19
37
  as_device_array,
20
38
  assert_same_backend,
21
39
  backend_info,
22
40
  cupy_available,
41
+ cupy_version_supported,
23
42
  default_float_dtype,
24
43
  get_array_backend,
25
44
  get_array_module,
@@ -36,6 +55,7 @@ from cunumpy.xp import (
36
55
  )
37
56
 
38
57
  try:
58
+ #: The installed version of cunumpy; ``"0.0.0+unknown"`` without package metadata.
39
59
  __version__ = version("cunumpy")
40
60
  except PackageNotFoundError:
41
61
  __version__ = "0.0.0+unknown"
@@ -46,18 +66,27 @@ def _version_key(text: str) -> tuple[int, ...]:
46
66
 
47
67
 
48
68
  def require_version(minimum: str) -> None:
49
- """Raise ``ImportError`` if this cunumpy is older than `minimum`.
50
-
51
- For projects that depend on a feature of a given release, as a clearer
52
- error than an ``AttributeError`` later::
53
-
54
- import cunumpy as xp
55
-
56
- xp.require_version("0.4.0")
57
-
58
- Only the numeric part of the versions is compared (``0.4.0`` and
59
- ``0.4.0.dev1`` compare equal). Nothing is checked when the installed
60
- version is unknown (cunumpy not installed as a package).
69
+ """Raise ``ImportError`` if the installed cunumpy is older than `minimum`.
70
+
71
+ A clearer error than a later ``AttributeError`` for code that needs a
72
+ feature of a given release. Only the numeric part is compared (``0.4.0``
73
+ and ``0.4.0.dev1`` are equal); nothing is checked when the installed
74
+ version is unknown (cunumpy not installed as a package, ``xp.__version__``
75
+ is then ``"0.0.0+unknown"``).
76
+
77
+ Parameters
78
+ ----------
79
+ minimum : str
80
+ The oldest acceptable version, e.g. ``"0.4.0"``.
81
+
82
+ Raises
83
+ ------
84
+ ImportError
85
+ If the installed version is older than `minimum`.
86
+
87
+ Examples
88
+ --------
89
+ >>> xp.require_version("0.4.0")
61
90
  """
62
91
  if __version__.startswith("0.0.0+unknown"):
63
92
  return
@@ -69,6 +98,7 @@ def require_version(minimum: str) -> None:
69
98
 
70
99
 
71
100
  __all__ = [
101
+ "MIN_CUPY_VERSION",
72
102
  "__version__",
73
103
  "algorithms",
74
104
  "arguments",
@@ -78,16 +108,21 @@ __all__ = [
78
108
  "cuda",
79
109
  "cupy_available",
80
110
  "cupy_backend",
111
+ "cupy_version_supported",
81
112
  "default_float_dtype",
113
+ "evaluate_on_host",
82
114
  "get_array_backend",
83
115
  "get_array_module",
84
116
  "get_backend",
117
+ "host_call",
118
+ "integrate",
85
119
  "is_cpu",
86
120
  "is_gpu",
87
121
  "kernels",
88
122
  "memory",
89
123
  "mpi",
90
124
  "numpy_backend",
125
+ "optimize",
91
126
  "petsc",
92
127
  "profiling",
93
128
  "require_version",
@@ -95,9 +130,11 @@ __all__ = [
95
130
  "same_backend",
96
131
  "scipy",
97
132
  "set_backend",
133
+ "setup_on_host",
98
134
  "synchronize",
99
135
  "to_cunumpy",
100
136
  "to_cupy",
137
+ "to_host_async",
101
138
  "to_numpy",
102
139
  "use_backend",
103
140
  "xp",
@@ -105,12 +142,7 @@ __all__ = [
105
142
 
106
143
 
107
144
  def __getattr__(name: str):
108
- """Resolve names that are not in the namespace: cunumpy.<name> -> cunumpy.xp.<name>.
109
-
110
- The public names of the active backend are copied into this namespace (see
111
- `_sync_backend_namespace`), so this only runs for names missing from the
112
- backend's ``__all__`` and for ``numpy_backend``/``cupy_backend``.
113
- """
145
+ """Resolve ``numpy_backend``, ``cupy_backend`` and backend names not copied here."""
114
146
  if name == "numpy_backend":
115
147
  return xp.numpy_backend
116
148
  if name == "cupy_backend":
@@ -1,7 +1,7 @@
1
1
  # Stub file for Pylance/mypy: exposes all numpy symbols so that
2
2
  # `import cunumpy as xp` followed by `xp.<Tab>` shows numpy completions.
3
3
  # At runtime the real __init__.py dispatches to numpy or cupy via __getattr__.
4
- from collections.abc import Generator
4
+ from collections.abc import Callable, Generator
5
5
  from contextlib import contextmanager
6
6
  from typing import Any
7
7
 
@@ -11,19 +11,27 @@ from numpy import *
11
11
  from cunumpy import algorithms as algorithms
12
12
  from cunumpy import arguments as arguments
13
13
  from cunumpy import cuda as cuda
14
+ from cunumpy import integrate as integrate
14
15
  from cunumpy import kernels as kernels
15
16
  from cunumpy import memory as memory
16
17
  from cunumpy import mpi as mpi
18
+ from cunumpy import optimize as optimize
17
19
  from cunumpy import petsc as petsc
18
20
  from cunumpy import profiling as profiling
19
21
  from cunumpy import rng as rng
20
22
  from cunumpy import xp as xp
21
23
  from cunumpy._scipy_backend import scipy as scipy
24
+ from cunumpy._staging import HostCopy
22
25
 
23
26
  def to_numpy(array: Any) -> np.ndarray: ...
24
27
  def to_cupy(array: Any) -> Any: ...
25
28
  def to_cunumpy(array: Any) -> Any: ...
29
+ def to_host_async(array: Any) -> HostCopy: ...
26
30
  def cupy_available() -> bool: ...
31
+ def cupy_version_supported(cupy_version: str) -> bool: ...
32
+
33
+ MIN_CUPY_VERSION: str
34
+
27
35
  def backend_info() -> dict[str, Any]: ...
28
36
  def get_array_module(array: Any) -> Any: ...
29
37
  def get_backend() -> str: ...
@@ -38,6 +46,9 @@ def set_backend(backend: str, *, strict: bool = ...) -> None: ...
38
46
  def require_version(minimum: str) -> None: ...
39
47
  def default_float_dtype() -> Any: ...
40
48
  def synchronize() -> None: ...
49
+ def host_call(fun: Callable[..., Any], *args: Any, **kwargs: Any) -> Any: ...
50
+ def evaluate_on_host(method: Callable[..., Any]) -> Callable[..., Any]: ...
51
+ def setup_on_host(init: Callable[..., None]) -> Callable[..., None]: ...
41
52
  def as_device_array(
42
53
  value: Any, dtype: Any = ..., ndim: int | None = ..., *, name: str | None = ...
43
54
  ) -> Any: ...