From 144a3970f0fcf733cba9c43b1fbb4f3d89b9d69c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 26 Sep 2025 19:27:15 +0200 Subject: [PATCH 1/3] Add missing includes --- SeQuant/core/options.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SeQuant/core/options.hpp b/SeQuant/core/options.hpp index 11b4f0f1b7..b72ecf71eb 100644 --- a/SeQuant/core/options.hpp +++ b/SeQuant/core/options.hpp @@ -5,6 +5,8 @@ #ifndef SEQUANT_CORE_OPTIONS_HPP #define SEQUANT_CORE_OPTIONS_HPP +#include +#include #include namespace sequant { From 2f798e838920e1323007d2374306dddd363e07b5 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 26 Sep 2025 20:29:07 +0200 Subject: [PATCH 2/3] Use vector for named indices In order for this to be possible, Index needed to be decoupled from the context header in order to avoid circular includes when including the index header in the options header. --- SeQuant/core/index.cpp | 5 +++++ SeQuant/core/index.hpp | 18 ++++++++++-------- SeQuant/core/options.cpp | 4 ++-- SeQuant/core/options.hpp | 11 +++++------ 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/SeQuant/core/index.cpp b/SeQuant/core/index.cpp index 0c4da854b5..24efc88612 100644 --- a/SeQuant/core/index.cpp +++ b/SeQuant/core/index.cpp @@ -71,4 +71,9 @@ std::string Index::to_string() const { return sequant::to_string(this->label()); } +std::shared_ptr +Index::obtain_default_index_registry() { + return get_default_context().index_space_registry(); +} + } // namespace sequant diff --git a/SeQuant/core/index.hpp b/SeQuant/core/index.hpp index fed1ffaf20..92b9869e59 100644 --- a/SeQuant/core/index.hpp +++ b/SeQuant/core/index.hpp @@ -6,8 +6,9 @@ #define SEQUANT_INDEX_H #include -#include #include +#include +#include #include #include #include @@ -15,7 +16,6 @@ #include #include -#include #include #include #include @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -328,12 +329,11 @@ class Index : public Taggable { /// the cost of slightly increased danger template Index(String &&label) - : Index( - get_default_context().index_space_registry() - ? get_default_context().index_space_registry()->retrieve(label) - : IndexSpace{base_label(label), IndexSpace::Type::reserved, - IndexSpace::QuantumNumbers::reserved}, - to_ordinal(label), {}) { + : Index(obtain_default_index_registry() + ? obtain_default_index_registry()->retrieve(label) + : IndexSpace{base_label(label), IndexSpace::Type::reserved, + IndexSpace::QuantumNumbers::reserved}, + to_ordinal(label), {}) { check_nonreserved(); if constexpr (std::is_same_v) { label_ = std::move(label); @@ -1043,6 +1043,8 @@ class Index : public Taggable { return i1_Q < i2_Q ? SO::less : SO::greater; } + static std::shared_ptr obtain_default_index_registry(); + }; // class Index inline const IndexSpace::Attr Index::default_space_attr{ diff --git a/SeQuant/core/options.cpp b/SeQuant/core/options.cpp index acd7fbb2fd..597ec35a9c 100644 --- a/SeQuant/core/options.cpp +++ b/SeQuant/core/options.cpp @@ -45,9 +45,9 @@ CanonicalizeOptions CanonicalizeOptions::copy_and_set( } CanonicalizeOptions CanonicalizeOptions::copy_and_set( - std::optional> arg) const { + std::optional> arg) const { auto result = *this; - result.named_indices = arg; + result.named_indices = std::move(arg); return result; } diff --git a/SeQuant/core/options.hpp b/SeQuant/core/options.hpp index b72ecf71eb..435394dd80 100644 --- a/SeQuant/core/options.hpp +++ b/SeQuant/core/options.hpp @@ -5,14 +5,14 @@ #ifndef SEQUANT_CORE_OPTIONS_HPP #define SEQUANT_CORE_OPTIONS_HPP -#include +#include + #include #include +#include namespace sequant { -class Index; - /// canonicalization methods enum class CanonicalizationMethod { /// Enables use of expression topology in the canonicalization, may be @@ -49,7 +49,7 @@ struct CanonicalizeOptions { /// specifies named indices; by default all indices that appear only once are /// deduced to be named, but this may be misleading if e.g. single /// summed-over dummy index appears in an expression - std::optional> named_indices = std::nullopt; + std::optional> named_indices = std::nullopt; /// whether to ignore the labels of named indices. Setting /// to false will cause named indices to be treated as equivalent slots, which /// the result to be independent of their labels. This does not make sense in @@ -59,8 +59,7 @@ struct CanonicalizeOptions { static CanonicalizeOptions default_options(); CanonicalizeOptions copy_and_set(CanonicalizationMethod) const; - CanonicalizeOptions copy_and_set( - std::optional>) const; + CanonicalizeOptions copy_and_set(std::optional>) const; CanonicalizeOptions copy_and_set(IgnoreNamedIndexLabel) const; friend constexpr bool operator==(const CanonicalizeOptions& a, From 5f97003ea4166577eb30ea6ba48ea7d0ff743acb Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 26 Sep 2025 20:36:10 +0200 Subject: [PATCH 3/3] Add test case demonstrating failure --- tests/unit/test_spin.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index b3d1cb9983..4f720c78f6 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -632,6 +632,20 @@ SECTION("partial spintracing + S_maps = full spintracing") { //(canonicalized: -2 t{a_1,a_2;i_2,i_1}:N-C-S + 4 t{a_1,a_2;i_1,i_2}:N-C-S) } +SECTION("external indices have to remain consistent") { + // Internally, closed_shell_spintrace performs canonicalization which can end + // up renaming external indices (due to the presence of the symmetrizer). This + // will cause an inconsistency with the explicitly provided external index + // names. + ExprPtr input = parse_expr( + L"A{p_8,p_9;a_8,a_9}:A-C-S * y{p_3,p_4;p_8,p_9}:A-C-S * " + L"g{i_3,a_8;p_3,p_4}:A-C-S * t{a_9;i_3}:A-C-S"); + ExprPtr result = + closed_shell_spintrace(input, {{L"a_8", L"p_8"}, {L"a_9", L"p_9"}}); + + // REQUIRE_THAT(result, EquivalentTo(L"TODO")); +} + SECTION("Symmetrize expression") { { // g * t1 + g * t1