sparsax 0.7.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 (59) hide show
  1. sparsax-0.7.0/.github/release.yml +19 -0
  2. sparsax-0.7.0/.github/workflows/build-wheels.yml +191 -0
  3. sparsax-0.7.0/.github/workflows/build_docs.yml +63 -0
  4. sparsax-0.7.0/.github/workflows/unittests.yml +73 -0
  5. sparsax-0.7.0/.gitignore +107 -0
  6. sparsax-0.7.0/CMakeLists.txt +146 -0
  7. sparsax-0.7.0/LICENSE.txt +13 -0
  8. sparsax-0.7.0/PKG-INFO +271 -0
  9. sparsax-0.7.0/README.md +257 -0
  10. sparsax-0.7.0/ci/311.yml +27 -0
  11. sparsax-0.7.0/ci/312.yml +26 -0
  12. sparsax-0.7.0/ci/313.yml +26 -0
  13. sparsax-0.7.0/ci/314.yml +27 -0
  14. sparsax-0.7.0/docs/Makefile +22 -0
  15. sparsax-0.7.0/docs/docs_env.yml +35 -0
  16. sparsax-0.7.0/docs/source/_static/custom.css +1 -0
  17. sparsax-0.7.0/docs/source/_static/references.bib +35 -0
  18. sparsax-0.7.0/docs/source/api.rst +109 -0
  19. sparsax-0.7.0/docs/source/conf.py +167 -0
  20. sparsax-0.7.0/docs/source/generated/sparsax.MODE_A.rst +6 -0
  21. sparsax-0.7.0/docs/source/generated/sparsax.MODE_D.rst +6 -0
  22. sparsax-0.7.0/docs/source/generated/sparsax.MODE_DLT.rst +6 -0
  23. sparsax-0.7.0/docs/source/generated/sparsax.MODE_L.rst +6 -0
  24. sparsax-0.7.0/docs/source/generated/sparsax.MODE_LD.rst +6 -0
  25. sparsax-0.7.0/docs/source/generated/sparsax.MODE_LDLT.rst +6 -0
  26. sparsax-0.7.0/docs/source/generated/sparsax.MODE_LT.rst +6 -0
  27. sparsax-0.7.0/docs/source/generated/sparsax.MODE_P.rst +6 -0
  28. sparsax-0.7.0/docs/source/generated/sparsax.MODE_PT.rst +6 -0
  29. sparsax-0.7.0/docs/source/generated/sparsax.cache_size.rst +6 -0
  30. sparsax-0.7.0/docs/source/generated/sparsax.clear_cache.rst +6 -0
  31. sparsax-0.7.0/docs/source/generated/sparsax.factor_solve.rst +6 -0
  32. sparsax-0.7.0/docs/source/generated/sparsax.factorization_count.rst +6 -0
  33. sparsax-0.7.0/docs/source/generated/sparsax.logdet.rst +6 -0
  34. sparsax-0.7.0/docs/source/generated/sparsax.logdet_bcoo.rst +6 -0
  35. sparsax-0.7.0/docs/source/generated/sparsax.lu_solve.rst +6 -0
  36. sparsax-0.7.0/docs/source/generated/sparsax.lu_solve_bcoo.rst +6 -0
  37. sparsax-0.7.0/docs/source/generated/sparsax.pytensor.logdet.rst +6 -0
  38. sparsax-0.7.0/docs/source/generated/sparsax.pytensor.selinv.rst +6 -0
  39. sparsax-0.7.0/docs/source/generated/sparsax.pytensor.solve.rst +6 -0
  40. sparsax-0.7.0/docs/source/generated/sparsax.sample_gaussian.rst +6 -0
  41. sparsax-0.7.0/docs/source/generated/sparsax.selinv.rst +6 -0
  42. sparsax-0.7.0/docs/source/generated/sparsax.set_lu_cache_size.rst +6 -0
  43. sparsax-0.7.0/docs/source/generated/sparsax.set_options.rst +6 -0
  44. sparsax-0.7.0/docs/source/generated/sparsax.solve.rst +6 -0
  45. sparsax-0.7.0/docs/source/generated/sparsax.solve_bcoo.rst +6 -0
  46. sparsax-0.7.0/docs/source/generated/sparsax.update_solve.rst +6 -0
  47. sparsax-0.7.0/docs/source/generated/sparsax.update_solve_bcoo.rst +6 -0
  48. sparsax-0.7.0/docs/source/index.md +25 -0
  49. sparsax-0.7.0/docs/source/installation.md +34 -0
  50. sparsax-0.7.0/docs/source/references.md +5 -0
  51. sparsax-0.7.0/docs/update_version_json.py +40 -0
  52. sparsax-0.7.0/docs/versions.json +3 -0
  53. sparsax-0.7.0/environment.yml +17 -0
  54. sparsax-0.7.0/pyproject.toml +25 -0
  55. sparsax-0.7.0/src/cpp/sparsax.cpp +1575 -0
  56. sparsax-0.7.0/src/sparsax/__init__.py +780 -0
  57. sparsax-0.7.0/src/sparsax/pytensor.py +215 -0
  58. sparsax-0.7.0/tests/test_pytensor.py +150 -0
  59. sparsax-0.7.0/tests/test_smoke.py +733 -0
