jaxht 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.
- jaxht-0.1.0/.github/workflows/release.yml +52 -0
- jaxht-0.1.0/.github/workflows/test.yml +36 -0
- jaxht-0.1.0/.gitignore +25 -0
- jaxht-0.1.0/CHANGELOG.md +34 -0
- jaxht-0.1.0/CITATION.cff +27 -0
- jaxht-0.1.0/CLAUDE.md +195 -0
- jaxht-0.1.0/DISCREPANCIES.md +84 -0
- jaxht-0.1.0/LICENSE +21 -0
- jaxht-0.1.0/PKG-INFO +165 -0
- jaxht-0.1.0/README.md +129 -0
- jaxht-0.1.0/ROADMAP.md +270 -0
- jaxht-0.1.0/docs/accuracy.md +101 -0
- jaxht-0.1.0/docs/consumers.md +105 -0
- jaxht-0.1.0/docs/design.md +183 -0
- jaxht-0.1.0/docs/findings_phase0.md +102 -0
- jaxht-0.1.0/docs/gpu.md +218 -0
- jaxht-0.1.0/docs/masked.md +197 -0
- jaxht-0.1.0/docs/motivation.md +120 -0
- jaxht-0.1.0/docs/offgrid.md +121 -0
- jaxht-0.1.0/docs/performance.md +145 -0
- jaxht-0.1.0/pixi.lock +3819 -0
- jaxht-0.1.0/pyproject.toml +118 -0
- jaxht-0.1.0/scripts/accuracy_sweep.py +104 -0
- jaxht-0.1.0/scripts/bench_transforms.py +126 -0
- jaxht-0.1.0/scripts/exploratory/phase0_floor.py +165 -0
- jaxht-0.1.0/scripts/gpu_check.py +224 -0
- jaxht-0.1.0/scripts/gpu_diagnostic.py +714 -0
- jaxht-0.1.0/scripts/masked_sweep.py +161 -0
- jaxht-0.1.0/scripts/offgrid_sweep.py +138 -0
- jaxht-0.1.0/scripts/profile_adjoint.py +188 -0
- jaxht-0.1.0/scripts/profile_offgrid_forward.py +209 -0
- jaxht-0.1.0/scripts/profile_ongrid_compile.py +192 -0
- jaxht-0.1.0/scripts/submit_compile_probe_2048.sh +50 -0
- jaxht-0.1.0/scripts/submit_confirm_2048.sh +51 -0
- jaxht-0.1.0/scripts/submit_gpu_diagnostic.sh +92 -0
- jaxht-0.1.0/scripts/submit_profile_adjoint.sh +48 -0
- jaxht-0.1.0/scripts/submit_profile_offgrid.sh +56 -0
- jaxht-0.1.0/src/jht/__init__.py +86 -0
- jaxht-0.1.0/src/jht/_nufft.py +171 -0
- jaxht-0.1.0/src/jht/_recursion.py +572 -0
- jaxht-0.1.0/src/jht/_reference.py +143 -0
- jaxht-0.1.0/src/jht/analysis.py +54 -0
- jaxht-0.1.0/src/jht/diff.py +100 -0
- jaxht-0.1.0/src/jht/healpix.py +350 -0
- jaxht-0.1.0/src/jht/masked.py +383 -0
- jaxht-0.1.0/src/jht/offgrid.py +209 -0
- jaxht-0.1.0/src/jht/py.typed +0 -0
- jaxht-0.1.0/src/jht/weights.py +101 -0
- jaxht-0.1.0/tests/.gitkeep +0 -0
- jaxht-0.1.0/tests/test_accuracy.py +90 -0
- jaxht-0.1.0/tests/test_api.py +72 -0
- jaxht-0.1.0/tests/test_floor.py +64 -0
- jaxht-0.1.0/tests/test_gpu.py +83 -0
- jaxht-0.1.0/tests/test_grad.py +258 -0
- jaxht-0.1.0/tests/test_healpix.py +132 -0
- jaxht-0.1.0/tests/test_masked.py +262 -0
- jaxht-0.1.0/tests/test_offgrid.py +253 -0
- jaxht-0.1.0/tests/test_recursion.py +223 -0
- jaxht-0.1.0/tests/test_vectorized.py +140 -0
- jaxht-0.1.0/tests/test_weights.py +79 -0
- jaxht-0.1.0/tests/test_wiener.py +346 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Build and publish to PyPI on a version tag (e.g. `git tag v0.1.0 && git push --tags`).
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) -- no API token / secret.
|
|
5
|
+
#
|
|
6
|
+
# ONE-TIME PyPI SETUP before the first tag (the project need not exist yet -- use a
|
|
7
|
+
# "pending publisher"): on https://pypi.org -> your account -> Publishing, add a
|
|
8
|
+
# trusted publisher with:
|
|
9
|
+
# PyPI project name: jaxht (distribution name; the import name stays `jht`)
|
|
10
|
+
# Owner: jrcheshire
|
|
11
|
+
# Repository name: jht
|
|
12
|
+
# Workflow name: release.yml
|
|
13
|
+
# Environment name: pypi
|
|
14
|
+
# Keep __version__ in src/jht/__init__.py in sync with the tag (the build reads the
|
|
15
|
+
# version from there via hatch).
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
push:
|
|
19
|
+
tags: ["v*"]
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
name: Build sdist + wheel
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- run: python -m pip install --upgrade build twine
|
|
31
|
+
- run: python -m build
|
|
32
|
+
- run: python -m twine check dist/*
|
|
33
|
+
- uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
name: Publish to PyPI (Trusted Publishing)
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: pypi
|
|
44
|
+
url: https://pypi.org/project/jaxht/
|
|
45
|
+
permissions:
|
|
46
|
+
id-token: write # OIDC for Trusted Publishing -- no token needed
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/download-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint-and-test:
|
|
14
|
+
name: Lint + typecheck + tests (linux)
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 30
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: prefix-dev/setup-pixi@v0.8.10
|
|
21
|
+
with:
|
|
22
|
+
# `locked: true` runs `pixi install --locked`, so any pyproject.toml
|
|
23
|
+
# drift from pixi.lock surfaces as a CI failure (the co-commit invariant).
|
|
24
|
+
locked: true
|
|
25
|
+
cache: true
|
|
26
|
+
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: pixi run lint
|
|
29
|
+
|
|
30
|
+
- name: Typecheck
|
|
31
|
+
run: pixi run typecheck
|
|
32
|
+
|
|
33
|
+
# The default pixi env pulls the ducc0/healpy oracles, so this is the full
|
|
34
|
+
# gated parity suite (CPU, float64). The GPU parity gate auto-skips (no device).
|
|
35
|
+
- name: Test
|
|
36
|
+
run: pixi run test
|
jaxht-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Pixi / envs
|
|
10
|
+
.pixi/
|
|
11
|
+
.venv/
|
|
12
|
+
|
|
13
|
+
# Tool caches
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
|
|
18
|
+
# Scratch / run outputs (not fixtures)
|
|
19
|
+
runs/
|
|
20
|
+
scratch/
|
|
21
|
+
gpu_diag_results.jsonl
|
|
22
|
+
|
|
23
|
+
# OS / editor
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.swp
|
jaxht-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to jht are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-06-07
|
|
10
|
+
|
|
11
|
+
Inaugural release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **On-grid transforms** — spin-0 and spin-2 `synthesis` (a_lm → map) and the
|
|
15
|
+
exact adjoint `adjoint_synthesis`, validated to machine precision against
|
|
16
|
+
healpy **and** ducc0 (spin-2 inverse at the HEALPix floor, no s2fft-style
|
|
17
|
+
structural defect).
|
|
18
|
+
- **Approximate inverse** — `bare_analysis` (Sᵀ W) and `map2alm` (jht's own ring
|
|
19
|
+
quadrature weights + Jacobi iteration; ~1e-13 on band-limited maps).
|
|
20
|
+
- **Quadrature weights** — `ring_weights`, `pixel_weights`.
|
|
21
|
+
- **Partial-sky / masked** — `pseudo_alm`, `deconvolve` (cut-sky CG), `wiener`
|
|
22
|
+
(masked Wiener filter / MUSE inner solve), `constrained_realization`.
|
|
23
|
+
- **Off-grid (NUFFT)** — `synthesis_general` / `adjoint_synthesis_general` for
|
|
24
|
+
spin 0–3 at arbitrary pointings; alm- **and** pointing-differentiable under
|
|
25
|
+
native autodiff. JAX-native replacement for ducc0's `synthesis_general`.
|
|
26
|
+
- **Differentiable real-DOF interface** — `synthesis_real`, `analysis_real`,
|
|
27
|
+
`bandpower`, with the `alm_to_real` / `real_to_alm` isometry and the
|
|
28
|
+
`alm_metric_weight` (2 − δ_m0) bridge.
|
|
29
|
+
- **GPU (CUDA)** — pure JAX, runs on GPU with no code change; a parity harness
|
|
30
|
+
(`scripts/gpu_check.py`) and a single-slot performance diagnostic
|
|
31
|
+
(`scripts/gpu_diagnostic.py`).
|
|
32
|
+
|
|
33
|
+
[Unreleased]: https://github.com/jrcheshire/jht/compare/v0.1.0...HEAD
|
|
34
|
+
[0.1.0]: https://github.com/jrcheshire/jht/releases/tag/v0.1.0
|
jaxht-0.1.0/CITATION.cff
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use jht in your research, please cite it."
|
|
3
|
+
title: "jht: JAX-native spherical harmonic transforms"
|
|
4
|
+
abstract: >-
|
|
5
|
+
A clean-room, pure-JAX implementation of the forward and inverse spherical
|
|
6
|
+
harmonic transform (map <-> a_lm) for spin-0 and spin-2 fields on the HEALPix
|
|
7
|
+
ring pixelization, plus an off-grid (NUFFT) path for arbitrary pointings.
|
|
8
|
+
GPU-capable, fully differentiable under JAX autodiff, and dependency-controlled
|
|
9
|
+
(JAX + numpy at runtime).
|
|
10
|
+
type: software
|
|
11
|
+
authors:
|
|
12
|
+
- given-names: James
|
|
13
|
+
family-names: Cheshire
|
|
14
|
+
email: cheshire@caltech.edu
|
|
15
|
+
affiliation: California Institute of Technology
|
|
16
|
+
version: 0.1.0
|
|
17
|
+
date-released: 2026-06-07
|
|
18
|
+
license: MIT
|
|
19
|
+
repository-code: "https://github.com/jrcheshire/jht"
|
|
20
|
+
keywords:
|
|
21
|
+
- spherical harmonic transform
|
|
22
|
+
- HEALPix
|
|
23
|
+
- JAX
|
|
24
|
+
- GPU
|
|
25
|
+
- automatic differentiation
|
|
26
|
+
- NUFFT
|
|
27
|
+
- cosmic microwave background
|
jaxht-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# jht — JAX Harmonic Transforms
|
|
2
|
+
|
|
3
|
+
JAX-native spherical harmonic transforms. GPU-capable, fully differentiable,
|
|
4
|
+
and **dependency-controlled** — pure JAX + numpy at runtime, no compiled C++
|
|
5
|
+
extension and no heavyweight third-party SHT library. Born from the BICEP/Keck
|
|
6
|
+
`bk-jax` pipeline's need for a transform it can run on GPU, differentiate
|
|
7
|
+
through, and *own* the numerics of.
|
|
8
|
+
|
|
9
|
+
**Status (2026-06-06): scaffolding only.** This file, `ROADMAP.md`, and the
|
|
10
|
+
`docs/` notes were written to carry the design into a future development
|
|
11
|
+
session. No transforms are implemented yet. **Read `ROADMAP.md` next; the first
|
|
12
|
+
action is the Phase-0 feasibility spike.**
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## What jht is, in one paragraph
|
|
17
|
+
|
|
18
|
+
A focused, clean-room implementation of the forward and inverse spherical
|
|
19
|
+
harmonic transform (map ↔ aₗₘ) for **spin-0 and spin-2** fields on the
|
|
20
|
+
**HEALPix ring** pixelization, in the **BICEP/Keck angular regime**
|
|
21
|
+
(ℓ_max ≲ 1000, nside ≤ ~2048). It exists to serve the GPU / differentiable
|
|
22
|
+
tier of analysis that a CPU-only C++ transform structurally cannot. It is
|
|
23
|
+
*not* an attempt to reimplement a general all-spin / all-sampling library.
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
**In scope (the target):**
|
|
28
|
+
- Spin-0 and spin-2 (T and Q/U / E/B) transforms, forward and adjoint.
|
|
29
|
+
- HEALPix ring pixelization.
|
|
30
|
+
- BK regime: ℓ_max ≲ 1000, nside ≤ ~2048; partial-sky (masked) analysis.
|
|
31
|
+
- GPU execution (the primary motivation) and JAX autodiff (alm-linear).
|
|
32
|
+
- Validation to the relevant accuracy tier against ducc0 / healpy.
|
|
33
|
+
|
|
34
|
+
**Out of scope (for now — keep the surface small):**
|
|
35
|
+
- Arbitrary spin-s, non-HEALPix samplings, extreme ℓ_max. We are not chasing
|
|
36
|
+
s2fft-style generality.
|
|
37
|
+
- **Off-grid synthesis (the NUFFT)** — synthesis at arbitrary detector
|
|
38
|
+
pointings, i.e. the `bk-jax` *sim-forward* path. That is a distinct
|
|
39
|
+
capability (a JAX NUFFT) and a separate, later phase. "JAX-native SHT" ≠
|
|
40
|
+
"escape ducc everywhere": jht replaces the **on-grid** SHT only.
|
|
41
|
+
- Being gratuitously BK-coupled. Write the core so BK assumptions live at the
|
|
42
|
+
edges; the transform itself should be reusable.
|
|
43
|
+
|
|
44
|
+
## Why jht exists (decision record — see `docs/motivation.md` for the full story)
|
|
45
|
+
|
|
46
|
+
The recurring constraint is a single C++ black box (ducc0) that does the
|
|
47
|
+
harmonic transforms. It is excellent but:
|
|
48
|
+
- **CPU-only.** The forecast / field-level-inference (MUSE) ambitions are
|
|
49
|
+
GPU-hungry; ducc can't go there.
|
|
50
|
+
- **A black box.** ducc is vendored in `bk-jax` as a submodule wrapped in a
|
|
51
|
+
custom XLA FFI — that is *build* control, not *code* control. Its internals
|
|
52
|
+
are unmodifiable C++, and off-grid pointing tangents raise
|
|
53
|
+
`NotImplementedError`.
|
|
54
|
+
- **A build tax.** The C++/scikit-build/CUDA toolchain has repeatedly broken
|
|
55
|
+
CI and local installs.
|
|
56
|
+
|
|
57
|
+
The leading JAX-native alternative, **s2fft**, was evaluated and rejected:
|
|
58
|
+
its **spin-2 HEALPix inverse has a structural precision defect** (measured
|
|
59
|
+
3–28% error at production ℓ, vs its own documented ~1e-3), and as of
|
|
60
|
+
2026-06 it is **still at v1.4.0 (12 Feb 2026)** with the relevant issues triaged
|
|
61
|
+
and parked ("no time") and a stagnant PR queue. (The 2026-05 evaluation spike
|
|
62
|
+
ran on v1.3.0; the v1.3.0→v1.4.0 diff is CUDA/build-only, so the spin-2 accuracy
|
|
63
|
+
is byte-identical — the date in the original record was wrong, the conclusion
|
|
64
|
+
stands.) **Do not depend on s2fft, and do not fork it** (owning a large codebase
|
|
65
|
+
you find broken is the opposite of dependency control). Clean-room, *informed
|
|
66
|
+
by* the published methods (libsharp / Kostelec–Rockmore for the spin-2-safe
|
|
67
|
+
ℓ-recursion; Price & McEwen for the JAX branch-free renorm *technique* only —
|
|
68
|
+
explicitly NOT their m-recursion, which is the suspected defect home), is the
|
|
69
|
+
control-aligned path.
|
|
70
|
+
|
|
71
|
+
Crucially, the s2fft evaluation **validated the architecture**: a JAX-native
|
|
72
|
+
SHT integrates cleanly (JIT-clean, spin-0 machine-precision, finite/well-scaled
|
|
73
|
+
VJP). The concept works; one library's spin-2 corner does not.
|
|
74
|
+
|
|
75
|
+
## Relationship to bk-jax (the consumer)
|
|
76
|
+
|
|
77
|
+
- jht is a **future dependency** of `bk-jax` (`~/bicepkeck/bk-jax`), added as a
|
|
78
|
+
path/pip dependency once Phase 0–1 land. It is developed and versioned here,
|
|
79
|
+
independently — that's the point.
|
|
80
|
+
- **ducc stays in bk-jax for the production path.** The ~1e-4 purity /
|
|
81
|
+
E→B-leakage-critical bandpower pipeline keeps using ducc; that accuracy is
|
|
82
|
+
person-decades of hard-won tuning and is not the thing to risk first.
|
|
83
|
+
- **jht earns its keep in the tier ducc can't serve:** GPU forecast sweeps,
|
|
84
|
+
the MUSE inner Wiener solve, and (eventually) fully-differentiable geometry —
|
|
85
|
+
where the HEALPix ~1e-3 accuracy floor is acceptable.
|
|
86
|
+
- `bk-jax` already dispatches its transform via a `BK_JAX_SHT_BACKEND` env var;
|
|
87
|
+
jht becomes a new backend option there (Phase 3).
|
|
88
|
+
|
|
89
|
+
## Accuracy philosophy (the validation contract)
|
|
90
|
+
|
|
91
|
+
This is the make-or-break of the project; treat it with the same rigor `bk-jax`
|
|
92
|
+
applies — **a-priori tolerances, gate-driven, and tolerances are NOT relaxed
|
|
93
|
+
without an explicit discussion and sign-off.**
|
|
94
|
+
|
|
95
|
+
- HEALPix has **no sampling theorem**, so any HEALPix SHT is approximate. The
|
|
96
|
+
fundamental floor without ring weights is ~1e-3; ducc reaches ~1e-4 and
|
|
97
|
+
better via **ring quadrature weights + Jacobi (map2alm-style) iteration**.
|
|
98
|
+
- jht's first target is **reach the HEALPix floor with no structural defect**
|
|
99
|
+
(i.e. spin-2 must NOT show s2fft's 3–28% — it must sit at ~1e-3 like spin-0),
|
|
100
|
+
then close toward ducc by adding ring weights + iteration.
|
|
101
|
+
- **Spin-2 analysis is the gate.** It is exactly what s2fft got wrong — and the
|
|
102
|
+
2026-06 dive pinned the defect to the *analysis quadrature* (missing ring
|
|
103
|
+
weights + polar folding + a stalling unweighted iteration), NOT the recursion
|
|
104
|
+
(see the crux section). Gate the weighted spin-2 round-trip **per-(ℓ,m), below
|
|
105
|
+
the ℓ≤1.5·nside band-limit ceiling** so recursion / quadrature / ceiling errors
|
|
106
|
+
are never conflated; if it reaches the floor flat across all m (no s2fft-style
|
|
107
|
+
m<ℓ leakage), the project is viable.
|
|
108
|
+
- Every transform lands with a parity test vs **both** healpy and ducc0
|
|
109
|
+
(cross-checking conventions), at a documented tolerance. Log any residual
|
|
110
|
+
mismatch (a `DISCREPANCIES.md`, mirroring bk-jax discipline) rather than
|
|
111
|
+
burying it.
|
|
112
|
+
|
|
113
|
+
## The crux technical risk
|
|
114
|
+
|
|
115
|
+
Two distinct risks — and the 2026-06 literature dive **corrected which one is
|
|
116
|
+
make-or-break** (full reasoning in `docs/design.md` and the session plan
|
|
117
|
+
`~/.claude/plans/yeah-let-s-get-to-lovely-feather.md`):
|
|
118
|
+
|
|
119
|
+
1. **Recursion stability to ℓ_max~1000 — necessary, but NOT s2fft's bug.**
|
|
120
|
+
Associated-Legendre / Wigner-d recursions underflow at high ℓ; the solved
|
|
121
|
+
technique is libsharp-style enhanced-exponent ("X-number") scaling. jht uses
|
|
122
|
+
the libsharp 3-term ℓ-recursion (Kostelec–Rockmore), in the increasing-ℓ
|
|
123
|
+
direction, with a **branch-free per-step log-renorm** — a *known, solved*
|
|
124
|
+
problem with a published spin-2≡spin-0 accuracy proof (libsharp §5.1.2).
|
|
125
|
+
This is **table-stakes, not the differentiator**: s2fft already rescales its
|
|
126
|
+
recursion and still fails spin-2, so getting the recursion right is necessary
|
|
127
|
+
but not sufficient.
|
|
128
|
+
|
|
129
|
+
2. **Spin-2 HEALPix *analysis quadrature* — the actual make-or-break.** s2fft's
|
|
130
|
+
defect lives here, not in the recursion: its spin-2 single-mode errors appear
|
|
131
|
+
at ℓ=8/16/32 and *shrink* with ℓ (35→28→22%), spin-0 (identical recursion) is
|
|
132
|
+
machine-precision, and the failures sit far below the ℓ≤1.5·nside ceiling —
|
|
133
|
+
all incompatible with recursion underflow, all consistent with an
|
|
134
|
+
aliasing/folding + missing-weights defect in the spin-2 map2alm path. jht's
|
|
135
|
+
gate is the **weighted spin-2 round-trip, validated per-(ℓ,m) below the
|
|
136
|
+
band-limit ceiling**, so recursion / quadrature / ceiling errors are never
|
|
137
|
+
conflated. Test both risks explicitly and early (Phase 0).
|
|
138
|
+
|
|
139
|
+
## Conventions — pin and document these from day one
|
|
140
|
+
|
|
141
|
+
These have repeatedly bitten `bk-jax`; do not leave them implicit.
|
|
142
|
+
|
|
143
|
+
- **Polarization sign:** HEALPix-internal vs IAU U-sign. Decide the internal
|
|
144
|
+
convention, document it, and cross-validate against healpy (`pol=True`) and
|
|
145
|
+
ducc. (bk-jax stores HEALPix-internal and flips U at I/O boundaries.)
|
|
146
|
+
- **Spin sign convention** (spin +2 vs −2 / E,B sign).
|
|
147
|
+
- **aₗₘ layout** (healpy m-major triangular packing; real vs complex; which ℓ,m
|
|
148
|
+
ordering) and **normalization** (4π vs orthonormal). Mirror healpy/ducc and
|
|
149
|
+
document the exact mapping.
|
|
150
|
+
- **Differentiability convention:** the on-grid SHT is alm-linear, so JVP/VJP
|
|
151
|
+
are straightforward — but note the **JAX-VJP-convention vs strict-math-adjoint**
|
|
152
|
+
subtlety (the `2·conj(·)` on m>0 modes) that bit bk-jax. Register transposes
|
|
153
|
+
deliberately and test `jacfwd ≡ jacrev` and the inner-product adjoint
|
|
154
|
+
identity.
|
|
155
|
+
|
|
156
|
+
## Tooling & conventions
|
|
157
|
+
|
|
158
|
+
- **Pixi** is the env tool (`pixi run test|lint|format|typecheck`). Standard.
|
|
159
|
+
- **Pure Python / JAX — no compiled extension.** This is a deliberate feature:
|
|
160
|
+
zero build-toolchain pain, unlike bk-jax's vendored-ducc FFI. Keep it that
|
|
161
|
+
way; if a kernel ever truly needs C++/CUDA, that's a major decision, not a
|
|
162
|
+
drive-by.
|
|
163
|
+
- **Runtime deps = jax + numpy only.** ducc0 / healpy are dev/test oracles in
|
|
164
|
+
the pixi env, never `[project.dependencies]`.
|
|
165
|
+
- **No Jupyter notebooks for working code** — scripts + `ipython %run` for
|
|
166
|
+
exploration (user-level standard).
|
|
167
|
+
- **JAX float64 is opt-in per entry point** (`jax.config.update("jax_enable_x64",
|
|
168
|
+
True)` in tests/scripts; library code does not touch global config) — same
|
|
169
|
+
rule as bk-jax; SHT accuracy work needs float64.
|
|
170
|
+
|
|
171
|
+
## Attribution (release blocker)
|
|
172
|
+
|
|
173
|
+
Published / shippable author fields must read **"James Cheshire"**
|
|
174
|
+
(`cheshire@caltech.edu`) — already set in `pyproject.toml` `authors`/
|
|
175
|
+
`maintainers`. "jamie" is fine only in the local git author config. This repo
|
|
176
|
+
may become a published dependency; verify the name before any tag / build /
|
|
177
|
+
publish. ("Jamie" must never appear in a shipped artifact.)
|
|
178
|
+
|
|
179
|
+
## Dev workflow (per transform)
|
|
180
|
+
|
|
181
|
+
1. Implement the transform over typed JAX arrays (parametric where a systematic
|
|
182
|
+
knob could later live).
|
|
183
|
+
2. Gate it against healpy **and** ducc0 at an a-priori documented tolerance.
|
|
184
|
+
3. Pin & document the conventions it assumes (`docs/design.md`).
|
|
185
|
+
4. `pixi run lint` + `typecheck` before committing; terse single-line commit
|
|
186
|
+
messages (user preference).
|
|
187
|
+
5. Log any numeric mismatch in `DISCREPANCIES.md`, don't bury it as a TODO.
|
|
188
|
+
|
|
189
|
+
## Pointers
|
|
190
|
+
|
|
191
|
+
- `ROADMAP.md` — phased plan + hard gates + open questions.
|
|
192
|
+
- `docs/motivation.md` — why jht exists (full decision record).
|
|
193
|
+
- `docs/design.md` — technical design, accuracy tiers, conventions, algorithm.
|
|
194
|
+
- Consumer / sibling context: `~/bicepkeck/bk-jax` (the pipeline that motivates
|
|
195
|
+
jht; do not make jht depend on reading bk-jax to be understood).
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# DISCREPANCIES
|
|
2
|
+
|
|
3
|
+
Residual numeric mismatches and known-limitation notes, with their cause — logged
|
|
4
|
+
rather than buried as TODOs (mirroring bk-jax discipline). Each entry says
|
|
5
|
+
whether it is a *defect* (to fix) or an *expected property* (to document).
|
|
6
|
+
|
|
7
|
+
## Spin-2 cut-sky recovery — E/B ambiguous modes (expected, not a defect)
|
|
8
|
+
|
|
9
|
+
`jht.masked.deconvolve` recovers the true a_lm from a cut sky only where the cut
|
|
10
|
+
leaves the modes constrained. For **spin-2** there is an additional fundamental
|
|
11
|
+
floor: on a cut sky, E and B are not cleanly separable — a subspace of modes
|
|
12
|
+
(the *ambiguous modes*, Bunn et al. 2003) is supported almost entirely under the
|
|
13
|
+
mask and is therefore unconstrained by the data. These near-null directions of
|
|
14
|
+
`Sᵀ(MW)S` are not recoverable; CG from `x0=0` returns the minimum-norm solution
|
|
15
|
+
within that subspace, and `reg>0` (Tikhonov) bounds it.
|
|
16
|
+
|
|
17
|
+
- **Status:** expected physics (information loss from the cut), **not** a
|
|
18
|
+
transform defect. The *operator* is correct — gated to ~1e-13 against the
|
|
19
|
+
explicit dense `Sᵀ diag(MW) S` (`tests/test_masked.py::test_operator_matches_dense`)
|
|
20
|
+
and symmetric/PSD — and the pseudo-a_lm reproduces healpy/ducc to machine
|
|
21
|
+
precision. Only the *recoverability* of cut-localized E/B modes is limited.
|
|
22
|
+
- **Manifestation:** in `scripts/masked_sweep.py`, deconvolution error grows as
|
|
23
|
+
fsky shrinks and blows up under heavy apodization (which down-weights data).
|
|
24
|
+
spin-2 reaches the same recovery floor as spin-0 for mild cuts (no s2fft-style
|
|
25
|
+
m<ℓ defect — that was ruled out in Phase 0); the spin-2-specific limitation is
|
|
26
|
+
only the ambiguous-mode subspace at aggressive cuts.
|
|
27
|
+
- **Mitigation / scope:** use `reg>0` to stabilise, restrict the recovered band,
|
|
28
|
+
or — now implemented — bound the ambiguous subspace with a signal prior via
|
|
29
|
+
`jht.masked.wiener` (the Cℓ-informed generalization of `reg`; the MUSE
|
|
30
|
+
inner-solve tier), with `constrained_realization` for posterior draws. The
|
|
31
|
+
operator and recovery are unchanged; the prior simply gives the unconstrained
|
|
32
|
+
subspace a principled, finite answer instead of the min-norm CG one. See
|
|
33
|
+
`docs/masked.md`.
|
|
34
|
+
|
|
35
|
+
## Differentiability: `jax.linear_transpose` / forward-mode of the raw kernels (limitation, not a defect)
|
|
36
|
+
|
|
37
|
+
The supported autodiff path is JAX's **native reverse/forward AD** through the
|
|
38
|
+
public transforms — `jax.grad` / `vjp` / `jacrev` (complex aₗₘ), and `jacfwd ≡
|
|
39
|
+
jacrev` / FD on the real-DOF layer (`jht.diff`). All Phase-2 gates pass on these
|
|
40
|
+
(`tests/test_grad.py`).
|
|
41
|
+
|
|
42
|
+
Two lower-level entry points are **not** supported, by deliberate choice:
|
|
43
|
+
|
|
44
|
+
- `jax.linear_transpose(synthesis, …)` — the forward `lax.scan` in the recursion
|
|
45
|
+
engine is not directly transposable as a standalone linear function ("Error
|
|
46
|
+
interpreting argument to scan as a JAX value"). `jax.vjp`/`grad` are unaffected:
|
|
47
|
+
they linearize first (baking the static recursion plan as constants) and
|
|
48
|
+
transpose the linearized jaxpr, which is clean. Forward scatters carry
|
|
49
|
+
`unique_indices=True` so the *scatter*-transpose blocker is gone; the residual is
|
|
50
|
+
the scan, which native AD sidesteps.
|
|
51
|
+
- `jax.jacfwd` / `jax.jvp` on the **complex** `synthesis` — `jacfwd` requires
|
|
52
|
+
real-valued inputs (a JAX constraint, not jht-specific). Forward-mode lives on
|
|
53
|
+
the **real-DOF layer** (`synthesis_real`/`analysis_real`), where `jacfwd ≡
|
|
54
|
+
jacrev` to ~1e-16.
|
|
55
|
+
|
|
56
|
+
- **Status:** expected JAX-mechanics limitations with first-class supported
|
|
57
|
+
alternatives, **not** transform defects. A `custom_vjp` would *block* forward
|
|
58
|
+
mode entirely; the only kernel-routing fix needs JAX's internal `jax.core.
|
|
59
|
+
Primitive` API (removed in jax 0.9.2). Native AD is the chosen, version-stable
|
|
60
|
+
path. See `docs/design.md` §Differentiability.
|
|
61
|
+
|
|
62
|
+
## Off-grid (`synthesis_general`): spin>0 m=0 phantom vs ducc (expected, not a defect)
|
|
63
|
+
|
|
64
|
+
For spin > 0 the off-grid synthesis keeps both real components at every m (Re→first
|
|
65
|
+
plane, Im→second), so the field depends on the **unphysical `Im(a_{l,0})`** at m=0 —
|
|
66
|
+
the same phantom documented on-grid in `docs/design.md`. jht's *strict adjoint*
|
|
67
|
+
`adjoint_synthesis_general` therefore carries a nonzero m=0 imaginary part, while
|
|
68
|
+
ducc0's `adjoint_synthesis_general` zeros it.
|
|
69
|
+
|
|
70
|
+
- **Status:** expected; jht's adjoint is the exact transpose (the inner-product
|
|
71
|
+
identity `<S a, v> == <a, Sᵀ v>_G` holds to ~1e-16, `tests/test_offgrid.py::
|
|
72
|
+
test_adjoint_identity`), ducc applies a different m=0 convention. **Harmless** —
|
|
73
|
+
physical skies have real `a_{l,0}`, so the phantom is never excited. The ducc
|
|
74
|
+
cross-check (`test_adjoint_vs_ducc`) compares the physical m≥1 modes (matched to
|
|
75
|
+
~1e-13); the m≥1 / real-m=0 content agrees with ducc bit-for-bit.
|
|
76
|
+
|
|
77
|
+
## Off-grid: the −spin channel carries `(−1)^s` (convention pin, now matched to ducc)
|
|
78
|
+
|
|
79
|
+
The spin-weighted E/B → ±spin mapping is `+s a = −(aE + i aB)`, `−s a =
|
|
80
|
+
−(−1)^s (aE − i aB)`. The `(−1)^s` on the −spin channel is **invisible for even
|
|
81
|
+
spin** (the on-grid 0/2 path never exposed it) but flips the result for **odd spin
|
|
82
|
+
(1, 3)**. Pinned by matching ducc0's `synthesis_general` for spin 1/3 (a sign probe;
|
|
83
|
+
`tests/test_offgrid.py::test_forward_vs_ducc` covers all of spin 0–3). Documented so
|
|
84
|
+
the even-only on-grid convention is not assumed to carry over to odd spin.
|
jaxht-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 James Cheshire
|
|
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.
|
jaxht-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jaxht
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: JAX-native spherical harmonic transforms (BK-regime first): GPU-capable, differentiable, dependency-controlled.
|
|
5
|
+
Project-URL: Homepage, https://github.com/jrcheshire/jht
|
|
6
|
+
Project-URL: Repository, https://github.com/jrcheshire/jht
|
|
7
|
+
Project-URL: Issues, https://github.com/jrcheshire/jht/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/jrcheshire/jht/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: James Cheshire <cheshire@caltech.edu>
|
|
10
|
+
Maintainer-email: James Cheshire <cheshire@caltech.edu>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: automatic-differentiation,cosmic-microwave-background,cosmology,gpu,healpix,jax,nufft,spherical-harmonic-transform
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: jax>=0.4.30
|
|
27
|
+
Requires-Dist: numpy>=1.24
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
32
|
+
Provides-Extra: validation
|
|
33
|
+
Requires-Dist: ducc0; extra == 'validation'
|
|
34
|
+
Requires-Dist: healpy; extra == 'validation'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# jht — JAX Harmonic Transforms
|
|
38
|
+
|
|
39
|
+
JAX-native spherical harmonic transforms (map ↔ aₗₘ): **GPU-capable**, **fully
|
|
40
|
+
differentiable**, and **dependency-controlled** (pure JAX + numpy at runtime — no
|
|
41
|
+
compiled C++ extension, no heavyweight third-party SHT library). Scoped to the
|
|
42
|
+
BICEP/Keck regime — **spin-0 and spin-2** on the **HEALPix RING** pixelization,
|
|
43
|
+
ℓ_max ≲ 1000, nside ≤ ~2048 — but written cleanly so it can serve as a general
|
|
44
|
+
transform dependency.
|
|
45
|
+
|
|
46
|
+
It exists to serve the GPU / differentiable tier of analysis that a CPU-only C++
|
|
47
|
+
transform (ducc0) structurally cannot, while *owning the numerics*. See
|
|
48
|
+
[`docs/motivation.md`](docs/motivation.md) for the full decision record.
|
|
49
|
+
|
|
50
|
+
## Status (2026-06-10)
|
|
51
|
+
|
|
52
|
+
Phases 0–4 **complete and validated** (190 tests pass + 8 GPU-gated skips,
|
|
53
|
+
CPU/float64):
|
|
54
|
+
|
|
55
|
+
- **On-grid transforms** — spin-0 & spin-2 synthesis (`aₗₘ→map`) and the exact
|
|
56
|
+
adjoint `Sᵀ`, validated to machine precision vs healpy **and** ducc0; spin-2
|
|
57
|
+
inverse at the HEALPix floor with **no s2fft-style structural defect**.
|
|
58
|
+
- **Accuracy** — jht's own ring quadrature weights + Jacobi iteration reach
|
|
59
|
+
~1e-13 on band-limited maps (matches `healpy.map2alm(use_weights=True)`); see
|
|
60
|
+
[`docs/accuracy.md`](docs/accuracy.md).
|
|
61
|
+
- **Partial-sky** — masked pseudo-aₗₘ, a cut-sky CG deconvolution, and a masked
|
|
62
|
+
Wiener filter / constrained realization (the MUSE inner solve); see
|
|
63
|
+
[`docs/masked.md`](docs/masked.md).
|
|
64
|
+
- **Off-grid (NUFFT)** — `synthesis_general` / `adjoint_synthesis_general`
|
|
65
|
+
evaluate a band-limited field at **arbitrary pointings** (spin 0–3), alm- **and**
|
|
66
|
+
pointing-differentiable. The JAX-native replacement for ducc0's
|
|
67
|
+
`sht.experimental.synthesis_general` (on-grid SHT + this NUFFT = the full ducc0
|
|
68
|
+
surface bk-jax depends on); see [`docs/offgrid.md`](docs/offgrid.md).
|
|
69
|
+
- **Differentiability** — native JAX autodiff (`jacfwd ≡ jacrev`, tight adjoint
|
|
70
|
+
identity), plus a convention-clean real-DOF layer `jht.diff`; see
|
|
71
|
+
[`docs/design.md`](docs/design.md) §Differentiability.
|
|
72
|
+
- **GPU** — pure JAX, so the transforms run on CUDA with no code change. Measured on
|
|
73
|
+
Cannon A100/V100 (fp64): GPU==CPU parity ~1e-13 across the BK regime **including
|
|
74
|
+
nside=2048**, forward synthesis 14–60× the CPU. See **Performance** below and
|
|
75
|
+
[`docs/gpu.md`](docs/gpu.md).
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
Standard env is [pixi](https://pixi.sh):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pixi install # CPU env (osx-arm64 / linux-64)
|
|
83
|
+
pixi run test # the gated suite
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
GPU (CUDA, linux-64 — see [`docs/gpu.md`](docs/gpu.md)):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pixi run -e gpu python scripts/gpu_check.py # on an NVIDIA box
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
As a dependency in another project (runtime deps are just `jax` + `numpy`):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install jaxht # once released on PyPI — then `import jht`
|
|
96
|
+
# or track the repo directly:
|
|
97
|
+
pip install "jaxht @ git+https://github.com/jrcheshire/jht.git"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Quick start
|
|
101
|
+
|
|
102
|
+
float64 is **opt-in per entry point** — enable it *before creating any array*
|
|
103
|
+
(library code never touches the global config):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import jax
|
|
107
|
+
jax.config.update("jax_enable_x64", True)
|
|
108
|
+
import jax.numpy as jnp
|
|
109
|
+
import jht
|
|
110
|
+
|
|
111
|
+
nside, lmax, spin = 256, 512, 0
|
|
112
|
+
m = jht.synthesis(alm, nside, lmax, spin=spin) # aₗₘ -> map
|
|
113
|
+
a = jht.map2alm(m, nside, lmax, spin=spin, niter=3) # map -> aₗₘ (weighted + iterated)
|
|
114
|
+
cl = jht.bandpower(a, lmax, spin=spin) # angular auto-power C_ℓ
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`spin=2` takes/returns `(E, B)` aₗₘ of shape `(2, …)` and `(Q, U)` maps of shape
|
|
118
|
+
`(2, npix)`. `jht.adjoint_synthesis` is the **exact unweighted transpose** `Sᵀ`
|
|
119
|
+
(the operator seam / VJP), distinct from `map2alm` (the approximate inverse). For
|
|
120
|
+
gradient-based work use the real-DOF layer `jht.synthesis_real` /
|
|
121
|
+
`jht.analysis_real` (plain ℝⁿ→ℝᵐ, no complex-conjugate convention subtlety).
|
|
122
|
+
|
|
123
|
+
## Conventions
|
|
124
|
+
|
|
125
|
+
healpy m-major triangular aₗₘ packing, orthonormal Yₗₘ with the Condon–Shortley
|
|
126
|
+
phase, HEALPix-internal (COSMO) polarization — verified against healpy 1.19.0 and
|
|
127
|
+
ducc0 0.41.0. Pinned in [`docs/design.md`](docs/design.md).
|
|
128
|
+
|
|
129
|
+
## Accuracy tiers (the contract)
|
|
130
|
+
|
|
131
|
+
jht targets the **GPU/differentiable tier** where the HEALPix ~1e-3 sampling
|
|
132
|
+
floor is acceptable; weights + iteration close it to ~1e-13 on band-limited
|
|
133
|
+
inputs. It is **not** a drop-in for ducc's purity-critical (~1e-4 E→B-leakage)
|
|
134
|
+
production path. Tolerances are a-priori and gate-driven, never relaxed without
|
|
135
|
+
sign-off. Residual mismatches are logged in
|
|
136
|
+
[`DISCREPANCIES.md`](DISCREPANCIES.md).
|
|
137
|
+
|
|
138
|
+
## Performance
|
|
139
|
+
|
|
140
|
+
Pure JAX runs unchanged on CUDA. Measured on Cannon A100 (incl. a 20 GB MIG) / V100, fp64:
|
|
141
|
+
|
|
142
|
+
- **GPU==CPU parity ~1e-13** across the BK regime, **including nside=2048** (synthesis and `map2alm`).
|
|
143
|
+
- **Forward synthesis 14–60×** the 8-core CPU; fp64/fp32 ≈ 2.2×.
|
|
144
|
+
- **Off-grid forward** ~0.5–0.9 s at ℓ_max=1000 (independent of the number of points; recursion-bound), with the pointing gradient ~1× a forward.
|
|
145
|
+
- **nside=2048** compiles and runs on GPU — a ~20 GB slice holds synthesis + `map2alm`; the one-time compile is multi-minute (jit-cached).
|
|
146
|
+
|
|
147
|
+
The recurring GPU lesson: fp64/complex scatters are catastrophic on GPU, so jht packs and assembles via **gathers**. CPU perf model + memory in [`docs/performance.md`](docs/performance.md); GPU detail in [`docs/gpu.md`](docs/gpu.md).
|
|
148
|
+
|
|
149
|
+
## Using jht as a dependency
|
|
150
|
+
|
|
151
|
+
jht is standalone and consumer-agnostic. The operator/grad seam a downstream
|
|
152
|
+
needs (e.g. to use jht *in place of ducc0*) — and the accuracy boundary — are
|
|
153
|
+
documented in [`docs/consumers.md`](docs/consumers.md). Any backend-selection
|
|
154
|
+
wiring lives in the consumer, not here.
|
|
155
|
+
|
|
156
|
+
## Docs
|
|
157
|
+
|
|
158
|
+
- [`docs/design.md`](docs/design.md) — technical design, conventions, the crux, differentiability.
|
|
159
|
+
- [`docs/accuracy.md`](docs/accuracy.md) — the accuracy contract + ring-weight algorithm.
|
|
160
|
+
- [`docs/masked.md`](docs/masked.md) — partial-sky estimators.
|
|
161
|
+
- [`docs/performance.md`](docs/performance.md) — CPU perf model + memory.
|
|
162
|
+
- [`docs/gpu.md`](docs/gpu.md) — the GPU env, the x64 requirement, the harness.
|
|
163
|
+
- [`docs/consumers.md`](docs/consumers.md) — the downstream-dependency seam.
|
|
164
|
+
- [`docs/motivation.md`](docs/motivation.md) — why jht exists.
|
|
165
|
+
- [`ROADMAP.md`](ROADMAP.md) — phased plan + gates.
|