pfcuda 0.1.0__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 (36) hide show
  1. pfcuda-0.1.0/.gitignore +13 -0
  2. pfcuda-0.1.0/CMakeLists.txt +149 -0
  3. pfcuda-0.1.0/LICENSE +21 -0
  4. pfcuda-0.1.0/PKG-INFO +258 -0
  5. pfcuda-0.1.0/README.md +208 -0
  6. pfcuda-0.1.0/benchmarking/benchmark_pfaffian.py +257 -0
  7. pfcuda-0.1.0/benchmarking/benchmark_slog_pfaffian.py +265 -0
  8. pfcuda-0.1.0/benchmarks/pfaffian_comparison.png +0 -0
  9. pfcuda-0.1.0/benchmarks/pfaffian_data.json +228 -0
  10. pfcuda-0.1.0/benchmarks/slog_pfaffian_comparison.png +0 -0
  11. pfcuda-0.1.0/benchmarks/slog_pfaffian_data.json +222 -0
  12. pfcuda-0.1.0/bindings/jax_bindings.cu +182 -0
  13. pfcuda-0.1.0/bindings/pybind_bindings.cpp +30 -0
  14. pfcuda-0.1.0/build.sh +19 -0
  15. pfcuda-0.1.0/dev +409 -0
  16. pfcuda-0.1.0/dev.ps1 +18 -0
  17. pfcuda-0.1.0/include/pfaffian.cuh +20 -0
  18. pfcuda-0.1.0/include/pfaffian_cpp.h +4 -0
  19. pfcuda-0.1.0/include/pfaffian_cpu.h +5 -0
  20. pfcuda-0.1.0/include/pfaffian_sm.cuh +17 -0
  21. pfcuda-0.1.0/include/pfaffian_utils.cuh +72 -0
  22. pfcuda-0.1.0/include/slog_pfaffian.cuh +27 -0
  23. pfcuda-0.1.0/include/slog_pfaffian_lg.cuh +10 -0
  24. pfcuda-0.1.0/pfcuda/__init__.py +40 -0
  25. pfcuda-0.1.0/pfcuda/cpp_api.py +4 -0
  26. pfcuda-0.1.0/pfcuda/cuda_api.py +136 -0
  27. pfcuda-0.1.0/pfcuda/pfaffian_py.py +51 -0
  28. pfcuda-0.1.0/pyproject.toml +55 -0
  29. pfcuda-0.1.0/requirements.txt +20 -0
  30. pfcuda-0.1.0/src/pfaffian.cu +60 -0
  31. pfcuda-0.1.0/src/pfaffian_cpu.cpp +106 -0
  32. pfcuda-0.1.0/src/pfaffian_sm.cu +110 -0
  33. pfcuda-0.1.0/src/slog_pfaffian.cu +58 -0
  34. pfcuda-0.1.0/src/slog_pfaffian_lg.cu +216 -0
  35. pfcuda-0.1.0/test/pfaffian_test.py +110 -0
  36. pfcuda-0.1.0/test/slog_pfaffian_test.py +107 -0