@@ -0,0 +1,19 @@
1
+ changelog:
2
+ exclude:
3
+ labels:
4
+ - ignore-for-release
5
+ authors:
6
+ - dependabot
7
+ categories:
8
+ - title: Bug Fixes
9
+ labels:
10
+ - bug
11
+ - title: Enhancements
12
+ labels:
13
+ - enhancement
14
+ - title: Maintenance
15
+ labels:
16
+ - maintenance
17
+ - title: Other Changes
18
+ labels:
19
+ - "*"
@@ -0,0 +1,191 @@
1
+ name: Build and publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*" # v1.2.3 → PyPI, v1.2.3a1/b1/rc1 → TestPyPI
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build_wheels:
11
+ name: Build wheels on ${{ matrix.os }}
12
+ permissions:
13
+ id-token: write # MANDATORY for trusted publishing to PyPI
14
+ contents: write # MANDATORY for the GitHub release action
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ include:
19
+ # Linux x86_64 — manylinux via cibuildwheel's default container
20
+ - os: ubuntu-latest
21
+ arch: x86_64
22
+ # Linux aarch64 — emulated via QEMU
23
+ - os: ubuntu-latest
24
+ arch: aarch64
25
+ # macOS Apple Silicon (arm64) -- pinned rather than macos-latest,
26
+ # since macos-latest now resolves to macOS 26 (Tahoe) and
27
+ # Homebrew's bottles for that host embed a minimum target of 26.0,
28
+ # which would make the wheel unusable on any older macOS. Bump
29
+ # this pin (and the target below) deliberately, not automatically.
30
+ - os: macos-15
31
+ arch: arm64
32
+ macosx_deployment_target: "15.0"
33
+ # Windows x86_64 -- pinned rather than windows-latest, since
34
+ # windows-latest now resolves to a windows-2025-vs2026 preview
35
+ # image whose bundled flang compiler fails to compile reference
36
+ # LAPACK's Fortran source (a compiler bug, not fixable here).
37
+ # Bump this pin deliberately once VS2026/flang has matured.
38
+ - os: windows-2022
39
+ arch: AMD64
40
+ runs-on: ${{ matrix.os }}
41
+ env:
42
+ CIBW_BUILD: "cp311-* cp312-* cp313-* cp314-*"
43
+ # jaxlib (needed at build time for XLA FFI headers) has no musllinux
44
+ # wheels published at all -- only manylinux, macOS, and Windows. That's
45
+ # an upstream gap, not something fixable from this workflow, so skip
46
+ # musllinux targets entirely rather than let every build fail on it.
47
+ CIBW_SKIP: "*-musllinux*"
48
+ CIBW_ARCHS: ${{ matrix.arch }}
49
+ CIBW_BUILD_VERBOSITY: 1
50
+ MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
51
+
52
+ # ── Linux ──────────────────────────────────────────────────────────────
53
+ # The default manylinux2014 image is too old: jaxlib and scipy no
54
+ # longer publish wheels for it, which forced pip to backtrack to
55
+ # ancient jax versions and eventually build scipy from source (which
56
+ # then failed on a missing OpenBLAS). Use manylinux_2_28 instead, which
57
+ # is current enough for modern prebuilt wheels of everything involved.
58
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
59
+ CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
60
+ # CHOLMOD (from SuiteSparse) isn't preinstalled in the manylinux
61
+ # container either; manylinux_2_28 has PowerTools enabled by default,
62
+ # so plain yum/dnf install works, no EPEL/CRB needed. However the RPM
63
+ # installs headers to /usr/include/suitesparse/ (not /usr/include/
64
+ # directly), and the project's CMakeLists.txt only guesses that
65
+ # subdirectory under CHOLMOD_PREFIX, which defaults to the Python venv
66
+ # path (wrong) rather than /usr when unset. Pass CHOLMOD_PREFIX=/usr
67
+ # explicitly via scikit-build-core's CMake-args env var so it lands as
68
+ # an actual CMake variable regardless of how the project reads it.
69
+ CIBW_BEFORE_BUILD_LINUX: >
70
+ (yum install -y suitesparse-devel || dnf install -y suitesparse-devel || apt-get install -y libsuitesparse-dev || true) &&
71
+ pip install jax
72
+ CIBW_ENVIRONMENT_LINUX: >
73
+ SKBUILD_CMAKE_ARGS="-DCHOLMOD_PREFIX=/usr"
74
+
75
+ # ── macOS ──────────────────────────────────────────────────────────────
76
+ # CHOLMOD (from SuiteSparse) isn't preinstalled on the macOS runner
77
+ # images, so CMake can't find it. Install it via Homebrew and point
78
+ # CMake at the Homebrew prefix so configure can locate it.
79
+ CIBW_BEFORE_BUILD_MACOS: >
80
+ brew install suitesparse &&
81
+ pip install jax
82
+ CIBW_ENVIRONMENT_MACOS: >
83
+ SKBUILD_CMAKE_ARGS="-DCHOLMOD_PREFIX=$(brew --prefix suitesparse);-DCMAKE_PREFIX_PATH=$(brew --prefix suitesparse)"
84
+
85
+ # ── Windows ────────────────────────────────────────────────────────────
86
+ # CHOLMOD (from SuiteSparse) isn't preinstalled here either. vcpkg
87
+ # ships preinstalled on GitHub-hosted Windows runners, so use it. The
88
+ # umbrella `suitesparse` port only pulls in CHOLMOD's partition module
89
+ # by default -- sparsax needs Modify (cholmod_updown) too, which vcpkg
90
+ # gates behind its `gpl` feature since Modify/MatrixOps/Supernodal are
91
+ # GPL-licensed (this also pulls in MatrixOps and Supernodal, which
92
+ # apt/Homebrew's suitesparse packages build unconditionally but vcpkg
93
+ # treats as opt-in).
94
+ CIBW_BEFORE_BUILD_WINDOWS: >
95
+ pip install delvewheel jax &&
96
+ vcpkg install "suitesparse[gpl]:x64-windows"
97
+ CIBW_ENVIRONMENT_WINDOWS: >
98
+ CMAKE_PREFIX_PATH="C:/vcpkg/installed/x64-windows"
99
+ # --add-path tells delvewheel where to find the CHOLMOD DLLs to bundle,
100
+ # since the vcpkg bin dir isn't on PATH by default.
101
+ CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
102
+ delvewheel repair -w {dest_dir} --add-path C:\vcpkg\installed\x64-windows\bin {wheel}
103
+
104
+ steps:
105
+ - uses: actions/checkout@v6
106
+
107
+ # Enable QEMU for Linux aarch64 cross-compilation.
108
+ - name: Set up QEMU (Linux aarch64 only)
109
+ if: matrix.os == 'ubuntu-latest' && matrix.arch == 'aarch64'
110
+ uses: docker/setup-qemu-action@v3
111
+ with:
112
+ platforms: arm64
113
+
114
+ - name: Build wheels
115
+ uses: pypa/cibuildwheel@v2.21
116
+ with:
117
+ output-dir: wheelhouse
118
+
119
+ - name: Upload wheel artifacts
120
+ uses: actions/upload-artifact@v4
121
+ with:
122
+ name: wheels-${{ matrix.os }}-${{ matrix.arch }}
123
+ path: wheelhouse/*.whl
124
+
125
+ build_sdist:
126
+ name: Build source distribution
127
+ runs-on: ubuntu-latest
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+ - name: Set up Python
131
+ uses: actions/setup-python@v5
132
+ with:
133
+ python-version: "3.12"
134
+ - name: Build sdist
135
+ run: |
136
+ pip install build
137
+ python -m build --sdist --outdir wheelhouse
138
+ - name: Upload sdist artifact
139
+ uses: actions/upload-artifact@v4
140
+ with:
141
+ name: sdist
142
+ path: wheelhouse/*.tar.gz
143
+
144
+ publish:
145
+ name: Publish to PyPI and create release
146
+ needs: [build_wheels, build_sdist]
147
+ runs-on: ubuntu-latest
148
+ environment: pypi
149
+ permissions:
150
+ id-token: write
151
+ contents: write
152
+ # Runs on any tag. test-* tags build+artifact only (no publish step fires).
153
+ if: startsWith(github.ref, 'refs/tags/')
154
+ steps:
155
+ - name: Download all artifacts
156
+ uses: actions/download-artifact@v4
157
+ with:
158
+ path: dist
159
+ merge-multiple: true
160
+
161
+ # v1.2.3a1 / v1.2.3b2 / v1.2.3rc1 → TestPyPI
162
+ - name: Publish pre-release to TestPyPI
163
+ if: |
164
+ startsWith(github.ref_name, 'v') &&
165
+ (contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc'))
166
+ uses: pypa/gh-action-pypi-publish@release/v1
167
+ with:
168
+ repository-url: https://test.pypi.org/legacy/
169
+ packages-dir: dist/
170
+
171
+ # v1.2.3 (clean) → real PyPI
172
+ - name: Publish release to PyPI
173
+ if: |
174
+ startsWith(github.ref_name, 'v') &&
175
+ !contains(github.ref_name, 'a') &&
176
+ !contains(github.ref_name, 'b') &&
177
+ !contains(github.ref_name, 'rc')
178
+ uses: pypa/gh-action-pypi-publish@release/v1
179
+ with:
180
+ packages-dir: dist/
181
+
182
+ # GitHub Release for any v* tag; marked pre-release when applicable
183
+ - name: Create GitHub Release
184
+ if: startsWith(github.ref_name, 'v')
185
+ uses: softprops/action-gh-release@v2
186
+ with:
187
+ prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
188
+ files: dist/*
189
+ generate_release_notes: true
190
+ env:
191
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,63 @@
1
+ name: Build Docs
2
+
3
+ on:
4
+ push:
5
+ # Sequence of patterns matched against refs/tags
6
+ tags:
7
+ - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
8
+ workflow_dispatch:
9
+ inputs:
10
+ version:
11
+ description: Manual Doc Build
12
+ default: test
13
+ required: false
14
+ permissions:
15
+ contents: write # required to push to gh-pages
16
+ jobs:
17
+ docs:
18
+ name: build & push docs
19
+ runs-on: ${{ matrix.os }}
20
+ timeout-minutes: 90
21
+ strategy:
22
+ matrix:
23
+ os: ['ubuntu-latest']
24
+ environment-file: [docs/docs_env.yml]
25
+ experimental: [false]
26
+ defaults:
27
+ run:
28
+ shell: bash -l {0}
29
+
30
+ steps:
31
+ - name: checkout repo
32
+ uses: actions/checkout@v3
33
+
34
+ - name: setup micromamba
35
+ uses: mamba-org/setup-micromamba@v2
36
+ with:
37
+ environment-file: ${{ matrix.environment-file }}
38
+ micromamba-version: 'latest'
39
+
40
+ - name: make docs
41
+ run: |
42
+ pip install -e . --no-deps --force-reinstall
43
+ cd docs; make html
44
+
45
+ - name: commit docs
46
+ run: |
47
+ git clone https://github.com/knaaptime/sparsax.git --branch gh-pages --single-branch gh-pages
48
+ cp -r docs/build/html/* gh-pages/
49
+ cd gh-pages
50
+ git config --local user.email "action@github.com"
51
+ git config --local user.name "GitHub Action"
52
+ git add .
53
+ git commit -m "Update documentation" -a || true
54
+ # The above command will fail if no changes were present,
55
+ # so we ignore the return code.
56
+
57
+ - name: push to gh-pages
58
+ uses: ad-m/github-push-action@master
59
+ with:
60
+ branch: gh-pages
61
+ directory: gh-pages
62
+ github_token: ${{ secrets.GITHUB_TOKEN }}
63
+ force: true
@@ -0,0 +1,73 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches:
8
+ - "*"
9
+ schedule:
10
+ - cron: "0 0 * * 1,4"
11
+
12
+ jobs:
13
+ Test:
14
+ name: ${{ matrix.os }}, ${{ matrix.environment-file }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ os: [ubuntu-latest]
20
+ environment-file:
21
+ - ci/311.yml
22
+ - ci/312.yml
23
+ - ci/313.yml
24
+ - ci/314.yml
25
+ include:
26
+ - environment-file: ci/313.yml
27
+ os: macos-latest
28
+ - environment-file: ci/313.yml
29
+ os: windows-latest
30
+ defaults:
31
+ run:
32
+ shell: bash -l {0}
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: setup micromamba
38
+ uses: mamba-org/setup-micromamba@v2
39
+ with:
40
+ environment-file: ${{ matrix.environment-file }}
41
+ micromamba-version: 'latest'
42
+
43
+ - name: Install package
44
+ run: pip install -e . --no-build-isolation --no-deps
45
+
46
+ - name: Test package
47
+ run: |
48
+ pytest --cov src/sparsax \
49
+ -v \
50
+ -r a \
51
+ -n auto \
52
+ --color yes \
53
+ --cov src \
54
+ --cov-append \
55
+ --cov-report term-missing \
56
+ --cov-report xml \
57
+ tests
58
+ - name: codecov
59
+ uses: codecov/codecov-action@v5
60
+ with:
61
+ token: ${{ secrets.CODECOV_TOKEN }}
62
+ file: ./coverage.xml
63
+ name: cholax-codecov
64
+
65
+ - name: Generate and publish the report
66
+ if: |
67
+ failure()
68
+ && steps.status.outcome == 'failure'
69
+ && github.event_name == 'schedule'
70
+ && github.repository_owner == 'knaaptime'
71
+ uses: xarray-contrib/issue-from-pytest-log@v1
72
+ with:
73
+ log-path: pytest-log.jsonl
@@ -0,0 +1,107 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ # C extensions
6
+ *.so
7
+ notebooks/cache
8
+ **.DS_Store
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ env/
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *,cover
48
+ .hypothesis/
49
+
50
+ # Translations
51
+ *.mo
52
+ *.pot
53
+
54
+ # Django stuff:
55
+ *.log
56
+ local_settings.py
57
+
58
+ # Flask stuff:
59
+ instance/
60
+ .webassets-cache
61
+
62
+ # Scrapy stuff:
63
+ .scrapy
64
+
65
+ # Sphinx documentation
66
+ docs/_build/
67
+
68
+ # PyBuilder
69
+ target/
70
+
71
+ # Jupyter Notebook
72
+ .ipynb_checkpoints
73
+
74
+ # pyenv
75
+ .python-version
76
+
77
+ # celery beat schedule file
78
+ celerybeat-schedule
79
+
80
+ # dotenv
81
+ .env
82
+
83
+ # virtualenv
84
+ .venv
85
+ venv/
86
+ ENV/
87
+
88
+ # Spyder project settings
89
+ .spyderproject
90
+
91
+ # Rope project settings
92
+ .ropeproject
93
+
94
+ # Pycharm
95
+ .idea
96
+
97
+ # VSCode
98
+ .vscode
99
+ # pixi environments
100
+ .pixi
101
+ *.egg-info
102
+
103
+ .claude/
104
+
105
+ **.ruff_cache/
106
+
107
+ **.pytest_cache
@@ -0,0 +1,146 @@
1
+ cmake_minimum_required(VERSION 3.18)
2
+ project(sparsax LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
8
+
9
+ # XLA FFI headers ship with jaxlib (header-only, nothing to link).
10
+ execute_process(
11
+ COMMAND "${Python_EXECUTABLE}" -c "import jax.ffi; print(jax.ffi.include_dir())"
12
+ OUTPUT_VARIABLE XLA_FFI_INCLUDE_DIR
13
+ OUTPUT_STRIP_TRAILING_WHITESPACE
14
+ COMMAND_ERROR_IS_FATAL ANY)
15
+
16
+ # --- CHOLMOD ---
17
+ # Prefer CMake's own package discovery first: modern SuiteSparse (>=6, as
18
+ # shipped by conda-forge, vcpkg, Homebrew, and a from-source `cmake --install`)
19
+ # exports a CHOLMODConfig.cmake with an imported target. This works
20
+ # regardless of where/how CHOLMOD was installed and needs no prefix-guessing.
21
+ # The exported target has been named both `SuiteSparse::CHOLMOD` and
22
+ # `CHOLMOD::CHOLMOD` across SuiteSparse/packaging versions, so check for both.
23
+ find_package(CHOLMOD CONFIG QUIET)
24
+
25
+ set(CHOLMOD_TARGET "")
26
+ if(TARGET SuiteSparse::CHOLMOD)
27
+ set(CHOLMOD_TARGET SuiteSparse::CHOLMOD)
28
+ elseif(TARGET CHOLMOD::CHOLMOD)
29
+ set(CHOLMOD_TARGET CHOLMOD::CHOLMOD)
30
+ endif()
31
+
32
+ if(NOT CHOLMOD_TARGET)
33
+ # No CMake package found (e.g. an older distro package with no config
34
+ # file). Fall back to guessing a prefix: default to the active Python
35
+ # environment's prefix (conda-forge suitesparse), overridable with
36
+ # -DCHOLMOD_PREFIX=... for non-Python-environment installs (system
37
+ # packages, Homebrew, a custom install location).
38
+ if(NOT DEFINED CHOLMOD_PREFIX)
39
+ execute_process(
40
+ COMMAND "${Python_EXECUTABLE}" -c "import sys; print(sys.prefix)"
41
+ OUTPUT_VARIABLE CHOLMOD_PREFIX
42
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
43
+ endif()
44
+
45
+ # conda-forge installs headers/libs under <prefix>/Library on Windows,
46
+ # directly under <prefix> on Linux/macOS.
47
+ if(WIN32)
48
+ set(CHOLMOD_HINT_PREFIX "${CHOLMOD_PREFIX}/Library")
49
+ else()
50
+ set(CHOLMOD_HINT_PREFIX "${CHOLMOD_PREFIX}")
51
+ endif()
52
+
53
+ # HINTS (not PATHS+NO_DEFAULT_PATH): try the guessed prefix first, but
54
+ # still fall through to CMake's normal system search (e.g. Debian/Ubuntu's
55
+ # multiarch /usr/lib/x86_64-linux-gnu/, Homebrew's /opt/homebrew) if that
56
+ # guess is wrong or CHOLMOD lives somewhere standard instead.
57
+ find_library(CHOLMOD_LIBRARY NAMES cholmod libcholmod
58
+ HINTS ${CHOLMOD_HINT_PREFIX}/lib)
59
+ find_path(CHOLMOD_INCLUDE_DIR cholmod.h
60
+ HINTS ${CHOLMOD_HINT_PREFIX}/include/suitesparse ${CHOLMOD_HINT_PREFIX}/include)
61
+
62
+ if(NOT CHOLMOD_LIBRARY OR NOT CHOLMOD_INCLUDE_DIR)
63
+ message(FATAL_ERROR
64
+ "CHOLMOD not found. Checked for a CMake package config, the "
65
+ "prefix ${CHOLMOD_HINT_PREFIX}, and standard system locations. "
66
+ "Install suitesparse (conda install -c conda-forge suitesparse; "
67
+ "apt install libsuitesparse-dev; brew install suitesparse; vcpkg "
68
+ "install suitesparse) or set CHOLMOD_PREFIX / CMAKE_PREFIX_PATH.")
69
+ endif()
70
+ endif()
71
+
72
+ # --- KLU (SuiteSparse sparse LU for non-symmetric matrices) ---
73
+ # Same discovery strategy as CHOLMOD: prefer the CMake package config
74
+ # (SuiteSparse::KLU, shipped by conda-forge et al.), fall back to a prefix guess.
75
+ find_package(KLU CONFIG QUIET)
76
+
77
+ set(KLU_TARGET "")
78
+ if(TARGET SuiteSparse::KLU)
79
+ set(KLU_TARGET SuiteSparse::KLU)
80
+ elseif(TARGET KLU::KLU)
81
+ set(KLU_TARGET KLU::KLU)
82
+ endif()
83
+
84
+ if(NOT KLU_TARGET)
85
+ if(NOT DEFINED KLU_HINT_PREFIX)
86
+ if(DEFINED CHOLMOD_HINT_PREFIX)
87
+ set(KLU_HINT_PREFIX "${CHOLMOD_HINT_PREFIX}")
88
+ else()
89
+ execute_process(
90
+ COMMAND "${Python_EXECUTABLE}" -c "import sys; print(sys.prefix)"
91
+ OUTPUT_VARIABLE KLU_PREFIX
92
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
93
+ if(WIN32)
94
+ set(KLU_HINT_PREFIX "${KLU_PREFIX}/Library")
95
+ else()
96
+ set(KLU_HINT_PREFIX "${KLU_PREFIX}")
97
+ endif()
98
+ endif()
99
+ endif()
100
+ find_library(KLU_LIBRARY NAMES klu libklu HINTS ${KLU_HINT_PREFIX}/lib)
101
+ find_path(KLU_INCLUDE_DIR klu.h
102
+ HINTS ${KLU_HINT_PREFIX}/include/suitesparse ${KLU_HINT_PREFIX}/include)
103
+ if(NOT KLU_LIBRARY OR NOT KLU_INCLUDE_DIR)
104
+ message(FATAL_ERROR
105
+ "KLU not found. Checked for a CMake package config, the prefix "
106
+ "${KLU_HINT_PREFIX}, and standard system locations. Install "
107
+ "suitesparse (it ships KLU alongside CHOLMOD) or set "
108
+ "CMAKE_PREFIX_PATH.")
109
+ endif()
110
+ endif()
111
+
112
+ # Locate nanobind via its Python package.
113
+ execute_process(
114
+ COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
115
+ OUTPUT_VARIABLE NANOBIND_CMAKE_DIR
116
+ OUTPUT_STRIP_TRAILING_WHITESPACE
117
+ COMMAND_ERROR_IS_FATAL ANY)
118
+ list(APPEND CMAKE_PREFIX_PATH "${NANOBIND_CMAKE_DIR}")
119
+ find_package(nanobind REQUIRED CONFIG)
120
+
121
+ nanobind_add_module(sparsax_cpp src/cpp/sparsax.cpp)
122
+
123
+ target_include_directories(sparsax_cpp PRIVATE ${XLA_FFI_INCLUDE_DIR})
124
+
125
+ if(CHOLMOD_TARGET)
126
+ target_link_libraries(sparsax_cpp PRIVATE ${CHOLMOD_TARGET})
127
+ else()
128
+ target_include_directories(sparsax_cpp PRIVATE ${CHOLMOD_INCLUDE_DIR})
129
+ target_link_libraries(sparsax_cpp PRIVATE ${CHOLMOD_LIBRARY})
130
+ endif()
131
+
132
+ if(KLU_TARGET)
133
+ target_link_libraries(sparsax_cpp PRIVATE ${KLU_TARGET})
134
+ else()
135
+ target_include_directories(sparsax_cpp PRIVATE ${KLU_INCLUDE_DIR})
136
+ target_link_libraries(sparsax_cpp PRIVATE ${KLU_LIBRARY})
137
+ endif()
138
+
139
+ # Embed an RPATH covering every library actually linked (CHOLMOD, and
140
+ # whatever BLAS/LAPACK/METIS it pulls in), wherever they really live --
141
+ # conda, Homebrew, apt, or vcpkg -- rather than hardcoding one guessed path.
142
+ set_target_properties(sparsax_cpp PROPERTIES
143
+ BUILD_WITH_INSTALL_RPATH TRUE
144
+ INSTALL_RPATH_USE_LINK_PATH TRUE)
145
+
146
+ install(TARGETS sparsax_cpp LIBRARY DESTINATION .)
@@ -0,0 +1,13 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright 2026 elijah knaap
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.