Fix mixed real/complex BlockUniTensor contract order dependency (#758)#760
Fix mixed real/complex BlockUniTensor contract order dependency (#758)#760yingjerkao wants to merge 2 commits intomasterfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #760 +/- ##
==========================================
+ Coverage 35.44% 35.47% +0.02%
==========================================
Files 215 214 -1
Lines 33071 33063 -8
Branches 13170 13170
==========================================
+ Hits 11723 11730 +7
+ Misses 19424 19406 -18
- Partials 1924 1927 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| // #ifdef UNI_MKL | ||
| // // Initialize!! | ||
| // if (true or | ||
| // (this->dtype() != Type.Double and this->dtype() != Type.ComplexDouble) and | ||
| // (this->dtype() != Type.Float and this->dtype() != Type.ComplexFloat) or | ||
| // this->is_diag() or Rtn->is_diag()) { | ||
| // tmp->Init(out_bonds, out_labels, out_rowrank, common_dtype, this->device(), | ||
| // false, false); | ||
| // } else { | ||
| // tmp->Init(out_bonds, out_labels, out_rowrank, common_dtype, this->device(), | ||
| // false, true); | ||
| // } | ||
| // #else |
There was a problem hiding this comment.
Is there any reason to leave the commented code? We can revert it from git history if we need it again in the future. If there is a reason to do this, I suggest leave a comment for the reason.
There was a problem hiding this comment.
I did not delete these parts because I did not write them, and I am not sure what is happening here. If no one needs this currently, we can certainly delete it.
pcchen
left a comment
There was a problem hiding this comment.
PR #760 Review — Fix mixed real/complex BlockUniTensor contract order dependency
Posted by Claude Code on behalf of @pcchen
Critical Issues (2)
1. Missing LHS cast in MKL path — fragile correctness
src/BlockUniTensor.cpp:1044, src/BlockFermionicUniTensor.cpp:1710
The cast guard only casts RHS when Rtn->dtype() != common_dtype. It never casts this->_blocks when this->dtype() != common_dtype. For the case real.contract(complex): RHS is already ComplexDouble so no cast happens, and linalg::Matmul(Double_blocks, ComplexDouble_blocks) is called with mismatched types. This currently produces correct results by accident — Matmul has internal type promotion — but this is an undocumented side-effect. If Matmul's promotion behavior changes, or if the gemm_batch path is re-enabled, this will silently break. The fix should symmetrically cast LHS when this->dtype() != common_dtype.
2. Scalar (all-legs) contraction path ignores common_dtype
src/BlockUniTensor.cpp:~854-859, src/BlockFermionicUniTensor.cpp:~1499-1509
The scalar branch sets tmp->_block directly from linalg::Vectordot(...) without initializing with common_dtype. The output block dtype is entirely determined by Vectordot's return type — the fix has no effect on this path.
Important Issues (4)
3. Dead variables allocated on every contraction call
src/BlockUniTensor.cpp:~1000-1007, src/BlockFermionicUniTensor.cpp:~1663-1670
transs, ms, ns, ks, LMems, RMems, CMems, group_size, and alphas are allocated proportional to Rtn->_blocks.size() on every call but are entirely unused because the gemm_batch code is now commented out. These should be deleted along with the dead commented-out code, not left as silent memory waste.
4. Large blocks of commented-out code should be deleted
src/BlockUniTensor.cpp:~919-939, ~1094-1132, src/BlockFermionicUniTensor.cpp:~1563-1583, ~1756-1801
The #ifdef UNI_MKL initialization branch and the gemm_batch loop are preserved as comments. If this code is abandoned, delete it. If it's deferred work, replace with a // TODO(#issue): ... note at most.
5. BlockFermionicUniTensor mixed-dtype contract: zero test coverage
tests/BlockFermionicUniTensor_test.cpp
The identical four-line fix was applied to both files but only BlockUniTensor has a regression test. A mirrored test in BlockFermionicUniTensor_test.cpp is needed.
6. Exception safety: tmpp leaks if astype throws
src/BlockUniTensor.cpp:~1046-1052, src/BlockFermionicUniTensor.cpp:~1709-1715
BlockUniTensor *tmpp = Rtn->clone_meta(true, true); // raw owning ptr
for (...) tmpp->_blocks[blk] = Rtn->_blocks[blk].astype(common_dtype); // can throw
tmp_Rtn = tmpp;
tmp_rtn_is_casted = true;If astype throws on any iteration, tmpp leaks. Pre-existing issue but worsened by this PR adding a new throw-capable site.
Suggestions (3)
7. Float/ComplexFloat not tested — type_promote(Float, ComplexFloat) is a distinct code path; add a second parameterized case.
8. Outer product (no shared index) path not tested — both branches of the contraction received the common_dtype fix but the test only exercises the matrix-contraction branch.
9. Add explicit dtype() assertion on same-type contract in contract2 to make the same-dtype invariant explicit.
Strengths
- The core fix (
type_promotefor output init) is correct and clearly targets the root cause. tmp_rtn_is_castedflag is a clean fix to the pre-existing buggy delete condition.- The
Initcode deduplication (movingsize[i]assignment outside theif/else) is a good cleanup. - The new test correctly checks both orderings for dtype and value, and
EXPECT_NO_THROWis good practice.
Recommended Action Before Merge
- Add symmetric LHS cast in the MKL path (or document the
Matmulpromotion dependency explicitly) - Apply
common_dtypeto the scalarVectordotbranch - Delete dead variables and commented-out gemm_batch code (or track with a TODO issue)
- Add a
BlockFermionicUniTensormixed-dtype contract test
Summary
Testing
Fixes #758