Skip to content

Reduce cache footprint by decoupling degeneracy-dependent data#387

Open
lkdvos wants to merge 20 commits intomainfrom
ld-caching
Open

Reduce cache footprint by decoupling degeneracy-dependent data#387
lkdvos wants to merge 20 commits intomainfrom
ld-caching

Conversation

@lkdvos
Copy link
Copy Markdown
Member

@lkdvos lkdvos commented Mar 23, 2026

Refactors the internal block structure representation for TensorMap by splitting FusionBlockStructure into two separately-cached structs:

  • SectorStructure (cached by sector structure, shared across spaces with the same sectors): coupled sectors and valid fusion tree pairs as Dictionaries.Indices.
  • DegeneracyStructure (cached per HomSpace): total dimension, block sizes/ranges, and sub-block strides as plain Vectors.

The public blockstructure(W) and subblockstructure(W) combine these on the fly into a Dictionary.

Previously, FusionBlockStructure stored a fusiontreelist, a fusiontreeindices lookup dict, and a fusiontreestructure vector — the tree pairs were stored twice and lookup required manual indirection. Dictionaries.jl handles this naturally via its token system.

A secondary benefit: DegeneracyStructure{N} is parameterized only by the number of indices N, not the sector type. Kernels that only need sizes/strides/offsets can therefore be compiled once and reused across different symmetry groups — a potential route to much faster precompilation.

Also moves the tensor structure computation out of homspace.jl into a new tensorstructure.jl.


Open questions:

  • Should we replace all remaining internal dicts with Dictionaries.jl types to avoid maintaining two APIs?
  • Are there other places that would benefit from a similar sector/degeneracy split?

@lkdvos lkdvos linked an issue Mar 23, 2026 that may be closed by this pull request
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 95.59471% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/spaces/structure.jl 96.00% 4 Missing ⚠️
src/tensors/treetransformers.jl 80.95% 4 Missing ⚠️
src/spaces/homspace.jl 96.00% 1 Missing ⚠️
src/tensors/abstracttensor.jl 80.00% 1 Missing ⚠️
Files with missing lines Coverage Δ
ext/TensorKitMooncakeExt/utility.jl 85.71% <ø> (ø)
src/TensorKit.jl 13.79% <ø> (ø)
src/auxiliary/dicts.jl 44.50% <100.00%> (-1.83%) ⬇️
src/fusiontrees/braiding_manipulations.jl 87.70% <100.00%> (-0.06%) ⬇️
src/spaces/gradedspace.jl 92.52% <100.00%> (+0.40%) ⬆️
src/spaces/productspace.jl 88.60% <100.00%> (+0.60%) ⬆️
src/spaces/vectorspaces.jl 85.83% <100.00%> (+1.14%) ⬆️
src/tensors/braidingtensor.jl 65.60% <100.00%> (-0.22%) ⬇️
src/tensors/tensor.jl 85.05% <100.00%> (-0.17%) ⬇️
src/tensors/tensoroperations.jl 97.67% <100.00%> (-0.02%) ⬇️
... and 4 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@@ -0,0 +1,276 @@
# Block and fusion tree ranges: structure information for building tensors
#--------------------------------------------------------------------------
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though some of this information is very much tailored towards the tensor construction, in my mind spaces come before tensors, and could in principle exist on their own. However, the current code in the spaces subfolder is broken without the definitions in this file, so I would prefer this file to live in the spaces folder. (There is not a single mention of TensorMap in this file).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now moved a bunch of the functions around, let me know if this feels more natural!


function sectorequal(V₁::ElementarySpace, V₂::ElementarySpace)
isdual(V₁) == isdual(V₂) || return false
return issetequal(sectors(V₁), sectors(V₂))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since sectorhash does in principle depend on the order in which the sectors are iterated (which is fine, since they are anyway ordered), it does perhaps make sense to make sure that sectorequal is fully consistent and also explicitly iterates through the sectors. Then it can also be made completely allocation free, but is of course a bit more code. Since length(sectors(...)) doesn't generally work, I can also not use zip so I found no better solution then writing out the iteration exaclty

Suggested change
return issetequal(sectors(V₁), sectors(V₂))
s₁, s₂ = sectors(V₁), sectors(V₂)
next₁, next₂ = iterate(s₁), iterate(s₂)
while !isnothing(next₁) && !isnothing(next₂)
val₁, state₁ = next₁
val₂, state₂ = next₂
val₁ == val₂ || return false
end
return isnothing(next₁) && isnothing(next₂)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I'm okay with this, another approach could be to simply not define this fallback definition at all. I have added specializations for our specific GradedSpace implementations, so I think this method currently only ends up being called with Cartesian or complex spaces, for which I probably should also just overload this. In the end, this code would then never be called and it might be better to just have a methoderror if we end up accidentally changing something in the future?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fusionblockstructure should reuse data that is degeneracy-independent

2 participants