cunumpy 0.6.3__tar.gz → 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {cunumpy-0.6.3/src/cunumpy.egg-info → cunumpy-0.7.1}/PKG-INFO +35 -41
  2. {cunumpy-0.6.3 → cunumpy-0.7.1}/README.md +30 -30
  3. {cunumpy-0.6.3 → cunumpy-0.7.1}/pyproject.toml +5 -13
  4. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/LLM_GUIDE.md +2 -2
  5. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/__init__.py +1 -1
  6. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_algorithms.py +18 -1
  7. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_cuda_kernel.py +4 -3
  8. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_device.py +4 -3
  9. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_dispatch.py +4 -3
  10. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_fake_cupyx.py +10 -8
  11. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_kernel.py +95 -14
  12. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_morton.py +1 -1
  13. cunumpy-0.7.1/src/cunumpy/_pyccel_cache.py +305 -0
  14. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_random_streams.py +2 -2
  15. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_scipy_backend.py +7 -5
  16. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_transfers.py +1 -1
  17. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/kernel_testing.py +6 -4
  18. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/kernels.py +11 -2
  19. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/xp.py +9 -7
  20. {cunumpy-0.6.3 → cunumpy-0.7.1/src/cunumpy.egg-info}/PKG-INFO +35 -41
  21. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy.egg-info/SOURCES.txt +1 -0
  22. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy.egg-info/requires.txt +4 -10
  23. {cunumpy-0.6.3 → cunumpy-0.7.1}/LICENSE.txt +0 -0
  24. {cunumpy-0.6.3 → cunumpy-0.7.1}/setup.cfg +0 -0
  25. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/__init__.pyi +0 -0
  26. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_emulation.py +0 -0
  27. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_fake_cupy.py +0 -0
  28. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_fake_cupy_impl.py +0 -0
  29. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_fusion.py +0 -0
  30. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_host.py +0 -0
  31. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_linalg.py +0 -0
  32. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_metal_kernel.py +0 -0
  33. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_mirror.py +0 -0
  34. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_mpi.py +0 -0
  35. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_optimize.py +0 -0
  36. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_philox.py +0 -0
  37. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_profiling.py +0 -0
  38. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_scipy_host.py +0 -0
  39. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_staging.py +0 -0
  40. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/_streams.py +0 -0
  41. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/algorithms.py +0 -0
  42. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/arguments.py +0 -0
  43. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/__init__.py +0 -0
  44. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/array_view.cuh +0 -0
  45. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/atomic.cuh +0 -0
  46. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/index.cuh +0 -0
  47. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/morton.cuh +0 -0
  48. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/random.cuh +0 -0
  49. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/reduce.cuh +0 -0
  50. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/cuda/include/cunumpy/scan.cuh +0 -0
  51. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/integrate.py +0 -0
  52. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/memory.py +0 -0
  53. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/mpi.py +0 -0
  54. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/optimize.py +0 -0
  55. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/petsc.py +0 -0
  56. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/profiling.py +0 -0
  57. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/py.typed +0 -0
  58. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy/rng.py +0 -0
  59. {cunumpy-0.6.3 → cunumpy-0.7.1}/src/cunumpy.egg-info/dependency_links.txt +0 -0
  60. {cunumpy-0.6.3 → cunumpy-0.7.1}/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.6.3
3
+ Version: 0.7.1
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
  License: MIT License
@@ -41,19 +41,13 @@ Requires-Dist: array-api-compat
41
41
  Requires-Dist: maybempi>=0.1.2
42
42
  Requires-Dist: numpy
43
43
  Provides-Extra: dev
44
- Requires-Dist: ruff; extra == "dev"
45
44
  Requires-Dist: cunumpy[docs,test-compiled]; extra == "dev"
45
+ Requires-Dist: pre-commit; extra == "dev"
46
+ Requires-Dist: pyproject-fmt; extra == "dev"
47
+ Requires-Dist: ruff; extra == "dev"
46
48
  Provides-Extra: docs
49
+ Requires-Dist: griffe; extra == "docs"
47
50
  Requires-Dist: ipykernel; extra == "docs"
48
- Requires-Dist: myst-parser; extra == "docs"
49
- Requires-Dist: nbconvert; extra == "docs"
50
- Requires-Dist: nbsphinx; extra == "docs"
51
- Requires-Dist: numpydoc; extra == "docs"
52
- Requires-Dist: jupyterlab; extra == "docs"
53
- Requires-Dist: pre-commit; extra == "docs"
54
- Requires-Dist: pyproject-fmt; extra == "docs"
55
- Requires-Dist: sphinx; extra == "docs"
56
- Requires-Dist: sphinx-book-theme; extra == "docs"
57
51
  Provides-Extra: metal
