[WIP] Named-operator constructors, projection verbs, and balanced gram_eigh_full#229
Draft
mtfishman wants to merge 15 commits into
Draft
[WIP] Named-operator constructors, projection verbs, and balanced gram_eigh_full#229mtfishman wants to merge 15 commits into
mtfishman wants to merge 15 commits into
Conversation
Match TensorAlgebra's switch to A ≈ X * X' for gram_eigh_full: relabel X with the domain dimension names (rank trailing) so it mirrors V_R from eigh. The companion left inverse Y from gram_eigh_full_with_pinv likewise carries the domain names with the rank axis leading. Pin the matching TensorAlgebra branch via [sources] until that patch is registered.
- `Base.one(::AbstractNamedDimsArray, codomain, domain)` and `Base.one(::AbstractNamedDimsOperator)` build identity-shaped named arrays/operators by dispatching to `TensorAlgebra.one` on the underlying raw storage. The result sits in canonical (codomain, domain) name order. - `similar_operator(prototype, [T,] axes, [codomain_names,] domain_names)` allocates an operator with the user-supplied side as the domain and the codomain derived by `conj`-ing the domain axes; codomain names default to fresh `randname` outputs. Exported. - `Random.randn!` / `Random.rand!` peel down to the concrete storage, working around the ITensor `eltype(::Type) === Any` issue.
Previously `domain(::Bijection)` returned `values(::OrderedDict)`, a `Base.ValueIterator` that compares by object identity. Switching it to `keys` of the reverse dict gives a `Base.KeySet` whose `==` matches elementwise, so `domainnames(op1) == domainnames(op2)` does what one would expect. The two dicts are constructed from the same pairs in the same order, so `codomain(b)[i]` and `domain(b)[i]` remain in lock-step positional order. Also reworks the `Base.one` docstring examples to use `apply` (which renames the codomain back to the domain), replacing the matricize + identity-matrix check that's better suited to tests than docs.
1 task
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #229 +/- ##
==========================================
- Coverage 71.77% 68.56% -3.22%
==========================================
Files 20 19 -1
Lines 992 1021 +29
==========================================
- Hits 712 700 -12
- Misses 280 321 +41
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Centralizes the linear-map allocation primitive in TensorAlgebra so backends only need to overload `similar_map`, while `similar_operator` keeps the named-axis wrapping. Also drops the redundant `[sources.TensorAlgebra]` pin from `docs/`, `examples/`, and `test/Project.toml`; the workspace root `Project.toml` already pins the upstream feature branch for all subprojects.
`TensorAlgebra.similar_map` now flips the domain direction itself when laying out storage (ITensor/TensorAlgebra.jl#177), so the wrapper passes both sides in the same direction and lets the primitive handle the bra/ket flip.
Without this, `Base.conj(::AbstractArray{<:Real})` short-circuits on a `NamedUnitRange` (element type `NamedInteger{Int, Name}` is a `Real` subtype) and returns the wrapper unchanged — never touching the inner range. That's a no-op for plain ranges but wrong for graded axes, where the inner conj needs to flip sector arrows.
1 task
`Base.conj`, `Base.:*(::, ::Number)`, `Base.:/(::, ::Number)`, and `LinearAlgebra.normalize` are overloaded for `AbstractNamedDimsArray` to delegate to the underlying array rather than the default broadcast path. Broadcast on a `NamedDimsArrayStyle` reaches into the storage via scalar `getindex`, which is unsupported on block-structured arrays such as `AbelianGradedArray` (and would round-trip through scalar slots even when the storage could handle the op block-wise). `denamed(a, inds)` also short-circuits when the requested order already matches `dimnames(a)`, returning `denamed(a)` directly. The previous path always wrapped in a `PermutedDimsArray` for the identity permutation, which hid the storage type from downstream dispatch (`LinearAlgebra.dot` is the visible case). Co-authored-by: Claude <noreply@anthropic.com>
`TensorAlgebra.projectto!(::AbstractNamedDimsArray, ::AbstractArray)` delegates to the underlying-storage `projectto!`, so a named operator allocated via `similar_map(prototype, T, named_codomain, named_domain)` can be filled from raw dense data uniformly across backends. The named layer doesn't need to know which storage the named array wraps. Co-authored-by: Claude <noreply@anthropic.com>
Mirrors the existing `projectto!` forwarder, so backend specializations (e.g. `AbelianGradedArray`'s materialize-and-compare check) are reachable through the named layer with their tolerance defaults intact. Co-authored-by: Claude <noreply@anthropic.com>
Avoids the broadcast-scalar-indexing path on `fill!`, matching the same pattern used for the other top-level array ops on `AbstractNamedDimsArray`. Co-authored-by: Claude <noreply@anthropic.com>
Match the project convention of `using Module: Module [as Alias]` over `import Module [as Alias]`. No behavior change. Co-authored-by: Claude <noreply@anthropic.com>
Compare via `Tuple` in the `denamed(a::AbstractNamedDimsArray, inds)` fast path so a permutation falls through to `aligneddims`. The old check `name.(inds) == dimnames(a)` compared two `LittleSet`s with set-equality, which let the shortcut return the un-permuted inner array even when `inds` and `dimnames(a)` differed only in order, silently breaking permuted broadcast. With that fix the linear broadcast path routes correctly through `bipermutedimsopadd!`, so the `Base.conj`, `Base.:*(::, ::Number)` (both directions), and `Base.:/(::, ::Number)` workaround forwarders are no longer needed and are removed. Co-authored-by: Claude <noreply@anthropic.com>
Remove `TensorAlgebra.projectto!(::AbstractNamedDimsArray, ::AbstractArray)` and the matching `checked_projectto!`. Projecting an unnamed array into a named one is unsafe by design: the mapping from raw positions to named dims is implicit, so the dim ordering of the destination leaks into the result. Callers that need named-projection semantics should strip names from the destination first, or define a separate all-named-inputs API. Promote `Random.randn!` / `Random.rand!` from `AbstractNamedDimsOperator` to the generic `AbstractNamedDimsArray`. The operator-specific methods now forward to the state, which dispatches into the generic one. Co-authored-by: Claude <noreply@anthropic.com>
Forwards conjugation to the underlying so that graded axes flip their sector arrows, matching the existing `Base.conj(::AbstractNamedUnitRange)` behavior. The default `AbstractArray` fallback for `conj` broadcasts element-wise without touching the named axes' duality flags. For tensors with U(1)-graded axes that produces a contraction-orientation mismatch in bra-ket reductions: the bra-side links keep their original `isdual` flag, so the bra-internal contraction runs in the opposite duality orientation from the ket and the graded contraction code silently applies a `(-1)` per bond. The bug accumulated to `⟨ψ_AKLT|ψ_AKLT⟩ = -9/16` on a 3-bond path graph and was masked by even bond counts on closed cycles.
The generic fallback iterates elements, which can be expensive or unsupported on block-structured backends. Named-array wrappers do not change the leaf eltype, so the underlying storage's leaf eltype is the answer. Co-Authored-By: Claude Opus 4.7 <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.
Summary
AbstractNamedDimsArray/AbstractNamedDimsOperator:Base.one(operator)andBase.one(array, codomain, domain),similar_operator(prototype, [T,] axes, [codomain_names,] domain_names)(routed throughTensorAlgebra.similar_map), genericRandom.randn!andRandom.rand!.Base.conjonAbstractNamedUnitRangeandAbstractNamedDimsArray, forwarded through the underlying so that graded axes flip their sector arrows. Without the array-level method,conjfalls through to an element-wise broadcast that leaves the named axes' duality flags untouched, silently producing a(-1)per dual bond in graded bra-ket contractions.LinearAlgebra.normalizeandBase.fill!onAbstractNamedDimsArrayrouted throughdenamedto avoid scalar-indexing block-structured storage.denamed(a::AbstractNamedDimsArray, inds)now compares dim-name order viaTupleinstead ofLittleSet. The old check usedLittleSet ==, which is set-equality, so a permutation looked identical to the identity order and the lazy-permute step got skipped, silently breaking permuted broadcast.gram_eigh_fullaligned with the balanced convention from Balanced gram_eigh_full convention, add one and operator-construction primitives TensorAlgebra.jl#177.TODO
[sources]pin onTensorAlgebraonce the matching version (0.9.5) registers.