From 0a237deb29037f671370f632c63ed889a7dff7c3 Mon Sep 17 00:00:00 2001 From: Ajay Date: Sun, 1 Feb 2026 23:45:12 -0500 Subject: [PATCH 1/3] =?UTF-8?q?fix(eval):=20handle=20full=20contraction=20?= =?UTF-8?q?(tensor=20=C3=97=20tensor=20=E2=86=92=20scalar)=20in=20Flops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously Flops::operator() assumed tensor products produced a tensor as a result. This throws an error from ContractedIndexCount when all indices are contracted. Full contraction is now handled correctly and cost is computed as 2 * O^occ * V^virt. Note that we only use the info from left, because it is a full contraction it will be the same as right. --- SeQuant/core/eval/eval_node.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/SeQuant/core/eval/eval_node.hpp b/SeQuant/core/eval/eval_node.hpp index 2be333062a..4d94e3de3d 100644 --- a/SeQuant/core/eval/eval_node.hpp +++ b/SeQuant/core/eval/eval_node.hpp @@ -114,9 +114,14 @@ struct Flops { if (n->op_type() == EvalOp::Product // && n.left()->is_tensor() // && n.right()->is_tensor()) { - auto const idx_count = ContractedIndexCount{n}; - auto c = AsyCost{idx_count.unique_occs(), idx_count.unique_virts()}; - return idx_count.is_outerpod() ? c : 2 * c; + if (n->is_tensor()) { + auto const idx_count = ContractedIndexCount{n}; + auto c = AsyCost{idx_count.unique_occs(), idx_count.unique_virts()}; + return idx_count.is_outerpod() ? c : 2 * c; + } else { // full contraction to scalar + SEQUANT_ASSERT(n->is_scalar()); + return 2 * AsyCost{occ_virt(n.left()->as_tensor())}; + } } else if (n->is_tensor()) { // scalar times a tensor // or a tensor plus a tensor From 132b2853f517746469bdcdf24d71a6d6db099e83 Mon Sep 17 00:00:00 2001 From: Ajay Date: Sun, 1 Feb 2026 23:47:04 -0500 Subject: [PATCH 2/3] test(eval): add test for full contraction in asy_cost See 0a237deb. A new test case added which mimics CC energy expression. --- tests/unit/test_eval_node.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/test_eval_node.cpp b/tests/unit/test_eval_node.cpp index 84b8290a93..4b742d36a8 100644 --- a/tests/unit/test_eval_node.cpp +++ b/tests/unit/test_eval_node.cpp @@ -276,6 +276,13 @@ TEST_CASE("eval_node", "[EvalNode]") { auto const np3 = eval_node(p3); REQUIRE(asy_cost(np3) == AsyCost{2, 2, 1} + AsyCost{2, 3, 1}); + // full contraction to scalar: cc energy-like expression + auto const p_e = parse_expr_antisymm( + L"f_{i1}^{a1} * t_{a1}^{i1} + g_{i1,i2}^{a1,a2} * t_{a1,a2}^{i1,i2}"); + auto const n_e = eval_node(p_e); + REQUIRE(n_e->is_scalar()); + REQUIRE(asy_cost(n_e) == 2 * AsyCost{2, 2} + 2 * AsyCost{1, 1}); + #if 0 auto const s1 = parse_expr(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Symm); From 19e1c054db81bd5691c6f91f7c10ae4c892541b7 Mon Sep 17 00:00:00 2001 From: Ajay Date: Mon, 2 Feb 2026 01:23:28 -0500 Subject: [PATCH 3/3] fix(eval): add an assertion for equal dims for full contractions In case this is not satisfied, we should catch it before, but just to be safe. --- SeQuant/core/eval/eval_node.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SeQuant/core/eval/eval_node.hpp b/SeQuant/core/eval/eval_node.hpp index 4d94e3de3d..2e645820c1 100644 --- a/SeQuant/core/eval/eval_node.hpp +++ b/SeQuant/core/eval/eval_node.hpp @@ -120,6 +120,8 @@ struct Flops { return idx_count.is_outerpod() ? c : 2 * c; } else { // full contraction to scalar SEQUANT_ASSERT(n->is_scalar()); + SEQUANT_ASSERT(occ_virt(n.left()->as_tensor()) == + occ_virt(n.right()->as_tensor())); return 2 * AsyCost{occ_virt(n.left()->as_tensor())}; } } else if (n->is_tensor()) {