58
52
  Requires-Dist: mlx; (sys_platform == "darwin" and platform_machine == "arm64") and extra == "metal"
59
53
  Provides-Extra: test
@@ -104,7 +98,7 @@ never hide a NumPy name:
104
98
 
105
99
  Everything except `xp.cuda`, `xp.arguments` and `CudaKernel` works on both backends.
106
100
  `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)).
101
+ `pip install 'cunumpy[metal]'`, see [the guide](https://max-models.github.io/cunumpy/kernels/metal-kernel/)).
108
102
 
109
103
  ## Install
110
104
 
@@ -120,7 +114,7 @@ installation instructions for your system. CuNumpy does not install CUDA.
120
114
  `array-api-compat` supplies NumPy and CuPy compatibility modules with more
121
115
  consistent behavior for shared array operations. CuNumpy uses them internally;
122
116
  your arrays remain ordinary NumPy or CuPy arrays. See [why CuNumpy uses
123
- `array-api-compat`](docs/source/array-api-compat.md) for a plain-language
117
+ `array-api-compat`](https://max-models.github.io/cunumpy/guides/array-api-compat/) for a plain-language
124
118
  explanation and examples.
125
119
 
126
120
  ## Choose a backend
@@ -146,7 +140,7 @@ Use `xp.set_backend("cupy", strict=True)` to raise when CUDA is unavailable,
146
140
  preserving the previous backend. `xp.backend_info()` returns structured backend,
147
141
  dependency, and CUDA diagnostics. Reusable streams/events, MPI staging, cell
148
142
  ranges, and prepared reductions are described in the
149
- [execution helpers guide](docs/source/guides/execution-helpers.md).
143
+ [execution helpers guide](https://max-models.github.io/cunumpy/guides/execution-helpers/).
150
144
 
151
145
  Use `use_backend()` for a temporary selection. It restores the previous
152
146
  selection when the block exits, including when an exception is raised:
@@ -367,7 +361,7 @@ read-only inputs. For a keyword call, declare the keyword name, such as
367
361
  `outputs=("out",)`. A wrong declaration can leave GPU output values stale.
368
362
  The wrapper can also traverse arrays nested in lists, tuples, dictionaries,
369
363
  and selected application objects; see [Host kernels with GPU
370
- data](docs/source/kernels/pyccel-kernel.md) for `object_modules`, `is_array`,
364
+ data](https://max-models.github.io/cunumpy/kernels/pyccel-kernel/) for `object_modules`, `is_array`,
371
365
  aliasing, and output declarations.
372
366
 
373
367
  ## Write CUDA kernels next to host kernels
@@ -504,8 +498,8 @@ an explicit `grid`, with dynamic shared memory (`shared_mem`) and a `stream`.
504
498
  C++ function templates are instantiated with `template_args`, and
505
499
  `CudaKernelVariants` caches kernels whose source is generated per variant
506
500
  (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.
501
+ kernels](https://max-models.github.io/cunumpy/kernels/cuda-kernel/) and [CUDA
502
+ headers](https://max-models.github.io/cunumpy/kernels/cuda-headers/) for details.
509
503
 
510
504
  Kernels run asynchronously, so a CUDA error (an illegal memory access, say)
511
505
  normally surfaces at a later `.get()` or MPI call, far from the kernel that
@@ -565,36 +559,36 @@ mirror.to_host() # vector._data holds the result on both backends
565
559
 
566
560
  CuNumpy supports the NumPy backend in Pyodide. It does not provide CuPy/CUDA
567
561
  there. Ordinary Python callables can be wrapped with `PyccelKernel` without
568
- compilation. See the [Pyodide guide](docs/source/pyodide.md) for a complete
562
+ compilation. See the [Pyodide guide](https://max-models.github.io/cunumpy/pyodide/) for a complete
569
563
  installation example and compatibility notes.
570
564
 
571
565
  ## Documentation
572
566
 
573
- The full documentation lives in [`docs/source`](docs/source/index.md) and is
567
+ The full documentation lives in [`docs/`](docs/) (Astro + Starlight, `make docs-build`) and is
574
568
  published at <https://max-models.github.io/cunumpy/>:
575
569
 
576
- * Getting started: [installation](docs/source/installation.md) and a
577
- [quickstart](docs/source/quickstart.md) with a map of which guide covers what.
578
- * User guide: [choosing a backend](docs/source/guides/backends.md),
579
- [backend-agnostic code](docs/source/guides/portable-code.md),
580
- [data movement](docs/source/guides/data-movement.md),
581
- [devices, memory and streams](docs/source/guides/gpu-devices.md),
582
- [MPI with one rank per GPU](docs/source/guides/mpi.md),
583
- [timing and profiling](docs/source/guides/profiling.md).
584
- * Porting kernels: [overview](docs/source/kernels/overview.md),
585
- [`PyccelKernel`](docs/source/kernels/pyccel-kernel.md),
586
- [`CudaKernel`](docs/source/kernels/cuda-kernel.md),
587
- [`Kernel` and `KernelCatalog`](docs/source/kernels/dispatch.md),
588
- [argument objects and structs](docs/source/kernels/arguments.md),
589
- [accumulation kernels](docs/source/kernels/accumulation.md),
590
- [debugging](docs/source/kernels/debugging.md),
591
- [testing](docs/source/kernels/testing.md).
592
- * [Worked examples](docs/source/examples/index.md),
593
- [best practices](docs/source/best-practices.md),
594
- [troubleshooting](docs/source/troubleshooting.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).
570
+ * Getting started: [installation](https://max-models.github.io/cunumpy/getting-started/installation/) and a
571
+ [quickstart](https://max-models.github.io/cunumpy/getting-started/quickstart/) with a map of which guide covers what.
572
+ * User guide: [choosing a backend](https://max-models.github.io/cunumpy/guides/backends/),
573
+ [backend-agnostic code](https://max-models.github.io/cunumpy/guides/portable-code/),
574
+ [data movement](https://max-models.github.io/cunumpy/guides/data-movement/),
575
+ [devices, memory and streams](https://max-models.github.io/cunumpy/guides/gpu-devices/),
576
+ [MPI with one rank per GPU](https://max-models.github.io/cunumpy/guides/mpi/),
577
+ [timing and profiling](https://max-models.github.io/cunumpy/guides/profiling/).
578
+ * Porting kernels: [overview](https://max-models.github.io/cunumpy/kernels/overview/),
579
+ [`PyccelKernel`](https://max-models.github.io/cunumpy/kernels/pyccel-kernel/),
580
+ [`CudaKernel`](https://max-models.github.io/cunumpy/kernels/cuda-kernel/),
581
+ [`Kernel` and `KernelCatalog`](https://max-models.github.io/cunumpy/kernels/dispatch/),
582
+ [argument objects and structs](https://max-models.github.io/cunumpy/kernels/arguments/),
583
+ [accumulation kernels](https://max-models.github.io/cunumpy/kernels/accumulation/),
584
+ [debugging](https://max-models.github.io/cunumpy/kernels/debugging/),
585
+ [testing](https://max-models.github.io/cunumpy/kernels/testing/).
586
+ * [Worked examples](https://max-models.github.io/cunumpy/examples/),
587
+ [best practices](https://max-models.github.io/cunumpy/best-practices/),
588
+ [troubleshooting](https://max-models.github.io/cunumpy/troubleshooting/),
589
+ [Pyodide](https://max-models.github.io/cunumpy/pyodide/), the
590
+ [API overview](https://max-models.github.io/cunumpy/reference/overview/) and the
591
+ [API reference](https://max-models.github.io/cunumpy/reference/).
598
592
 
599
593
  ### For AI coding assistants
600
594
 
@@ -37,7 +37,7 @@ never hide a NumPy name:
37
37
 
38
38
  Everything except `xp.cuda`, `xp.arguments` and `CudaKernel` works on both backends.
39
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)).
40
+ `pip install 'cunumpy[metal]'`, see [the guide](https://max-models.github.io/cunumpy/kernels/metal-kernel/)).
41
41
 
42
42
  ## Install
43
43
 
@@ -53,7 +53,7 @@ installation instructions for your system. CuNumpy does not install CUDA.
53
53
  `array-api-compat` supplies NumPy and CuPy compatibility modules with more
54
54
  consistent behavior for shared array operations. CuNumpy uses them internally;
55
55
  your arrays remain ordinary NumPy or CuPy arrays. See [why CuNumpy uses
56
- `array-api-compat`](docs/source/array-api-compat.md) for a plain-language
56
+ `array-api-compat`](https://max-models.github.io/cunumpy/guides/array-api-compat/) for a plain-language
57
57
  explanation and examples.
58
58
 
59
59
  ## Choose a backend
@@ -79,7 +79,7 @@ Use `xp.set_backend("cupy", strict=True)` to raise when CUDA is unavailable,
79
79
  preserving the previous backend. `xp.backend_info()` returns structured backend,
80
80
  dependency, and CUDA diagnostics. Reusable streams/events, MPI staging, cell
81
81
  ranges, and prepared reductions are described in the
82
- [execution helpers guide](docs/source/guides/execution-helpers.md).
82
+ [execution helpers guide](https://max-models.github.io/cunumpy/guides/execution-helpers/).
83
83
 
84
84
  Use `use_backend()` for a temporary selection. It restores the previous
85
85
  selection when the block exits, including when an exception is raised:
@@ -300,7 +300,7 @@ read-only inputs. For a keyword call, declare the keyword name, such as
300
300
  `outputs=("out",)`. A wrong declaration can leave GPU output values stale.
301
301
  The wrapper can also traverse arrays nested in lists, tuples, dictionaries,
302
302
  and selected application objects; see [Host kernels with GPU
303
- data](docs/source/kernels/pyccel-kernel.md) for `object_modules`, `is_array`,
303
+ data](https://max-models.github.io/cunumpy/kernels/pyccel-kernel/) for `object_modules`, `is_array`,
304
304
  aliasing, and output declarations.
305
305
 
306
306
  ## Write CUDA kernels next to host kernels
@@ -437,8 +437,8 @@ an explicit `grid`, with dynamic shared memory (`shared_mem`) and a `stream`.
437
437
  C++ function templates are instantiated with `template_args`, and
438
438
  `CudaKernelVariants` caches kernels whose source is generated per variant
439
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.
440
+ kernels](https://max-models.github.io/cunumpy/kernels/cuda-kernel/) and [CUDA
441
+ headers](https://max-models.github.io/cunumpy/kernels/cuda-headers/) for details.
442
442
 
443
443
  Kernels run asynchronously, so a CUDA error (an illegal memory access, say)
444
444
  normally surfaces at a later `.get()` or MPI call, far from the kernel that
@@ -498,36 +498,36 @@ mirror.to_host() # vector._data holds the result on both backends
498
498
 
499
499
  CuNumpy supports the NumPy backend in Pyodide. It does not provide CuPy/CUDA
500
500
  there. Ordinary Python callables can be wrapped with `PyccelKernel` without
501
- compilation. See the [Pyodide guide](docs/source/pyodide.md) for a complete
501
+ compilation. See the [Pyodide guide](https://max-models.github.io/cunumpy/pyodide/) for a complete
502
502
  installation example and compatibility notes.
503
503
 
504
504
  ## Documentation
505
505
 
506
- The full documentation lives in [`docs/source`](docs/source/index.md) and is
506
+ The full documentation lives in [`docs/`](docs/) (Astro + Starlight, `make docs-build`) and is
507
507
  published at <https://max-models.github.io/cunumpy/>:
508
508
 
509
- * Getting started: [installation](docs/source/installation.md) and a
510
- [quickstart](docs/source/quickstart.md) with a map of which guide covers what.
511
- * User guide: [choosing a backend](docs/source/guides/backends.md),
512
- [backend-agnostic code](docs/source/guides/portable-code.md),
513
- [data movement](docs/source/guides/data-movement.md),
514
- [devices, memory and streams](docs/source/guides/gpu-devices.md),
515
- [MPI with one rank per GPU](docs/source/guides/mpi.md),
516
- [timing and profiling](docs/source/guides/profiling.md).
517
- * Porting kernels: [overview](docs/source/kernels/overview.md),
518
- [`PyccelKernel`](docs/source/kernels/pyccel-kernel.md),
519
- [`CudaKernel`](docs/source/kernels/cuda-kernel.md),
520
- [`Kernel` and `KernelCatalog`](docs/source/kernels/dispatch.md),
521
- [argument objects and structs](docs/source/kernels/arguments.md),
522
- [accumulation kernels](docs/source/kernels/accumulation.md),
523
- [debugging](docs/source/kernels/debugging.md),
524
- [testing](docs/source/kernels/testing.md).
525
- * [Worked examples](docs/source/examples/index.md),
526
- [best practices](docs/source/best-practices.md),
527
- [troubleshooting](docs/source/troubleshooting.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).
509
+ * Getting started: [installation](https://max-models.github.io/cunumpy/getting-started/installation/) and a
510
+ [quickstart](https://max-models.github.io/cunumpy/getting-started/quickstart/) with a map of which guide covers what.
511
+ * User guide: [choosing a backend](https://max-models.github.io/cunumpy/guides/backends/),
512
+ [backend-agnostic code](https://max-models.github.io/cunumpy/guides/portable-code/),
513
+ [data movement](https://max-models.github.io/cunumpy/guides/data-movement/),
514
+ [devices, memory and streams](https://max-models.github.io/cunumpy/guides/gpu-devices/),
515
+ [MPI with one rank per GPU](https://max-models.github.io/cunumpy/guides/mpi/),
516
+ [timing and profiling](https://max-models.github.io/cunumpy/guides/profiling/).
517
+ * Porting kernels: [overview](https://max-models.github.io/cunumpy/kernels/overview/),
518
+ [`PyccelKernel`](https://max-models.github.io/cunumpy/kernels/pyccel-kernel/),
519
+ [`CudaKernel`](https://max-models.github.io/cunumpy/kernels/cuda-kernel/),
520
+ [`Kernel` and `KernelCatalog`](https://max-models.github.io/cunumpy/kernels/dispatch/),
521
+ [argument objects and structs](https://max-models.github.io/cunumpy/kernels/arguments/),
522
+ [accumulation kernels](https://max-models.github.io/cunumpy/kernels/accumulation/),
523
+ [debugging](https://max-models.github.io/cunumpy/kernels/debugging/),
524
+ [testing](https://max-models.github.io/cunumpy/kernels/testing/).
525
+ * [Worked examples](https://max-models.github.io/cunumpy/examples/),
526
+ [best practices](https://max-models.github.io/cunumpy/best-practices/),
527
+ [troubleshooting](https://max-models.github.io/cunumpy/troubleshooting/),
528
+ [Pyodide](https://max-models.github.io/cunumpy/pyodide/), the
529
+ [API overview](https://max-models.github.io/cunumpy/reference/overview/) and the
530
+ [API reference](https://max-models.github.io/cunumpy/reference/).
531
531
 
532
532
  ### For AI coding assistants
533
533
 
@@ -5,7 +5,7 @@ requires = [ "setuptools", "wheel" ]
5
5
 
6
6
  [project]
7
7
  name = "cunumpy"
8
- version = "0.6.3"
8
+ version = "0.7.1"
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" ]
@@ -28,22 +28,14 @@ dependencies = [
28
28
  ]
29
29
 
30
30
  optional-dependencies.dev = [
31
- "ruff",
32
31
  "cunumpy[test-compiled,docs]",
33
- ]
34
- # https://medium.com/@pratikdomadiya123/build-project-documentation-quickly-with-the-sphinx-python-2a9732b66594
35
- optional-dependencies.docs = [
36
- "ipykernel",
37
- "myst-parser",
38
- "nbconvert",
39
- "nbsphinx",
40
- "numpydoc",
41
- "jupyterlab",
42
32
  "pre-commit",
43
33
  "pyproject-fmt",
44
- "sphinx",
45
- "sphinx-book-theme",
34
+ "ruff",
46
35
  ]
36
+ # the API reference of the docs site (docs/, Astro + Starlight) reads the docstrings with griffe;
37
+ # the shared docs workflow registers an ipykernel even without notebooks
38
+ optional-dependencies.docs = [ "griffe", "ipykernel" ]
47
39
  optional-dependencies.metal = [ "mlx; sys_platform == 'darwin' and platform_machine == 'arm64'" ]
48
40
  optional-dependencies.test = [ "coverage", "pytest", "scipy" ]
49
41
  optional-dependencies.test-compiled = [ "cunumpy[test]", "pyccel" ]
@@ -5,7 +5,7 @@ correct code with `cunumpy`. It ships inside the installed package
5
5
  (`<site-packages>/cunumpy/LLM_GUIDE.md`; locate it with
6
6
  `python -c "import cunumpy, pathlib; print(pathlib.Path(cunumpy.__file__).parent / 'LLM_GUIDE.md')"`).
7
7
  It is self-contained. The full documentation is at
8
- https://max-models.github.io/cunumpy/ and in `docs/source/` of the repository.
8
+ https://max-models.github.io/cunumpy/ and in `docs/` of the repository.
9
9
 
10
10
  ## What cunumpy is
11
11
 
@@ -83,7 +83,7 @@ https://max-models.github.io/cunumpy/ and in `docs/source/` of the repository.
83
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` |
84
84
  | host arrays reach kernels while CuPy is active | `Kernel(..., dispatch="arrays")` / `from_package(..., dispatch="arrays")`: CUDA only for device arguments |
