Repository navigation
Description
With #580, #581 and #592, most of the time spent building the braid transformers of product sectors goes into their serial preparation. That preparation is sectorstructure, degeneracystructure and fusionblocks, and it runs before the threaded part.
Building all transformers of one CTMRG iteration with fℤ₂ × U(1) × SU(2) × U(1) (D = 7) on 8 threads takes 19.5–20.6 s. 13.5–14.9 s of that is the serial preparation.
The cost comes from storing every tree pair. That's 5.6 M pairs from 1.3 M splitting and fusion trees, over 776 spaces:
sectorstructure: about half the time hashes every pair into theIndices. The rest enumerates the trees.fusionblocks: about 40 % sorts the groups, with tuple comparisons of product sectors. About 15 % hashes the uncoupled sectors of every pair into theDict.
Proposal
fusiontrees(W)keeps the splitting and fusion trees of every coupled sector, not the pairs.- A pair's position follows from the trees:
offset[c] + (j₂ - 1) * n₁[c] + j₁. - A small
AbstractIndicestype would hand out positions as tokens, sogettoken/gettokenvalueand the call sites stay unchanged. subblockstructure(W)would become a dictionary that shares those tokens.
- A pair's position follows from the trees:
fusionblocksgroups each side and combines the groups.- The groups are keyed by the position of their uncoupled sectors among the sectors of each space, so the sort compares integers.
- Every block is filled once, at its final size.
Prototype
The prototype gives identical transformers, block order included, on all transformers of the iteration (550 for SU(2), 554 for SU(3)). The spaces, construction, indexmanipulations, braidingtensor, diagonal and doubletree tests pass.
Baseline: main + #580 + #581 + #592 (as of 832ec10, plus the unique-product shortcut it has now). Both versions also preallocate the pair vector in sectorstructure. Measured on a shared machine at load 34–50, two rounds in the same run:
| 1 thread | 8 threads | |
|---|---|---|
| fℤ₂ × U(1) × SU(2) × U(1), before | 28.0–29.6 s | 19.5–20.6 s |
| fℤ₂ × U(1) × SU(2) × U(1), after | 18.4–19.6 s | 12.5–13.8 s |
| fℤ₂ × U(1) × SU(3), before | 9.0–12.6 s | 4.2–4.3 s |
| fℤ₂ × U(1) × SU(3), after | 7.9–8.0 s | 3.3–3.4 s |
The serial part (SU(2) case) goes from 13.5–14.9 s to 5.3–7.5 s:
fusionblocks: 6.6–8.0 s → 1.9–2.2 s;sectorstructure: 4.4–5.9 s → 2.2–2.8 s;degeneracystructure: unchanged.
Caching the trees of each side across spaces didn't pay off, so the prototype adds no new cache.
Questions
fusiontrees(W)andsubblockstructure(W)would no longer return anIndicesand aDictionary. Is that acceptable, or does other code rely on those types?- The integer positions in
fusionblocksdepend on the order in whichsectors(V)iterates, as the current sort key already does implicitly. Is that acceptable now thatfusionblocksdominates?
Related: #581 (current fusionblocks), #587 (caching fusionblocks).
Activity
lkdvos commentedon Oct 26, 2026
I don't think there's an issue with changing the types, as long as they follow the same
Dictionaries.jlinterface nothing should really change. I think there are some internals in TensorKit that depend on the tokens being integers, but I guess that is what the prototype is already doing so that should probably be fine?For the positions in
fusionblocks, nothing really depends on that order. I think the previous order is mostly chosen such that the.datafield is traversed in order (ish, because strided), but there is no real reason except for some data locality in the case of many small blocks, which we aren't really making use of. Actually, the changes in #592 already change the order within hte fusionblocks a bit, since it really is easier to just use the natural "kronecker product" order to make these computations fast.Feel free to open a draft PR with the prototype though, I'd love to have a look