Skip to content

Add unproject to invert project given the codomain/domain split#211

Merged
mtfishman merged 2 commits into
mainfrom
mf/unproject
Jul 21, 2026
Merged

Add unproject to invert project given the codomain/domain split#211
mtfishman merged 2 commits into
mainfrom
mf/unproject

Conversation

@mtfishman

@mtfishman mtfishman commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Adds unproject(a, ndims_codomain::Val), the inverse of project. It recovers the dense array that project maps a from, given the codomain/domain split as a Val. The default is convert(Array, a). A backend that changes basis during project overloads unproject to undo that change, so that unproject(project(raw, codomain_axes, domain_axes), Val(length(codomain_axes))) recovers raw.

project and tryproject now run their is_projected self-check through unproject with the split they were handed, instead of comparing against convert(Array, dest). A backend that stores the projection in the plain dense layout is unaffected. A backend that changes basis in project is now checked in the frame the input was given in, so its self-check passes. The two-argument is_projected forwards to this split-aware form using the destination's own codomain rank, a new ndims_codomain accessor that a TensorMap overloads with numout.

The TensorKit extension defines unproject for AbstractTensorMap as convert(Array, ·), which already undoes the domain braiding, guarded by a check that the passed split matches the map's codomain rank.

Adds `unproject(a, ndims_codomain::Val)`, the inverse of `project`: it recovers the dense array `project` maps to `a`, taking the codomain/domain split as a `Val`. The default is `convert(Array, a)`. A backend that changes basis during `project` (for example a graded array that bends its domain legs into the codomain to carry a fermion sign) overloads it to undo that change.

`project` and `tryproject` now verify their result through a split-aware `is_projected(dest, src, ndims_codomain::Val)` that compares against `unproject` rather than `convert(Array, dest)`, so such a backend passes its own round-trip check. Behavior is unchanged for backends that store the projection in the plain dense layout such as a dense array or a `TensorMap`, for which `unproject` is `convert(Array, ·)`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 81.48%. Comparing base (2fe9d5c) to head (4405997).

Files with missing lines Patch % Lines
ext/TensorAlgebraTensorKitExt.jl 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #211      +/-   ##
==========================================
- Coverage   81.53%   81.48%   -0.06%     
==========================================
  Files          26       26              
  Lines         975      983       +8     
==========================================
+ Hits          795      801       +6     
- Misses        180      182       +2     
Flag Coverage Δ
docs 22.31% <0.00%> (-0.19%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The two-argument `is_projected` now forwards to the split-aware form using the
destination's own codomain rank, given by a new `ndims_codomain` accessor
(`ndims` by default, `numout` for a `TensorMap`). This removes the duplicated
method body and makes the two-argument form the split-aware check at the
destination's own split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mtfishman
mtfishman merged commit 508e24e into main Jul 21, 2026
21 checks passed
@mtfishman
mtfishman deleted the mf/unproject branch July 21, 2026 18:09
mtfishman added a commit to ITensor/GradedArrays.jl that referenced this pull request Jul 21, 2026
## Summary

`project(A, codomain, domain)` for a fermionic operator now bends its
domain (bra) legs, multiplying each stored block by the `±1` fermion
sign of moving them past the codomain, so the operator contracts like
the corresponding `TensorMap`. Previously the three-argument operator
projection collapsed to the naive dual form and dropped that sign, so an
operator with a both-odd domain block (for example a hopping gate's
doubly-occupied diagonal) contracted with the wrong sign. `unproject(a,
Val(K))` inverts the bend given the codomain/domain split, and `project`
runs its self-check through it so the round trip holds. This builds on
the `unproject` verb from
ITensor/TensorAlgebra.jl#211.

This also fixes the fermion permutation and bend signs in
`matricizeopperm`, `unmatricizeperm!`, and `bipermutedimsopadd!`, so
fermionic contraction no longer depends on contraction order.

Breaking (`0.14.0`) because it changes the results of fermionic operator
contraction.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant