augax 0.3.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.
- augax-0.3.0/.github/release.yml +19 -0
- augax-0.3.0/.github/workflows/build-wheels.yml +170 -0
- augax-0.3.0/.github/workflows/build_docs.yml +65 -0
- augax-0.3.0/.github/workflows/unittests.yml +75 -0
- augax-0.3.0/.gitignore +107 -0
- augax-0.3.0/CMakeLists.txt +28 -0
- augax-0.3.0/LICENSE.txt +13 -0
- augax-0.3.0/PKG-INFO +105 -0
- augax-0.3.0/README.md +92 -0
- augax-0.3.0/ci/311.yml +30 -0
- augax-0.3.0/ci/312.yml +29 -0
- augax-0.3.0/ci/313.yml +29 -0
- augax-0.3.0/ci/314.yml +30 -0
- augax-0.3.0/ci/check_wheel.py +44 -0
- augax-0.3.0/docs/.gitignore +1 -0
- augax-0.3.0/docs/Makefile +22 -0
- augax-0.3.0/docs/docs_env.yml +24 -0
- augax-0.3.0/docs/source/_static/references.bib +23 -0
- augax-0.3.0/docs/source/api.rst +46 -0
- augax-0.3.0/docs/source/conf.py +110 -0
- augax-0.3.0/docs/source/generated/augax._dispatch.rst +6 -0
- augax-0.3.0/docs/source/generated/augax._ks_dispatch.rst +6 -0
- augax-0.3.0/docs/source/generated/augax._ks_sample_batched.rst +6 -0
- augax-0.3.0/docs/source/generated/augax._require_x64.rst +6 -0
- augax-0.3.0/docs/source/generated/augax._sample_batched.rst +6 -0
- augax-0.3.0/docs/source/generated/augax._seed_from_key.rst +6 -0
- augax-0.3.0/docs/source/generated/augax.ks_sample.rst +6 -0
- augax-0.3.0/docs/source/generated/augax.ks_sample_numpy.rst +6 -0
- augax-0.3.0/docs/source/generated/augax.pg_sample.rst +6 -0
- augax-0.3.0/docs/source/index.md +25 -0
- augax-0.3.0/docs/source/installation.md +45 -0
- augax-0.3.0/docs/source/references.md +5 -0
- augax-0.3.0/docs/update_version_json.py +42 -0
- augax-0.3.0/docs/versions.json +3 -0
- augax-0.3.0/environment.yml +16 -0
- augax-0.3.0/pyproject.toml +39 -0
- augax-0.3.0/src/augax/__init__.py +259 -0
- augax-0.3.0/src/cpp/augax.cpp +1185 -0
- augax-0.3.0/tests/test_batching.py +42 -0
- augax-0.3.0/tests/test_ks.py +113 -0
- augax-0.3.0/tests/test_pg.py +151 -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,170 @@
|
|
|
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
|
+
# augax is self-contained C++ (it links no SuiteSparse and no OpenMP), so
|
|
61
|
+
# no platform needs system libraries. jax comes from pyproject's
|
|
62
|
+
# [build-system] pin -- the build is isolated, so nothing is
|
|
63
|
+
# pip-installed here. cibuildwheel v4 repairs Windows wheels with
|
|
64
|
+
# delvewheel by default.
|
|
65
|
+
|
|
66
|
+
# ── Every platform: test the repaired wheel before it can publish ─────
|
|
67
|
+
# The reference-sampler tests need polyagamma, which has no macOS arm64 or
|
|
68
|
+
# cp314 wheels; they run in unittests.yml. These checks run everywhere.
|
|
69
|
+
CIBW_TEST_REQUIRES: pytest
|
|
70
|
+
CIBW_TEST_COMMAND: >
|
|
71
|
+
python {project}/ci/check_wheel.py &&
|
|
72
|
+
pytest -q {project}/tests/test_batching.py
|
|
73
|
+
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v6
|
|
76
|
+
with:
|
|
77
|
+
fetch-depth: 0 # setuptools-scm needs tags
|
|
78
|
+
|
|
79
|
+
# Enable QEMU for Linux aarch64 cross-compilation.
|
|
80
|
+
- name: Set up QEMU (Linux aarch64 only)
|
|
81
|
+
if: matrix.os == 'ubuntu-latest' && matrix.arch == 'aarch64'
|
|
82
|
+
uses: docker/setup-qemu-action@v3
|
|
83
|
+
with:
|
|
84
|
+
platforms: arm64
|
|
85
|
+
|
|
86
|
+
- name: Build wheels
|
|
87
|
+
# v2.x predates CPython 3.14, so the cp314-* selector above matched
|
|
88
|
+
# nothing and 3.14 wheels were silently never built.
|
|
89
|
+
uses: pypa/cibuildwheel@v4.2.1
|
|
90
|
+
with:
|
|
91
|
+
output-dir: wheelhouse
|
|
92
|
+
|
|
93
|
+
- name: Upload wheel artifacts
|
|
94
|
+
uses: actions/upload-artifact@v4
|
|
95
|
+
with:
|
|
96
|
+
name: wheels-${{ matrix.os }}-${{ matrix.arch }}
|
|
97
|
+
path: wheelhouse/*.whl
|
|
98
|
+
|
|
99
|
+
build_sdist:
|
|
100
|
+
name: Build source distribution
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
with:
|
|
105
|
+
fetch-depth: 0 # setuptools-scm needs tags
|
|
106
|
+
- name: Set up Python
|
|
107
|
+
uses: actions/setup-python@v5
|
|
108
|
+
with:
|
|
109
|
+
python-version: "3.12"
|
|
110
|
+
- name: Build sdist
|
|
111
|
+
run: |
|
|
112
|
+
pip install build
|
|
113
|
+
python -m build --sdist --outdir wheelhouse
|
|
114
|
+
- name: Upload sdist artifact
|
|
115
|
+
uses: actions/upload-artifact@v4
|
|
116
|
+
with:
|
|
117
|
+
name: sdist
|
|
118
|
+
path: wheelhouse/*.tar.gz
|
|
119
|
+
|
|
120
|
+
publish:
|
|
121
|
+
name: Publish to PyPI and create release
|
|
122
|
+
needs: [build_wheels, build_sdist]
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
permissions:
|
|
125
|
+
id-token: write
|
|
126
|
+
contents: write
|
|
127
|
+
# MANDATORY for trusted publishing: the environment name here MUST match the
|
|
128
|
+
# one configured in the PyPI/TestPyPI trusted-publisher UI. Without it, the
|
|
129
|
+
# OIDC token exchange fails with `invalid-publisher` / `environment: MISSING`.
|
|
130
|
+
environment: pypi
|
|
131
|
+
# Runs on any tag. test-* tags build+artifact only (no publish step fires).
|
|
132
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
133
|
+
steps:
|
|
134
|
+
- name: Download all artifacts
|
|
135
|
+
uses: actions/download-artifact@v4
|
|
136
|
+
with:
|
|
137
|
+
path: dist
|
|
138
|
+
merge-multiple: true
|
|
139
|
+
|
|
140
|
+
# v1.2.3a1 / v1.2.3b2 / v1.2.3rc1 → TestPyPI
|
|
141
|
+
- name: Publish pre-release to TestPyPI
|
|
142
|
+
if: |
|
|
143
|
+
startsWith(github.ref_name, 'v') &&
|
|
144
|
+
(contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc'))
|
|
145
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
146
|
+
with:
|
|
147
|
+
repository-url: https://test.pypi.org/legacy/
|
|
148
|
+
packages-dir: dist/
|
|
149
|
+
|
|
150
|
+
# v1.2.3 (clean) → real PyPI
|
|
151
|
+
- name: Publish release to PyPI
|
|
152
|
+
if: |
|
|
153
|
+
startsWith(github.ref_name, 'v') &&
|
|
154
|
+
!contains(github.ref_name, 'a') &&
|
|
155
|
+
!contains(github.ref_name, 'b') &&
|
|
156
|
+
!contains(github.ref_name, 'rc')
|
|
157
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
158
|
+
with:
|
|
159
|
+
packages-dir: dist/
|
|
160
|
+
|
|
161
|
+
# GitHub Release for any v* tag; marked pre-release when applicable
|
|
162
|
+
- name: Create GitHub Release
|
|
163
|
+
if: startsWith(github.ref_name, 'v')
|
|
164
|
+
uses: softprops/action-gh-release@v2
|
|
165
|
+
with:
|
|
166
|
+
prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
|
|
167
|
+
files: dist/*
|
|
168
|
+
generate_release_notes: true
|
|
169
|
+
env:
|
|
170
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,65 @@
|
|
|
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@v4
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0 # setuptools-scm needs tags
|
|
35
|
+
|
|
36
|
+
- name: setup micromamba
|
|
37
|
+
uses: mamba-org/setup-micromamba@v2
|
|
38
|
+
with:
|
|
39
|
+
environment-file: ${{ matrix.environment-file }}
|
|
40
|
+
micromamba-version: 'latest'
|
|
41
|
+
|
|
42
|
+
- name: make docs
|
|
43
|
+
run: |
|
|
44
|
+
pip install -e . --no-deps --force-reinstall
|
|
45
|
+
cd docs; make html
|
|
46
|
+
|
|
47
|
+
- name: commit docs
|
|
48
|
+
run: |
|
|
49
|
+
git clone https://github.com/knaaptime/augax.git --branch gh-pages --single-branch gh-pages
|
|
50
|
+
cp -r docs/build/html/* gh-pages/
|
|
51
|
+
cd gh-pages
|
|
52
|
+
git config --local user.email "action@github.com"
|
|
53
|
+
git config --local user.name "GitHub Action"
|
|
54
|
+
git add .
|
|
55
|
+
git commit -m "Update documentation" -a || true
|
|
56
|
+
# The above command will fail if no changes were present,
|
|
57
|
+
# so we ignore the return code.
|
|
58
|
+
|
|
59
|
+
- name: push to gh-pages
|
|
60
|
+
uses: ad-m/github-push-action@master
|
|
61
|
+
with:
|
|
62
|
+
branch: gh-pages
|
|
63
|
+
directory: gh-pages
|
|
64
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
force: true
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
with:
|
|
37
|
+
fetch-depth: 0 # setuptools-scm needs tags
|
|
38
|
+
|
|
39
|
+
- name: setup micromamba
|
|
40
|
+
uses: mamba-org/setup-micromamba@v1
|
|
41
|
+
with:
|
|
42
|
+
environment-file: ${{ matrix.environment-file }}
|
|
43
|
+
micromamba-version: 'latest'
|
|
44
|
+
|
|
45
|
+
- name: Install package
|
|
46
|
+
run: pip install -e . --no-build-isolation --no-deps
|
|
47
|
+
|
|
48
|
+
- name: Test package
|
|
49
|
+
run: |
|
|
50
|
+
pytest --cov src/augax \
|
|
51
|
+
-v \
|
|
52
|
+
-r a \
|
|
53
|
+
-n auto \
|
|
54
|
+
--color yes \
|
|
55
|
+
--cov src \
|
|
56
|
+
--cov-append \
|
|
57
|
+
--cov-report term-missing \
|
|
58
|
+
--cov-report xml \
|
|
59
|
+
tests
|
|
60
|
+
- name: codecov
|
|
61
|
+
uses: codecov/codecov-action@v5
|
|
62
|
+
with:
|
|
63
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
64
|
+
file: ./coverage.xml
|
|
65
|
+
name: cholax-codecov
|
|
66
|
+
|
|
67
|
+
- name: Generate and publish the report
|
|
68
|
+
if: |
|
|
69
|
+
failure()
|
|
70
|
+
&& steps.status.outcome == 'failure'
|
|
71
|
+
&& github.event_name == 'schedule'
|
|
72
|
+
&& github.repository_owner == 'knaaptime'
|
|
73
|
+
uses: xarray-contrib/issue-from-pytest-log@v1
|
|
74
|
+
with:
|
|
75
|
+
log-path: pytest-log.jsonl
|
augax-0.3.0/.gitignore
ADDED
|
@@ -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,28 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.18)
|
|
2
|
+
project(augax 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
|
+
# Locate nanobind via its Python package.
|
|
17
|
+
execute_process(
|
|
18
|
+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
|
19
|
+
OUTPUT_VARIABLE NANOBIND_CMAKE_DIR
|
|
20
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
21
|
+
COMMAND_ERROR_IS_FATAL ANY)
|
|
22
|
+
list(APPEND CMAKE_PREFIX_PATH "${NANOBIND_CMAKE_DIR}")
|
|
23
|
+
find_package(nanobind REQUIRED CONFIG)
|
|
24
|
+
|
|
25
|
+
nanobind_add_module(augax_cpp NOMINSIZE src/cpp/augax.cpp)
|
|
26
|
+
target_include_directories(augax_cpp PRIVATE ${XLA_FFI_INCLUDE_DIR})
|
|
27
|
+
|
|
28
|
+
install(TARGETS augax_cpp LIBRARY DESTINATION .)
|
augax-0.3.0/LICENSE.txt
ADDED
|
@@ -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.
|
augax-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: augax
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: JAX-native on-device augmentation draws (Pólya-Gamma) as XLA FFI custom calls
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: jax>=0.9.0
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Provides-Extra: test
|
|
10
|
+
Requires-Dist: pytest; extra == "test"
|
|
11
|
+
Requires-Dist: scipy; extra == "test"
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# augax
|
|
15
|
+
|
|
16
|
+
On-device draws for **conditionally Gaussian augmentation** in JAX, each an XLA
|
|
17
|
+
FFI custom call: **Pólya-Gamma** sampling (`pg_sample`) and the **Kolmogorov
|
|
18
|
+
scale** of the logistic's normal scale mixture (`ks_sample`).
|
|
19
|
+
|
|
20
|
+
`augax.pg_sample(h, z, key)` draws `omega ~ PG(h, z)` from *inside* a JIT'd /
|
|
21
|
+
`lax.scan`-ed / `pmap`-ed program with **no host round-trip** — the native
|
|
22
|
+
replacement for `jax.pure_callback(random_polyagamma, ...)`, which pays a
|
|
23
|
+
device↔host round-trip on every Pólya-Gamma Gibbs sweep and serializes under
|
|
24
|
+
`jax.pmap`.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import jax, augax
|
|
28
|
+
jax.config.update("jax_enable_x64", True)
|
|
29
|
+
|
|
30
|
+
@jax.jit
|
|
31
|
+
def omega_update(h, z, key):
|
|
32
|
+
return augax.pg_sample(h, z, key)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Method
|
|
36
|
+
|
|
37
|
+
`pg_sample` follows the hybrid dispatch of
|
|
38
|
+
[`zoj613/polyagamma`](https://github.com/zoj613/polyagamma) (`method=None`),
|
|
39
|
+
changed where that scheme is measurably biased:
|
|
40
|
+
|
|
41
|
+
- **`h > 50`**: a normal with the exact Pólya-Gamma mean and variance.
|
|
42
|
+
- **`8 <= h <= 50`**: the **saddlepoint** rejection sampler (Windle, Polson &
|
|
43
|
+
Scott 2014, arXiv:1405.0506), O(1) per accepted draw.
|
|
44
|
+
- **`h = 1`** (Bernoulli/logit), **or integer `h <= 4` with `|z| <= 1`**: the
|
|
45
|
+
exact **Devroye** sampler for `PG(1, z)` (Polson, Scott & Windle 2013), summed
|
|
46
|
+
`h` times.
|
|
47
|
+
- **Otherwise, `1 < h < 8`** — e.g. Negative-Binomial `h = y + alpha`: the
|
|
48
|
+
**alternate** sampler (Windle, Polson & Scott 2014, Section 4).
|
|
49
|
+
- **`h < 1`**: the Gamma-series representation — 20 terms plus a remainder
|
|
50
|
+
matched in mean and variance — exact in both moments.
|
|
51
|
+
|
|
52
|
+
Each departure from `polyagamma` 2.0.2 removes a bias measured with 2M draws
|
|
53
|
+
against the exact mean and variance:
|
|
54
|
+
|
|
55
|
+
- **Alternate sampler, `1 < h < 4`, small `|z|`**: `polyagamma` runs +0.4–0.9% in
|
|
56
|
+
the mean and +1–5% in the variance. Its right-hand envelope uses the constant
|
|
57
|
+
`(π/2)^(h/2)`, where both its Gamma proposal and the density's tail carry
|
|
58
|
+
`(π/2)^h`; augax uses the latter.
|
|
59
|
+
- **`h < 1`**: the alternate method assumes `h >= 1`; below it `polyagamma` runs
|
|
60
|
+
−0.4–0.8% in the mean. augax uses the Gamma series.
|
|
61
|
+
- **`4 < h < 8` with `|z| <= 4`**: `polyagamma` uses the saddlepoint
|
|
62
|
+
approximation, 1–2% low in the variance. augax uses the exact alternate
|
|
63
|
+
sampler.
|
|
64
|
+
|
|
65
|
+
The saddlepoint and alternate samplers are ported from `polyagamma`
|
|
66
|
+
(BSD-3-Clause). Every regime is tested against the closed-form mean and
|
|
67
|
+
variance.
|
|
68
|
+
|
|
69
|
+
The RNG is a per-call `xoshiro256**` seeded (via `splitmix64`) from the JAX PRNG
|
|
70
|
+
key, so draws are reproducible and each call owns its stream — safe when several
|
|
71
|
+
threads call at once. Normals and exponentials come from ziggurat samplers.
|
|
72
|
+
|
|
73
|
+
An array longer than 65,536 elements is split into blocks of that size, each
|
|
74
|
+
with its own stream, and the blocks are drawn in parallel on every hardware
|
|
75
|
+
thread (set `AUGAX_NUM_THREADS` to cap it). The blocks depend only on the array
|
|
76
|
+
length, so a draw is the same on any machine and any thread count. On a large
|
|
77
|
+
Negative-Binomial model (8.1M cells) this takes the draw from 3.6 s to 0.33 s on
|
|
78
|
+
16 threads.
|
|
79
|
+
|
|
80
|
+
## The Kolmogorov scale
|
|
81
|
+
|
|
82
|
+
A standard logistic disturbance is a normal scale mixture,
|
|
83
|
+
`eps | lam ~ N(0, lam^2)` with `lam = 2K` and `K` Kolmogorov distributed
|
|
84
|
+
(Stefanski 1991). `ks_sample(u, key)` draws `lam` given `eps = u`, which makes a
|
|
85
|
+
logistic latent variable conditionally Gaussian: the propagated disturbance of a
|
|
86
|
+
latent SAR logit, for one. The draw is exact: `1/lam^2` is proposed from an
|
|
87
|
+
inverse Gaussian, `IG(1/|u|, 1)` (a chi-square at `|u| < 1e-4`), and accepted
|
|
88
|
+
with probability `S(v) = f(v) e^{v/2} <= 1`, `f` the density of `(2K)^2`,
|
|
89
|
+
evaluated lazily by the squeeze of an alternating series — the Kolmogorov
|
|
90
|
+
series above `v = 4/3` and its Jacobi transform below, where the first's
|
|
91
|
+
partial sums are not bounds (Holmes & Held 2006). It is checked against the
|
|
92
|
+
identity `E[exp(-t^2 lam^2 / 2)] = pi t / sinh(pi t)` and against the
|
|
93
|
+
posterior moments by quadrature. `ks_sample_numpy(u, rng)` is the same C++
|
|
94
|
+
sampler for host callers.
|
|
95
|
+
|
|
96
|
+
## Status
|
|
97
|
+
|
|
98
|
+
- ✅ All `h > 0`: the logit (`h = 1`), Negative-Binomial (`h = y + alpha`) and
|
|
99
|
+
large-shape regimes.
|
|
100
|
+
- Dispatch is automatic: the sampler is selected from `(h, z)` with no API
|
|
101
|
+
change.
|
|
102
|
+
- ✅ `ks_sample`: exact for every `u`, on the device and on the host.
|
|
103
|
+
|
|
104
|
+
CPU-only (the sampler is cheap scalar work; the point is to keep it on-device
|
|
105
|
+
next to the rest of a JIT'd Gibbs sweep).
|
augax-0.3.0/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# augax
|
|
2
|
+
|
|
3
|
+
On-device draws for **conditionally Gaussian augmentation** in JAX, each an XLA
|
|
4
|
+
FFI custom call: **Pólya-Gamma** sampling (`pg_sample`) and the **Kolmogorov
|
|
5
|
+
scale** of the logistic's normal scale mixture (`ks_sample`).
|
|
6
|
+
|
|
7
|
+
`augax.pg_sample(h, z, key)` draws `omega ~ PG(h, z)` from *inside* a JIT'd /
|
|
8
|
+
`lax.scan`-ed / `pmap`-ed program with **no host round-trip** — the native
|
|
9
|
+
replacement for `jax.pure_callback(random_polyagamma, ...)`, which pays a
|
|
10
|
+
device↔host round-trip on every Pólya-Gamma Gibbs sweep and serializes under
|
|
11
|
+
`jax.pmap`.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import jax, augax
|
|
15
|
+
jax.config.update("jax_enable_x64", True)
|
|
16
|
+
|
|
17
|
+
@jax.jit
|
|
18
|
+
def omega_update(h, z, key):
|
|
19
|
+
return augax.pg_sample(h, z, key)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Method
|
|
23
|
+
|
|
24
|
+
`pg_sample` follows the hybrid dispatch of
|
|
25
|
+
[`zoj613/polyagamma`](https://github.com/zoj613/polyagamma) (`method=None`),
|
|
26
|
+
changed where that scheme is measurably biased:
|
|
27
|
+
|
|
28
|
+
- **`h > 50`**: a normal with the exact Pólya-Gamma mean and variance.
|
|
29
|
+
- **`8 <= h <= 50`**: the **saddlepoint** rejection sampler (Windle, Polson &
|
|
30
|
+
Scott 2014, arXiv:1405.0506), O(1) per accepted draw.
|
|
31
|
+
- **`h = 1`** (Bernoulli/logit), **or integer `h <= 4` with `|z| <= 1`**: the
|
|
32
|
+
exact **Devroye** sampler for `PG(1, z)` (Polson, Scott & Windle 2013), summed
|
|
33
|
+
`h` times.
|
|
34
|
+
- **Otherwise, `1 < h < 8`** — e.g. Negative-Binomial `h = y + alpha`: the
|
|
35
|
+
**alternate** sampler (Windle, Polson & Scott 2014, Section 4).
|
|
36
|
+
- **`h < 1`**: the Gamma-series representation — 20 terms plus a remainder
|
|
37
|
+
matched in mean and variance — exact in both moments.
|
|
38
|
+
|
|
39
|
+
Each departure from `polyagamma` 2.0.2 removes a bias measured with 2M draws
|
|
40
|
+
against the exact mean and variance:
|
|
41
|
+
|
|
42
|
+
- **Alternate sampler, `1 < h < 4`, small `|z|`**: `polyagamma` runs +0.4–0.9% in
|
|
43
|
+
the mean and +1–5% in the variance. Its right-hand envelope uses the constant
|
|
44
|
+
`(π/2)^(h/2)`, where both its Gamma proposal and the density's tail carry
|
|
45
|
+
`(π/2)^h`; augax uses the latter.
|
|
46
|
+
- **`h < 1`**: the alternate method assumes `h >= 1`; below it `polyagamma` runs
|
|
47
|
+
−0.4–0.8% in the mean. augax uses the Gamma series.
|
|
48
|
+
- **`4 < h < 8` with `|z| <= 4`**: `polyagamma` uses the saddlepoint
|
|
49
|
+
approximation, 1–2% low in the variance. augax uses the exact alternate
|
|
50
|
+
sampler.
|
|
51
|
+
|
|
52
|
+
The saddlepoint and alternate samplers are ported from `polyagamma`
|
|
53
|
+
(BSD-3-Clause). Every regime is tested against the closed-form mean and
|
|
54
|
+
variance.
|
|
55
|
+
|
|
56
|
+
The RNG is a per-call `xoshiro256**` seeded (via `splitmix64`) from the JAX PRNG
|
|
57
|
+
key, so draws are reproducible and each call owns its stream — safe when several
|
|
58
|
+
threads call at once. Normals and exponentials come from ziggurat samplers.
|
|
59
|
+
|
|
60
|
+
An array longer than 65,536 elements is split into blocks of that size, each
|
|
61
|
+
with its own stream, and the blocks are drawn in parallel on every hardware
|
|
62
|
+
thread (set `AUGAX_NUM_THREADS` to cap it). The blocks depend only on the array
|
|
63
|
+
length, so a draw is the same on any machine and any thread count. On a large
|
|
64
|
+
Negative-Binomial model (8.1M cells) this takes the draw from 3.6 s to 0.33 s on
|
|
65
|
+
16 threads.
|
|
66
|
+
|
|
67
|
+
## The Kolmogorov scale
|
|
68
|
+
|
|
69
|
+
A standard logistic disturbance is a normal scale mixture,
|
|
70
|
+
`eps | lam ~ N(0, lam^2)` with `lam = 2K` and `K` Kolmogorov distributed
|
|
71
|
+
(Stefanski 1991). `ks_sample(u, key)` draws `lam` given `eps = u`, which makes a
|
|
72
|
+
logistic latent variable conditionally Gaussian: the propagated disturbance of a
|
|
73
|
+
latent SAR logit, for one. The draw is exact: `1/lam^2` is proposed from an
|
|
74
|
+
inverse Gaussian, `IG(1/|u|, 1)` (a chi-square at `|u| < 1e-4`), and accepted
|
|
75
|
+
with probability `S(v) = f(v) e^{v/2} <= 1`, `f` the density of `(2K)^2`,
|
|
76
|
+
evaluated lazily by the squeeze of an alternating series — the Kolmogorov
|
|
77
|
+
series above `v = 4/3` and its Jacobi transform below, where the first's
|
|
78
|
+
partial sums are not bounds (Holmes & Held 2006). It is checked against the
|
|
79
|
+
identity `E[exp(-t^2 lam^2 / 2)] = pi t / sinh(pi t)` and against the
|
|
80
|
+
posterior moments by quadrature. `ks_sample_numpy(u, rng)` is the same C++
|
|
81
|
+
sampler for host callers.
|
|
82
|
+
|
|
83
|
+
## Status
|
|
84
|
+
|
|
85
|
+
- ✅ All `h > 0`: the logit (`h = 1`), Negative-Binomial (`h = y + alpha`) and
|
|
86
|
+
large-shape regimes.
|
|
87
|
+
- Dispatch is automatic: the sampler is selected from `(h, z)` with no API
|
|
88
|
+
change.
|
|
89
|
+
- ✅ `ks_sample`: exact for every `u`, on the device and on the host.
|
|
90
|
+
|
|
91
|
+
CPU-only (the sampler is cheap scalar work; the point is to keep it on-device
|
|
92
|
+
next to the rest of a JIT'd Gibbs sweep).
|