Skip to content

Don't materialize an AdjointTensorMap in TO.tensoradd! when conjA = true - #520

Open
leburgel wants to merge 1 commit into
mainfrom
lb/conj_threading
Open

Don't materialize an AdjointTensorMap in TO.tensoradd! when conjA = true#520
leburgel wants to merge 1 commit into
mainfrom
lb/conj_threading

Conversation

@leburgel

@leburgel leburgel commented Aug 27, 2026

Copy link
Copy Markdown
Member

A second option for a proper fix for #516, which avoids materializing an AdjointTensorMap in TO.tensoradd! altogether, but rather passes the conj flag along to the array-level tensoradd!

Basically the same underlying idea as #519, but since I don't exactly know what I'm doing here I'm posting both, mostly just for inspiration.

Some benchmark timings based on the reproducer of #516:

sector type old patched plain TensorMap old ratio patched ratio speedup
fℤ₂ ⊠ U(1) 0.1245 ms 0.0375 ms 0.0336 ms 3.70× 1.12× 3.32×
SU(2) 2.8247 ms 0.3739 ms 0.3448 ms 8.19× 1.08× 7.55×
fℤ₂ ⊠ U(1) ⊠ SU(3) 4.0868 ms 0.0510 ms 0.0512 ms 79.80× 1.00× 80.08×

@leburgel leburgel changed the title Don't materialize an AdjointTensorMap in ` Don't materialize an AdjointTensorMap in TO.tensoradd! when conjA = true Aug 27, 2026
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/tensors/indexmanipulations.jl 83.33% 1 Missing ⚠️
src/tensors/tensoroperations.jl 85.71% 1 Missing ⚠️
src/tensors/treetransformers.jl 85.71% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/spaces/homspace.jl 91.83% <100.00%> (+0.46%) ⬆️
src/tensors/indexmanipulations.jl 88.23% <83.33%> (-0.12%) ⬇️
src/tensors/tensoroperations.jl 97.00% <85.71%> (-0.38%) ⬇️
src/tensors/treetransformers.jl 82.07% <85.71%> (-0.45%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lkdvos lkdvos left a comment

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.

Overall this looks great, it's definitely the right call to also specialize the adjointtensor, I've just been too lazy and putting it off, hoping that the actual runtime fraction is not too high. I somehow didn't realize that the structure mapping in the current state is actually not that complicated, so this does indeed open up many optimization possibilities.


On a larger scale comment, I'm not sure if this is really a route we want to take, but it does look like we could conceivably reduce the cache sizes by simply making the "treetransformers" only store which tokens go to which other tokens, and therefore decouple that from the actual subblockstructure dictionaries that can then fill out which data is where. This might be a bit larger of a refactor though, so I'll see what I can do next week and if this really makes sense

Comment on lines +613 to +614
tdst, tsrc, p, transformer, α, β, backend, allocator, scheduler;
conjsrc::Bool = false

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.

Suggested change
tdst, tsrc, p, transformer, α, β, backend, allocator, scheduler;
conjsrc::Bool = false
tdst, tsrc, p, conjsrc, transformer, α, β, backend, allocator, scheduler

little bit of a nitpick, but I'd be inclined to just make this a mandatory (positional) argument instead, which is more in line with the remainder of these functions. Obviously doens't matter, just for consistency.

A′ = adjoint(A)
pA′ = adjointtensorindices(A, _canonicalize(pA, C))
permute!(C, A′, pA′, α, β, backend, allocator)
if C isa TensorMap && A isa TensorMap

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.

It somehow feels like this specialization might make more sense on the permute!-level directly, where we could intercept TensorMap-AdjointTensorMap combinations directly? That would also immediately catch manual calls to permute!(tdst, tsrc', ...) in the same way.

To achieve this, I'm wondering if it makes sense to just have treebraider(Vdst, Vsrc, p, levels, conj) as the entrypoint, which then also fixes the caching keys being distinct? There's some subtleties left with this, mostly with respect to other combinations like permute!(tdst', tsrc, ...), which we would then probably want to map to the other case, but that might be reasonable?

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 think the first part of this comment is more similar to what is sketched in #519, as opposed to the tensoradd!-level fix that is sketched here.

Comment thread src/spaces/homspace.jl

See also [`subblockstructure`](@ref).
"""
function adjoint_subblockstructure(W::HomSpace)

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.

code-organization wise, would it be easier to make this subblockstructure(W::HomSpace, conjW::Bool=false)?

Comment thread src/spaces/homspace.jl
Comment on lines +189 to +190
newkeys = map(((f₁, f₂),) -> (f₂, f₁), collect(keys(structs)))
newvals = map(collect(values(structs))) do (sz, str, off)

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.

it feels like it should be possible to avoid the collect calls here, but I'd have to dig through the Dictionaries interface(s) to see what is possible?

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.

2 participants