racah-py 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. racah_py-0.1.0/.github/workflows/ci.yml +88 -0
  2. racah_py-0.1.0/.github/workflows/wheels.yml +116 -0
  3. racah_py-0.1.0/.gitignore +16 -0
  4. racah_py-0.1.0/AGENTS.md +90 -0
  5. racah_py-0.1.0/CHANGELOG.md +290 -0
  6. racah_py-0.1.0/CITATION.cff +16 -0
  7. racah_py-0.1.0/Cargo.lock +1955 -0
  8. racah_py-0.1.0/Cargo.toml +93 -0
  9. racah_py-0.1.0/LICENSE-APACHE +202 -0
  10. racah_py-0.1.0/LICENSE-MIT +21 -0
  11. racah_py-0.1.0/PKG-INFO +142 -0
  12. racah_py-0.1.0/README.md +250 -0
  13. racah_py-0.1.0/benches/bcd_fr.rs +122 -0
  14. racah_py-0.1.0/benches/sun_cgc.rs +63 -0
  15. racah_py-0.1.0/benches/sun_fr.rs +118 -0
  16. racah_py-0.1.0/benches/sun_product.rs +228 -0
  17. racah_py-0.1.0/benches/wigner.rs +193 -0
  18. racah_py-0.1.0/doc/katex-header.html +34 -0
  19. racah_py-0.1.0/docs/README.md +38 -0
  20. racah_py-0.1.0/docs/developer/README.md +100 -0
  21. racah_py-0.1.0/docs/developer/coefficient-cache-audit-timings.jsonl +45 -0
  22. racah_py-0.1.0/docs/developer/coefficient-cache-audit-trace.jsonl +27 -0
  23. racah_py-0.1.0/docs/developer/coefficient-cache-audit.md +42 -0
  24. racah_py-0.1.0/docs/developer/coefficient-cache-budget-pressure.jsonl +15 -0
  25. racah_py-0.1.0/docs/developer/coefficient-cache-budget-tradeoff.md +45 -0
  26. racah_py-0.1.0/docs/developer/coefficient-cache-trim-pressure.jsonl +15 -0
  27. racah_py-0.1.0/docs/developer/coefficient-cache-trim-tradeoff.md +31 -0
  28. racah_py-0.1.0/docs/gauge.md +454 -0
  29. racah_py-0.1.0/docs/gauge_soN.md +1000 -0
  30. racah_py-0.1.0/docs/references.md +227 -0
  31. racah_py-0.1.0/docs/theory.md +34 -0
  32. racah_py-0.1.0/docs/theory.pdf +0 -0
  33. racah_py-0.1.0/docs/theory.tex +1833 -0
  34. racah_py-0.1.0/docs/user-guide/README.md +44 -0
  35. racah_py-0.1.0/docs/user-guide/clebsch-gordan.md +166 -0
  36. racah_py-0.1.0/docs/user-guide/fusion.md +130 -0
  37. racah_py-0.1.0/docs/user-guide/getting-started.md +126 -0
  38. racah_py-0.1.0/docs/user-guide/groups.md +207 -0
  39. racah_py-0.1.0/docs/user-guide/recoupling.md +277 -0
  40. racah_py-0.1.0/docs/user-guide/representations.md +174 -0
  41. racah_py-0.1.0/docs/user-guide/resources.md +158 -0
  42. racah_py-0.1.0/pyproject.toml +40 -0
  43. racah_py-0.1.0/racah-py/Cargo.toml +24 -0
  44. racah_py-0.1.0/racah-py/README.md +120 -0
  45. racah_py-0.1.0/racah-py/racah.pyi +34 -0
  46. racah_py-0.1.0/racah-py/src/lib.rs +234 -0
  47. racah_py-0.1.0/racah-py/tests/test_smoke.py +100 -0
  48. racah_py-0.1.0/racah.pyi +34 -0
  49. racah_py-0.1.0/src/bcd/catalog/tests.rs +515 -0
  50. racah_py-0.1.0/src/bcd/catalog.rs +975 -0
  51. racah_py-0.1.0/src/bcd/fr/tests.rs +224 -0
  52. racah_py-0.1.0/src/bcd/fr.rs +416 -0
  53. racah_py-0.1.0/src/bcd/linalg.rs +280 -0
  54. racah_py-0.1.0/src/bcd/qspace_oracle_tests.rs +661 -0
  55. racah_py-0.1.0/src/bcd/seeds/tests.rs +292 -0
  56. racah_py-0.1.0/src/bcd/seeds.rs +682 -0
  57. racah_py-0.1.0/src/bcd/sweep/tests.rs +613 -0
  58. racah_py-0.1.0/src/bcd/sweep.rs +1573 -0
  59. racah_py-0.1.0/src/bcd/tests.rs +526 -0
  60. racah_py-0.1.0/src/bcd.rs +1344 -0
  61. racah_py-0.1.0/src/cache.rs +1955 -0
  62. racah_py-0.1.0/src/cache_audit.rs +264 -0
  63. racah_py-0.1.0/src/cache_budget_pressure.rs +106 -0
  64. racah_py-0.1.0/src/cache_trim_pressure.rs +121 -0
  65. racah_py-0.1.0/src/exact.rs +407 -0
  66. racah_py-0.1.0/src/frcore.rs +767 -0
  67. racah_py-0.1.0/src/group.rs +537 -0
  68. racah_py-0.1.0/src/lib.rs +235 -0
  69. racah_py-0.1.0/src/primefactor.rs +534 -0
  70. racah_py-0.1.0/src/su2.rs +2023 -0
  71. racah_py-0.1.0/src/sun/cgc.rs +966 -0
  72. racah_py-0.1.0/src/sun/fr.rs +424 -0
  73. racah_py-0.1.0/src/sun/linalg.rs +239 -0
  74. racah_py-0.1.0/src/sun.rs +1419 -0
  75. racah_py-0.1.0/tests/base_cache_contract.rs +132 -0
  76. racah_py-0.1.0/tests/bcd_fingerprint.rs +53 -0
  77. racah_py-0.1.0/tests/bcd_fr.rs +307 -0
  78. racah_py-0.1.0/tests/cache_trim_base.rs +41 -0
  79. racah_py-0.1.0/tests/cache_trim_generated_routing.rs +63 -0
  80. racah_py-0.1.0/tests/coefficient_cache_budget_base.rs +36 -0
  81. racah_py-0.1.0/tests/coefficient_cache_budget_generated.rs +39 -0
  82. racah_py-0.1.0/tests/coefficient_cache_budget_observation.rs +17 -0
  83. racah_py-0.1.0/tests/doc_examples.rs +507 -0
  84. racah_py-0.1.0/tests/fixtures/bcd_fixtures.json +60 -0
  85. racah_py-0.1.0/tests/fixtures/groupmath_products.txt +26 -0
  86. racah_py-0.1.0/tests/fixtures/qspace_cgc.txt +2976 -0
  87. racah_py-0.1.0/tests/fixtures/qspace_generators.txt +230 -0
  88. racah_py-0.1.0/tests/fixtures/su2_6j_large.txt +44 -0
  89. racah_py-0.1.0/tests/fixtures/su2_f.txt +109909 -0
  90. racah_py-0.1.0/tests/fixtures/su2_r.txt +625 -0
  91. racah_py-0.1.0/tests/fixtures/su3_fr_f.txt +135814 -0
  92. racah_py-0.1.0/tests/fixtures/su3_fr_r.txt +898 -0
  93. racah_py-0.1.0/tests/fixtures/sun/sun_fixtures.txt +1814 -0
  94. racah_py-0.1.0/tests/fixtures/sun_cgc.txt +3782 -0
  95. racah_py-0.1.0/tests/fixtures_large.rs +96 -0
  96. racah_py-0.1.0/tests/fr_oracle.rs +82 -0
  97. racah_py-0.1.0/tests/gauge_golden.rs +547 -0
  98. racah_py-0.1.0/tests/generated_backend_identity.rs +190 -0
  99. racah_py-0.1.0/tests/generated_cache_contract.rs +145 -0
  100. racah_py-0.1.0/tests/global_form.rs +942 -0
  101. racah_py-0.1.0/tests/groupmath_oracle.rs +145 -0
  102. racah_py-0.1.0/tests/isomorphism.rs +442 -0
  103. racah_py-0.1.0/tests/oracle.rs +256 -0
  104. racah_py-0.1.0/tests/properties.rs +369 -0
  105. racah_py-0.1.0/tests/spin.rs +404 -0
  106. racah_py-0.1.0/tests/su2_embedding.rs +128 -0
  107. racah_py-0.1.0/tests/su2_fingerprint.rs +38 -0
  108. racah_py-0.1.0/tests/sun_cgc_cache.rs +154 -0
  109. racah_py-0.1.0/tests/sun_cgc_fixtures.rs +145 -0
  110. racah_py-0.1.0/tests/sun_fingerprint.rs +45 -0
  111. racah_py-0.1.0/tests/sun_fr_cache.rs +68 -0
  112. racah_py-0.1.0/tests/sun_fr_gates.rs +96 -0
  113. racah_py-0.1.0/tests/sun_fr_su2.rs +211 -0
  114. racah_py-0.1.0/tests/sun_fr_su3_oracle.rs +163 -0
  115. racah_py-0.1.0/tests/sun_oracle.rs +172 -0
  116. racah_py-0.1.0/tests/sun_product_cache.rs +127 -0
  117. racah_py-0.1.0/tools/README.md +92 -0
  118. racah_py-0.1.0/tools/gen_bcd_fixtures.jl +88 -0
  119. racah_py-0.1.0/tools/gen_bcd_fixtures.py +109 -0
  120. racah_py-0.1.0/tools/gen_fixtures.jl +82 -0
  121. racah_py-0.1.0/tools/gen_fr_fixtures.jl +83 -0
  122. racah_py-0.1.0/tools/gen_su3_fr_fixtures.jl +103 -0
  123. racah_py-0.1.0/tools/gen_sun_cgc_fixtures.jl +72 -0
  124. racah_py-0.1.0/tools/gen_sun_fixtures.jl +154 -0
  125. racah_py-0.1.0/tools/groupmath/gen_groupmath_products.wls +113 -0
  126. racah_py-0.1.0/tools/qspace/README.md +46 -0
  127. racah_py-0.1.0/tools/qspace/gen_qspace_cgc.m +35 -0
  128. racah_py-0.1.0/tools/qspace/gen_qspace_generators.m +26 -0
  129. racah_py-0.1.0/tools/qspace/getCG_maca64_sync.patch +80 -0
  130. racah_py-0.1.0/tools/qspace/getRC_maca64_sync.patch +66 -0
