Intercept TO.tensoradd! instead of TensorKit.add_transform! - #78
Merged
Merged
Conversation
The blockwise `tensoradd` implementations were only reachable through `TensorKit`'s internals: `TO.tensoradd!` delegated to `permute!`, and the `add_transform!` methods defined here caught the kernel. Neither is a contract `TensorKit` owes us, and both stopped holding on TensorKit's index-manipulation refactor (QuantumKitHub/TensorKit.jl#526), where `TO.tensoradd!` calls the braid kernel directly and `add_transform!` gained a `conjsrc` argument. Nothing errors in that case: dense block tensors survive via TensorKit's generic subblock fallback, while sparse ones silently produce zeros, because the fallback writes through blocks a sparse container never materialized. Implement `TO.tensoradd!` for the block tensor types instead. That is the public entry point `@tensor` lowers to, so it is reachable no matter how TensorKit arranges its kernels, and `conjA` is handled explicitly rather than relying on it having been unwrapped into an adjoint beforehand. The mixed block/plain methods take the concrete `TensorMap` so that `(BlockTensorMap, SparseBlockTensorMap)` and the reverse resolve to the general method rather than being ambiguous. The added testset calls `TO.tensoradd!` directly rather than through `@tensor`, so this stays covered independently of TensorKit's routing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`planaradd!`, `planartrace!` and `planarcontract!` are reached through `transpose!`, `trace_permute!` and `contract!`, so they currently land on methods defined here and are not affected by TensorKit#526. They rest on the same undocumented delegation that broke `tensoradd!` though, and there was no planar coverage here at all, so pin them directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
Merged
lkdvos
referenced
this pull request
Sep 23, 2026
* Bump version to v0.3.19
* Adapt multifusion `SumSpace` to TensorKit v0.17.2 coloring rules
TensorKit v0.17.2 (#515) requires every `GradedSpace` to be homogeneously
colored, forbids `unitspace` for `GenericUnit` sectors and checks coloring
when building `ProductSpace`/`HomSpace`. This broke the multifusion `SumSpace`
tests, which flattened heterogeneous sums into a single `GradedSpace`.
- `unitspace(::Type{<:SumSpace})` returns one component per simple unit
- `leftunitspace`/`rightunitspace` inspect components instead of flattening
- `_leftrightunit(::SumSpace)` uses the shared unit, or a wildcard if components differ
- tests use `⊞` instead of flattening `⊕`, and expect incompatible products to throw
- require TensorKit v0.17.2
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Require multifusion SumSpace to be homogeneously colored as a whole
The previous fix let leftunitspace/rightunitspace answer for a SumSpace
whose components agreed on only one side (left xor right), by checking
each side independently and treating disagreement on the other side as
a wildcard. That's inconsistent with how a plain GradedSpace works: it
is either homogeneously colored (single left AND right unit shared by
every sector) or invalid. A SumSpace should follow the same rule as a
whole, so leftunitspace/rightunitspace/_leftrightunit again require
full agreement on both sides and reject (SpaceMismatch) otherwise,
without any special-casing for a partial match.
unitspace itself is unaffected by this: TensorKit already forbids it at
the type level for GenericUnit sector types, regardless of any given
instance's homogeneity, so it throws ArgumentError unconditionally for
IsingBimodule-sectored spaces, homogeneous or not. The Multifusion
testset is adjusted to expect this throughout, rather than the previous
attempt to give unitspace a real answer for such sector types.
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Problem
The blockwise
tensoraddimplementations here were only reachable through TensorKit internals:TO.tensoradd!delegated topermute!, which this package overrides, andTK.add_transform!methods insrc/tensors/indexmanipulations.jlcaught the kernel.Neither is a contract TensorKit owes us, and both stopped holding on TensorKit's
index-manipulation refactor (TensorKit.jl#526),
where
TO.tensoradd!calls the braid kernel directly andadd_transform!gained aconjsrcargument (8 → 9 positional arguments, so all five of our methods silently became unreachable).
Nothing errors in that state. Dense block tensors survive via TensorKit's generic subblock
fallback, but sparse block tensors silently produce zeros, because the fallback writes through
blocks a sparse container never materialized:
Measured against TensorKit#526,
test/linalg/tensoroperations.jlfails, andTO.tensoradd!on aSumSpace(ℂ^8, ℂ^8, ℂ^8)4-leg block tensor also regresses 262 µs → 10473 µs.Fix
Implement
TO.tensoradd!for the block tensor types. That is the public entry point@tensorlowers to, so it stays reachable however TensorKit arranges its kernels, and
conjAis handledexplicitly rather than relying on it having been unwrapped into an adjoint beforehand.
The mixed block/plain methods take the concrete
TensorMapso that(BlockTensorMap, SparseBlockTensorMap)and the reverse resolve to the general method instead ofbeing ambiguous.
The five
TK.add_transform!methods are left in place: they are still live against releasedTensorKit's
permute!routing, and our compat isTensorKit = "0.17". They can go when thatbound moves.
Tests
Two new testsets, both calling the entry points directly rather than through
@tensor, sincethe existing tests only exercised the macro and so pinned none of them:
tensoradd! entry point—TO.tensoradd!over sparse/dense × two permutations ×conjA.planar entry points—planaradd!, plus@planartrace and contraction. These are notcurrently broken (
planaradd! → transpose!,planartrace! → trace_permute!,planarcontract! → contract!all still land on methods we override), but they rest on the sameundocumented delegation, and there was previously no planar coverage here at all.
Verification
TO.tensoradd!@tensorpermuteAqua clean (including the method-ambiguity check) and
runicclean.🤖 Generated with Claude Code