@@ -0,0 +1,13 @@
1
+ # virtualenv
2
+ .venv/
3
+
4
+ # build outputs
5
+ build/
6
+ .build/
7
+ pfcuda/*.so
8
+ *.egg-info/
9
+
10
+ # python
11
+ __pycache__/
12
+ *.py[cod]
13
+ .pytest_cache/
@@ -0,0 +1,149 @@
1
+ cmake_minimum_required(VERSION 3.18)
2
+
3
+ # Target whatever GPU the build machine has, unless the caller pins an
4
+ # architecture (e.g. -DCMAKE_CUDA_ARCHITECTURES=75 for a portable wheel).
5
+ # Must be decided before project() so CUDA compiler detection picks it up.
6
+ if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
7
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
8
+ set(CMAKE_CUDA_ARCHITECTURES native)
9
+ else()
10
+ set(CMAKE_CUDA_ARCHITECTURES 70 75 80 86)
11
+ endif()
12
+ endif()
13
+
14
+ # A plain `pip install .` runs with whatever PATH the user's shell has, and a
15
+ # CUDA Toolkit installed outside PATH is the common case. Look in the usual
16
+ # places before project() gives up with "CUDA compiler identification unknown".
17
+ if(NOT DEFINED CMAKE_CUDA_COMPILER AND NOT DEFINED ENV{CUDACXX})
18
+ find_program(PFCUDA_NVCC nvcc
19
+ HINTS ENV CUDA_HOME ENV CUDA_PATH ENV CUDA_ROOT /usr/local/cuda /opt/cuda
20
+ PATH_SUFFIXES bin)
21
+ if(PFCUDA_NVCC)
22
+ set(CMAKE_CUDA_COMPILER "${PFCUDA_NVCC}")
23
+ else()
24
+ message(FATAL_ERROR
25
+ "Could not find nvcc, the CUDA compiler.\n"
26
+ "PfCUDA compiles CUDA kernels from source, so the CUDA Toolkit must "
27
+ "be installed: https://developer.nvidia.com/cuda-downloads\n"
28
+ "If it is installed somewhere unusual, point CMake at it with "
29
+ "CUDA_HOME=/path/to/cuda or CUDACXX=/path/to/nvcc.\n"
30
+ "Searched: PATH, $CUDA_HOME/bin, $CUDA_PATH/bin, $CUDA_ROOT/bin, "
31
+ "/usr/local/cuda/bin, /opt/cuda/bin.")
32
+ endif()
33
+ endif()
34
+
35
+ project(cu_pfaffian LANGUAGES CXX CUDA)
36
+
37
+ set(CMAKE_CXX_STANDARD 17)
38
+ set(CMAKE_CUDA_STANDARD 17)
39
+
40
+ # Drops the built libraries into the source package so `import pfcuda` needs no
41
+ # install step. Off for wheel builds, which stage them through install() below.
42
+ option(PFCUDA_DEV_INPLACE "Write built libraries into the source pfcuda/ package" OFF)
43
+
44
+ # Build GPU implementation's object file
45
+ find_package(CUDAToolkit REQUIRED)
46
+
47
+ # Resolve the interpreter that owns jax and pybind11. scikit-build-core presets
48
+ # Python_EXECUTABLE during `pip install`; ./dev passes it explicitly. Falling
49
+ # back to a bare `python3` would find the system interpreter, which has no jax.
50
+ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
51
+
52
+ execute_process(
53
+ COMMAND ${Python_EXECUTABLE} -c "import jax; import os; print(os.path.dirname(jax.__file__))"
54
+ OUTPUT_VARIABLE JAX_BASE_DIR
55
+ OUTPUT_STRIP_TRAILING_WHITESPACE
56
+ RESULT_VARIABLE JAX_LOOKUP_RESULT
57
+ ERROR_VARIABLE JAX_LOOKUP_ERROR
58
+ )
59
+
60
+ execute_process(
61
+ COMMAND ${Python_EXECUTABLE} -c "import jaxlib; import os; print(os.path.dirname(jaxlib.__file__))"
62
+ OUTPUT_VARIABLE JAXLIB_BASE_DIR
63
+ OUTPUT_STRIP_TRAILING_WHITESPACE
64
+ RESULT_VARIABLE JAXLIB_LOOKUP_RESULT
65
+ ERROR_VARIABLE JAXLIB_LOOKUP_ERROR
66
+ )
67
+
68
+ if(NOT JAX_LOOKUP_RESULT EQUAL 0 OR NOT JAXLIB_LOOKUP_RESULT EQUAL 0)
69
+ message(FATAL_ERROR
70
+ "Could not import jax/jaxlib with ${Python_EXECUTABLE}.\n"
71
+ "The CUDA bindings need jaxlib's XLA FFI headers at compile time.\n"
72
+ "Install them into that interpreter, or point CMake at the right one "
73
+ "with -DPython_EXECUTABLE=/path/to/python.\n\n"
74
+ "${JAX_LOOKUP_ERROR}${JAXLIB_LOOKUP_ERROR}")
75
+ endif()
76
+
77
+ add_library(cu_pfaffian_lib SHARED
78
+ src/pfaffian.cu
79
+ src/pfaffian_sm.cu
80
+ src/slog_pfaffian.cu
81
+ src/slog_pfaffian_lg.cu
82
+ bindings/jax_bindings.cu
83
+ )
84
+
85
+ target_include_directories(cu_pfaffian_lib PRIVATE
86
+ include
87
+ ${JAX_BASE_DIR}
88
+ ${JAXLIB_BASE_DIR}/include
89
+ )
90
+
91
+ set_target_properties(cu_pfaffian_lib PROPERTIES
92
+ CUDA_SEPARABLE_COMPILATION ON
93
+ CUDA_RESOLVE_DEVICE_SYMBOLS ON
94
+ POSITION_INDEPENDENT_CODE ON
95
+ OUTPUT_NAME "cupfaffian"
96
+ )
97
+
98
+ target_link_libraries(cu_pfaffian_lib PRIVATE
99
+ CUDA::cudart
100
+ )
101
+
102
+ target_compile_options(cu_pfaffian_lib PRIVATE
103
+ $<$<COMPILE_LANGUAGE:CUDA>:-O3 --use_fast_math>
104
+ $<$<COMPILE_LANGUAGE:CXX>:-O3>
105
+ )
106
+
107
+
108
+ # Build CPU implementation's object file
109
+ # Prefer the pybind11 shipped with Python_EXECUTABLE over any system-wide copy,
110
+ # so the extension module matches the interpreter that will import it.
111
+ if(NOT pybind11_DIR)
112
+ execute_process(
113
+ COMMAND ${Python_EXECUTABLE} -m pybind11 --cmakedir
114
+ OUTPUT_VARIABLE pybind11_DIR
115
+ OUTPUT_STRIP_TRAILING_WHITESPACE
116
+ ERROR_QUIET
117
+ )
118
+ endif()
119
+
120
+ # Let pybind11 use FindPython (and therefore Python_EXECUTABLE) rather than the
121
+ # deprecated PythonInterp path, unless a build backend already chose for us.
122
+ if(NOT DEFINED PYBIND11_FINDPYTHON)
123
+ set(PYBIND11_FINDPYTHON ON)
124
+ endif()
125
+ find_package(pybind11 REQUIRED)
126
+
127
+ pybind11_add_module(pfaffian_lib
128
+ bindings/pybind_bindings.cpp
129
+ src/pfaffian_cpu.cpp
130
+ )
131
+
132
+ target_include_directories(pfaffian_lib PRIVATE
133
+ include
134
+ )
135
+
136
+ target_compile_options(pfaffian_lib PRIVATE -O3)
137
+
138
+ set_target_properties(pfaffian_lib PROPERTIES
139
+ OUTPUT_NAME "cpupfaffian"
140
+ )
141
+
142
+ if(PFCUDA_DEV_INPLACE)
143
+ set_target_properties(cu_pfaffian_lib pfaffian_lib PROPERTIES
144
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/pfcuda"
145
+ )
146
+ endif()
147
+
148
+ install(TARGETS cu_pfaffian_lib DESTINATION pfcuda)
149
+ install(TARGETS pfaffian_lib DESTINATION pfcuda)
pfcuda-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Muhammad Mouiz Ghouri
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.
pfcuda-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.2
2
+ Name: pfcuda
3
+ Version: 0.1.0
4
+ Summary: GPU-accelerated Pfaffian computation for JAX, with C++ and NumPy CPU backends.
5
+ Keywords: pfaffian,cuda,gpu,jax,skew-symmetric,linear-algebra
6
+ Author: Muhammad Mouiz Ghouri
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 Muhammad Mouiz Ghouri
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Intended Audience :: Science/Research
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: POSIX :: Linux
33
+ Classifier: Programming Language :: C++
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
36
+ Classifier: Topic :: Scientific/Engineering :: Physics
37
+ Classifier: Environment :: GPU :: NVIDIA CUDA
38
+ Project-URL: Homepage, https://github.com/Mou1z/PfCUDA
39
+ Project-URL: Repository, https://github.com/Mou1z/PfCUDA
40
+ Project-URL: Issues, https://github.com/Mou1z/PfCUDA/issues
41
+ Requires-Python: >=3.10
42
+ Requires-Dist: jax>=0.5.0
43
+ Requires-Dist: jaxlib>=0.5.0
44
+ Requires-Dist: numpy>=1.24
45
+ Provides-Extra: cuda12
46
+ Requires-Dist: jax[cuda12]>=0.5.0; extra == "cuda12"
47
+ Provides-Extra: cuda13
48
+ Requires-Dist: jax[cuda13]>=0.5.0; extra == "cuda13"
49
+ Description-Content-Type: text/markdown
50
+
51
+ # PfCUDA
52
+
53
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
54
+ [![CUDA Supported](https://img.shields.io/badge/CUDA-Supported-76B900.svg)](https://developer.nvidia.com/cuda-toolkit)
55
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
56
+
57
+ **GPU-accelerated Pfaffian computation for JAX.**
58
+
59
+ For a `2n x 2n` skew-symmetric matrix `A`, the Pfaffian satisfies `Pf(A)^2 = det(A)`.
60
+ PfCUDA computes it with CUDA kernels exposed through JAX's foreign function
61
+ interface, with differentiable (JVP) support, plus C++ and NumPy CPU backends.
62
+
63
+ ---
64
+
65
+ ## ⚙️ Requirements
66
+
67
+ PfCUDA compiles CUDA kernels from source at install time, so you need a CUDA
68
+ Toolkit — not just a driver.
69
+
70
+ | Requirement | Notes |
71
+ | --- | --- |
72
+ | NVIDIA GPU | compute capability 3.0+ |
73
+ | CUDA Toolkit | provides `nvcc`; found via `PATH`, `CUDA_HOME` or `/usr/local/cuda` |
74
+ | CMake ≥ 3.18, C++17 compiler | |
75
+ | Python ≥ 3.10 | with development headers (`python3-dev`) |
76
+ | `jax` ≥ 0.5.0 | `jax.ffi` became public in 0.5.0; tested against 0.11.1 |
77
+
78
+ **Platform support** follows JAX's own CUDA support:
79
+
80
+ | Platform | Status |
81
+ | --- | --- |
82
+ | Linux x86_64 / aarch64 | Supported |
83
+ | Windows via WSL2 | Works; JAX calls WSL2 CUDA support experimental |
84
+ | Native Windows | **Not supported** — JAX has no CUDA wheels for it |
85
+ | macOS | **Not supported** — no NVIDIA CUDA |
86
+
87
+ On native Windows, install [WSL2](https://learn.microsoft.com/windows/wsl/install)
88
+ and use PfCUDA inside it. The CPU backends (`pfaffian_cpu`, `pfaffian_py`) work
89
+ anywhere the package can be built.
90
+
91
+ ---
92
+
93
+ ## 📦 Installation
94
+
95
+ Pick the extra matching your driver's CUDA version (`nvidia-smi` reports it):
96
+
97
+ ```bash
98
+ pip install "pfcuda[cuda13]" # or "pfcuda[cuda12]"
99
+ ```
100
+
101
+ This builds from source and takes a couple of minutes. The extra matters: plain
102
+ `pip install pfcuda` pulls a **CPU-only** jax, which compiles fine but then
103
+ fails at call time with `No FFI handler registered`.
104
+
105
+ If `nvcc` lives somewhere unusual, point the build at it:
106
+
107
+ ```bash
108
+ CUDA_HOME=/path/to/cuda pip install "pfcuda[cuda13]"
109
+ ```
110
+
111
+ To install from a clone instead:
112
+
113
+ ```bash
114
+ git clone https://github.com/Mou1z/PfCUDA.git
115
+ cd PfCUDA
116
+ pip install .
117
+ ```
118
+
119
+ > **Upgrading JAX?** The compiled kernels bind to the XLA FFI ABI of the jaxlib
120
+ > they were built against, and that ABI is not stable across releases. After
121
+ > upgrading `jax`/`jaxlib`, reinstall PfCUDA with
122
+ > `pip install --force-reinstall --no-binary pfcuda pfcuda`.
123
+
124
+ ---
125
+
126
+ ## 🚀 Quick Start
127
+
128
+ ```python
129
+ import numpy as np
130
+ import jax, jax.numpy as jnp
131
+ import pfcuda
132
+
133
+ jax.config.update("jax_enable_x64", True)
134
+
135
+ A = np.array([
136
+ [ 0.0, 1.0, 2.0, 3.0],
137
+ [-1.0, 0.0, 4.0, 5.0],
138
+ [-2.0, -4.0, 0.0, 6.0],
139
+ [-3.0, -5.0, -6.0, 0.0],
140
+ ], dtype=np.float64)
141
+
142
+ pfcuda.pfaffian(jnp.array(A)) # GPU, matrices up to 32x32 -> 8.0
143
+ pfcuda.pfaffian_cpu(A.copy()) # C++ backend, any even size -> 8.0
144
+ pfcuda.pfaffian_py(A.copy()) # NumPy reference -> 8.0
145
+
146
+ # slog_pfaffian is for large matrices and requires n >= 34.
147
+ rng = np.random.default_rng(0)
148
+ B = rng.normal(size=(64, 64))
149
+ B = B - B.T
150
+ log_abs, sign = pfcuda.slog_pfaffian(jnp.array(B))
151
+ ```
152
+
153
+ `pfcuda.CUDA_AVAILABLE` reports whether the GPU backend loaded; the CPU
154
+ functions remain usable when it did not.
155
+
156
+ ---
157
+
158
+ ## 📚 API Reference
159
+
160
+ All inputs must be **square, skew-symmetric and of even dimension**. Odd
161
+ dimensions return zero (`pfaffian`) or `(-inf, 0)` (`slog_pfaffian`).
162
+
163
+ | Function | Backend | Dtypes | Size | Returns |
164
+ | --- | --- | --- | --- | --- |
165
+ | `pfaffian(A)` | GPU (CUDA/JAX) | `float32`, `float64`, `complex64`, `complex128` | `n ≤ 32` | `Pf(A)` |
166
+ | `slog_pfaffian(A)` | GPU (CUDA/JAX) | `float32`, `float64`, `complex64`, `complex128` | `n ≥ 34` | `(log|Pf|, sign)` |
167
+ | `pfaffian_cpu(A)` | CPU (C++) | `float64` only | any even | `Pf(A)` |
168
+ | `pfaffian_py(A)` | CPU (NumPy) | any NumPy float dtype | any even | `Pf(A)` |
169
+
170
+ Two behaviours worth knowing:
171
+
172
+ - **`pfaffian_cpu` is float64-only.** Other dtypes are cast on the way in, so
173
+ complex input silently loses its imaginary part. Use `pfaffian` for complex
174
+ matrices.
175
+ - **`pfaffian_cpu` and `pfaffian_py` overwrite their input** for `n > 4`. Pass a
176
+ copy if you still need the matrix.
177
+
178
+ `pfaffian` and `slog_pfaffian` define custom JVP rules, so they work under
179
+ `jax.grad`, `jax.jit` and `jax.vmap`.
180
+
181
+ ---
182
+
183
+ ## 📊 Benchmarks vs. Lrux
184
+
185
+ Comparison against [Lrux](https://pypi.org/project/lrux/), an existing
186
+ JAX-based library. Scripts are in `benchmarking/`; raw data in `benchmarks/`.
187
+ All times are **milliseconds per call**, measured end to end from Python
188
+ (so they include JAX dispatch overhead, not kernel time alone).
189
+
190
+ ![pfaffian() benchmark](benchmarks/pfaffian_comparison.png)
191
+ ![slog_pfaffian() benchmark](benchmarks/slog_pfaffian_comparison.png)
192
+
193
+ **Small matrices — `pfaffian`, n = 2…32.** PfCUDA leads by 9.8× at n=2
194
+ (0.168 ms vs 1.639 ms), narrowing to 1.16× at n=32 (3.07 ms vs 3.56 ms).
195
+
196
+ **Large matrices — `slog_pfaffian`, n = 100…4900.** Lrux is faster at n=100
197
+ (10.2 ms vs 3.1 ms); PfCUDA overtakes it between n=100 and n=500 and pulls
198
+ ahead from there, reaching 20.4× at n=4900 (997.6 ms vs 20 369.9 ms).
199
+
200
+ **Accuracy.** Log-accuracy error stays between 10⁻¹¹ and 10⁻¹⁶ across all sizes
201
+ for both libraries.
202
+
203
+ ---
204
+
205
+ ## 🛠️ Implementation
206
+
207
+ - **GPU** — CUDA kernels behind JAX's FFI with custom JVP rules.
208
+ `src/pfaffian.cu`, `src/pfaffian_sm.cu`, `src/slog_pfaffian.cu`,
209
+ `src/slog_pfaffian_lg.cu`, `bindings/jax_bindings.cu`
210
+ - **CPU (C++)** — pybind11 module. `src/pfaffian_cpu.cpp`,
211
+ `bindings/pybind_bindings.cpp`
212
+ - **CPU (NumPy)** — pure-Python reference. `pfcuda/pfaffian_py.py`
213
+
214
+ ---
215
+
216
+ ## 🧑‍💻 Development
217
+
218
+ Use the `./dev` driver rather than `pip install .`. It builds the libraries
219
+ directly into `pfcuda/`, so an incremental rebuild takes about 5 seconds
220
+ instead of two minutes.
221
+
222
+ ```bash
223
+ git clone https://github.com/Mou1z/PfCUDA.git
224
+ cd PfCUDA
225
+ ./dev
226
+ ```
227
+
228
+ The first run creates `.venv`, picks the CUDA 12 or 13 `jax` plugin to match
229
+ your driver, installs dependencies and configures CMake.
230
+
231
+ | Command | Purpose |
232
+ | --- | --- |
233
+ | `./dev` | Incremental build |
234
+ | `./dev test` | Build, then run the test suite |
235
+ | `./dev bench` | Build, then run the benchmarks |
236
+ | `./dev doctor` | Report on the environment; changes nothing |
237
+ | `./dev clean` | Remove build outputs (`--all` also removes `.venv`) |
238
+
239
+ On Windows use `.\dev.ps1 <command>` from PowerShell, which forwards into WSL2.
240
+ `./dev --help` lists the environment overrides. If something fails to build,
241
+ `./dev doctor` reports what it found.
242
+
243
+ ---
244
+
245
+ ## 📝 Citation
246
+
247
+ > **Muhammad Mouiz Ghouri**, *"Optimized Pfaffian Computation and Its
248
+ > Differentiation: From CPU Implementations to GPU Acceleration"*,
249
+ > Eötvös Loránd University, Budapest, Hungary, 2026.
250
+
251
+ ## 🤝 Contributing
252
+
253
+ Issues and pull requests are welcome — particularly for broader dtype coverage
254
+ on the CPU backend, additional GPU architectures, and benchmark comparisons.
255
+
256
+ ## 📄 License
257
+
258
+ [MIT](LICENSE)
pfcuda-0.1.0/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # PfCUDA
2
+
3
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
4
+ [![CUDA Supported](https://img.shields.io/badge/CUDA-Supported-76B900.svg)](https://developer.nvidia.com/cuda-toolkit)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **GPU-accelerated Pfaffian computation for JAX.**
8
+
9
+ For a `2n x 2n` skew-symmetric matrix `A`, the Pfaffian satisfies `Pf(A)^2 = det(A)`.
10
+ PfCUDA computes it with CUDA kernels exposed through JAX's foreign function
11
+ interface, with differentiable (JVP) support, plus C++ and NumPy CPU backends.
12
+
13
+ ---
14
+
15
+ ## ⚙️ Requirements
16
+
17
+ PfCUDA compiles CUDA kernels from source at install time, so you need a CUDA
18
+ Toolkit — not just a driver.
19
+
20
+ | Requirement | Notes |
21
+ | --- | --- |
22
+ | NVIDIA GPU | compute capability 3.0+ |
23
+ | CUDA Toolkit | provides `nvcc`; found via `PATH`, `CUDA_HOME` or `/usr/local/cuda` |
24
+ | CMake ≥ 3.18, C++17 compiler | |
25
+ | Python ≥ 3.10 | with development headers (`python3-dev`) |
26
+ | `jax` ≥ 0.5.0 | `jax.ffi` became public in 0.5.0; tested against 0.11.1 |
27
+
28
+ **Platform support** follows JAX's own CUDA support:
29
+
30
+ | Platform | Status |
31
+ | --- | --- |
32
+ | Linux x86_64 / aarch64 | Supported |
33
+ | Windows via WSL2 | Works; JAX calls WSL2 CUDA support experimental |
34
+ | Native Windows | **Not supported** — JAX has no CUDA wheels for it |
35
+ | macOS | **Not supported** — no NVIDIA CUDA |
36
+
37
+ On native Windows, install [WSL2](https://learn.microsoft.com/windows/wsl/install)
38
+ and use PfCUDA inside it. The CPU backends (`pfaffian_cpu`, `pfaffian_py`) work
39
+ anywhere the package can be built.
40
+
41
+ ---
42
+
43
+ ## 📦 Installation
44
+
45
+ Pick the extra matching your driver's CUDA version (`nvidia-smi` reports it):
46
+
47
+ ```bash
48
+ pip install "pfcuda[cuda13]" # or "pfcuda[cuda12]"
49
+ ```
50
+
51
+ This builds from source and takes a couple of minutes. The extra matters: plain
52
+ `pip install pfcuda` pulls a **CPU-only** jax, which compiles fine but then
53
+ fails at call time with `No FFI handler registered`.
54
+
55
+ If `nvcc` lives somewhere unusual, point the build at it:
56
+
57
+ ```bash
58
+ CUDA_HOME=/path/to/cuda pip install "pfcuda[cuda13]"
59
+ ```
60
+
61
+ To install from a clone instead:
62
+
63
+ ```bash
64
+ git clone https://github.com/Mou1z/PfCUDA.git
65
+ cd PfCUDA
66
+ pip install .
67
+ ```
68
+
69
+ > **Upgrading JAX?** The compiled kernels bind to the XLA FFI ABI of the jaxlib
70
+ > they were built against, and that ABI is not stable across releases. After
71
+ > upgrading `jax`/`jaxlib`, reinstall PfCUDA with
72
+ > `pip install --force-reinstall --no-binary pfcuda pfcuda`.
73
+
74
+ ---
75
+
76
+ ## 🚀 Quick Start
77
+
78
+ ```python
79
+ import numpy as np
80
+ import jax, jax.numpy as jnp
81
+ import pfcuda
82
+
83
+ jax.config.update("jax_enable_x64", True)
84
+
85
+ A = np.array([
86
+ [ 0.0, 1.0, 2.0, 3.0],
87
+ [-1.0, 0.0, 4.0, 5.0],
88
+ [-2.0, -4.0, 0.0, 6.0],
89
+ [-3.0, -5.0, -6.0, 0.0],
90
+ ], dtype=np.float64)
91
+
92
+ pfcuda.pfaffian(jnp.array(A)) # GPU, matrices up to 32x32 -> 8.0
93
+ pfcuda.pfaffian_cpu(A.copy()) # C++ backend, any even size -> 8.0
94
+ pfcuda.pfaffian_py(A.copy()) # NumPy reference -> 8.0
95
+
96
+ # slog_pfaffian is for large matrices and requires n >= 34.
97
+ rng = np.random.default_rng(0)
98
+ B = rng.normal(size=(64, 64))
99
+ B = B - B.T
100
+ log_abs, sign = pfcuda.slog_pfaffian(jnp.array(B))
101
+ ```
102
+
103
+ `pfcuda.CUDA_AVAILABLE` reports whether the GPU backend loaded; the CPU
104
+ functions remain usable when it did not.
105
+
106
+ ---
107
+
108
+ ## 📚 API Reference
109
+
110
+ All inputs must be **square, skew-symmetric and of even dimension**. Odd
111
+ dimensions return zero (`pfaffian`) or `(-inf, 0)` (`slog_pfaffian`).
112
+
113
+ | Function | Backend | Dtypes | Size | Returns |
114
+ | --- | --- | --- | --- | --- |
115
+ | `pfaffian(A)` | GPU (CUDA/JAX) | `float32`, `float64`, `complex64`, `complex128` | `n ≤ 32` | `Pf(A)` |
116
+ | `slog_pfaffian(A)` | GPU (CUDA/JAX) | `float32`, `float64`, `complex64`, `complex128` | `n ≥ 34` | `(log|Pf|, sign)` |
117
+ | `pfaffian_cpu(A)` | CPU (C++) | `float64` only | any even | `Pf(A)` |
118
+ | `pfaffian_py(A)` | CPU (NumPy) | any NumPy float dtype | any even | `Pf(A)` |
119
+
120
+ Two behaviours worth knowing:
121
+
122
+ - **`pfaffian_cpu` is float64-only.** Other dtypes are cast on the way in, so
123
+ complex input silently loses its imaginary part. Use `pfaffian` for complex
124
+ matrices.
125
+ - **`pfaffian_cpu` and `pfaffian_py` overwrite their input** for `n > 4`. Pass a
126
+ copy if you still need the matrix.
127
+
128
+ `pfaffian` and `slog_pfaffian` define custom JVP rules, so they work under
129
+ `jax.grad`, `jax.jit` and `jax.vmap`.
130
+
131
+ ---
132
+
133
+ ## 📊 Benchmarks vs. Lrux
134
+
135
+ Comparison against [Lrux](https://pypi.org/project/lrux/), an existing
136
+ JAX-based library. Scripts are in `benchmarking/`; raw data in `benchmarks/`.
137
+ All times are **milliseconds per call**, measured end to end from Python
138
+ (so they include JAX dispatch overhead, not kernel time alone).
139
+
140
+ ![pfaffian() benchmark](benchmarks/pfaffian_comparison.png)
141
+ ![slog_pfaffian() benchmark](benchmarks/slog_pfaffian_comparison.png)
142
+
143
+ **Small matrices — `pfaffian`, n = 2…32.** PfCUDA leads by 9.8× at n=2
144
+ (0.168 ms vs 1.639 ms), narrowing to 1.16× at n=32 (3.07 ms vs 3.56 ms).
145
+
146
+ **Large matrices — `slog_pfaffian`, n = 100…4900.** Lrux is faster at n=100
147
+ (10.2 ms vs 3.1 ms); PfCUDA overtakes it between n=100 and n=500 and pulls
148
+ ahead from there, reaching 20.4× at n=4900 (997.6 ms vs 20 369.9 ms).
149
+
150
+ **Accuracy.** Log-accuracy error stays between 10⁻¹¹ and 10⁻¹⁶ across all sizes
151
+ for both libraries.
152
+
153
+ ---
154
+
155
+ ## 🛠️ Implementation
156
+
157
+ - **GPU** — CUDA kernels behind JAX's FFI with custom JVP rules.
158
+ `src/pfaffian.cu`, `src/pfaffian_sm.cu`, `src/slog_pfaffian.cu`,
159
+ `src/slog_pfaffian_lg.cu`, `bindings/jax_bindings.cu`
160
+ - **CPU (C++)** — pybind11 module. `src/pfaffian_cpu.cpp`,
161
+ `bindings/pybind_bindings.cpp`
162
+ - **CPU (NumPy)** — pure-Python reference. `pfcuda/pfaffian_py.py`
163
+
164
+ ---
165
+
166
+ ## 🧑‍💻 Development
167
+
168
+ Use the `./dev` driver rather than `pip install .`. It builds the libraries
169
+ directly into `pfcuda/`, so an incremental rebuild takes about 5 seconds
170
+ instead of two minutes.
171
+
172
+ ```bash
173
+ git clone https://github.com/Mou1z/PfCUDA.git
174
+ cd PfCUDA
175
+ ./dev
176
+ ```
177
+
178
+ The first run creates `.venv`, picks the CUDA 12 or 13 `jax` plugin to match
179
+ your driver, installs dependencies and configures CMake.
180
+
181
+ | Command | Purpose |
182
+ | --- | --- |
183
+ | `./dev` | Incremental build |
184
+ | `./dev test` | Build, then run the test suite |
185
+ | `./dev bench` | Build, then run the benchmarks |
186
+ | `./dev doctor` | Report on the environment; changes nothing |
187
+ | `./dev clean` | Remove build outputs (`--all` also removes `.venv`) |
188
+
189
+ On Windows use `.\dev.ps1 <command>` from PowerShell, which forwards into WSL2.
190
+ `./dev --help` lists the environment overrides. If something fails to build,
191
+ `./dev doctor` reports what it found.
192
+
193
+ ---
194
+
195
+ ## 📝 Citation
196
+
197
+ > **Muhammad Mouiz Ghouri**, *"Optimized Pfaffian Computation and Its
198
+ > Differentiation: From CPU Implementations to GPU Acceleration"*,
199
+ > Eötvös Loránd University, Budapest, Hungary, 2026.
200
+
201
+ ## 🤝 Contributing
202
+
203
+ Issues and pull requests are welcome — particularly for broader dtype coverage
204
+ on the CPU backend, additional GPU architectures, and benchmark comparisons.
205
+
206
+ ## 📄 License
207
+
208
+ [MIT](LICENSE)