@@ -0,0 +1,88 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ env:
9
+ CARGO_TERM_COLOR: always
10
+ RUSTFLAGS: -D warnings
11
+
12
+ jobs:
13
+ fmt:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - run: rustup toolchain install stable --profile minimal --component rustfmt
18
+ - run: cargo fmt --all --check
19
+
20
+ clippy:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - run: rustup toolchain install stable --profile minimal --component clippy
25
+ - uses: Swatinem/rust-cache@v2
26
+ - run: cargo clippy --all-targets --all-features
27
+
28
+ test:
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - run: rustup toolchain install stable --profile minimal
33
+ - uses: Swatinem/rust-cache@v2
34
+ # dev-dependency wigner-symbols pulls gmp-mpfr-sys, which builds GMP
35
+ # from source on first run; rust-cache keeps the build cached across runs.
36
+ - run: cargo test --all-features
37
+ - run: cargo test --no-default-features
38
+
39
+ # Non-blocking coverage measurement for the README badge. Line coverage is
40
+ # auxiliary here — the golden/coherence guards are the primary correctness
41
+ # gates — so this job never fails the pipeline. It publishes a shields.io
42
+ # endpoint JSON to the `badges` branch, which the README badge reads via
43
+ # raw.githubusercontent.com.
44
+ coverage:
45
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
46
+ runs-on: ubuntu-latest
47
+ continue-on-error: true
48
+ permissions:
49
+ contents: write
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - run: rustup toolchain install stable --profile minimal --component llvm-tools-preview
53
+ - uses: Swatinem/rust-cache@v2
54
+ - uses: taiki-e/install-action@cargo-llvm-cov
55
+ # One instrumented test run; both report formats are re-emitted from it.
56
+ - run: cargo llvm-cov --workspace --all-features --no-report
57
+ - run: cargo llvm-cov report --summary-only --json --output-path coverage.json
58
+ - run: cargo llvm-cov report --codecov --output-path codecov.json
59
+ - uses: codecov/codecov-action@v5
60
+ with:
61
+ files: codecov.json
62
+ fail_ci_if_error: false
63
+ - name: Publish badge JSON to badges branch
64
+ run: |
65
+ pct=$(jq -r '.data[0].totals.lines.percent' coverage.json)
66
+ msg=$(printf '%.1f%%' "$pct")
67
+ color=$(awk -v p="$pct" 'BEGIN{print (p<60)?"red":(p<80)?"yellow":"brightgreen"}')
68
+ jq -n --arg m "$msg" --arg c "$color" \
69
+ '{schemaVersion:1,label:"coverage",message:$m,color:$c}' > badge.json
70
+ git config user.name "github-actions[bot]"
71
+ git config user.email "github-actions[bot]@users.noreply.github.com"
72
+ # Single-file branch built from plumbing; history is disposable, force-push.
73
+ blob=$(git hash-object -w badge.json)
74
+ tree=$(printf '100644 blob %s\tbadge.json\n' "$blob" | git mktree)
75
+ commit=$(git commit-tree "$tree" -m "coverage badge: $msg ($GITHUB_SHA)")
76
+ git push -f origin "$commit:refs/heads/badges"
77
+
78
+ doc:
79
+ runs-on: ubuntu-latest
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+ - run: rustup toolchain install stable --profile minimal
83
+ - uses: Swatinem/rust-cache@v2
84
+ - run: cargo doc --no-deps --all-features
85
+ env:
86
+ # Same KaTeX header as docs.rs (Cargo.toml [package.metadata.docs.rs]);
87
+ # space-separated flags compose in the one RUSTDOCFLAGS env var.
88
+ RUSTDOCFLAGS: -D warnings --html-in-header doc/katex-header.html
@@ -0,0 +1,116 @@
1
+ name: wheels
2
+
3
+ # Python bindings (`racah-py`): abi3-py312 wheels for linux (x86_64, aarch64),
4
+ # macos (arm64, x86_64), windows (x86_64), plus an sdist.
5
+ #
6
+ # Trusted publishing (OIDC, no tokens), same ladder as tenet-py's release.yml:
7
+ # a `py-v*` tag push dry-runs the artifacts through TestPyPI; publishing a
8
+ # GitHub release whose tag starts with `py-v` ships them to PyPI. The crate's
9
+ # own `v*` releases never touch PyPI, so the two cadences stay independent.
10
+ on:
11
+ push:
12
+ branches: [main]
13
+ tags: ["py-v*"]
14
+ pull_request:
15
+ release:
16
+ types: [published]
17
+ workflow_dispatch:
18
+
19
+ env:
20
+ CARGO_TERM_COLOR: always
21
+
22
+ jobs:
23
+ # Build the extension in place and run the Python smoke tests.
24
+ test:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.12"
31
+ - run: rustup toolchain install stable --profile minimal
32
+ - uses: Swatinem/rust-cache@v2
33
+ - run: pip install maturin pytest numpy
34
+ # `maturin develop` needs an activated virtualenv; the runner's Python is
35
+ # already isolated, so build the wheel and install it.
36
+ - run: maturin build --release --manifest-path racah-py/Cargo.toml --out dist
37
+ - run: pip install --no-index --find-links dist racah-py
38
+ - run: pytest racah-py/tests -q
39
+
40
+ wheels:
41
+ if: >-
42
+ startsWith(github.ref, 'refs/tags/py-v') ||
43
+ github.event_name == 'workflow_dispatch' ||
44
+ (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'py-v'))
45
+ strategy:
46
+ fail-fast: false
47
+ matrix:
48
+ include:
49
+ # linux aarch64 cross-compiles inside maturin-action's manylinux
50
+ # docker image; macos x86_64 cross-compiles from the arm64 runner.
51
+ # The crate is pure Rust, so stock toolchains suffice everywhere.
52
+ - runner: ubuntu-latest
53
+ target: x86_64
54
+ - runner: ubuntu-latest
55
+ target: aarch64
56
+ - runner: macos-14
57
+ target: aarch64
58
+ - runner: macos-14
59
+ target: x86_64
60
+ - runner: windows-latest
61
+ target: x64
62
+ runs-on: ${{ matrix.runner }}
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+ - uses: actions/setup-python@v5
66
+ with:
67
+ python-version: "3.12"
68
+ - uses: PyO3/maturin-action@v1
69
+ with:
70
+ target: ${{ matrix.target }}
71
+ # abi3-py312: one wheel per platform covers CPython >= 3.12.
72
+ args: --release --out dist --manifest-path racah-py/Cargo.toml
73
+ manylinux: auto
74
+ - uses: actions/upload-artifact@v4
75
+ with:
76
+ name: wheels-${{ matrix.runner }}-${{ matrix.target }}
77
+ path: dist
78
+
79
+ # Unsupported platforms build from source with a Rust toolchain.
80
+ sdist:
81
+ if: >-
82
+ startsWith(github.ref, 'refs/tags/py-v') ||
83
+ github.event_name == 'workflow_dispatch' ||
84
+ (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'py-v'))
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+ - uses: PyO3/maturin-action@v1
89
+ with:
90
+ command: sdist
91
+ args: --out dist --manifest-path racah-py/Cargo.toml
92
+ - uses: actions/upload-artifact@v4
93
+ with:
94
+ name: sdist
95
+ path: dist
96
+
97
+ publish:
98
+ needs: [wheels, sdist]
99
+ if: >-
100
+ startsWith(github.ref, 'refs/tags/py-v') ||
101
+ (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'py-v'))
102
+ runs-on: ubuntu-latest
103
+ environment: pypi
104
+ permissions:
105
+ id-token: write
106
+ steps:
107
+ - uses: actions/download-artifact@v4
108
+ with:
109
+ path: dist
110
+ merge-multiple: true
111
+ - if: github.event_name == 'push'
112
+ uses: pypa/gh-action-pypi-publish@release/v1
113
+ with:
114
+ repository-url: https://test.pypi.org/legacy/
115
+ - if: github.event_name == 'release'
116
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,16 @@
1
+ /target
2
+ Cargo.lock
3
+ .DS_Store
4
+ # Developer-local tenferro-rs override (see AGENTS.md); never committed.
5
+ /.cargo/
6
+
7
+ # Python bindings dev venv (racah-py); never committed.
8
+ /racah-py/.venv/
9
+ __pycache__/
10
+ .pytest_cache/
11
+
12
+ # LaTeX build artifacts (docs/theory.tex); the built theory.pdf IS committed.
13
+ docs/*.aux
14
+ docs/*.log
15
+ docs/*.out
16
+ docs/*.toc
@@ -0,0 +1,90 @@
1
+ # racah Agent Policy
2
+
3
+ Design authority: the architecture, acceptance criteria, and reference-role
4
+ map recorded in the upstream design discussion. Read them before any
5
+ non-trivial change.
6
+
7
+ ## Boundaries
8
+
9
+ - Pure representation mathematics only. No fusion-category trait vocabulary,
10
+ no sector identity types, no tensor-network concepts, and no dependency on
11
+ any tensor-network engine crate.
12
+ - Base crate: exact SU(2) only; dependencies stay minimal (big-integer
13
+ arithmetic at most).
14
+ - `cgc-gen` feature: all runtime generation (SU(N)/SO(N)/Sp(2N)) and every
15
+ dense-kernel dependency live behind this feature. Factorizations
16
+ (nullspace/QR/least-squares) and CGC contractions route through the
17
+ selected dense backend; hand-rolled numeric kernels are not accepted.
18
+ - CGC are a legitimate public output of this crate; recoupling (F/R)
19
+ derivation and its caches live here, next to the gauge contract.
20
+
21
+ ## Acceptance criteria (every coefficient-affecting change)
22
+
23
+ 1. Combinatorial structure exact (integer/rational; no floats in
24
+ enumeration, multiplicities, or weights).
25
+ 2. Discrete data exact (duals, Frobenius–Schur phases, signs, basis order).
26
+ 3. Gauge fixing deterministic: pivot rules and sign conventions are part of
27
+ the specification; discrete gauge flips across runs/backends are defects.
28
+ 4. The gauge is a **frozen normative specification** (`docs/gauge.md`,
29
+ `docs/gauge_soN.md`): the documents are the authority, the code implements
30
+ them. A change that moves a coefficient value is a bug unless it ships as a
31
+ specification correction — spec edit, fingerprint `epoch` bump, CHANGELOG
32
+ breaking-change entry, regenerated `tests/gauge_golden.rs` values, one PR
33
+ (`docs/gauge.md`, "Status").
34
+ 5. Floating-point stages verification-gated: orthogonality, unitarity,
35
+ pentagon/hexagon checks run at generation time; violations are typed
36
+ errors, never silently degraded coefficients.
37
+
38
+ ## Guard inventory (every port PR)
39
+
40
+ Reference guards that live *outside* the expression being ported are the
41
+ recurring defect class tracked in issue #15 (dropped `@assert`s, type
42
+ constraints, and unreachable-by-construction assumptions). Every port PR must:
43
+
44
+ 1. Enumerate, in the PR body, each guard on the reference path being ported —
45
+ `@assert`/`@check`, type or domain constraints, and invariants the reference
46
+ relies on as unreachable-by-construction.
47
+ 2. Map each to exactly one of: a typed error, a documented loud-panic invariant,
48
+ or an explicit N/A with justification. An unmapped guard blocks merge.
49
+ 3. Ensure every fast-path / special-case branch runs the same verification
50
+ gates as the general path, or prove those gates vacuous for that branch.
51
+
52
+ ## tenferro-rs dependency (cgc-gen)
53
+
54
+ The `cgc-gen` feature depends on `tenferro-{linalg,cpu,runtime}` via a **git
55
+ dependency pinned to an exact revision** (`Cargo.toml`), so a standalone CI
56
+ checkout with no sibling `tenferro-rs` on disk resolves. Bumping the pinned
57
+ `rev` is an ordinary reviewed commit (it can change coefficient values — see the
58
+ gauge semver contract).
59
+
60
+ To develop against an in-progress local `tenferro-rs` checkout, use a
61
+ **developer-local, never-committed** override: a gitignored `.cargo/config.toml`
62
+ with
63
+
64
+ ```toml
65
+ [patch."https://github.com/tensor4all/tenferro-rs"]
66
+ tenferro-linalg = { path = "../tenferro-rs/crates/tenferro-linalg" }
67
+ tenferro-cpu = { path = "../tenferro-rs/crates/tenferro-cpu" }
68
+ tenferro-runtime = { path = "../tenferro-rs/crates/tenferro-runtime" }
69
+ ```
70
+
71
+ `.cargo/` is gitignored. CI and every reviewed build use the pinned git rev, not
72
+ the patch.
73
+
74
+ ## Verification
75
+
76
+ - Oracles are independent: reference-implementation outputs (WignerSymbols,
77
+ SUNRepresentations, GroupMath, QSpace after gauge alignment), checked-in
78
+ fixtures with provenance, and self-consistency identities. Values derived
79
+ from the code under test are not oracles.
80
+ - **Reference-inventory rule.** Any "existing X" a spec/issue/PR relies on
81
+ (function, symbol, test, fixture) must be cited as `file::symbol` and
82
+ verified to exist before it is built on. Unverified references block merge.
83
+ - **Warm-no-resweep rule.** Adding or touching a coefficient-generation API
84
+ requires a test proving a warm second call is served from cache without
85
+ re-sweeping. Consumer-only strands (they call an existing decomposition)
86
+ are exempt.
87
+ - Fixture strands document normalization, OM-axis convention, and label
88
+ format in their header (`tools/README.md`, "Fixture and spec rules").
89
+ - `cargo fmt` and `cargo test` (all feature combinations touched) before
90
+ every commit; fine-grained commits, each building and testing green.
@@ -0,0 +1,290 @@
1
+ # Changelog
2
+
3
+ All notable changes to this crate are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) with the
6
+ value/gauge rule noted below.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.0] - 2026-08-18
11
+
12
+ **Value/gauge status of this release:** the one value-affecting change is the
13
+ [#90](https://github.com/Ryo-wtnb11/racah/issues/90) base-case frame
14
+ correction below — `SO(2r+1)`/`SO(2r)`/`Spin(N)` coefficient values move and
15
+ `bcd_authority_fingerprint()` is now `epoch=2`; persisted B/D coefficients
16
+ generated under `epoch=1` must be regenerated. `Sp(2r)` values are
17
+ byte-identical, and the SU(2) and SU(N) fingerprints do not move. No
18
+ coefficient, gauge, normalization or fingerprint change has landed since that
19
+ `epoch=2` correction; everything else in this release is API surface
20
+ (`racah::group`, `Spin(N)`, `from_dynkin_in`, the `BcdError` rename), docs,
21
+ tests and CI.
22
+
23
+ ### Added
24
+
25
+ - **Documentation reorganized into five layers** with a top-level index
26
+ ([`docs/README.md`](docs/README.md)). New task-oriented **User Guide**
27
+ (`docs/user-guide/`): getting started, representations, choosing a group,
28
+ fusion, Clebsch–Gordan, recoupling (F/R), and numerical behaviour/resources.
29
+ New `docs/developer/` index; the coefficient-cache audit and trade-off
30
+ documents and their raw JSONL moved there, marked non-user-facing. The README
31
+ is roughly 60% shorter: the provider contract, cache contract, layering
32
+ diagram, kernel routing and verification-strategy prose moved to the User
33
+ Guide, the crate rustdoc and the developer docs rather than being duplicated.
34
+ Rustdoc gained per-item examples and explicit **Returns** sections stating
35
+ array shapes, axis order and multiplicity-index meaning for the CGC / F / R
36
+ surfaces; `tests/doc_examples.rs` compiles and runs every code block in the
37
+ README and the guide. Documentation only — no API, convention, gauge or
38
+ coefficient value changes.
39
+
40
+ - **`sun::Irrep::from_dynkin_in(&GroupId, &[i64])`** — the `A`-series
41
+ form-aware constructor, matching `bcd::Irrep::from_dynkin_in`
42
+ ([#87](https://github.com/Ryo-wtnb11/racah/issues/87)). `SU(N)/Z_k` (`k | N`)
43
+ and `PSU(N)` now restrict the admissible highest weights by `N`-ality; the
44
+ rule itself stays in `GroupId::admits` and `sun` does not restate it.
45
+ `from_dynkin` is unchanged and remains the simply connected `SU(N)`. **No
46
+ coefficient value, basis ordering, phase, normalization or fingerprint
47
+ moves**: a global form gates which weights may be requested, and every
48
+ admissible weight goes through the one Gelfand–Tsetlin engine and the same
49
+ (form-free) cache entry.
50
+ - **`Spin(N)` — the spinor irreps of the `B`/`D` covers**
51
+ ([#54](https://github.com/Ryo-wtnb11/racah/issues/54), stage (b) of
52
+ [#87](https://github.com/Ryo-wtnb11/racah/issues/87)). `Irrep::from_dynkin_in`
53
+ takes a `GroupId`, so `GroupId::spin(N)` admits the spinor labels that
54
+ `SO(N)` rejects; their dimensions, duals, Frobenius–Schur indicators, fusion,
55
+ CGC and F/R are produced by the same bootstrap. Three parts:
56
+ - `bcd::Irrep` now stores the **doubled** ε-basis weight `2λ`, so a spinor's
57
+ half-integer highest weight is exact (`Irrep::two_partition`,
58
+ `Irrep::is_spinor`; `Irrep::partition` returns `Option`, `None` on a
59
+ spinor, and `Irrep::weight_multiplicities` likewise, with
60
+ `two_weight_multiplicities` always available).
61
+ - `bcd::spinor_seeds` — the second base case: the Clifford/Fock generator
62
+ seeds for `ω_r` (`B_r`) and `ω_{r-1}`, `ω_r` (`D_r`), gated by the same
63
+ exact `check_commutators`, specified in
64
+ [`docs/gauge_soN.md`](docs/gauge_soN.md) §16 and pinned by new
65
+ `tests/gauge_golden.rs` rows (`Spin(5)`, `Spin(6)`, `Spin(7)`, `Spin(10)`).
66
+ - The canonical-parent candidate set is **class-indexed** (§14.2): a tensor
67
+ irrep's parents are searched in the tensor sublattice only. **Every shipped
68
+ `SO(N)`/`Sp(2N)` coefficient is byte-identical** and the `bcd` `epoch` stays
69
+ at `1` at this stage; `bcd_authority_fingerprint()` gains no tag (§16.4
70
+ states why). (The separate #90 correction under **Changed** below is what
71
+ later moves the `bcd` epoch to `2`.)
72
+
73
+ Oracles in `tests/isomorphism.rs` and `tests/spin.rs`: `Spin(6) ≅ SU(4)` and
74
+ `Spin(5) ≅ Sp(4)` now agree on the **whole** weight lattice (dimensions,
75
+ duals, every ordered product, Frobenius–Schur), and `Spin(3) ≅ SU(2)` is the
76
+ form-aware low-rank redirect.
77
+
78
+ - **`racah::group` — root datum plus global form** (stage (a) of
79
+ [#87](https://github.com/Ryo-wtnb11/racah/issues/87)). `RootSystem`,
80
+ `GlobalForm`, `CenterSubgroup`, `GroupId` with fallible named constructors
81
+ (`GroupId::su/su_quotient/psu/sp/psp/spin/so/pso/half_spin_plus/
82
+ half_spin_minus`), and `GroupId::admits(&[i64])` — the central-character
83
+ predicate that says which dominant weights are representations of which
84
+ connected compact group. Ungated (pure integer arithmetic). `bcd`'s spinor
85
+ rejection is now the one call site of that predicate, and
86
+ `Series::root_system(r)` is the single bridge to the label-lattice type.
87
+ Conventions pinned in [`docs/references.md`](docs/references.md): the
88
+ `D_r`-odd `Z4` generator `[ω_r]`, and the `D_r`-even half-spin forms named
89
+ by the class they retain. **No coefficient value changes**; no `epoch` moves.
90
+
91
+ - **Frozen gauge specification.** [`docs/gauge.md`](docs/gauge.md) and
92
+ [`docs/gauge_soN.md`](docs/gauge_soN.md) are declared **normative**: the
93
+ documents are the authority and the code implements them. `docs/gauge.md` also
94
+ now specifies the base SU(2) conventions (§12) and marks the rules the
95
+ implementation fixes only implicitly. **No coefficient value changes** from
96
+ this declaration itself; all three `epoch` tags stayed at `1` at this stage
97
+ (the #90 correction below is the release's one epoch move). What changes is the meaning of the authority
98
+ fingerprints: they are now **specification versions**, moving only on a
99
+ specification correction (spec edit + `epoch` bump + CHANGELOG breaking-change
100
+ entry + regenerated goldens, in one PR), never because a refactor moved a
101
+ value. A refactor that moves a value is now a bug by definition.
102
+ ([#84](https://github.com/Ryo-wtnb11/racah/issues/84))
103
+ - **`tests/gauge_golden.rs`**: the in-repo gauge tripwire — a small committed
104
+ table of SU(3) CGC (including the OM = 2 adjoint vertex), SU(3) F, and signed
105
+ SO(5)/Sp(4) CGC and F values, asserted at `1e-12` in the default `cgc-gen` test
106
+ run. It covers the drift the external oracles miss by default: SU(3) F symbols
107
+ are otherwise pinned only by an `#[ignore]`d heavy table oracle, and the QSpace
108
+ B/C/D anchor compares an isotypic projector that is blind to the coupled-side
109
+ gauge. It is a drift detector, not an oracle.
110
+ - **`racah-py`**: PyO3/maturin Python bindings for the SU(N) surface, built as
111
+ a workspace member with `cgc-gen` always on. Import name `racah`,
112
+ distribution `racah-py`; abi3-py312 wheels are built by the `wheels`
113
+ workflow. See [`racah-py/README.md`](racah-py/README.md).
114
+ - **Coverage badges** — self-hosted llvm-cov badge branch plus Codecov
115
+ integration ([#93](https://github.com/Ryo-wtnb11/racah/pull/93)).
116
+ - **`CITATION.cff`**, README badges and a Citation section
117
+ ([#92](https://github.com/Ryo-wtnb11/racah/pull/92)).
118
+
119
+ ### Changed
120
+
121
+ - **BREAKING (error variant renamed): `BcdError::SpinorLabel` is now
122
+ `BcdError::NotAdmissible { group: GroupId, dynkin: Vec<i64> }`**
123
+ ([#87](https://github.com/Ryo-wtnb11/racah/issues/87)). The old name was
124
+ mathematically misleading: the variant is returned whenever
125
+ `group.admits(dynkin)` is false, and `PSp(2r)`, `PSO(2r)` and the two
126
+ half-spin forms all reject *non-spinor* weights through it — `C_r` has no
127
+ spinor sector at all. It now carries the rejecting `GroupId` rather than just
128
+ the `Series`. Pre-1.0, no deprecation shim: the variant is renamed outright,
129
+ and the meaning is exactly "a dominant integral highest weight of the cover
130
+ that is not a genuine representation of the selected global form", kept
131
+ distinct from the invalid-label, rank, excluded-rank, generation and
132
+ verification errors.
133
+ - `SunError` gains `NotAdmissible`, `UnsupportedRootSystem` and
134
+ `LabelRankMismatch` for the new `sun::Irrep::from_dynkin_in` guards.
135
+
136
+ - **BREAKING (gauge specification correction, B/D coefficient values move):
137
+ the B/D defining-rep base case is re-framed into the sweep's descending-weight
138
+ order; `bcd_authority_fingerprint()` moves to `epoch=2`**
139
+ ([#90](https://github.com/Ryo-wtnb11/racah/issues/90)). The QSpace-ported
140
+ defining seed was stored in QSpace's `Setup_*` state order while every
141
+ rediscovery of the same irrep inside a product was produced in the sweep's
142
+ descending-weight order, and `assemble_cgc` exempted base cases from both the
143
+ coherence guard and the intertwiner alignment — so the two frames were mixed
144
+ silently inside a contraction. Measured on `epoch=1`: the `B_2` vector
145
+ pentagon residual `0.285_239_560_970_874_55`, the `D_3` vector pentagon
146
+ residual `0.129_099_444_873_580_74`, and `B_2 F(1,v,v;adj|v,adj) = 0.0` where
147
+ unitarity forces `1.0`. The seed is now put through one §1–§8 sweep pass over
148
+ its own carrier before it enters the catalog (`docs/gauge_soN.md` §14.2,
149
+ "Base-case frame"), so there is one frame convention in the crate, and the
150
+ base-case exemption is **removed**: the coherence guard and the alignment now
151
+ bind every block, base cases included, which makes this defect class
152
+ impossible to reintroduce silently. Consequences for consumers: persisted
153
+ `SO(2r+1)`/`SO(2r)`/`Spin(N)` coefficients derived under `epoch=1` are stale
154
+ and must be regenerated; `Sp(2r)` (the `C` family) values are **unchanged** —
155
+ `Setup_SpN`'s order was already descending-weight, which is why the C gates
156
+ always closed. `tests/gauge_golden.rs` is regenerated for the moved entries.
157
+
158
+ - **`BcdError::ExcludedRank::redirect` is form-aware** (stage (b) of #87, Q3):
159
+ the low-rank isomorphism is an isomorphism of *groups*, so `Spin(3)` now
160
+ redirects to `"use SU(2) instead"` while `SO(3)` redirects to `"use SU(2)
161
+ with integer j only instead"` (likewise `Spin(4)`/`SO(4)`). Diagnostic strings
162
+ only; no coefficient value changes.
163
+ - **`bcd::Irrep::partition` returns `Option<Vec<i64>>`** and
164
+ `bcd::Irrep::weight_multiplicities` returns `Option<…>`, both `None` on a
165
+ spinor irrep, whose ε-basis weights are half-integers. The always-exact
166
+ doubled forms are `two_partition` / `two_weight_multiplicities`.
167
+ - Docs: replaced the "no label ceiling" absolute with the machine-word label
168
+ bounds that report a typed overflow, and corrected "selectable dense
169
+ backend" to the single Tenferro seam with no public backend-selection API.
170
+ - **`wheels` workflow: full abi3 wheel matrix + PyPI trusted publishing for
171
+ `racah-py`** ([#103](https://github.com/Ryo-wtnb11/racah/pull/103)): linux
172
+ x86_64/aarch64, macOS arm64/x86_64, windows x86_64, plus an sdist. Publishing
173
+ is gated on `py-v*` tags/releases, so the crate's own `v*` releases never
174
+ touch PyPI. `racah-py` itself is unversioned by this release (stays 0.1.0,
175
+ `publish = false` for crates.io) and releases separately.
176
+
177
+ ## [0.1.1] - 2026-08-12
178
+
179
+ This release publishes the generated-provider dependency closure against the
180
+ published Tenferro 0.3.0 registry line.
181
+
182
+ ### Changed
183
+
184
+ - Use registry `tenferro-* = "0.3.0"` dependencies for `cgc-gen`.
185
+ - Document crates.io installation and the published feature configuration.
186
+
187
+ Generated-provider (`cgc-gen`) observability and convention-identity surface
188
+ (issue [#47](https://github.com/Ryo-wtnb11/racah/issues/47)). This whole surface
189
+ is **unstable: shape may change while the generated-provider contract is
190
+ negotiated** — Cargo features cannot express instability tiers, so the rustdoc
191
+ labels plus issue #47 are the ledger.
192
+
193
+ ### Added
194
+
195
+ - **Per-tier coefficient-cache trim**: `trim_to(CoefficientCacheTier, bytes)`
196
+ releases the oldest FIFO prefix of exactly one tier and reports its
197
+ linearization-point charged-entry accounting in `CacheTrimReport`. It is a
198
+ process-global single-owner lifecycle operation; it preserves the one-shot
199
+ cache budget and makes no allocator/RSS release claim.
200
+
201
+ - **One-shot coefficient-cache budgets**: `CoefficientCacheBudgets`,
202
+ `CoefficientCacheTier`, `configure_cache_budgets`, and `cache_budgets` let
203
+ applications shrink independent process-local tier caps before first use.
204
+ Zero retains no entry; reset preserves the selected policy.
205
+
206
+ - **Shared SU(N) product tier** (`cgc-gen`, issue #59): exact
207
+ `directproduct` decompositions are retained once as sorted shared channels
208
+ under an order-normalized irrep pair. The unchanged public API reconstructs
209
+ its `BTreeMap`; Racah-private multiplicity and channel consumers use the
210
+ shared value directly. The tier is bounded at 256 entries and a 128 KiB
211
+ retained-charge backstop, sized from the checked-in collector and a
212
+ downstream SU(3)+SU(4) Generic HomSpace/topology probe.
213
+ - **Generated-tier cache stats** (`cgc-gen`): `generated_cache_stats() ->
214
+ GeneratedCacheStats` (`#[non_exhaustive]`, reusing `TierStats`) reports the
215
+ five generated tiers (SU(N) product / CGC / F, B/C/D CGC / F) per-tier plus a
216
+ field-wise `total()`. `GeneratedCacheStats` `bytes` fields are conservative
217
+ retained charges of cache-owned entries, with container scaffolding,
218
+ allocator/RSS costs, external clones, and returned values excluded.
219
+ `GENERATED_CACHE_MAX_BYTES` (640 MiB + 128 KiB) is the documented aggregate
220
+ retained-charge cap, tied to the per-tier caps by a `const` assertion.
221
+ Two-layer cache story: base = `BASE_CACHE_MAX_BYTES`, generated =
222
+ `GENERATED_CACHE_MAX_BYTES`, whole = the documented sum; no cross-feature
223
+ constant. `reset()` clears the generated tiers alongside the base ones.
224
+ - **Generated authority fingerprints** (`cgc-gen`):
225
+ `sun::sun_authority_fingerprint()` and `bcd::bcd_authority_fingerprint()`
226
+ (`&'static [u8]`). Their contract is weaker than the exact SU(2) fingerprint —
227
+ equal fingerprints identify the same convention, generation pipeline, and
228
+ tolerance policy, but do not imply byte-identical values or independently prove
229
+ numerical agreement (verification gates and oracles own that). Epochs are
230
+ per-family and independent. Backend identity is excluded by design.
231
+ - **Backend structural-identity gate** (`cgc-gen`, D2): a test asserting the
232
+ discrete/structural generation outputs are a function of the convention alone
233
+ (stable across independent in-process runs), the single-backend reduction of
234
+ the cross-backend gate.
235
+
236
+ ## [0.1.0] - 2026-07-24
237
+
238
+ First tagged release of the v0 scope: the full representation-theory
239
+ coefficient set for SU(2), SU(N), SO(N), and Sp(2N), computed on demand with no
240
+ label ceiling.
241
+
242
+ ### Added
243
+
244
+ - **Exact SU(2)** (default build, no features): 3j, 6j, Clebsch–Gordan, and
245
+ F / R / Frobenius–Schur symbols in closed-form big-rational arithmetic with a
246
+ single final rounding. Dependency-light (`num-bigint` / `num-rational` /
247
+ `num-traits` only).
248
+ - **Generated SU(N)** (`cgc-gen` feature): the Gelfand–Tsetlin pipeline — CGC,
249
+ F, and R with outer-multiplicity indices.
250
+ - **Generated SO(N) / Sp(2N)** (`cgc-gen` feature): the generator-bootstrap
251
+ pipeline over the B/C/D Cartan series — CGC, F, and R.
252
+ - **Base SU(2) provider contract:**
253
+ - `su2_authority_fingerprint()` — opaque bytes identifying the value-fixing
254
+ convention set; compared by equality, changed only on a value-affecting
255
+ breaking release.
256
+ - Checked representation surface — `Su2Irrep` (with `dj` / `dim` / `dual` /
257
+ `fusion`), `Su2Fusion`, `Su2Error` / `AdmissibilityViolation`, and the
258
+ `wigner_3j_checked` / `wigner_6j_checked` / `clebsch_gordan_checked` /
259
+ `su2_f_symbol_checked` / `su2_r_symbol_checked` functions. Additive over the
260
+ infallible zero-convention functions; distinguishes `Ok(0)` (an admissible
261
+ accidental zero) from `Err(NotAdmissible)` (a forbidden coupling).
262
+ - Cache resource contract — `BASE_CACHE_MAX_BYTES` static partition over the
263
+ three base tiers, `base_cache_stats()` / `BaseCacheStats` / `TierStats`
264
+ per-tier statistics (entries, bytes, hits, misses, evictions), and
265
+ single-owner `reset()`.
266
+ - **Self-check / oracle batteries**, shipped as public API and used as
267
+ generation gates: CGC orthogonality, F-unitarity, R-orthogonality, and the
268
+ pentagon / hexagon identities.
269
+
270
+ ### Notes
271
+
272
+ - Not published to crates.io: the `cgc-gen` feature depends on the unpublished
273
+ `tenferro-rs`, so a crates.io release is blocked upstream. The git dependency
274
+ is the supported path.
275
+
276
+ ### Versioning policy
277
+
278
+ Coefficient *values* are floating point, but the *computation* is exact:
279
+ combinatorial structure, discrete data (duals, signs, Frobenius–Schur phases),
280
+ and gauge fixing are deterministic. Any change that can alter a coefficient
281
+ value, its normalization, or its canonical gauge is a **breaking** change, so
282
+ consumers may key caches and persisted data on the crate version. For the base
283
+ SU(2) provider this rule is mechanized by `su2_authority_fingerprint()`: its
284
+ epoch is bumped only on such a value-affecting release, so a fingerprint change
285
+ and a breaking release are one reviewable event.
286
+
287
+ [Unreleased]: https://github.com/Ryo-wtnb11/racah/compare/v0.2.0...HEAD
288
+ [0.2.0]: https://github.com/Ryo-wtnb11/racah/compare/v0.1.1...v0.2.0
289
+ [0.1.1]: https://github.com/Ryo-wtnb11/racah/releases/tag/v0.1.1
290
+ [0.1.0]: https://github.com/Ryo-wtnb11/racah/releases/tag/v0.1.0
@@ -0,0 +1,16 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ type: software
4
+ title: racah
5
+ authors:
6
+ - family-names: Watanabe
7
+ given-names: Ryo
8
+ repository-code: "https://github.com/Ryo-wtnb11/racah"
9
+ license:
10
+ - MIT
11
+ - Apache-2.0
12
+ abstract: >-
13
+ A Rust library for Racah-Wigner calculus: Clebsch-Gordan and recoupling
14
+ (F/R) coefficients for SU(2), SU(N), SO(N), and Sp(2N), computed on demand
15
+ for any admissible labels under a gauge frozen as a normative specification,
16
+ with Python bindings.