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.
- pfcuda-0.1.0/.gitignore +13 -0
- pfcuda-0.1.0/CMakeLists.txt +149 -0
- pfcuda-0.1.0/LICENSE +21 -0
- pfcuda-0.1.0/PKG-INFO +258 -0
- pfcuda-0.1.0/README.md +208 -0
- pfcuda-0.1.0/benchmarking/benchmark_pfaffian.py +257 -0
- pfcuda-0.1.0/benchmarking/benchmark_slog_pfaffian.py +265 -0
- pfcuda-0.1.0/benchmarks/pfaffian_comparison.png +0 -0
- pfcuda-0.1.0/benchmarks/pfaffian_data.json +228 -0
- pfcuda-0.1.0/benchmarks/slog_pfaffian_comparison.png +0 -0
- pfcuda-0.1.0/benchmarks/slog_pfaffian_data.json +222 -0
- pfcuda-0.1.0/bindings/jax_bindings.cu +182 -0
- pfcuda-0.1.0/bindings/pybind_bindings.cpp +30 -0
- pfcuda-0.1.0/build.sh +19 -0
- pfcuda-0.1.0/dev +409 -0
- pfcuda-0.1.0/dev.ps1 +18 -0
- pfcuda-0.1.0/include/pfaffian.cuh +20 -0
- pfcuda-0.1.0/include/pfaffian_cpp.h +4 -0
- pfcuda-0.1.0/include/pfaffian_cpu.h +5 -0
- pfcuda-0.1.0/include/pfaffian_sm.cuh +17 -0
- pfcuda-0.1.0/include/pfaffian_utils.cuh +72 -0
- pfcuda-0.1.0/include/slog_pfaffian.cuh +27 -0
- pfcuda-0.1.0/include/slog_pfaffian_lg.cuh +10 -0
- pfcuda-0.1.0/pfcuda/__init__.py +40 -0
- pfcuda-0.1.0/pfcuda/cpp_api.py +4 -0
- pfcuda-0.1.0/pfcuda/cuda_api.py +136 -0
- pfcuda-0.1.0/pfcuda/pfaffian_py.py +51 -0
- pfcuda-0.1.0/pyproject.toml +55 -0
- pfcuda-0.1.0/requirements.txt +20 -0
- pfcuda-0.1.0/src/pfaffian.cu +60 -0
- pfcuda-0.1.0/src/pfaffian_cpu.cpp +106 -0
- pfcuda-0.1.0/src/pfaffian_sm.cu +110 -0
- pfcuda-0.1.0/src/slog_pfaffian.cu +58 -0
- pfcuda-0.1.0/src/slog_pfaffian_lg.cu +216 -0
- pfcuda-0.1.0/test/pfaffian_test.py +110 -0
- pfcuda-0.1.0/test/slog_pfaffian_test.py +107 -0
pfcuda-0.1.0/.gitignore
ADDED
|
@@ -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
|
+
[](https://www.python.org/downloads/)
|
|
54
|
+
[](https://developer.nvidia.com/cuda-toolkit)
|
|
55
|
+
[](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
|
+

|
|
191
|
+

|
|
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
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](https://developer.nvidia.com/cuda-toolkit)
|
|
5
|
+
[](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
|
+

|
|
141
|
+

|
|
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)
|