85
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 |
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 |
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:` (several: `with xp.kernels.kernel_outputs(a, b, like=grid, dtype=float) as (buf_a, buf_b):`); `strided=True` passes NumPy views in C order with gaps (`a[:, :n]`) to Pyccel hosts without a copy |
87
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()` |
88
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 |
89
89
  | check host and CUDA kernels take the same parameters | `catalog.check_signatures()` (in a unit test) |
@@ -55,8 +55,8 @@ from cunumpy.xp import (
55
55
  )
56
56
 
57
57
  try:
58
- #: The installed version of cunumpy; ``"0.0.0+unknown"`` without package metadata.
59
58
  __version__ = version("cunumpy")
59
+ """The installed version of cunumpy; ``"0.0.0+unknown"`` without package metadata."""
60
60
  except PackageNotFoundError:
61
61
  __version__ = "0.0.0+unknown"
62
62
 
@@ -380,6 +380,8 @@ def sort_by_key(keys: Any, *arrays: Any, axis: int = 0) -> tuple[Any, ...]:
380
380
  radix sort; on NumPy, integer keys of more than 4096 entries get an LSD
381
381
  radix sort too (one stable ``uint16`` argsort per 16-bit digit the key range
382
382
  needs), about ten times faster than NumPy's stable sort of 64-bit integers.
