Repository navigation
Conversation
The Weyl dimension formula used `Rational` arithmetic. The factors with indices up to `k2` give the dimension of an SU(k2) irrep, so the product of each row of factors can be divided exactly in integer arithmetic (with an `Int128` intermediate), which keeps the intermediate values close to the result. Tested against the rational formula up to SU(12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- For N ≥ 6, irreps with few boxes (or whose dual has few boxes) use the hook-content formula, which costs O(number of boxes) instead of O(N²). Its numerator is bounded up front, which selects exact Int, Int128 or BigInt arithmetic. - The Weyl formula bounds the product of every row of factors, and switches to BigInt when it could overflow an Int128. Since Dynkin labels are at most 255, no checks are needed for N ≤ 6. Previously, irreps of SU(N ≥ 22) could throw an `AssertionError` from the wrapped products. Dimensions that do not fit in an `Int` throw an `OverflowError`. - `weight` is computed in O(N) instead of O(N²). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`s1 ⊗ s2` iterated the keys of a `Dict`, so the order of fusion products, and with it the
fusion tree order and data layout of SU(N) tensors, depended on `hash(::SUNIrrep)`. The
direct product is now returned in sorted (`isless`) order, which coincides with the order of
`values(SUNIrrep{N})`. Fixes #54.
- Direct products are computed with a pruned Littlewood-Richardson enumeration in GT form,
which only visits patterns that contribute. `Nsymbol` uses the same search restricted to a
single target irrep, after cheap box-count and containment checks (10-1000x faster).
- `directproduct` counts multiplicities in a `Dict` during the enumeration. For `⊗`, they are
counted by sorting for small products, and in a `Dict` for large ones.
Note that the order of `⊗` changes once, so tensor data stored with the previous layout must
be converted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The F- and R-symbols were cached in one `LRU` per rank, which takes a lock on every lookup, hits included, so that TensorKit reading them from many threads serializes on it. They are now `@cached` with MemoizationKit, whose default `ClockCache` is read without a lock (on the `lockfree-clock` branch, selected through `[sources]`). The sorted direct product behind `⊗` is cached in the same way. `Nsymbol` stays uncached: most calls are rejected by its cheap checks in about 5 ns, and the others are comparable to a cache lookup for small products. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
This was referenced Oct 15, 2026
lkdvos
force-pushed
the
ld/sorted-directproduct
branch
from
October 15, 2026 15:35
8beb0d7 to
cba7abe
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #58 (base branch
ld/sorted-directproduct); the diff below only contains the commit on top of it. An alternative to the caches of #57, using MemoizationKit.jl.The F- and R-symbols were cached in one
LRUper rank, which takes a lock on every lookup, hits included, so TensorKit reading them from many threads serializes on it. #57 replaces this by a snapshot cache and a 64-shardLRU. Here, the caches are@cachedfunctions instead:FsymbolandRsymbolcall@cachedfunctionsfsymbolandrsymbol(the former_Fsymboland_Rsymbol). These are owned by SUNRepresentations, so their size can be set per function in the[SUNRepresentations.MemoizationKit.fsymbol]preferences, and they show up in MemoizationKit'scache_infoand dashboard.sorteddirectproduct, which⊗iterates over, is@cachedas well. Its result is shared and must not be mutated.Nsymbolstays uncached: most calls are rejected by its cheap checks in about 5 ns, and the others are comparable to a cache lookup for small products.ClockCache, is read without a lock on thelockfree-clockbranch (Make ClockCache hits lock-free MemoizationKit.jl#21), which is selected through a[sources]entry until that is released.The CGC cache is unchanged: it is only read when F- and R-symbols are computed, which are now cached.
Benchmarks
The reproducer of #56/#57 (cold construction of the recoupling data of one permutation in a CTMRG step of an SU(3)-symmetric PEPS), minimum over two interleaved rounds. "#58 + #57" applies the caches of #57 on top of #58, with the snapshot cache holding the sorted direct product of
⊗:mainEnd to end, MemoizationKit matches the dedicated caches of #57. The difference shows in the cost of a cache hit (
@belapsed), and when many threads hit at once (8 tasks, each looking up the same 2404 warm SU(3) F-symbols, time per lookup):LRUper rank)LRU)FsymbolhitRsymbolhitFsymbolhits, 8 tasks(1, 1) ⊗ (2, 1)The lock-free
ClockCachemakes F- and R-symbol hits 3–4× cheaper than the shardedLRU, and these hits scale with threads (19 ns per lookup over 8 tasks, against 116 ns for 1 task on the same keys). Iterating over⊗is about 14 ns slower than with the snapshot cache, whose hits are a plainDictlookup in an immutable snapshot (at the cost of copying the snapshot on every insertion).Details and caveats
mainlets a package ship its own defaults (default_cache_settings, Package-provided default cache settings per function MemoizationKit.jl#20), but thelockfree-clockbranch predates that; with both, this PR could keep the previous limits.[sources], and resolves the registered MemoizationKit 0.1.0, whoseClockCachelocks on hits (correct, but without the scaling above).LRUCacheis still used for the CGC cache.TensorKit v0.17.2, TensorKitSectors v0.3.10, Julia 1.13.1, shared 32-core workstation (load average 12–16 during these runs; the tables use the minimum).
🤖 Generated with Claude Code