Reduce cache footprint by decoupling degeneracy-dependent data#387
Reduce cache footprint by decoupling degeneracy-dependent data#387
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
src/tensors/tensorstructure.jl
Outdated
| @@ -0,0 +1,276 @@ | |||
| # Block and fusion tree ranges: structure information for building tensors | |||
| #-------------------------------------------------------------------------- | |||
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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₂)) |
There was a problem hiding this comment.
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
| 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₂) |
There was a problem hiding this comment.
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?
Refactors the internal block structure representation for
TensorMapby splittingFusionBlockStructureinto two separately-cached structs:SectorStructure(cached by sector structure, shared across spaces with the same sectors): coupled sectors and valid fusion tree pairs asDictionaries.Indices.DegeneracyStructure(cached perHomSpace): total dimension, block sizes/ranges, and sub-block strides as plainVectors.The public
blockstructure(W)andsubblockstructure(W)combine these on the fly into aDictionary.Previously,
FusionBlockStructurestored afusiontreelist, afusiontreeindiceslookup dict, and afusiontreestructurevector — the tree pairs were stored twice and lookup required manual indirection.Dictionaries.jlhandles this naturally via its token system.A secondary benefit:
DegeneracyStructure{N}is parameterized only by the number of indicesN, 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.jlinto a newtensorstructure.jl.Open questions:
Dictionaries.jltypes to avoid maintaining two APIs?