383
+ On NumPy, a 2-D array sorted along its last axis (component-major markers) is
384
+ gathered row by row, about twice as fast as ``take`` along that axis.
383
385
 
384
386
  Parameters
385
387
  ----------
@@ -437,12 +439,27 @@ def sort_by_key(keys: Any, *arrays: Any, axis: int = 0) -> tuple[Any, ...]:
437
439
  keys[order],
438
440
  order,
439
441
  *(
440
- xpm.take(array, order, axis=array_axis)
442
+ _take_along_axis(xpm, array, order, array_axis)
441
443
  for array, array_axis in zip(arrays, axes, strict=True)
442
444
  ),
443
445
  )
444
446
 
445
447
 
448
+ def _take_along_axis(xpm: Any, array: Any, order: Any, axis: int) -> Any:
449
+ """Return `array` taken at `order` along `axis`, as a new C-contiguous array.
450
+
451
+ A component-major ``(ncomp, n)`` NumPy array is gathered one contiguous row at a
452
+ time, which is about twice as fast as ``take`` along its last axis; everything
453
+ else, and CuPy, goes through ``take``.
454
+ """
455
+ if xpm is np and array.ndim == 2 and axis == 1:
456
+ gathered = np.empty((array.shape[0], order.shape[0]), dtype=array.dtype)
457
+ for row in range(array.shape[0]):
458
+ gathered[row] = array[row][order]
459
+ return gathered
460
+ return xpm.take(array, order, axis=axis)
461
+
462
+
446
463
  def compact_by_mask(mask: Any, *arrays: Any, axis: int = 0) -> int:
