Enforce strict Einstein index rules in @tensor - #289
Merged
Conversation
Codecov Report❌ Patch coverage is
... and 4 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Cover the paths of `verifyindices` that were not yet exercised: - `/` and `\`: the divisor is verified as a separate scope, while the closed labels of the tensor operand do escape to the enclosing term. - `conj`, adjoint and unary minus, which pass the labels through. - `@notensor`, which is skipped entirely. This also adds the first runtime test for `@notensor`, covering the corresponding branches in `processcontractions`, `replaceindices` and `tensorify`. - the argument of an explicit `tensorscalar`, which is a separate scope but is still verified. Two branches turned out to be unreachable and are removed instead: an explicit `tensorscalar(...)` call is not an `istensorexpr`, and a sum node cannot carry scalar terms since `istensorexpr` requires all of its terms to be tensor expressions. Both cases are already handled through the scalar factor fallback of `_indexscope_product`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Member
|
I'm not familiar at all with programming macros and working with expressions so perhaps @Jutho best review this. |
Member
|
Looks good to me. Was this logic completely Claude generated? Not bad. |
Jutho
approved these changes
Jul 31, 2026
Member
Author
|
Yeah, it mostly is, although I did steer it a little and had to clean up some things in the end. |
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.
Fixes #288.
@tensorsilently accepted expressions where an index label appears more than twice, e.g.The only arity check lived in
insertcontractiontrees!and countedgetindicesper factor.Since
getindicesreturns open indices, a label traced away inside one factor becameinvisible and could be reused by the other factors. The check also never ran for products of
fewer than three factors, nor for a single tensor.
This adds
verifyindices, a single pass over each statement that enforces the documented rule:within one additive term every label appears once (open) or exactly twice (contracted), counting
all occurrences including traces. Parentheses group factors but do not open a new label scope;
different terms of a sum, different statements and explicit
tensorscalararguments do.Violations throw an
ArgumentErrorat macro expansion time. The pass runs as a preprocessorright after
normalizeindices(so primed labels compare equal) and replaces the partial check incontractiontrees.jl; it also covers labels appearing more than twice in a single tensor andterms of a sum with differing open indices, which previously surfaced as confusing runtime
IndexErrors.Note this rejects code that used to run: three
@test_throws IndexErrorcases intest/tensor.jl/test/cutensor.jlnow fail at expansion instead and were updated. Scanningthe
@tensorcalls in TensorKit, MPSKit, MPSKitModels, PEPSKit and TensorKitManifolds turned upno violations on current
main. It does flag PEPSKit v0.3.0'sright_projector, where a tracelabel was reused across factors -- a bug that had to be found and fixed by hand in
QuantumKitHub/PEPSKit.jl@9b43dad, and which this check would have caught at macro expansion.
🤖 Generated with Claude Code