447
464
  """Move the entries where `mask` is True to the front of every array, in place.
448
465
 
@@ -116,10 +116,11 @@ def cuda_include_dir() -> str:
116
116
  return str(_CUDA_INCLUDE_DIR)
117
117
 
118
118
 
119
- #: NVRTC options added in debug mode: line information for
120
- #: ``compute-sanitizer``/``nsys`` and bounds checks in the array views
121
- #: (no ``-G``: NVRTC does not support it).
122
119
  DEBUG_OPTIONS = ("-lineinfo", "-DCUNUMPY_BOUNDS_CHECK")
120
+ """NVRTC options added in debug mode: line information for
121
+ ``compute-sanitizer``/``nsys`` and bounds checks in the array views
122
+ (no ``-G``: NVRTC does not support it).
123
+ """
123
124
 
124
125
 
125
126
  class CudaArguments:
@@ -192,10 +192,11 @@ def memory_info() -> tuple[int, int] | None:
192
192
  return cp.cuda.runtime.memGetInfo()
193
193
 
194
194
 
195
- #: Dynamic shared memory per block that every CUDA device provides without an
196
- #: opt-in (48 KiB); also the answer of :func:`max_shared_memory_per_block`
197
- #: without a GPU.
198
195
  DEFAULT_SHARED_MEMORY_PER_BLOCK = 48 * 1024
196
+ """Dynamic shared memory per block that every CUDA device provides without an
197
+ opt-in (48 KiB); also the answer of :func:`max_shared_memory_per_block`
198
+ without a GPU.
199
+ """
199
200
 
200
201
 
201
202
  def max_shared_memory_per_block(
@@ -64,10 +64,11 @@ def _on_device(arg: Any) -> bool:
64
64
  return _is_device_array(arg) or callable(getattr(type(arg), "__cuda_args__", None))
65
65
 
66
66
 
67
- #: Longest name a Fortran compiler accepts. Pyccel names the wrapper module of a
68
- #: host kernel module ``bind_c_<module>``, so a module name longer than
69
- #: ``63 - len("bind_c_")`` characters cannot be compiled with the Fortran backend.
70
67
  FORTRAN_NAME_LIMIT = 63
68
+ """Longest name a Fortran compiler accepts. Pyccel names the wrapper module of a
69
+ host kernel module ``bind_c_<module>``, so a module name longer than
70
+ ``63 - len("bind_c_")`` characters cannot be compiled with the Fortran backend.
71
+ """
71
72
 
72
73
 
73
74
  def _pyccel_stub_parameters(function: Any) -> list[str] | None:
@@ -29,7 +29,6 @@ from typing import Any
29
29
 
30
30
  import numpy as np
31
31
 
32
- #: The subpackages of ``cupyx.scipy``; the same as ``_scipy_backend.SUBMODULES``.
33
32
  SUBPACKAGES = (
34
33
  "fft",
35
34
  "fftpack",
@@ -44,21 +43,19 @@ SUBPACKAGES = (
44
43
  "special",
45
44
  "stats",
46
45
  )
46
+ """The subpackages of ``cupyx.scipy``; the same as ``_scipy_backend.SUBMODULES``."""
47
47
 
48
48
 
49
49
  def _names(text: str) -> frozenset[str]:
50
50
  return frozenset(text.split())
51
51
 
52
52
 
53
- #: The CuPy release whose ``cupyx.scipy`` names :data:`NAMES` lists: the oldest
54
- #: supported one (``cunumpy.xp.MIN_CUPY_VERSION``), so code that runs on the
55
- #: fake runs on every supported CuPy. Newer CuPy releases have more names.
56
53
  NAMES_VERSION = "14.0.0"
54
+ """The CuPy release whose ``cupyx.scipy`` names :data:`NAMES` lists: the oldest
55
+ supported one (``cunumpy.xp.MIN_CUPY_VERSION``), so code that runs on the
56
+ fake runs on every supported CuPy. Newer CuPy releases have more names.
57
+ """
57
58
 
58
- #: The public names of each ``cupyx.scipy`` subpackage in CuPy
59
- #: :data:`NAMES_VERSION`. Check them against an installed CuPy with
60
- #: ``python testing/cupyx_names.py`` (the GPU CI does, in
61
- #: ``tests/unit/test_cupyx_names.py``).
62
59
  NAMES = {
63
60
  "fft": _names(
64
61
  """
@@ -193,6 +190,11 @@ NAMES = {
193
190
  """,
194
191
  ),
195
192
  }
193
+ """The public names of each ``cupyx.scipy`` subpackage in CuPy
194
+ :data:`NAMES_VERSION`. Check them against an installed CuPy with
195
+ ``python testing/cupyx_names.py`` (the GPU CI does, in
196
+ ``tests/unit/test_cupyx_names.py``).
197
+ """
196
198
 
197
199
 
198
200
  def public_names(module: types.ModuleType) -> frozenset[str]:
@@ -438,9 +438,10 @@ class PyccelKernel:
438
438
  return self._outputs
439
439
 
440
440
 
441
- #: The names of the host implementations a kernel can have, see
442
- #: :class:`~cunumpy.kernels.HostImplementations`.
443
441
  HOST_IMPLEMENTATIONS = ("pyccel", "numba", "numpy", "python")
442
+ """The names of the host implementations a kernel can have, see
443
+ :class:`~cunumpy.kernels.HostImplementations`.
444
+ """
444
445
 
445
446
 
446
447
  def _check_implementation(name: str | None) -> str | None:
@@ -536,8 +537,8 @@ def use_host_kernel_implementation(name: str | None) -> Iterator[None]:
536
537
  _KERNEL_IMPLEMENTATION = previous
537
538
 
538
539
 
539
- #: The names of the device implementations a kernel can have.
540
540
  DEVICE_IMPLEMENTATIONS = ("cuda",)
541
+ """The names of the device implementations a kernel can have."""
541
542
 
542
543
 
543
544
  def _check_device_implementation(name: str | None) -> str | None:
@@ -950,7 +951,16 @@ def _with_unit_axis_strides(array: np.ndarray) -> np.ndarray:
950
951
  extent = array.itemsize
951
952
  for axis in reversed(range(array.ndim)):
952
953
  if array.shape[axis] == 1:
953
- strides[axis] = max(strides[axis], extent)
954
+ # Any stride is valid for a length-1 axis, but Pyccel's C-order check
955
+ # wants each stride at least the extent of the axes inside it. Inside
956
+ # a longer axis, use exactly the C stride, so that the outer stride
957
+ # (e.g. 8 in a (2, 1) array with strides (8, 16)) still covers it;
958
+ # a leading length-1 axis may keep a larger stride (a row of a
959
+ # wider array, ``storage[1:2, :n]``).
960
+ if any(length > 1 for length in array.shape[:axis]):
961
+ strides[axis] = extent
962
+ else:
963
+ strides[axis] = max(strides[axis], extent)
954
964
  else:
955
965
  extent = strides[axis] * array.shape[axis]
956
966
  if tuple(strides) == array.strides:
@@ -1052,6 +1062,18 @@ def _same_memory(buffer: Any, out: Any) -> bool:
1052
1062
  )
1053
1063
 
1054
1064
 
1065
+ def _copy_back(buffer: Any, out: Any) -> None:
1066
+ """Write `buffer` into `out`, on its own side, unless they share their memory."""
1067
+ if _same_memory(buffer, out):
1068
+ return
1069
+ if is_gpu(out) and not is_gpu(buffer):
1070
+ out[...] = to_cupy(buffer)
1071
+ elif not is_gpu(out) and is_gpu(buffer):
1072
+ out[...] = to_numpy(buffer)
1073
+ else:
1074
+ out[...] = buffer
1075
+
1076
+
1055
1077
  class _KernelOutput:
1056
1078
  """The context manager of :func:`kernel_output` (a class: cheaper than a generator)."""
1057
1079
 
@@ -1070,16 +1092,8 @@ class _KernelOutput:
1070
1092
  exc: BaseException | None,
1071
1093
  traceback: TracebackType | None,
1072
1094
  ) -> None:
1073
- if exc_type is not None:
1074
- return
1075
- buffer, out = self._buffer, self._out
1076
- if not _same_memory(buffer, out):
1077
- if is_gpu(out) and not is_gpu(buffer):
1078
- out[...] = to_cupy(buffer)
1079
- elif not is_gpu(out) and is_gpu(buffer):
1080
- out[...] = to_numpy(buffer)
1081
- else:
1082
- out[...] = buffer
1095
+ if exc_type is None:
1096
+ _copy_back(self._buffer, self._out)
1083
1097
 
1084
1098
 
1085
1099
  def kernel_output(
@@ -1118,6 +1132,73 @@ def kernel_output(
1118
1132
  return _KernelOutput(out, as_kernel_array(out, like, dtype, strided=strided))
1119
1133
 
1120
1134
 
1135
+ class _KernelOutputs:
1136
+ """The context manager of :func:`kernel_outputs`."""
1137
+
1138
+ __slots__ = ("_buffers", "_outs")
1139
+
1140
+ def __init__(self, outs: tuple[Any, ...], buffers: tuple[Any, ...]) -> None:
1141
+ self._outs = outs
1142
+ self._buffers = buffers
1143
+
1144
+ def __enter__(self) -> tuple[Any, ...]:
1145
+ return self._buffers
1146
+
1147
+ def __exit__(
1148
+ self,
1149
+ exc_type: type[BaseException] | None,
1150
+ exc: BaseException | None,
1151
+ traceback: TracebackType | None,
1152
+ ) -> None:
1153
+ if exc_type is None:
1154
+ for buffer, out in zip(self._buffers, self._outs, strict=True):
1155
+ _copy_back(buffer, out)
1156
+
1157
+
1158
+ def kernel_outputs(
1159
+ *outs: Any, like: Any, dtype: Any = None, strided: bool = False
1160
+ ) -> _KernelOutputs:
1161
+ """Provide the output buffers of several arrays in one block.
1162
+
1163
+ As :func:`~cunumpy.kernels.kernel_output` for each of `outs`, with one
1164
+ context manager for all: cheaper for a kernel that writes several arrays,
1165
+ called at every step. Each buffer is its array itself if
1166
+ :func:`~cunumpy.kernels.as_kernel_array` takes it unchanged, else a
1167
+ converted copy written back into the array when the block ends without an
1168
+ error.
1169
+
1170
+ Parameters
1171
+ ----------
1172
+ *outs : array
1173
+ The arrays the kernel should write.
1174
+ like : array
1175
+ A CuPy array for a device kernel, anything else for a host kernel.
1176
+ dtype : dtype, optional
1177
+ Required dtype of every buffer; by default any.
1178
+ strided : bool, optional
1179
+ As for :func:`~cunumpy.kernels.as_kernel_array`. Default False.
1180
+
1181
+ Returns
1182
+ -------
1183
+ context manager
1184
+ Yields the tuple of buffers to pass to the kernel, in the order of
1185
+ `outs`.
1186
+
1187
+ Examples
1188
+ --------
1189
+ >>> x = np.zeros(2)
1190
+ >>> counts = np.zeros(2, dtype=np.float32)
1191
+ >>> with xp.kernels.kernel_outputs(x, counts, like=x, dtype=float) as (a, b):
1192
+ ... a[:] = 1.0 # x itself
1193
+ ... b[:] = 2.5 # a float64 copy, written back into counts
1194
+ >>> x, counts
1195
+ (array([1., 1.]), array([2.5, 2.5], dtype=float32))
1196
+ """
1197
+ return _KernelOutputs(
1198
+ outs, tuple(as_kernel_array(out, like, dtype, strided=strided) for out in outs)
1199
+ )
1200
+
1201
+
1121
1202
  _SCALAR_ANNOTATIONS = {"int", "float", "bool", "complex", "str"}
1122
1203
 
1123
1204
 
@@ -33,8 +33,8 @@ __all__ = [
33
33
  "morton_scales",
34
34
  ]
35
35
 
36
- #: Largest number of bits per axis that fits a uint64 key, by dimension.
37
36
  MAX_MORTON_LEVELS = {2: 32, 3: 21}
37
+ """Largest number of bits per axis that fits a uint64 key, by dimension."""
38
38
 
39
39
  _SPREAD = {
40
40
  2: (