diff --git a/INSTALL.md b/INSTALL.md index 842a7d2c6..ec1c5e26a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -5,7 +5,7 @@ prerequisites: * mandatory: * CMake 3.15 or later * a C++17 compiler - * [Boost](https://www.boost.org/), version 1.67 or higher (N.B. critical bugs make the following versions unusable: 1.70, 1.77, 1.78); the following non-header-only Boost libraries are required: + * [Boost](https://www.boost.org/), version 1.81 or higher (N.B. older compilers _may_ work with older Boost releases). *SeQuant can download and build Boost if configured with `Boost_FETCH_IF_MISSING=ON`, but this is not recommended.* The following non-header-only Boost libraries are required, hence Boost must be configured/built: - [Boost.Regex](https://www.boost.org/doc/libs/master/libs/regex/doc/html/index.html) - [Boost.Locale](https://www.boost.org/doc/libs/master/libs/locale/doc/html/index.html) * [Range-V3](https://github.com/ericniebler/range-v3.git), tag 0.12.0, *if not found, SeQuant will download and build Range-V3* @@ -20,8 +20,9 @@ for the impatient (from the top of the SeQuant source directory): * `cmake --build build --target check-sequant` useful CMake variables: - * `BUILD_TESTING` --- enables unit tests targets, e.g. `check-sequant` [default=ON] - * `CMAKE_CXX_COMPILER` --- specifies the C++ compiler to use - * `CMAKE_PREFIX_PATH` --- this semicolon-separated list specifies search paths for dependencies (Boost, Range-V3, etc.) + * [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/module/CTest.html) --- enables unit tests targets, e.g. `check-sequant` [default=ON] + * [`CMAKE_CXX_COMPILER`](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html#variable:CMAKE_%3CLANG%3E_COMPILER) --- specifies the C++ compiler to use + * [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) --- this semicolon-separated list specifies search paths for dependencies (Boost, Range-V3, etc.) * `SEQUANT_MIMALLOC` --- use [mimalloc](https://github.com/microsoft/mimalloc) for fast memory allocation * `SEQUANT_EVAL_TRACE` --- enables tracing of expression interpretation; especially useful in combination with TiledArray's memory tracing mechanism (configure TiledArray with `TA_TENSOR_MEM_PROFILE=ON` to enable that) + * `Boost_FETCH_IF_MISSING` --- if set to `ON`, SeQuant will download and build Boost if it is not found by `find_package(Boost ...)`; this is not recommended. [default=OFF] diff --git a/SeQuant/core/hash.hpp b/SeQuant/core/hash.hpp index f71cea284..b7ef212fb 100644 --- a/SeQuant/core/hash.hpp +++ b/SeQuant/core/hash.hpp @@ -14,6 +14,10 @@ namespace sequant_boost = boost; #include #endif +#if SEQUANT_BOOST_VERSION < 108100 +#error "SeQuant requires Boost 1.81 or later for hashing" +#endif + #include "meta.hpp" namespace sequant { @@ -27,17 +31,7 @@ enum class Impl { BoostPre181 = 1, Boost181OrLater = 2 }; /// @return the version of hashing used by SeQuant, depends on the version of /// Boost -constexpr hash::Impl hash_version() { -#ifdef SEQUANT_USE_SYSTEM_BOOST_HASH -#if SEQUANT_BOOST_VERSION < 108100 - return hash::Impl::BoostPre181; -#else - return hash::Impl::Boost181OrLater; -#endif -#else - return hash::Impl::Boost181OrLater; -#endif -} +constexpr hash::Impl hash_version() { return hash::Impl::Boost181OrLater; } namespace detail { template @@ -116,45 +110,7 @@ template inline void combine(std::size_t& seed, T const& v) { _ hasher; -#ifdef SEQUANT_USE_SYSTEM_BOOST_HASH -#if SEQUANT_BOOST_VERSION >= 108100 - boost::hash_combine(seed, hasher(v)); -#else // older boost workarounds - // in boost 1.78 hash_combine_impl implementation changed - // https://github.com/boostorg/container_hash/commit/21f2b5e1db1a118c83a3690055c110d0f5637da3 - // probably no longer need these acrobatics - if constexpr (sizeof(std::size_t) == sizeof(boost::uint32_t) && - sizeof(decltype(hasher(v))) == sizeof(boost::uint32_t)) { - const boost::uint32_t value = hasher(v); -#if SEQUANT_BOOST_VERSION >= 107800 - seed = boost::hash_detail::hash_combine_impl<32>::fn( - static_cast(seed), value); -#else - // N.B. seed passed by reference - boost::hash_detail::hash_combine_impl( - reinterpret_cast(seed), value); -#endif - return; - } else if constexpr (sizeof(std::size_t) == sizeof(boost::uint64_t) && - sizeof(decltype(hasher(v))) == sizeof(boost::uint64_t)) { - const boost::uint64_t value = hasher(v); - -#if SEQUANT_BOOST_VERSION >= 107800 - seed = boost::hash_detail::hash_combine_impl<64>::fn( - static_cast(seed), value); -#else - // N.B. seed passed by reference - boost::hash_detail::hash_combine_impl( - reinterpret_cast(seed), value); -#endif - return; - } else { - seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } -#endif // older boost workarounds -#else // !defined(SEQUANT_USE_SYSTEM_BOOST_HASH) sequant_boost::hash_combine(seed, hasher(v)); -#endif // assert(seed == seed_ref); } diff --git a/SeQuant/domain/eval/eval.hpp b/SeQuant/domain/eval/eval.hpp index 008dbf520..9e5358b86 100644 --- a/SeQuant/domain/eval/eval.hpp +++ b/SeQuant/domain/eval/eval.hpp @@ -32,7 +32,7 @@ void log_eval(Args const&... args) noexcept { #endif } -void log_cache_access(size_t key, CacheManager const& cm) { +[[maybe_unused]] void log_cache_access(size_t key, CacheManager const& cm) { #ifdef SEQUANT_EVAL_TRACE auto& l = Logger::get_instance(); if (l.log_level_eval > 0) { @@ -50,7 +50,7 @@ void log_cache_access(size_t key, CacheManager const& cm) { #endif } -void log_cache_store(size_t key, CacheManager const& cm) { +[[maybe_unused]] void log_cache_store(size_t key, CacheManager const& cm) { #ifdef SEQUANT_EVAL_TRACE auto& l = Logger::get_instance(); if (l.log_level_eval > 0) { diff --git a/SeQuant/domain/eval/eval_result.hpp b/SeQuant/domain/eval/eval_result.hpp index a59afac06..368a1d9a4 100644 --- a/SeQuant/domain/eval/eval_result.hpp +++ b/SeQuant/domain/eval/eval_result.hpp @@ -487,7 +487,7 @@ inline void log_constant(Args const&... args) noexcept { void log_ta_tensor_host_memory_use(madness::World& world, std::string_view label = ""); -struct EvalResult; +class EvalResult; using ERPtr = std::shared_ptr; diff --git a/tests/unit/test_canonicalize.cpp b/tests/unit/test_canonicalize.cpp index 3dffb91f4..974bc6a6b 100644 --- a/tests/unit/test_canonicalize.cpp +++ b/tests/unit/test_canonicalize.cpp @@ -134,17 +134,10 @@ TEST_CASE("Canonicalizer", "[algorithms]") { ex(L"t", IndexList{{L"p_4"}}, IndexList{{L"p_2"}}, Symmetry::nonsymm); canonicalize(input); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(input) == - L"{ " - L"\\bigl({{g^{{p_2}{p_3}}_{{p_1}{p_4}}}{t^{{p_1}}_{{p_2}}}{t^{{p_" - L"4}}_{{p_3}}}}\\bigr) }"); - else - REQUIRE(to_latex(input) == - L"{ " - L"\\bigl({{g^{{p_1}{p_4}}_{{p_2}{p_3}}}{t^{{p_2}}_{{p_1}}}{t^{{" - L"p_3}}_{{p_4}}}}\\bigr) }"); + REQUIRE(to_latex(input) == + L"{ " + L"\\bigl({{g^{{p_1}{p_4}}_{{p_2}{p_3}}}{t^{{p_2}}_{{p_1}}}{t^{{" + L"p_3}}_{{p_4}}}}\\bigr) }"); } // CASE 2: Symmetric tensors diff --git a/tests/unit/test_mbpt.cpp b/tests/unit/test_mbpt.cpp index 3d1954fff..f14a26b66 100644 --- a/tests/unit/test_mbpt.cpp +++ b/tests/unit/test_mbpt.cpp @@ -276,30 +276,18 @@ TEST_CASE("NBodyOp", "[mbpt]") { auto t = t1 + t2; - if constexpr (hash_version() == hash::Impl::BoostPre181) { - REQUIRE( - to_latex(simplify(f * t * t)) == - to_latex(f * t1 * t1 + f * t2 * t2 + ex(2) * f * t1 * t2)); - } else { - // std::wcout << "to_latex(simplify(f * t * t)): " - // << to_latex(simplify(f * t * t)) << std::endl; - REQUIRE( - to_latex(simplify(f * t * t)) == - to_latex(ex(2) * f * t1 * t2 + f * t2 * t2 + f * t1 * t1)); - } - - if constexpr (hash_version() == hash::Impl::BoostPre181) { - REQUIRE(to_latex(simplify(f * t * t * t)) == - to_latex(ex(3) * f * t1 * t2 * t2 + f * t2 * t2 * t2 + - ex(3) * f * t1 * t1 * t2 + f * t1 * t1 * t1)); - } else { - // std::wcout << "to_latex(simplify(f * t * t * t): " - // << to_latex(simplify(f * t * t * t)) << std::endl; - REQUIRE(to_latex(simplify(f * t * t * t)) == - to_latex(f * t2 * t2 * t2 + f * t1 * t1 * t1 + - ex(3) * f * t1 * t1 * t2 + - ex(3) * f * t1 * t2 * t2)); - } + // std::wcout << "to_latex(simplify(f * t * t)): " + // << to_latex(simplify(f * t * t)) << std::endl; + REQUIRE( + to_latex(simplify(f * t * t)) == + to_latex(ex(2) * f * t1 * t2 + f * t2 * t2 + f * t1 * t1)); + + // std::wcout << "to_latex(simplify(f * t * t * t): " + // << to_latex(simplify(f * t * t * t)) << std::endl; + REQUIRE(to_latex(simplify(f * t * t * t)) == + to_latex(f * t2 * t2 * t2 + f * t1 * t1 * t1 + + ex(3) * f * t1 * t1 * t2 + + ex(3) * f * t1 * t2 * t2)); } // SECTION("canonicalize") diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index 1c516d04f..e08f659b5 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -146,15 +146,9 @@ TEST_CASE("Spin", "[spin]") { REQUIRE(result->is()); canonicalize(result); REQUIRE(result->size() == 2); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ \\bigl({{{2}}{g^{{p_3}{p_4}}_{{p_1}{p_2}}}} - " - L"{{{2}}{g^{{p_4}{p_3}}_{{p_1}" - L"{p_2}}}}\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - {{{2}}{g^{{p_4}{p_3}}_{{p_1}{p_2}}}} + " - L"{{{2}}{g^{{p_3}{p_4}}_{{p_1}{p_2}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - {{{2}}{g^{{p_4}{p_3}}_{{p_1}{p_2}}}} + " + L"{{{2}}{g^{{p_3}{p_4}}_{{p_1}{p_2}}}}\\bigr) }"); } SECTION("Product") { @@ -213,28 +207,15 @@ TEST_CASE("Spin", "[spin]") { canonicalize(result); REQUIRE(result->is()); REQUIRE(result->size() == 5); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{ \\bigl( - " - L"{{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}} + {{" - L"{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}} " - L"+ {{{2}}{f^{" - L"{a_1}}_{{i_1}}}{t^{{i_1}}_{{a_1}}}} - " - L"{{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}" - L"}_{{a_1}}}{t^{{i_1}}_{{a_2}}}} + " - L"{{{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}" - L"}_{{a_1}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(result) == - L"{ \\bigl({{{2}}{f^{{a_1}}_{{i_1}}}{t^{{i_1}}_{{a_1}}}} + " - L"{{{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}} " - L"- {{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}} - " - L"{{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}}_{{a_1}}}{t^{{i_1}}_{{a_2}}" - L"}} + " - L"{{{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}}_{{a_1}}}{t^{{i_2}}_{{" - L"a_2}}}}\\bigr) }"); + REQUIRE( + to_latex(result) == + L"{ \\bigl({{{2}}{f^{{a_1}}_{{i_1}}}{t^{{i_1}}_{{a_1}}}} + " + L"{{{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}} " + L"- {{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}} - " + L"{{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}}_{{a_1}}}{t^{{i_1}}_{{a_2}}" + L"}} + " + L"{{{2}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}}_{{a_1}}}{t^{{i_2}}_{{" + L"a_2}}}}\\bigr) }"); } // Sum SECTION("Expand Antisymmetrizer"){// 0-body @@ -466,38 +447,21 @@ SECTION("Expand Symmetrizer") { REQUIRE(result->size() == 6); result->canonicalize(); rapid_simplify(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{ " - L"\\bigl({{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_3}}}{t^{{" - L"i_2}}_{{a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_3}}_{{a_1}{a_2}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_3}}}{t^{{i_2}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_3}{i_4}}_{{a_1}{a_2}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_2}}_{{" - L"a_4}}}{t^{{i_3}}_{{a_5}}}{t^{{i_1}{i_5}}_{{a_2}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_2}}}{t^{{i_3}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_2}}_{{a_1}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_3}}_{{" - L"a_4}}}{t^{{i_2}}_{{a_5}}}{t^{{i_5}{i_1}}_{{a_2}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_2}}}{t^{{i_3}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_2}{i_4}}_{{a_1}{a_3}}}}\\bigr) }"); - else - REQUIRE( - to_latex(result) == - L"{ " - L"\\bigl({{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_2}}}{t^{{" - L"i_3}}_{{a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_2}}_{{a_1}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_2}}_{{" - L"a_4}}}{t^{{i_3}}_{{a_5}}}{t^{{i_1}{i_5}}_{{a_2}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_3}}_{{" - L"a_4}}}{t^{{i_2}}_{{a_5}}}{t^{{i_5}{i_1}}_{{a_2}{a_3}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_3}}}{t^{{i_2}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_3}{i_4}}_{{a_1}{a_2}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_3}}}{t^{{i_2}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_3}}_{{a_1}{a_2}}}} + " - L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_2}}}{t^{{i_3}}_{{" - L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_2}{i_4}}_{{a_1}{a_3}}}}\\bigr) }"); + REQUIRE( + to_latex(result) == + L"{ " + L"\\bigl({{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_2}}}{t^{{" + L"i_3}}_{{a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_2}}_{{a_1}{a_3}}}} + " + L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_2}}_{{" + L"a_4}}}{t^{{i_3}}_{{a_5}}}{t^{{i_1}{i_5}}_{{a_2}{a_3}}}} + " + L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_1}}}{t^{{i_3}}_{{" + L"a_4}}}{t^{{i_2}}_{{a_5}}}{t^{{i_5}{i_1}}_{{a_2}{a_3}}}} + " + L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_3}}}{t^{{i_2}}_{{" + L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_3}{i_4}}_{{a_1}{a_2}}}} + " + L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_4}}_{{a_3}}}{t^{{i_2}}_{{" + L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_5}{i_3}}_{{a_1}{a_2}}}} + " + L"{{{4}}{g^{{a_4}{a_5}}_{{i_4}{i_5}}}{t^{{i_5}}_{{a_2}}}{t^{{i_3}}_{{" + L"a_4}}}{t^{{i_1}}_{{a_5}}}{t^{{i_2}{i_4}}_{{a_1}{a_3}}}}\\bigr) }"); } } @@ -557,16 +521,10 @@ SECTION("Symmetrize expression") { auto result = factorize_S(input, {{L"i_1", L"a_1"}, {L"i_2", L"a_2"}}, true); REQUIRE(result->is() == false); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{{{2}}{S^{{a_1}{a_2}}_{{i_1}{i_2}}}{g^{{a_3}{a_4}}_{{i_3}{i_4}}}{t^" - L"{{i_3}}_{{a_3}}}{t^{{i_4}}_{{a_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_4}}}}"); - else - REQUIRE( - to_latex(result) == - L"{{{2}}{S^{{a_1}{a_2}}_{{i_1}{i_2}}}{g^{{a_3}{a_4}}_{{i_3}{i_4}}}{t^" - L"{{i_3}}_{{a_4}}}{t^{{i_4}}_{{a_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_3}}}}"); + REQUIRE( + to_latex(result) == + L"{{{2}}{S^{{a_1}{a_2}}_{{i_1}{i_2}}}{g^{{a_3}{a_4}}_{{i_3}{i_4}}}{t^" + L"{{i_3}}_{{a_4}}}{t^{{i_4}}_{{a_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_3}}}}"); } } @@ -582,32 +540,18 @@ SECTION("Transform expression") { expand(result); rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{ \\bigl({{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} - " - L"{{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(result) == - L"{ \\bigl( - {{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} + " - L"{{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - {{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} + " + L"{{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); container::map idxmap = {{Index{L"i_1"}, Index{L"i_2"}}, {Index{L"i_2"}, Index{L"i_1"}}}; auto transformed_result = transform_expr(result, idxmap); REQUIRE(transformed_result->is()); REQUIRE(transformed_result->size() == 2); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(transformed_result) == - L"{ \\bigl({{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} - " - L"{{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(transformed_result) == - L"{ \\bigl( - {{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} + " - L"{{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); + REQUIRE(to_latex(transformed_result) == + L"{ \\bigl( - {{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} + " + L"{{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); } SECTION("Swap bra kets") { @@ -663,14 +607,9 @@ SECTION("Closed-shell spintrace CCD") { const auto input = ex(ExprPtrList{parse_expr( L"1/4 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}", Symmetry::antisymm)}); auto result = closed_shell_CC_spintrace(input); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(result == parse_expr(L"- g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_2,i_1} + " - L"2 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}", - Symmetry::nonsymm)); - else - REQUIRE(result == parse_expr(L"2 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2} - " - L"g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_2,i_1}", - Symmetry::nonsymm)); + REQUIRE(result == parse_expr(L"2 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2} - " + L"g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_2,i_1}", + Symmetry::nonsymm)); } } @@ -701,32 +640,20 @@ SECTION("Closed-shell spintrace CCSD") { ex(rational{1, 2}) * spintrace(input, {{L"i_1", L"a_1"}}); simplify(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{ \\bigl({{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} - " - L"{{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(result) == - L"{ \\bigl( - {{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} + " - L"{{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); + REQUIRE( + to_latex(result) == + L"{ \\bigl( - {{g^{{a_2}{i_1}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}} + " + L"{{{2}}{g^{{i_1}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}}_{{a_2}}}}\\bigr) }"); container::map idxmap = {{Index{L"i_1"}, Index{L"i_2"}}, {Index{L"i_2"}, Index{L"i_1"}}}; auto transformed_result = transform_expr(result, idxmap); REQUIRE(transformed_result->is()); REQUIRE(transformed_result->size() == 2); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(transformed_result) == - L"{ \\bigl({{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} - " - L"{{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(transformed_result) == - L"{ \\bigl( - {{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} + " - L"{{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); + REQUIRE( + to_latex(transformed_result) == + L"{ \\bigl( - {{g^{{a_2}{i_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}} + " + L"{{{2}}{g^{{i_2}{a_2}}_{{a_1}{i_1}}}{t^{{i_1}}_{{a_2}}}}\\bigr) }"); } { @@ -795,19 +722,11 @@ SECTION("Closed-shell spintrace CCSD") { rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ " - L"\\bigl({{{2}}{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_" - L"2}{a_3}}}} - " - L"{{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_2}{a_3}}}}" - L"\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_2}{a_3}}}} + " - L"{{{2}}{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_2}{a_3}}" - L"}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - " + L"{{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_2}{a_3}}}} + " + L"{{{2}}{g^{{a_3}{a_2}}_{{a_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_2}{a_3}}" + L"}}\\bigr) }"); } { @@ -822,16 +741,10 @@ SECTION("Closed-shell spintrace CCSD") { expand(result); rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE( - to_latex(result) == - L"{ \\bigl({{{2}}{f^{{a_2}}_{{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}} - " - L"{{f^{{a_2}}_{{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}}\\bigr) }"); - else - REQUIRE( - to_latex(result) == - L"{ \\bigl( - {{f^{{a_2}}_{{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}} + " - L"{{{2}}{f^{{a_2}}_{{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}}\\bigr) }"); + REQUIRE( + to_latex(result) == + L"{ \\bigl( - {{f^{{a_2}}_{{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}}} + " + L"{{{2}}{f^{{a_2}}_{{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}}}\\bigr) }"); } { @@ -909,20 +822,12 @@ SECTION("Closed-shell spintrace CCSD") { expand(result); rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ " - L"\\bigl({{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_" - L"3}{i_1}}_{{a_2}{a_3}}}} - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{i_2}" - L"{i_1}}_{{a_2}{a_3}}}}\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{" - L"i_2}{i_1}}_{{a_2}{a_3}}}} + " - L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_3}{" - L"i_1}}_{{a_2}{a_3}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - " + L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{" + L"i_2}{i_1}}_{{a_2}{a_3}}}} + " + L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_3}{" + L"i_1}}_{{a_2}{a_3}}}}\\bigr) }"); } { @@ -962,28 +867,16 @@ SECTION("Closed-shell spintrace CCSD") { expand(result); rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_3}" - L"{i_1}}_{{a_1}{a_3}}}} + " - L"{{{4}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_1}" - L"{i_3}}_{{a_1}{a_3}}}} + " - L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_2}}}{t^{{i_2}{i_1}" - L"}_{{a_1}{a_3}}}} - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_3}}}{t^{{i_1}" - L"{i_3}}_{{a_1}{a_2}}}}\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_3}}}{t^{{i_1}" - L"{i_3}}_{{a_1}{a_2}}}} + " - L"{{{4}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_1}" - L"{i_3}}_{{a_1}{a_3}}}} - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_3}" - L"{i_1}}_{{a_1}{a_3}}}} + " - L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_2}}}{t^{{i_2}{i_1}" - L"}_{{a_1}{a_3}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - " + L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_3}}}{t^{{i_1}" + L"{i_3}}_{{a_1}{a_2}}}} + " + L"{{{4}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_1}" + L"{i_3}}_{{a_1}{a_3}}}} - " + L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_2}}}{t^{{i_3}" + L"{i_1}}_{{a_1}{a_3}}}} + " + L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_2}}}{t^{{i_2}{i_1}" + L"}_{{a_1}{a_3}}}}\\bigr) }"); } { @@ -999,20 +892,12 @@ SECTION("Closed-shell spintrace CCSD") { expand(result); rapid_simplify(result); canonicalize(result); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ " - L"\\bigl({{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_" - L"3}}_{{a_2}}}{t^{{i_1}}_{{a_3}}}} - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{i_2}" - L"}_{{a_2}}}{t^{{i_1}}_{{a_3}}}}\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{i_2}" - L"}_{{a_2}}}{t^{{i_1}}_{{a_3}}}} + " - L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_3}}_{{" - L"a_2}}}{t^{{i_1}}_{{a_3}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - " + L"{{{2}}{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_3}}_{{a_1}}}{t^{{i_2}" + L"}_{{a_2}}}{t^{{i_1}}_{{a_3}}}} + " + L"{{g^{{a_2}{a_3}}_{{i_2}{i_3}}}{t^{{i_2}}_{{a_1}}}{t^{{i_3}}_{{" + L"a_2}}}{t^{{i_1}}_{{a_3}}}}\\bigr) }"); } } // CCSD R1 @@ -1033,28 +918,16 @@ SECTION("Closed-shell spintrace CCSDT terms") { input, {{L"i_1", L"a_1"}, {L"i_2", L"a_2"}, {L"i_3", L"a_3"}}); simplify(result); REQUIRE(result->size() == 4); - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(to_latex(result) == - L"{ " - L"\\bigl({{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{" - L"{i_3}}_{{i_4}}}{t^{{i_4}{i_1}{i_2}}_{{a_1}{a_2}{a_3}}}} - " - L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_" - L"{{i_4}}}{t^{{i_1}{i_4}{i_2}}_{{a_1}{a_2}{a_3}}}} + " - L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_1}{i_2}{i_4}}_{{a_1}{a_2}{a_3}}}} - " - L"{{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_2}{i_1}{i_4}}_{{a_1}{a_2}{a_3}}}}\\bigr) }"); - else - REQUIRE(to_latex(result) == - L"{ \\bigl( - " - L"{{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_2}{i_1}{i_4}}_{{a_1}{a_2}{a_3}}}} + " - L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_1}{i_2}{i_4}}_{{a_1}{a_2}{a_3}}}} + " - L"{{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_4}{i_1}{i_2}}_{{a_1}{a_2}{a_3}}}} - " - L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" - L"}{t^{{i_1}{i_4}{i_2}}_{{a_1}{a_2}{a_3}}}}\\bigr) }"); + REQUIRE(to_latex(result) == + L"{ \\bigl( - " + L"{{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" + L"}{t^{{i_2}{i_1}{i_4}}_{{a_1}{a_2}{a_3}}}} + " + L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" + L"}{t^{{i_1}{i_2}{i_4}}_{{a_1}{a_2}{a_3}}}} + " + L"{{{2}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" + L"}{t^{{i_4}{i_1}{i_2}}_{{a_1}{a_2}{a_3}}}} - " + L"{{{4}}{S^{{a_1}{a_2}{a_3}}_{{i_1}{i_2}{i_3}}}{f^{{i_3}}_{{i_4}}" + L"}{t^{{i_1}{i_4}{i_2}}_{{a_1}{a_2}{a_3}}}}\\bigr) }"); } { // f * t3 @@ -1416,20 +1289,12 @@ SECTION("Open-shell spin-tracing") { REQUIRE(to_latex(result[0]) == L"{{{\\frac{1}{12}}}{f^{{a↑_4}}_{{a↑_1}}}{\\bar{t}^{{i↑_1}{i↑_2}{" L"i↑_3}}_{{a↑_2}{a↑_3}{a↑_4}}}}"); - if constexpr (hash_version() == hash::Impl::BoostPre181) { - REQUIRE(to_latex(result[1]) == - L"{ \\bigl( - " - L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_1}{i↑_2}{i↓_3}}_" - L"{{a↑_2}{a↑_3}{a↓_3}}}} + " - L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_2}{i↑_1}{i↓_3}}_" - L"{{a↑_2}{a↑_3}{a↓_3}}}}\\bigr) }"); - } else - REQUIRE(to_latex(result[1]) == - L"{ \\bigl( - " - L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_1}{i↑_2}{i↓_3}}_" - L"{{a↑_2}{a↑_3}{a↓_3}}}} + " - L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_2}{i↑_1}{i↓_3}}_" - L"{{a↑_2}{a↑_3}{a↓_3}}}}\\bigr) }"); + REQUIRE(to_latex(result[1]) == + L"{ \\bigl( - " + L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_1}{i↑_2}{i↓_3}}_" + L"{{a↑_2}{a↑_3}{a↓_3}}}} + " + L"{{{\\frac{1}{12}}}{f^{{a↑_3}}_{{a↑_1}}}{t^{{i↑_2}{i↑_1}{i↓_3}}_" + L"{{a↑_2}{a↑_3}{a↓_3}}}}\\bigr) }"); REQUIRE(to_latex(result[2]) == L"{ \\bigl( - " L"{{{\\frac{1}{12}}}{f^{{a↑_2}}_{{a↑_1}}}{t^{{i↑_1}{i↓_3}{i↓_2}}_" diff --git a/tests/unit/test_tensor_network.cpp b/tests/unit/test_tensor_network.cpp index 98f8c7181..e1d4003f1 100644 --- a/tests/unit/test_tensor_network.cpp +++ b/tests/unit/test_tensor_network.cpp @@ -176,274 +176,139 @@ TEST_CASE("TensorNetwork", "[elements]") { std::basic_ostringstream oss; REQUIRE_NOTHROW(graph->write_dot(oss, vlabels)); std::wcout << "oss.str() = " << std::endl << oss.str() << std::endl; - if constexpr (hash_version() == hash::Impl::BoostPre181) - REQUIRE(oss.str() == - L"graph g {\n" - "v0 [label=\"{a_1}\"; color=\"#9e3,ba0\"];\n" - "v0 -- v29\n" - "v0 -- v58\n" - "v1 [label=\"{a_2}\"; color=\"#9e3,ba0\"];\n" - "v1 -- v29\n" - "v1 -- v58\n" - "v2 [label=\"{a_3}\"; color=\"#9e3,ba0\"];\n" - "v2 -- v33\n" - "v2 -- v54\n" - "v3 [label=\"{a_4}\"; color=\"#9e3,ba0\"];\n" - "v3 -- v33\n" - "v3 -- v54\n" - "v4 [label=\"{a_5}\"; color=\"#9e3,ba0\"];\n" - "v4 -- v37\n" - "v4 -- v50\n" - "v5 [label=\"{a_6}\"; color=\"#9e3,ba0\"];\n" - "v5 -- v37\n" - "v5 -- v50\n" - "v6 [label=\"{a_7}\"; color=\"#9e3,ba0\"];\n" - "v6 -- v22\n" - "v6 -- v41\n" - "v7 [label=\"{a_8}\"; color=\"#9e3,ba0\"];\n" - "v7 -- v22\n" - "v7 -- v41\n" - "v8 [label=\"{i_1}\"; color=\"#a78,ee8\"];\n" - "v8 -- v30\n" - "v8 -- v57\n" - "v9 [label=\"{i_2}\"; color=\"#a78,ee8\"];\n" - "v9 -- v30\n" - "v9 -- v57\n" - "v10 [label=\"{i_3}\"; color=\"#a78,ee8\"];\n" - "v10 -- v34\n" - "v10 -- v53\n" - "v11 [label=\"{i_4}\"; color=\"#a78,ee8\"];\n" - "v11 -- v34\n" - "v11 -- v53\n" - "v12 [label=\"{i_5}\"; color=\"#a78,ee8\"];\n" - "v12 -- v38\n" - "v12 -- v49\n" - "v13 [label=\"{i_6}\"; color=\"#a78,ee8\"];\n" - "v13 -- v38\n" - "v13 -- v49\n" - "v14 [label=\"{i_7}\"; color=\"#a78,ee8\"];\n" - "v14 -- v21\n" - "v14 -- v42\n" - "v15 [label=\"{i_8}\"; color=\"#a78,ee8\"];\n" - "v15 -- v21\n" - "v15 -- v42\n" - "v16 [label=\"{\\kappa_1}\"; color=\"#703,062\"];\n" - "v16 -- v25\n" - "v16 -- v46\n" - "v17 [label=\"{\\kappa_2}\"; color=\"#703,062\"];\n" - "v17 -- v25\n" - "v17 -- v46\n" - "v18 [label=\"{\\kappa_3}\"; color=\"#703,062\"];\n" - "v18 -- v26\n" - "v18 -- v45\n" - "v19 [label=\"{\\kappa_4}\"; color=\"#703,062\"];\n" - "v19 -- v26\n" - "v19 -- v45\n" - "v20 [label=\"A\"; color=\"#2ef,7ff\"];\n" - "v20 -- v23\n" - "v21 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v21 -- v23\n" - "v22 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v22 -- v23\n" - "v23 [label=\"bka\"; color=\"#2ef,7ff\"];\n" - "v24 [label=\"g\"; color=\"#96c,060\"];\n" - "v24 -- v27\n" - "v25 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v25 -- v27\n" - "v26 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v26 -- v27\n" - "v27 [label=\"bka\"; color=\"#96c,060\"];\n" - "v28 [label=\"t\"; color=\"#0f,016\"];\n" - "v28 -- v31\n" - "v29 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v29 -- v31\n" - "v30 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v30 -- v31\n" - "v31 [label=\"bka\"; color=\"#0f,016\"];\n" - "v32 [label=\"t\"; color=\"#0f,016\"];\n" - "v32 -- v35\n" - "v33 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v33 -- v35\n" - "v34 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v34 -- v35\n" - "v35 [label=\"bka\"; color=\"#0f,016\"];\n" - "v36 [label=\"t\"; color=\"#0f,016\"];\n" - "v36 -- v39\n" - "v37 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v37 -- v39\n" - "v38 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v38 -- v39\n" - "v39 [label=\"bka\"; color=\"#0f,016\"];\n" - "v40 [label=\"ã\"; color=\"#116,f93\"];\n" - "v40 -- v43\n" - "v41 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v41 -- v43\n" - "v42 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v42 -- v43\n" - "v43 [label=\"bka\"; color=\"#116,f93\"];\n" - "v44 [label=\"ã\"; color=\"#116,f93\"];\n" - "v44 -- v47\n" - "v45 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v45 -- v47\n" - "v46 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v46 -- v47\n" - "v47 [label=\"bka\"; color=\"#116,f93\"];\n" - "v48 [label=\"ã\"; color=\"#116,f93\"];\n" - "v48 -- v51\n" - "v49 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v49 -- v51\n" - "v50 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v50 -- v51\n" - "v51 [label=\"bka\"; color=\"#116,f93\"];\n" - "v52 [label=\"ã\"; color=\"#116,f93\"];\n" - "v52 -- v55\n" - "v53 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v53 -- v55\n" - "v54 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v54 -- v55\n" - "v55 [label=\"bka\"; color=\"#116,f93\"];\n" - "v56 [label=\"ã\"; color=\"#116,f93\"];\n" - "v56 -- v59\n" - "v57 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v57 -- v59\n" - "v58 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v58 -- v59\n" - "v59 [label=\"bka\"; color=\"#116,f93\"];\n" - "}\n"); - else - REQUIRE(oss.str() == - L"graph g {\n" - "v0 [label=\"{a_1}\"; color=\"#9e3,ba0\"];\n" - "v0 -- v29\n" - "v0 -- v58\n" - "v1 [label=\"{a_2}\"; color=\"#9e3,ba0\"];\n" - "v1 -- v29\n" - "v1 -- v58\n" - "v2 [label=\"{a_3}\"; color=\"#9e3,ba0\"];\n" - "v2 -- v33\n" - "v2 -- v54\n" - "v3 [label=\"{a_4}\"; color=\"#9e3,ba0\"];\n" - "v3 -- v33\n" - "v3 -- v54\n" - "v4 [label=\"{a_5}\"; color=\"#9e3,ba0\"];\n" - "v4 -- v37\n" - "v4 -- v50\n" - "v5 [label=\"{a_6}\"; color=\"#9e3,ba0\"];\n" - "v5 -- v37\n" - "v5 -- v50\n" - "v6 [label=\"{a_7}\"; color=\"#9e3,ba0\"];\n" - "v6 -- v22\n" - "v6 -- v41\n" - "v7 [label=\"{a_8}\"; color=\"#9e3,ba0\"];\n" - "v7 -- v22\n" - "v7 -- v41\n" - "v8 [label=\"{i_1}\"; color=\"#a78,ee8\"];\n" - "v8 -- v30\n" - "v8 -- v57\n" - "v9 [label=\"{i_2}\"; color=\"#a78,ee8\"];\n" - "v9 -- v30\n" - "v9 -- v57\n" - "v10 [label=\"{i_3}\"; color=\"#a78,ee8\"];\n" - "v10 -- v34\n" - "v10 -- v53\n" - "v11 [label=\"{i_4}\"; color=\"#a78,ee8\"];\n" - "v11 -- v34\n" - "v11 -- v53\n" - "v12 [label=\"{i_5}\"; color=\"#a78,ee8\"];\n" - "v12 -- v38\n" - "v12 -- v49\n" - "v13 [label=\"{i_6}\"; color=\"#a78,ee8\"];\n" - "v13 -- v38\n" - "v13 -- v49\n" - "v14 [label=\"{i_7}\"; color=\"#a78,ee8\"];\n" - "v14 -- v21\n" - "v14 -- v42\n" - "v15 [label=\"{i_8}\"; color=\"#a78,ee8\"];\n" - "v15 -- v21\n" - "v15 -- v42\n" - "v16 [label=\"{\\kappa_1}\"; color=\"#703,062\"];\n" - "v16 -- v25\n" - "v16 -- v46\n" - "v17 [label=\"{\\kappa_2}\"; color=\"#703,062\"];\n" - "v17 -- v25\n" - "v17 -- v46\n" - "v18 [label=\"{\\kappa_3}\"; color=\"#703,062\"];\n" - "v18 -- v26\n" - "v18 -- v45\n" - "v19 [label=\"{\\kappa_4}\"; color=\"#703,062\"];\n" - "v19 -- v26\n" - "v19 -- v45\n" - "v20 [label=\"A\"; color=\"#518,020\"];\n" - "v20 -- v23\n" - "v21 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v21 -- v23\n" - "v22 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v22 -- v23\n" - "v23 [label=\"bka\"; color=\"#518,020\"];\n" - "v24 [label=\"g\"; color=\"#2e0,351\"];\n" - "v24 -- v27\n" - "v25 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v25 -- v27\n" - "v26 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v26 -- v27\n" - "v27 [label=\"bka\"; color=\"#2e0,351\"];\n" - "v28 [label=\"t\"; color=\"#43,e44\"];\n" - "v28 -- v31\n" - "v29 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v29 -- v31\n" - "v30 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v30 -- v31\n" - "v31 [label=\"bka\"; color=\"#43,e44\"];\n" - "v32 [label=\"t\"; color=\"#43,e44\"];\n" - "v32 -- v35\n" - "v33 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v33 -- v35\n" - "v34 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v34 -- v35\n" - "v35 [label=\"bka\"; color=\"#43,e44\"];\n" - "v36 [label=\"t\"; color=\"#43,e44\"];\n" - "v36 -- v39\n" - "v37 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v37 -- v39\n" - "v38 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v38 -- v39\n" - "v39 [label=\"bka\"; color=\"#43,e44\"];\n" - "v40 [label=\"ã\"; color=\"#cbf,be5\"];\n" - "v40 -- v43\n" - "v41 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v41 -- v43\n" - "v42 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v42 -- v43\n" - "v43 [label=\"bka\"; color=\"#cbf,be5\"];\n" - "v44 [label=\"ã\"; color=\"#cbf,be5\"];\n" - "v44 -- v47\n" - "v45 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v45 -- v47\n" - "v46 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v46 -- v47\n" - "v47 [label=\"bka\"; color=\"#cbf,be5\"];\n" - "v48 [label=\"ã\"; color=\"#cbf,be5\"];\n" - "v48 -- v51\n" - "v49 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v49 -- v51\n" - "v50 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v50 -- v51\n" - "v51 [label=\"bka\"; color=\"#cbf,be5\"];\n" - "v52 [label=\"ã\"; color=\"#cbf,be5\"];\n" - "v52 -- v55\n" - "v53 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v53 -- v55\n" - "v54 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v54 -- v55\n" - "v55 [label=\"bka\"; color=\"#cbf,be5\"];\n" - "v56 [label=\"ã\"; color=\"#cbf,be5\"];\n" - "v56 -- v59\n" - "v57 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" - "v57 -- v59\n" - "v58 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" - "v58 -- v59\n" - "v59 [label=\"bka\"; color=\"#cbf,be5\"];\n" - "}\n"); + REQUIRE(oss.str() == + L"graph g {\n" + "v0 [label=\"{a_1}\"; color=\"#9e3,ba0\"];\n" + "v0 -- v29\n" + "v0 -- v58\n" + "v1 [label=\"{a_2}\"; color=\"#9e3,ba0\"];\n" + "v1 -- v29\n" + "v1 -- v58\n" + "v2 [label=\"{a_3}\"; color=\"#9e3,ba0\"];\n" + "v2 -- v33\n" + "v2 -- v54\n" + "v3 [label=\"{a_4}\"; color=\"#9e3,ba0\"];\n" + "v3 -- v33\n" + "v3 -- v54\n" + "v4 [label=\"{a_5}\"; color=\"#9e3,ba0\"];\n" + "v4 -- v37\n" + "v4 -- v50\n" + "v5 [label=\"{a_6}\"; color=\"#9e3,ba0\"];\n" + "v5 -- v37\n" + "v5 -- v50\n" + "v6 [label=\"{a_7}\"; color=\"#9e3,ba0\"];\n" + "v6 -- v22\n" + "v6 -- v41\n" + "v7 [label=\"{a_8}\"; color=\"#9e3,ba0\"];\n" + "v7 -- v22\n" + "v7 -- v41\n" + "v8 [label=\"{i_1}\"; color=\"#a78,ee8\"];\n" + "v8 -- v30\n" + "v8 -- v57\n" + "v9 [label=\"{i_2}\"; color=\"#a78,ee8\"];\n" + "v9 -- v30\n" + "v9 -- v57\n" + "v10 [label=\"{i_3}\"; color=\"#a78,ee8\"];\n" + "v10 -- v34\n" + "v10 -- v53\n" + "v11 [label=\"{i_4}\"; color=\"#a78,ee8\"];\n" + "v11 -- v34\n" + "v11 -- v53\n" + "v12 [label=\"{i_5}\"; color=\"#a78,ee8\"];\n" + "v12 -- v38\n" + "v12 -- v49\n" + "v13 [label=\"{i_6}\"; color=\"#a78,ee8\"];\n" + "v13 -- v38\n" + "v13 -- v49\n" + "v14 [label=\"{i_7}\"; color=\"#a78,ee8\"];\n" + "v14 -- v21\n" + "v14 -- v42\n" + "v15 [label=\"{i_8}\"; color=\"#a78,ee8\"];\n" + "v15 -- v21\n" + "v15 -- v42\n" + "v16 [label=\"{\\kappa_1}\"; color=\"#703,062\"];\n" + "v16 -- v25\n" + "v16 -- v46\n" + "v17 [label=\"{\\kappa_2}\"; color=\"#703,062\"];\n" + "v17 -- v25\n" + "v17 -- v46\n" + "v18 [label=\"{\\kappa_3}\"; color=\"#703,062\"];\n" + "v18 -- v26\n" + "v18 -- v45\n" + "v19 [label=\"{\\kappa_4}\"; color=\"#703,062\"];\n" + "v19 -- v26\n" + "v19 -- v45\n" + "v20 [label=\"A\"; color=\"#518,020\"];\n" + "v20 -- v23\n" + "v21 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v21 -- v23\n" + "v22 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v22 -- v23\n" + "v23 [label=\"bka\"; color=\"#518,020\"];\n" + "v24 [label=\"g\"; color=\"#2e0,351\"];\n" + "v24 -- v27\n" + "v25 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v25 -- v27\n" + "v26 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v26 -- v27\n" + "v27 [label=\"bka\"; color=\"#2e0,351\"];\n" + "v28 [label=\"t\"; color=\"#43,e44\"];\n" + "v28 -- v31\n" + "v29 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v29 -- v31\n" + "v30 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v30 -- v31\n" + "v31 [label=\"bka\"; color=\"#43,e44\"];\n" + "v32 [label=\"t\"; color=\"#43,e44\"];\n" + "v32 -- v35\n" + "v33 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v33 -- v35\n" + "v34 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v34 -- v35\n" + "v35 [label=\"bka\"; color=\"#43,e44\"];\n" + "v36 [label=\"t\"; color=\"#43,e44\"];\n" + "v36 -- v39\n" + "v37 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v37 -- v39\n" + "v38 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v38 -- v39\n" + "v39 [label=\"bka\"; color=\"#43,e44\"];\n" + "v40 [label=\"ã\"; color=\"#cbf,be5\"];\n" + "v40 -- v43\n" + "v41 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v41 -- v43\n" + "v42 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v42 -- v43\n" + "v43 [label=\"bka\"; color=\"#cbf,be5\"];\n" + "v44 [label=\"ã\"; color=\"#cbf,be5\"];\n" + "v44 -- v47\n" + "v45 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v45 -- v47\n" + "v46 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v46 -- v47\n" + "v47 [label=\"bka\"; color=\"#cbf,be5\"];\n" + "v48 [label=\"ã\"; color=\"#cbf,be5\"];\n" + "v48 -- v51\n" + "v49 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v49 -- v51\n" + "v50 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v50 -- v51\n" + "v51 [label=\"bka\"; color=\"#cbf,be5\"];\n" + "v52 [label=\"ã\"; color=\"#cbf,be5\"];\n" + "v52 -- v55\n" + "v53 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v53 -- v55\n" + "v54 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v54 -- v55\n" + "v55 [label=\"bka\"; color=\"#cbf,be5\"];\n" + "v56 [label=\"ã\"; color=\"#cbf,be5\"];\n" + "v56 -- v59\n" + "v57 [label=\"bra2a\"; color=\"#eaa,2ab\"];\n" + "v57 -- v59\n" + "v58 [label=\"ket2a\"; color=\"#5a8,fd3\"];\n" + "v58 -- v59\n" + "v59 [label=\"bka\"; color=\"#cbf,be5\"];\n" + "}\n"); // compute automorphism group { diff --git a/tests/unit/test_wick.cpp b/tests/unit/test_wick.cpp index 611035790..5fa41385c 100644 --- a/tests/unit/test_wick.cpp +++ b/tests/unit/test_wick.cpp @@ -147,186 +147,196 @@ TEST_CASE("WickTheorem", "[algorithms][wick]") { }); // number operator - {{auto opseq1 = - FNOperatorSeq({FNOperator({L"i_1"}, {}), FNOperator({}, {L"i_2"})}); - auto wick1 = FWickTheorem{opseq1}; - REQUIRE_NOTHROW(wick1.compute()); - // full contractions = null (N is already in normal form) - auto full_contractions = FWickTheorem{opseq1}.compute(); - REQUIRE(full_contractions->is()); - REQUIRE(full_contractions->as().value() == 0); - // partial contractions = N - auto partial_contractions = - FWickTheorem{opseq1}.full_contractions(false).compute(); - // std::wcout << "partial_contractions=" << to_latex(partial_contractions) - // << std::endl; - REQUIRE(partial_contractions->is()); - REQUIRE(partial_contractions->as().size() == 1); - } - { - auto opseq1 = - BNOperatorSeq({BNOperator({L"i_1"}, {}), BNOperator({}, {L"i_2"})}); - auto wick1 = BWickTheorem{opseq1}; - REQUIRE_NOTHROW(wick1.compute()); - // full contractions = null - auto full_contractions = BWickTheorem{opseq1}.compute(); - REQUIRE(full_contractions->is()); - REQUIRE(full_contractions->as().value() == 0); - // partial contractions = N - auto partial_contractions = - BWickTheorem{opseq1}.full_contractions(false).compute(); - // std::wcout << "partial_contractions=" << to_latex(partial_contractions) - // << std::endl; - REQUIRE(partial_contractions->is()); - REQUIRE(partial_contractions->as().size() == 1); - } -} + { + { + auto opseq1 = + FNOperatorSeq({FNOperator({L"i_1"}, {}), FNOperator({}, {L"i_2"})}); + auto wick1 = FWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.compute()); + // full contractions = null (N is already in normal form) + auto full_contractions = FWickTheorem{opseq1}.compute(); + REQUIRE(full_contractions->is()); + REQUIRE(full_contractions->as().value() == 0); + // partial contractions = N + auto partial_contractions = + FWickTheorem{opseq1}.full_contractions(false).compute(); + // std::wcout << "partial_contractions=" << + // to_latex(partial_contractions) + // << std::endl; + REQUIRE(partial_contractions->is()); + REQUIRE(partial_contractions->as().size() == 1); + } + { + auto opseq1 = + BNOperatorSeq({BNOperator({L"i_1"}, {}), BNOperator({}, {L"i_2"})}); + auto wick1 = BWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.compute()); + // full contractions = null + auto full_contractions = BWickTheorem{opseq1}.compute(); + REQUIRE(full_contractions->is()); + REQUIRE(full_contractions->as().value() == 0); + // partial contractions = N + auto partial_contractions = + BWickTheorem{opseq1}.full_contractions(false).compute(); + // std::wcout << "partial_contractions=" << + // to_latex(partial_contractions) + // << std::endl; + REQUIRE(partial_contractions->is()); + REQUIRE(partial_contractions->as().size() == 1); + } + } -// hole number operator -{{auto opseq1 = - FNOperatorSeq({FNOperator({}, {L"i_1"}), FNOperator({L"i_2"}, {})}); -auto wick1 = FWickTheorem{opseq1}; -REQUIRE_NOTHROW(wick1.compute()); -// full contractions = delta -auto full_contractions = FWickTheorem{opseq1}.compute(); -REQUIRE(full_contractions->is()); -REQUIRE(full_contractions->as().size() == 1); -// partial contractions = delta - N -auto partial_contractions = - FWickTheorem{opseq1}.full_contractions(false).compute(); -// std::wcout << "partial_contractions=" << to_latex(partial_contractions) << -// std::endl; -REQUIRE(partial_contractions->is()); -REQUIRE(partial_contractions->as().size() == 2); -REQUIRE(to_latex(partial_contractions) == - L"{ \\bigl({{s^{{i_2}}_{{i_1}}}} - {{a^{{i_2}}_{{i_1}}}}\\bigr) }"); -} -{ - auto opseq1 = - BNOperatorSeq({BNOperator({}, {L"i_1"}), BNOperator({L"i_2"}, {})}); - auto wick1 = BWickTheorem{opseq1}; - REQUIRE_NOTHROW(wick1.compute()); - // full contractions = delta - auto full_contractions = BWickTheorem{opseq1}.compute(); - REQUIRE(full_contractions->is()); - REQUIRE(full_contractions->as().size() == 1); - // partial contractions = delta + N - auto partial_contractions = - BWickTheorem{opseq1}.full_contractions(false).compute(); - // std::wcout << "partial_contractions=" << to_latex(partial_contractions) << - // std::endl; - REQUIRE(partial_contractions->is()); - REQUIRE(partial_contractions->as().size() == 2); - REQUIRE(to_latex(partial_contractions) == - L"{ \\bigl({{s^{{i_2}}_{{i_1}}}} + {{b^{{i_2}}_{{i_1}}}}\\bigr) }"); -} -} + // hole number operator + { + { + auto opseq1 = + FNOperatorSeq({FNOperator({}, {L"i_1"}), FNOperator({L"i_2"}, {})}); + auto wick1 = FWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.compute()); + // full contractions = delta + auto full_contractions = FWickTheorem{opseq1}.compute(); + REQUIRE(full_contractions->is()); + REQUIRE(full_contractions->as().size() == 1); + // partial contractions = delta - N + auto partial_contractions = + FWickTheorem{opseq1}.full_contractions(false).compute(); + // std::wcout << "partial_contractions=" << + // to_latex(partial_contractions) << std::endl; + REQUIRE(partial_contractions->is()); + REQUIRE(partial_contractions->as().size() == 2); + REQUIRE( + to_latex(partial_contractions) == + L"{ \\bigl({{s^{{i_2}}_{{i_1}}}} - {{a^{{i_2}}_{{i_1}}}}\\bigr) }"); + } + { + auto opseq1 = + BNOperatorSeq({BNOperator({}, {L"i_1"}), BNOperator({L"i_2"}, {})}); + auto wick1 = BWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.compute()); + // full contractions = delta + auto full_contractions = BWickTheorem{opseq1}.compute(); + REQUIRE(full_contractions->is()); + REQUIRE(full_contractions->as().size() == 1); + // partial contractions = delta + N + auto partial_contractions = + BWickTheorem{opseq1}.full_contractions(false).compute(); + // std::wcout << "partial_contractions=" << + // to_latex(partial_contractions) << std::endl; + REQUIRE(partial_contractions->is()); + REQUIRE(partial_contractions->as().size() == 2); + REQUIRE( + to_latex(partial_contractions) == + L"{ \\bigl({{s^{{i_2}}_{{i_1}}}} + {{b^{{i_2}}_{{i_1}}}}\\bigr) }"); + } + } -// three 1-body operators -{ - auto opseq1 = FNOperatorSeq({FNOperator({L"i_1"}, {L"i_2"}), - FNOperator({L"i_3"}, {L"i_4"}), - FNOperator({L"i_5"}, {L"i_6"})}); - auto wick1 = FWickTheorem{opseq1}; - REQUIRE_NOTHROW(wick1.compute()); - auto result = FWickTheorem{opseq1}.compute(); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 0); -} + // three 1-body operators + { + auto opseq1 = FNOperatorSeq({FNOperator({L"i_1"}, {L"i_2"}), + FNOperator({L"i_3"}, {L"i_4"}), + FNOperator({L"i_5"}, {L"i_6"})}); + auto wick1 = FWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.compute()); + auto result = FWickTheorem{opseq1}.compute(); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 0); + } -// two 2-body operators -{ - auto opseq = FNOperatorSeq( - {FNOperator({}, {L"i_1", L"i_2"}), FNOperator({L"i_3", L"i_4"}, {})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 2); -} + // two 2-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({}, {L"i_1", L"i_2"}), FNOperator({L"i_3", L"i_4"}, {})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 2); + } -// two 3-body operators -{ - auto opseq = FNOperatorSeq({FNOperator({}, {L"i_1", L"i_2", L"i_3"}), - FNOperator({L"i_4", L"i_5", L"i_6"}, {})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 6); -} + // two 3-body operators + { + auto opseq = FNOperatorSeq({FNOperator({}, {L"i_1", L"i_2", L"i_3"}), + FNOperator({L"i_4", L"i_5", L"i_6"}, {})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 6); + } -// two 4-body operators -{ - auto opseq = - FNOperatorSeq({FNOperator({}, {L"i_1", L"i_2", L"i_3", L"i_4"}), - FNOperator({L"i_5", L"i_6", L"i_7", L"i_8"}, {})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 24); -} + // two 4-body operators + { + auto opseq = + FNOperatorSeq({FNOperator({}, {L"i_1", L"i_2", L"i_3", L"i_4"}), + FNOperator({L"i_5", L"i_6", L"i_7", L"i_8"}, {})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 24); + } -// 1/2 * 1 * 1/2 body ops, full contraction -{ - auto opseq = - FNOperatorSeq({FNOperator({}, {L"i_1"}), FNOperator({L"i_2"}, {L"i_3"}), - FNOperator({L"i_4"}, {})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 2); // product of 2 terms -} + // 1/2 * 1 * 1/2 body ops, full contraction + { + auto opseq = FNOperatorSeq({FNOperator({}, {L"i_1"}), + FNOperator({L"i_2"}, {L"i_3"}), + FNOperator({L"i_4"}, {})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 2); // product of 2 terms + } -// 1/2 * 1 * 1/2 body ops, partial contraction -{ - auto opseq = - FNOperatorSeq({FNOperator({}, {L"i_1"}), FNOperator({L"i_2"}, {L"i_3"}), - FNOperator({L"i_4"}, {})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result = wick.full_contractions(false).compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 5); // sum of 4 terms - REQUIRE(to_latex(result) == - L"{ \\bigl( - {{s^{{i_2}}_{{i_1}}}{a^{{i_4}}_{{i_3}}}} + " - L"{{s^{{i_2}}_{{i_1}}}{s^{{i_4}}_{{i_3}}}} + " - L"{{s^{{i_4}}_{{i_1}}}{a^{{i_2}}_{{i_3}}}} - " - L"{{s^{{i_4}}_{{i_3}}}{a^{{i_2}}_{{i_1}}}} - " - L"{{a^{{i_2}{i_4}}_{{i_3}{i_1}}}}\\bigr) }"); -} + // 1/2 * 1 * 1/2 body ops, partial contraction + { + auto opseq = FNOperatorSeq({FNOperator({}, {L"i_1"}), + FNOperator({L"i_2"}, {L"i_3"}), + FNOperator({L"i_4"}, {})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result = wick.full_contractions(false).compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 5); // sum of 4 terms + REQUIRE(to_latex(result) == + L"{ \\bigl( - {{s^{{i_2}}_{{i_1}}}{a^{{i_4}}_{{i_3}}}} + " + L"{{s^{{i_2}}_{{i_1}}}{s^{{i_4}}_{{i_3}}}} + " + L"{{s^{{i_4}}_{{i_1}}}{a^{{i_2}}_{{i_3}}}} - " + L"{{s^{{i_4}}_{{i_3}}}{a^{{i_2}}_{{i_1}}}} - " + L"{{a^{{i_2}{i_4}}_{{i_3}{i_1}}}}\\bigr) }"); + } -// three 1-body operators, partial contraction -{ - auto opseq1 = FNOperatorSeq({FNOperator({L"i_1"}, {L"i_2"}), - FNOperator({L"i_3"}, {L"i_4"}), - FNOperator({L"i_5"}, {L"i_6"})}); - auto wick1 = FWickTheorem{opseq1}; - REQUIRE_NOTHROW(wick1.full_contractions(false).compute()); - auto result = FWickTheorem{opseq1}.full_contractions(false).compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 5); - REQUIRE(to_latex(result) == - L"{ \\bigl({{s^{{i_3}}_{{i_2}}}{a^{{i_1}{i_5}}_{{i_4}{i_6}}}} + " - L"{{s^{{i_3}}_{{i_2}}}{s^{{i_5}}_{{i_4}}}{a^{{i_1}}_{{i_6}}}} + " - L"{{s^{{i_5}}_{{i_2}}}{a^{{i_1}{i_3}}_{{i_6}{i_4}}}} + " - L"{{s^{{i_5}}_{{i_4}}}{a^{{i_1}{i_3}}_{{i_2}{i_6}}}} + " - L"{{a^{{i_1}{i_3}{i_5}}_{{i_2}{i_4}{i_6}}}}\\bigr) }"); -} + // three 1-body operators, partial contraction + { + auto opseq1 = FNOperatorSeq({FNOperator({L"i_1"}, {L"i_2"}), + FNOperator({L"i_3"}, {L"i_4"}), + FNOperator({L"i_5"}, {L"i_6"})}); + auto wick1 = FWickTheorem{opseq1}; + REQUIRE_NOTHROW(wick1.full_contractions(false).compute()); + auto result = FWickTheorem{opseq1}.full_contractions(false).compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 5); + REQUIRE(to_latex(result) == + L"{ \\bigl({{s^{{i_3}}_{{i_2}}}{a^{{i_1}{i_5}}_{{i_4}{i_6}}}} + " + L"{{s^{{i_3}}_{{i_2}}}{s^{{i_5}}_{{i_4}}}{a^{{i_1}}_{{i_6}}}} + " + L"{{s^{{i_5}}_{{i_2}}}{a^{{i_1}{i_3}}_{{i_6}{i_4}}}} + " + L"{{s^{{i_5}}_{{i_4}}}{a^{{i_1}{i_3}}_{{i_2}{i_6}}}} + " + L"{{a^{{i_1}{i_3}{i_5}}_{{i_2}{i_4}{i_6}}}}\\bigr) }"); + } -// two 2-body operators, partial contraction: Eq. 9b of DOI 10.1063/1.474405 -{ - auto opseq = FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"i_3", L"i_4"}), - FNOperator({L"i_5", L"i_6"}, {L"i_7", L"i_8"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result = wick.full_contractions(false).compute(); - auto result_latex = to_latex(result); - REQUIRE(result->is()); - REQUIRE(result->size() == 7); - REQUIRE(result_latex == + // two 2-body operators, partial contraction: Eq. 9b of DOI 10.1063/1.474405 + { + auto opseq = + FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"i_3", L"i_4"}), + FNOperator({L"i_5", L"i_6"}, {L"i_7", L"i_8"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result = wick.full_contractions(false).compute(); + auto result_latex = to_latex(result); + REQUIRE(result->is()); + REQUIRE(result->size() == 7); + REQUIRE( + result_latex == L"{ " L"\\bigl({{s^{{i_5}}_{{i_4}}}{a^{{i_1}{i_2}{i_6}}_{{i_3}{i_7}{i_8}}}}" L" + " @@ -338,429 +348,436 @@ REQUIRE(to_latex(partial_contractions) == L"+ {{s^{{i_6}}_{{i_3}}}{a^{{i_1}{i_2}{i_5}}_{{i_8}{i_4}{i_7}}}} + " L"{{a^{{i_1}{i_2}{i_5}{i_6}}_{{i_3}{i_4}{i_7}{i_8}}}}\\bigr) }"); - // if Wick's theorem's result is in "canonical" (columns-matching-inputs ... - // this is what Kutzelnigg calls generalized Wick's theorem) it works same - // for spinorbital and spinfree basis for physical vacuum - auto raii_tmp = switch_to_spinfree_context(); - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result_sf = wick.full_contractions(false).compute(); - auto result_sf_latex = to_latex(result_sf); - // std::wcout << "result_sf: " << to_latex(result_sf) << std::endl; - REQUIRE(result->is()); - REQUIRE(result->size() == 7); - auto result_sf_latex_with_E_replaced_by_a = - result_sf_latex | ranges::views::replace(L'E', L'a') | - ranges::to(); - REQUIRE(result_latex == result_sf_latex_with_E_replaced_by_a); -} + // if Wick's theorem's result is in "canonical" (columns-matching-inputs + // ... this is what Kutzelnigg calls generalized Wick's theorem) it works + // same for spinorbital and spinfree basis for physical vacuum + auto raii_tmp = switch_to_spinfree_context(); + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result_sf = wick.full_contractions(false).compute(); + auto result_sf_latex = to_latex(result_sf); + // std::wcout << "result_sf: " << to_latex(result_sf) << std::endl; + REQUIRE(result->is()); + REQUIRE(result->size() == 7); + auto result_sf_latex_with_E_replaced_by_a = + result_sf_latex | ranges::views::replace(L'E', L'a') | + ranges::to(); + REQUIRE(result_latex == result_sf_latex_with_E_replaced_by_a); + } -} // SECTION("physical vacuum") - -SECTION("fermi vacuum") { - // default vacuum is already spin-orbital Fermi vacuum - - auto switch_to_spinfree_context = detail::NoDiscard([&]() { - auto context_sf = get_default_context(); - context_sf.set(SPBasis::spinfree); - return set_scoped_default_context(context_sf); - }); - - // two (pure qp) 1-body operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"i_1"}, {L"a_1"}), FNOperator({L"a_2"}, {L"i_2"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 2); // product of 2 terms - - // spin-free result is simply twice the spin-orbital result - auto raii_tmp = switch_to_spinfree_context(); - auto result_sf = wick.compute(); - REQUIRE(simplify(result_sf - result * ex(2)) == ex(0)); - } + } // SECTION("physical vacuum") - // two (pure qp) N-nonconserving 2-body operators - { - auto opseq = FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"a_1"}), - FNOperator({L"a_2"}, {L"i_3", L"i_4"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 2); - } + SECTION("fermi vacuum") { + // default vacuum is already spin-orbital Fermi vacuum - // two general 1-body operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == - 2 * 2); // product of 4 terms (since each contraction of 2 - // *general* indices produces 2 overlaps) - REQUIRE(to_latex(result) == - L"{{s^{{p_1}}_{{m_{102}}}}{s^{{m_{102}}}_{{p_4}}}{s^{{E_{103}}}_{" - L"{p_2}}}{s^{{p_3}}_{{E_{103}}}}}"); - } - // two general 1-body operators, partial contractions: Eq. 21a of - // DOI 10.1063/1.474405 - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result = wick.full_contractions(false).compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 4); - REQUIRE( - to_latex(result) == - L"{ \\bigl( - " - L"{{s^{{p_1}}_{{m_{107}}}}{s^{{m_{107}}}_{{p_4}}}{\\tilde{a}^{{p_3}}_" - L"{{p_2}}}} + " - L"{{s^{{p_1}}_{{m_{107}}}}{s^{{m_{107}}}_{{p_4}}}{s^{{E_{108}}}_{{p_" - L"2}}}{s^{{p_3}}_{{E_{108}}}}} + " - L"{{s^{{E_{109}}}_{{p_2}}}{s^{{p_3}}_{{E_{109}}}}{\\tilde{a}^{{p_1}}_" - L"{{p_4}}}} + {{\\tilde{a}^{{p_1}{p_3}}_{{p_2}{p_4}}}}\\bigr) }"); - } + auto switch_to_spinfree_context = detail::NoDiscard([&]() { + auto context_sf = get_default_context(); + context_sf.set(SPBasis::spinfree); + return set_scoped_default_context(context_sf); + }); - // two (pure qp) 2-body operators - { - auto opseq = - FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"a_1", L"a_2"}), - FNOperator({L"a_3", L"a_4"}, {L"i_3", L"i_4"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - auto result_latex = to_latex(result); - // std::wcout << "<" << to_latex(opseq) << "> = " << result_latex << - // std::endl; - REQUIRE(result->is()); - REQUIRE(result->size() == 4); - REQUIRE(result_latex == - L"{ " - L"\\bigl({{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}" - L"{s^{{a_4}}_{{a_1}}}} - " - L"{{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{s^{{a_" - L"3}}_{{a_1}}}} - " - L"{{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}{s^{{a_" - L"4}}_{{a_1}}}} + " - L"{{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{s^{{a_" - L"3}}_{{a_1}}}}\\bigr) }"); - - // in spin-free result first and fourth terms are multiplied by 4, second - // and third terms multiplied by 2 - auto raii_tmp = switch_to_spinfree_context(); - auto result_sf = wick.compute(); - auto result_sf_latex = to_latex(result_sf); - // std::wcout << "<" << to_latex(opseq) << "> = " << to_latex(result_sf) - // << std::endl; - REQUIRE(result_sf->is()); - REQUIRE(result_sf->size() == 4); - REQUIRE(result_sf_latex == - L"{ " - L"\\bigl({{{4}}{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_3}}_{{" - L"a_2}}}{s^{{a_4}}_{{a_1}}}} - " - L"{{{2}}{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{" - L"s^{{a_3}}_{{a_1}}}} - " - L"{{{2}}{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}{" - L"s^{{a_4}}_{{a_1}}}} + " - L"{{{4}}{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{" - L"s^{{a_3}}_{{a_1}}}}\\bigr) }"); - } - // two (pure qp) 3-body operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"i_1", L"i_2", L"i_3"}, {L"a_1", L"a_2", L"a_3"}), - FNOperator({L"a_4", L"a_5", L"a_6"}, {L"i_4", L"i_5", L"i_6"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 36); - } + // two (pure qp) 1-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"i_1"}, {L"a_1"}), FNOperator({L"a_2"}, {L"i_2"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 2); // product of 2 terms - // one general 1-body operator + one general 2-body operator, partial - // contraction: Eq. 9 of DOI 10.1063/1.474405 - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1"}, {L"p_2"}), - FNOperator({L"p_3", L"p_4"}, {L"p_5", L"p_6"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result = wick.full_contractions(false).compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 9); - } + // spin-free result is simply twice the spin-orbital result + auto raii_tmp = switch_to_spinfree_context(); + auto result_sf = wick.compute(); + REQUIRE(simplify(result_sf - result * ex(2)) == + ex(0)); + } - // two general 2-body operators - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({L"p_5", L"p_6"}, {L"p_7", L"p_8"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 4); - } - // two general 2-body operators, partial contractions: Eqs. 22 of - // DOI 10.1063/1.474405 - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({L"p_5", L"p_6"}, {L"p_7", L"p_8"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.full_contractions(false).compute()); - auto result = wick.full_contractions(false).compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 49); // the MK paper only gives 47 terms, - // misses the 2 double-hole contractions - } - // one general 2-body operator and one 2-body excitation operator - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({L"a_3", L"a_4"}, {L"i_3", L"i_4"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - std::wcout << "<" << to_latex(opseq) << "> = " << to_latex(result) - << std::endl; - REQUIRE(result->is()); - REQUIRE(result->size() == 4); - REQUIRE(to_latex(result) == - L"{ " - L"\\bigl({{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_3}}_{{p_4}}}" - L"{s^{{a_4}}_{{p_3}}}} - " - L"{{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_4}}_{{p_4}}}{s^{{a_" - L"3}}_{{p_3}}}} - " - L"{{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_3}}_{{p_4}}}{s^{{a_" - L"4}}_{{p_3}}}} + " - L"{{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{s^{{a_" - L"3}}_{{p_3}}}}\\bigr) }"); - - // in spin-free result first and fourth terms are multiplied by 4, second - // and third terms multiplied by 2 - auto raii_tmp = switch_to_spinfree_context(); - auto result_sf = wick.compute(); - auto result_sf_latex = to_latex(result_sf); - // std::wcout << "<" << to_latex(opseq) << "> = " << to_latex(result_sf) - // << std::endl; - REQUIRE(result_sf->is()); - REQUIRE(result_sf->size() == 4); - REQUIRE(to_latex(result_sf) == - L"{ " - L"\\bigl({{{4}}{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_3}}_{{" - L"p_4}}}{s^{{a_4}}_{{p_3}}}} - " - L"{{{2}}{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_4}}_{{p_4}}}{" - L"s^{{a_3}}_{{p_3}}}} - " - L"{{{2}}{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_3}}_{{p_4}}}{" - L"s^{{a_4}}_{{p_3}}}} + " - L"{{{4}}{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{" - L"s^{{a_3}}_{{p_3}}}}\\bigr) }"); - } + // two (pure qp) N-nonconserving 2-body operators + { + auto opseq = FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"a_1"}), + FNOperator({L"a_2"}, {L"i_3", L"i_4"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 2); + } - // two general 3-body operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_4", L"p_5", L"p_6"}), - FNOperator({L"p_7", L"p_8", L"p_9"}, {L"p_10", L"p_11", L"p_12"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 36); - } + // two general 1-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == + 2 * 2); // product of 4 terms (since each contraction of 2 + // *general* indices produces 2 overlaps) + REQUIRE(to_latex(result) == + L"{{s^{{p_1}}_{{m_{102}}}}{s^{{m_{102}}}_{{p_4}}}{s^{{E_{103}}}_{" + L"{p_2}}}{s^{{p_3}}_{{E_{103}}}}}"); + } + // two general 1-body operators, partial contractions: Eq. 21a of + // DOI 10.1063/1.474405 + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result = wick.full_contractions(false).compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 4); + REQUIRE( + to_latex(result) == + L"{ \\bigl( - " + L"{{s^{{p_1}}_{{m_{107}}}}{s^{{m_{107}}}_{{p_4}}}{\\tilde{a}^{{p_3}}_" + L"{{p_2}}}} + " + L"{{s^{{p_1}}_{{m_{107}}}}{s^{{m_{107}}}_{{p_4}}}{s^{{E_{108}}}_{{p_" + L"2}}}{s^{{p_3}}_{{E_{108}}}}} + " + L"{{s^{{E_{109}}}_{{p_2}}}{s^{{p_3}}_{{E_{109}}}}{\\tilde{a}^{{p_1}}_" + L"{{p_4}}}} + {{\\tilde{a}^{{p_1}{p_3}}_{{p_2}{p_4}}}}\\bigr) }"); + } - // two N-nonconserving operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_4", L"p_5"}), - FNOperator({L"p_7", L"p_8"}, {L"p_10", L"p_11", L"p_12"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 12); - } + // two (pure qp) 2-body operators + { + auto opseq = + FNOperatorSeq({FNOperator({L"i_1", L"i_2"}, {L"a_1", L"a_2"}), + FNOperator({L"a_3", L"a_4"}, {L"i_3", L"i_4"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + auto result_latex = to_latex(result); + // std::wcout << "<" << to_latex(opseq) << "> = " << result_latex << + // std::endl; + REQUIRE(result->is()); + REQUIRE(result->size() == 4); + REQUIRE( + result_latex == + L"{ " + L"\\bigl({{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}" + L"{s^{{a_4}}_{{a_1}}}} - " + L"{{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{s^{{a_" + L"3}}_{{a_1}}}} - " + L"{{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}{s^{{a_" + L"4}}_{{a_1}}}} + " + L"{{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{s^{{a_" + L"3}}_{{a_1}}}}\\bigr) }"); + + // in spin-free result first and fourth terms are multiplied by 4, second + // and third terms multiplied by 2 + auto raii_tmp = switch_to_spinfree_context(); + auto result_sf = wick.compute(); + auto result_sf_latex = to_latex(result_sf); + // std::wcout << "<" << to_latex(opseq) << "> = " << + // to_latex(result_sf) + // << std::endl; + REQUIRE(result_sf->is()); + REQUIRE(result_sf->size() == 4); + REQUIRE( + result_sf_latex == + L"{ " + L"\\bigl({{{4}}{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_3}}_{{" + L"a_2}}}{s^{{a_4}}_{{a_1}}}} - " + L"{{{2}}{s^{{i_4}}_{{i_1}}}{s^{{i_3}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{" + L"s^{{a_3}}_{{a_1}}}} - " + L"{{{2}}{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_3}}_{{a_2}}}{" + L"s^{{a_4}}_{{a_1}}}} + " + L"{{{4}}{s^{{i_3}}_{{i_1}}}{s^{{i_4}}_{{i_2}}}{s^{{a_4}}_{{a_2}}}{" + L"s^{{a_3}}_{{a_1}}}}\\bigr) }"); + } + // two (pure qp) 3-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"i_1", L"i_2", L"i_3"}, {L"a_1", L"a_2", L"a_3"}), + FNOperator({L"a_4", L"a_5", L"a_6"}, {L"i_4", L"i_5", L"i_6"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 36); + } - // more N-nonconserving operators - { - auto input = - ex(WstrList{L"i_1"}, WstrList{L"a_3", L"a_4"}) * - (ex(rational{1, 4}) * - ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::antisymm) * - ex(WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"})) * - ex(WstrList{L"a_2"}, WstrList{}); - auto wick = FWickTheorem{input}; - wick.set_external_indices(IndexList{L"i_1", L"a_3", L"a_4", L"a_2"}) - .use_topology(true); - ExprPtr result; - REQUIRE_NOTHROW(result = wick.compute()); - // std::wcout << "result = " << to_latex(result) << std::endl; - REQUIRE(to_latex(result) == - L"{{{-1}}{\\bar{g}^{{i_1}{a_2}}_{{a_3}{a_4}}}}"); - } + // one general 1-body operator + one general 2-body operator, partial + // contraction: Eq. 9 of DOI 10.1063/1.474405 + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1"}, {L"p_2"}), + FNOperator({L"p_3", L"p_4"}, {L"p_5", L"p_6"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result = wick.full_contractions(false).compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 9); + } - // odd number of ops -> full contraction is 0 - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1", L"p_2"}, {L"p_4", L"p_5"}), - FNOperator({L"p_7", L"p_8"}, {L"p_10", L"p_11", L"p_12"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 0); - } + // two general 2-body operators + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({L"p_5", L"p_6"}, {L"p_7", L"p_8"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 4); + } + // two general 2-body operators, partial contractions: Eqs. 22 of + // DOI 10.1063/1.474405 + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({L"p_5", L"p_6"}, {L"p_7", L"p_8"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.full_contractions(false).compute()); + auto result = wick.full_contractions(false).compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 49); // the MK paper only gives 47 terms, + // misses the 2 double-hole contractions + } + // one general 2-body operator and one 2-body excitation operator + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({L"a_3", L"a_4"}, {L"i_3", L"i_4"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + std::wcout << "<" << to_latex(opseq) << "> = " << to_latex(result) + << std::endl; + REQUIRE(result->is()); + REQUIRE(result->size() == 4); + REQUIRE( + to_latex(result) == + L"{ " + L"\\bigl({{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_3}}_{{p_4}}}" + L"{s^{{a_4}}_{{p_3}}}} - " + L"{{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_4}}_{{p_4}}}{s^{{a_" + L"3}}_{{p_3}}}} - " + L"{{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_3}}_{{p_4}}}{s^{{a_" + L"4}}_{{p_3}}}} + " + L"{{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{s^{{a_" + L"3}}_{{p_3}}}}\\bigr) }"); + + // in spin-free result first and fourth terms are multiplied by 4, second + // and third terms multiplied by 2 + auto raii_tmp = switch_to_spinfree_context(); + auto result_sf = wick.compute(); + auto result_sf_latex = to_latex(result_sf); + // std::wcout << "<" << to_latex(opseq) << "> = " << + // to_latex(result_sf) + // << std::endl; + REQUIRE(result_sf->is()); + REQUIRE(result_sf->size() == 4); + REQUIRE( + to_latex(result_sf) == + L"{ " + L"\\bigl({{{4}}{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_3}}_{{" + L"p_4}}}{s^{{a_4}}_{{p_3}}}} - " + L"{{{2}}{s^{{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_3}}}{s^{{a_4}}_{{p_4}}}{" + L"s^{{a_3}}_{{p_3}}}} - " + L"{{{2}}{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_3}}_{{p_4}}}{" + L"s^{{a_4}}_{{p_3}}}} + " + L"{{{4}}{s^{{p_1}}_{{i_3}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{" + L"s^{{a_3}}_{{p_3}}}}\\bigr) }"); + } - // 4-body ^ 4-body - SEQUANT_PROFILE_SINGLE( - "wick(4^4)", - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, - {L"p_5", L"p_6", L"p_7", L"p_8"}), - FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, - {L"p_25", L"p_26", L"p_27", L"p_28"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 576); - }) - - // three general 1-body operators - { - auto opseq = FNOperatorSeq({FNOperator({L"p_1"}, {L"p_2"}), - FNOperator({L"p_3"}, {L"p_4"}), - FNOperator({L"p_5"}, {L"p_6"})}); - auto wick = FWickTheorem{opseq}; - REQUIRE_NOTHROW(wick.compute()); - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 2); - } + // two general 3-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_4", L"p_5", L"p_6"}), + FNOperator({L"p_7", L"p_8", L"p_9"}, {L"p_10", L"p_11", L"p_12"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 36); + } - // 4 general 1-body operators - { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"}), - FNOperator({L"p_5"}, {L"p_6"}), FNOperator({L"p_7"}, {L"p_8"})}); - auto ext_indices = make_indices>(WstrList{ - L"p_1", L"p_2", L"p_3", L"p_4", L"p_5", L"p_6", L"p_7", L"p_8"}); - auto wick1 = FWickTheorem{opseq}; - auto result1 = wick1.set_external_indices(ext_indices).compute(); - REQUIRE(result1->is()); - REQUIRE(result1->size() == 9); - auto wick2 = FWickTheorem{opseq}; - auto result2 = wick2.set_external_indices(ext_indices) - .set_nop_connections({{1, 2}, {1, 3}}) - .compute(); - REQUIRE(result2->is()); - REQUIRE(result2->size() == 2); - } + // two N-nonconserving operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_4", L"p_5"}), + FNOperator({L"p_7", L"p_8"}, {L"p_10", L"p_11", L"p_12"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 12); + } - // 4-body ^ 2-body ^ 2-body - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, - {L"p_5", L"p_6", L"p_7", L"p_8"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 576); - } + // more N-nonconserving operators + { + auto input = + ex(WstrList{L"i_1"}, WstrList{L"a_3", L"a_4"}) * + (ex(rational{1, 4}) * + ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, + Symmetry::antisymm) * + ex(WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"})) * + ex(WstrList{L"a_2"}, WstrList{}); + auto wick = FWickTheorem{input}; + wick.set_external_indices(IndexList{L"i_1", L"a_3", L"a_4", L"a_2"}) + .use_topology(true); + ExprPtr result; + REQUIRE_NOTHROW(result = wick.compute()); + // std::wcout << "result = " << to_latex(result) << std::endl; + REQUIRE(to_latex(result) == + L"{{{-1}}{\\bar{g}^{{i_1}{a_2}}_{{a_3}{a_4}}}}"); + } - // 2-body ^ 2-body ^ 2-body - { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_5", L"p_6"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.compute(); - REQUIRE(result->is()); - REQUIRE(result->size() == 80); - } + // odd number of ops -> full contraction is 0 + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1", L"p_2"}, {L"p_4", L"p_5"}), + FNOperator({L"p_7", L"p_8"}, {L"p_10", L"p_11", L"p_12"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 0); + } + + // 4-body ^ 4-body + SEQUANT_PROFILE_SINGLE( + "wick(4^4)", + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, + {L"p_5", L"p_6", L"p_7", L"p_8"}), + FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, + {L"p_25", L"p_26", L"p_27", L"p_28"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 576); + }) + + // three general 1-body operators + { + auto opseq = FNOperatorSeq({FNOperator({L"p_1"}, {L"p_2"}), + FNOperator({L"p_3"}, {L"p_4"}), + FNOperator({L"p_5"}, {L"p_6"})}); + auto wick = FWickTheorem{opseq}; + REQUIRE_NOTHROW(wick.compute()); + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 2); + } + + // 4 general 1-body operators + { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1"}, {L"p_2"}), FNOperator({L"p_3"}, {L"p_4"}), + FNOperator({L"p_5"}, {L"p_6"}), FNOperator({L"p_7"}, {L"p_8"})}); + auto ext_indices = make_indices>(WstrList{ + L"p_1", L"p_2", L"p_3", L"p_4", L"p_5", L"p_6", L"p_7", L"p_8"}); + auto wick1 = FWickTheorem{opseq}; + auto result1 = wick1.set_external_indices(ext_indices).compute(); + REQUIRE(result1->is()); + REQUIRE(result1->size() == 9); + auto wick2 = FWickTheorem{opseq}; + auto result2 = wick2.set_external_indices(ext_indices) + .set_nop_connections({{1, 2}, {1, 3}}) + .compute(); + REQUIRE(result2->is()); + REQUIRE(result2->size() == 2); + } + + // 4-body ^ 2-body ^ 2-body + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, + {L"p_5", L"p_6", L"p_7", L"p_8"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 576); + } - // 2-body ^ 2-body ^ 2-body ^ 2-body - SEQUANT_PROFILE_SINGLE("wick(2^2^2^2)", { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_5", L"p_6"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), - FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 4752); - }) + // 2-body ^ 2-body ^ 2-body + { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_5", L"p_6"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.compute(); + REQUIRE(result->is()); + REQUIRE(result->size() == 80); + } + + // 2-body ^ 2-body ^ 2-body ^ 2-body + SEQUANT_PROFILE_SINGLE("wick(2^2^2^2)", { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_5", L"p_6"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), + FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 4752); + }) #ifndef SEQUANT_SKIP_LONG_TESTS - // 4-body ^ 2-body ^ 2-body ^ 2-body - SEQUANT_PROFILE_SINGLE("wick(4^2^2^2)", { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, - {L"p_5", L"p_6", L"p_7", L"p_8"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), - FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.use_topology(true).compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 2088); - }) - - // 3-body ^ 2-body ^ 2-body ^ 3-body - SEQUANT_PROFILE_SINGLE("wick(3^2^2^3)", { - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_5", L"p_6", L"p_7"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), - FNOperator({L"p_17", L"p_18", L"p_19"}, {L"p_20", L"p_21", L"p_22"}, - V)}); - auto wick = FWickTheorem{opseq}; - auto result = wick.use_topology(true).compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 694); - }) - - // 4-body ^ 2-body ^ 4-body - SEQUANT_PROFILE_SINGLE("wick(4^2^4)", { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, - {L"p_5", L"p_6", L"p_7", L"p_8"}), - FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), - FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, - {L"p_25", L"p_26", L"p_27", L"p_28"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.use_topology(true).compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 28); - }) - - // 4-body ^ 4-body ^ 4-body - SEQUANT_PROFILE_SINGLE("wick(4^4^4)", { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, - {L"p_5", L"p_6", L"p_7", L"p_8"}), - FNOperator({L"p_11", L"p_12", L"p_13", L"p_14"}, - {L"p_15", L"p_16", L"p_17", L"p_18"}), - FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, - {L"p_25", L"p_26", L"p_27", L"p_28"})}); - auto wick = FWickTheorem{opseq}; - auto result = wick.use_topology(true).compute(true); - REQUIRE(result->is()); - REQUIRE(result->as().value() == 70); - }) + // 4-body ^ 2-body ^ 2-body ^ 2-body + SEQUANT_PROFILE_SINGLE("wick(4^2^2^2)", { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, + {L"p_5", L"p_6", L"p_7", L"p_8"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), + FNOperator({L"p_17", L"p_18"}, {L"p_19", L"p_20"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.use_topology(true).compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 2088); + }) + + // 3-body ^ 2-body ^ 2-body ^ 3-body + SEQUANT_PROFILE_SINGLE("wick(3^2^2^3)", { + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1", L"p_2", L"p_3"}, {L"p_5", L"p_6", L"p_7"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_13", L"p_14"}, {L"p_15", L"p_16"}), + FNOperator({L"p_17", L"p_18", L"p_19"}, {L"p_20", L"p_21", L"p_22"}, + V)}); + auto wick = FWickTheorem{opseq}; + auto result = wick.use_topology(true).compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 694); + }) + + // 4-body ^ 2-body ^ 4-body + SEQUANT_PROFILE_SINGLE("wick(4^2^4)", { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, + {L"p_5", L"p_6", L"p_7", L"p_8"}), + FNOperator({L"p_9", L"p_10"}, {L"p_11", L"p_12"}), + FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, + {L"p_25", L"p_26", L"p_27", L"p_28"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.use_topology(true).compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 28); + }) + + // 4-body ^ 4-body ^ 4-body + SEQUANT_PROFILE_SINGLE("wick(4^4^4)", { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2", L"p_3", L"p_4"}, + {L"p_5", L"p_6", L"p_7", L"p_8"}), + FNOperator({L"p_11", L"p_12", L"p_13", L"p_14"}, + {L"p_15", L"p_16", L"p_17", L"p_18"}), + FNOperator({L"p_21", L"p_22", L"p_23", L"p_24"}, + {L"p_25", L"p_26", L"p_27", L"p_28"})}); + auto wick = FWickTheorem{opseq}; + auto result = wick.use_topology(true).compute(true); + REQUIRE(result->is()); + REQUIRE(result->as().value() == 70); + }) #endif #if 0 @@ -778,72 +795,23 @@ SECTION("fermi vacuum") { auto result = wick.use_topology(true).compute(true); } #endif -} // SECTION("fermi vacuum") - -SECTION("Expression Reduction") { - constexpr Vacuum V = Vacuum::SingleProduct; - // default vacuum is already spin-orbital Fermi vacuum - - auto switch_to_spinfree_context = detail::NoDiscard([&]() { - auto context_sf = get_default_context(); - context_sf.set(SPBasis::spinfree); - return set_scoped_default_context(context_sf); - }); - - // 2-body ^ 2-body - SEQUANT_PROFILE_SINGLE("wick(H2*T2)", { - auto opseq = - FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({L"a_4", L"a_5"}, {L"i_4", L"i_5"})}); - auto wick = FWickTheorem{opseq}; - auto wick_result = wick.compute(); - REQUIRE(wick_result->is()); - REQUIRE(wick_result->size() == 4); - - // multiply tensor factors and expand - auto wick_result_2 = - ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::antisymm) * - ex(L"t", WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}, - Symmetry::antisymm) * - wick_result; - expand(wick_result_2); - REQUIRE(to_latex(wick_result_2) == - L"{ " - L"\\bigl({{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_" - L"5}}_{{a_4}{a_" - L"5}}}{s^{{p_1}}_{{i_5}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{" - L"s^{{a_5}}_{{p_3}}}} - {" - L"{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{a_" - L"4}{a_5}}}{s^{{" - L"p_1}}_{{i_5}}}{s^{{p_2}}_{{i_4}}}{s^{{a_5}}_{{p_4}}}{s^{{a_4}}_" - L"{{p_3}}}} - {" - L"{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{a_" - L"4}{a_5}}}{s^{{" - L"p_1}}_{{i_4}}}{s^{{p_2}}_{{i_5}}}{s^{{a_4}}_{{p_4}}}{s^{{a_5}}_" - L"{{p_3}}}} + " - L"{{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{" - L"a_4}{a_5}}}{s^{" - L"{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_5}}}{s^{{a_5}}_{{p_4}}}{s^{{a_4}}" - L"_{{p_3}}}}\\bigr) }"); - wick.reduce(wick_result_2); - rapid_simplify(wick_result_2); - TensorCanonicalizer::register_instance( - std::make_shared()); - canonicalize(wick_result_2); - rapid_simplify(wick_result_2); - - std::wcout << L"H2*T2 = " << to_latex(wick_result_2) << std::endl; - std::wcout << L"H2*T2 = " << to_wolfram(wick_result_2) << std::endl; - REQUIRE(to_latex(wick_result_2) == - L"{{{4}}" - L"{\\bar{g}^{{a_1}{a_2}}_{{i_1}{i_2}}}{\\bar{t}^{{i_1}{i_2}}_{{a_" - L"1}{a_2}}}}"); - - // spin-free case will produce 2 terms - { - auto raii_tmp = switch_to_spinfree_context(); + } // SECTION("fermi vacuum") + + SECTION("Expression Reduction") { + constexpr Vacuum V = Vacuum::SingleProduct; + // default vacuum is already spin-orbital Fermi vacuum + + auto switch_to_spinfree_context = detail::NoDiscard([&]() { + auto context_sf = get_default_context(); + context_sf.set(SPBasis::spinfree); + return set_scoped_default_context(context_sf); + }); + // 2-body ^ 2-body + SEQUANT_PROFILE_SINGLE("wick(H2*T2)", { + auto opseq = + FNOperatorSeq({FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({L"a_4", L"a_5"}, {L"i_4", L"i_5"})}); auto wick = FWickTheorem{opseq}; auto wick_result = wick.compute(); REQUIRE(wick_result->is()); @@ -852,263 +820,305 @@ SECTION("Expression Reduction") { // multiply tensor factors and expand auto wick_result_2 = ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::nonsymm) * + Symmetry::antisymm) * ex(L"t", WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}, - Symmetry::nonsymm) * + Symmetry::antisymm) * wick_result; expand(wick_result_2); - REQUIRE(wick_result_2->size() == 4); // still 4 terms - + REQUIRE(to_latex(wick_result_2) == + L"{ " + L"\\bigl({{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_" + L"5}}_{{a_4}{a_" + L"5}}}{s^{{p_1}}_{{i_5}}}{s^{{p_2}}_{{i_4}}}{s^{{a_4}}_{{p_4}}}{" + L"s^{{a_5}}_{{p_3}}}} - {" + L"{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{a_" + L"4}{a_5}}}{s^{{" + L"p_1}}_{{i_5}}}{s^{{p_2}}_{{i_4}}}{s^{{a_5}}_{{p_4}}}{s^{{a_4}}_" + L"{{p_3}}}} - {" + L"{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{a_" + L"4}{a_5}}}{s^{{" + L"p_1}}_{{i_4}}}{s^{{p_2}}_{{i_5}}}{s^{{a_4}}_{{p_4}}}{s^{{a_5}}_" + L"{{p_3}}}} + " + L"{{\\bar{g}^{{p_3}{p_4}}_{{p_1}{p_2}}}{\\bar{t}^{{i_4}{i_5}}_{{" + L"a_4}{a_5}}}{s^{" + L"{p_1}}_{{i_4}}}{s^{{p_2}}_{{i_5}}}{s^{{a_5}}_{{p_4}}}{s^{{a_4}}" + L"_{{p_3}}}}\\bigr) }"); wick.reduce(wick_result_2); rapid_simplify(wick_result_2); TensorCanonicalizer::register_instance( std::make_shared()); canonicalize(wick_result_2); rapid_simplify(wick_result_2); - REQUIRE(wick_result_2->size() == 2); // now 2 terms - std::wcout << L"spinfree H2*T2 = " << to_latex(wick_result_2) - << std::endl; - if constexpr (hash_version() == hash::Impl::BoostPre181) { - REQUIRE( - to_latex(wick_result_2) == - L"{ \\bigl( - " - L"{{{4}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_2}}" - L"}} + " - L"{{{8}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{a_1}{a_2}}" - L"}}\\bigr) }"); - } else { - REQUIRE(to_latex(wick_result_2) == - L"{ " - L"\\bigl({{{8}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{" - L"a_1}{a_2}}}} - " - L"{{{4}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_" - L"2}}}}\\bigr) }"); - } - } - }); - - // 2-body ^ 1-body ^ 1-body, with/without using topology - SEQUANT_PROFILE_SINGLE("wick(H2*T1*T1)", { - for (auto&& use_nop_partitions : {false}) { - for (auto&& use_op_partitions : {true, false}) { - std::wostringstream oss; - oss << "use_op_partitions=" << use_op_partitions << "}: H2*T1*T1 = "; - - auto opseq = FNOperatorSeq( - {FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({L"a_4"}, {L"i_4"}), FNOperator({L"a_5"}, {L"i_5"})}); - auto wick = FWickTheorem{opseq}; - wick.use_topology(use_nop_partitions || use_op_partitions); - // if (use_nop_partitions) wick.set_nop_partitions({{1, 2}}); - if (use_op_partitions) wick.set_op_partitions({{0, 1}, {2, 3}}); - auto wick_result = wick.compute(); - // print(oss.str() + L" (nopseq only) ", wick_result); - if (use_op_partitions) { - REQUIRE(wick_result->is()); - REQUIRE(wick_result->size() == 4 /* factors */); - } else { - REQUIRE(wick_result->is()); - REQUIRE(wick_result->size() == 4 /* summands */); - } + std::wcout << L"H2*T2 = " << to_latex(wick_result_2) << std::endl; + std::wcout << L"H2*T2 = " << to_wolfram(wick_result_2) << std::endl; + REQUIRE(to_latex(wick_result_2) == + L"{{{4}}" + L"{\\bar{g}^{{a_1}{a_2}}_{{i_1}{i_2}}}{\\bar{t}^{{i_1}{i_2}}_{{a_" + L"1}{a_2}}}}"); - // multiply tensor factors and expand - auto wick_result_2 = - ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::antisymm) * - ex(L"t", WstrList{L"a_4"}, WstrList{L"i_4"}, - Symmetry::antisymm) * - ex(L"t", WstrList{L"a_5"}, WstrList{L"i_5"}, - Symmetry::antisymm) * - wick_result; - expand(wick_result_2); - wick.reduce(wick_result_2); - rapid_simplify(wick_result_2); - TensorCanonicalizer::register_instance( - std::make_shared(std::vector{})); - canonicalize(wick_result_2); - rapid_simplify(wick_result_2); + // spin-free case will produce 2 terms + { + auto raii_tmp = switch_to_spinfree_context(); - // print(oss.str(), wick_result_2); - REQUIRE(wick_result_2->size() == 3 /* factors */); - REQUIRE(to_latex(wick_result_2) == - L"{{{4}}" - L"{\\bar{g}^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}}_{{a_1}}}{" - L"t^{{i_" - L"2}}_{{a_" - L"2}}}}"); - } // use_op_partitions - } // use_nop_partitions - }); - - // 2=body ^ 1-body ^ 2-body with dependent (PNO) indices - SEQUANT_PROFILE_SINGLE("wick(P2*H1*T2)", { - auto opseq = FNOperatorSeq({FNOperator(IndexList{L"i_1", L"i_2"}, - {Index(L"a_1", {L"i_1", L"i_2"}), - Index(L"a_2", {L"i_1", L"i_2"})}, - V), - FNOperator({L"p_1"}, {L"p_2"}), - FNOperator({Index(L"a_3", {L"i_3", L"i_4"}), - Index(L"a_4", {L"i_3", L"i_4"})}, - IndexList{L"i_3", L"i_4"})}); - auto wick = FWickTheorem{opseq}; - auto wick_result = wick.compute(); - REQUIRE(wick_result->is()); - REQUIRE(wick_result->size() == 16); - - // multiply tensor factors and expand - auto wick_result_2 = - ex( - L"A", IndexList{L"i_1", L"i_2"}, - IndexList{{L"a_1", {L"i_1", L"i_2"}}, {L"a_2", {L"i_1", L"i_2"}}}, - Symmetry::antisymm) * - ex(L"f", WstrList{L"p_1"}, WstrList{L"p_2"}, - Symmetry::antisymm) * - ex( - L"t", - IndexList{{L"a_3", {L"i_3", L"i_4"}}, {L"a_4", {L"i_3", L"i_4"}}}, - IndexList{L"i_3", L"i_4"}, Symmetry::antisymm) * - wick_result; - expand(wick_result_2); - wick.reduce(wick_result_2); - rapid_simplify(wick_result_2); - TensorCanonicalizer::register_instance( - std::make_shared()); - canonicalize(wick_result_2); - rapid_simplify(wick_result_2); - - // std::wcout << L"P2*H1*T2(PNO) = " << to_latex_align(wick_result_2) - // << std::endl; - // it appears that the two terms are swapped when using gcc 8 on linux - // TODO investigate why sum canonicalization seems to produce - // platform-dependent results. - // REQUIRE(to_latex(wick_result_2) == - // L"{ \\bigl( - {{{8}}" - // L"{A^{{a_1^{{i_1}{i_2}}}{a_2^{{i_1}{i_2}}}}_{{i_1}{i_2}}}{f^{{a_" - // L"3^{{i_1}{i_2}}}}_{{a_1^{{i_1}{i_2}}}}}{t^{{i_1}{i_2}}_{{a_2^{{" - // L"i_1}{i_2}}}{a_3^{{i_1}{i_2}}}}}} + {{{8}}" - // L"{A^{{a_1^{{i_1}{i_2}}}{a_2^{{i_1}{i_2}}}}_{{i_1}{i_2}}}{f^{{i_" - // L"1}}_{{i_3}}}{t^{{i_2}{i_3}}_{{a_3^{{i_2}{i_3}}}{a_4^{{i_2}{i_3}" - // L"}}}}{s^{{a_3^{{i_2}{i_3}}}}_{{a_1^{{i_1}{i_2}}}}}{s^{{a_4^{{i_" - // L"2}{i_3}}}}_{{a_2^{{i_1}{i_2}}}}}}\\bigr) }"); - }); - - // 2=body ^ 2-body ^ 2-body ^ 2-body with dependent (PNO) indices - SEQUANT_PROFILE_SINGLE("wick(P2*H2*T2*T2)", { - for (auto&& use_nop_partitions : {false}) { - for (auto&& use_op_partitions : {true, false}) { - std::wostringstream oss; - oss << "use_{nop,op}_partitions={" << use_nop_partitions << "," - << use_op_partitions << "}: P2*H2*T2*T2(PNO) = "; - - auto opseq = - FNOperatorSeq({FNOperator(IndexList{L"i_1", L"i_2"}, - {Index(L"a_1", {L"i_1", L"i_2"}), - Index(L"a_2", {L"i_1", L"i_2"})}, - V), - FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), - FNOperator({Index(L"a_3", {L"i_3", L"i_4"}), - Index(L"a_4", {L"i_3", L"i_4"})}, - IndexList{L"i_3", L"i_4"}), - FNOperator({Index(L"a_5", {L"i_5", L"i_6"}), - Index(L"a_6", {L"i_5", L"i_6"})}, - IndexList{L"i_5", L"i_6"})}); auto wick = FWickTheorem{opseq}; - wick.set_nop_connections({{1, 2}, {1, 3}}).use_topology(true); - - if (use_nop_partitions) wick.set_nop_partitions({{2, 3}}); - if (use_op_partitions) - wick.set_op_partitions({{0, 1}, - {2, 3}, - {4, 5}, - {6, 7}, - {8, 9}, - {10, 11}, - {12, 13}, - {14, 15}}); auto wick_result = wick.compute(); REQUIRE(wick_result->is()); - if (use_op_partitions) { - REQUIRE(wick_result->size() == 7); - } else { - REQUIRE(wick_result->size() == 544); - } + REQUIRE(wick_result->size() == 4); // multiply tensor factors and expand auto wick_result_2 = - ex(rational{1, 256}) * - ex(L"A", IndexList{L"i_1", L"i_2"}, - IndexList{{L"a_1", {L"i_1", L"i_2"}}, - {L"a_2", {L"i_1", L"i_2"}}}, - Symmetry::antisymm) * ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::antisymm) * - ex(L"t", - IndexList{{L"a_3", {L"i_3", L"i_4"}}, - {L"a_4", {L"i_3", L"i_4"}}}, - IndexList{L"i_3", L"i_4"}, Symmetry::antisymm) * - ex(L"t", - IndexList{{L"a_5", {L"i_5", L"i_6"}}, - {L"a_6", {L"i_5", L"i_6"}}}, - IndexList{L"i_5", L"i_6"}, Symmetry::antisymm) * + Symmetry::nonsymm) * + ex(L"t", WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}, + Symmetry::nonsymm) * wick_result; expand(wick_result_2); + REQUIRE(wick_result_2->size() == 4); // still 4 terms + wick.reduce(wick_result_2); rapid_simplify(wick_result_2); TensorCanonicalizer::register_instance( std::make_shared()); canonicalize(wick_result_2); - canonicalize(wick_result_2); - canonicalize(wick_result_2); rapid_simplify(wick_result_2); + REQUIRE(wick_result_2->size() == 2); // now 2 terms + + std::wcout << L"spinfree H2*T2 = " << to_latex(wick_result_2) + << std::endl; + REQUIRE(to_latex(wick_result_2) == + L"{ " + L"\\bigl({{{8}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}{i_2}}_{{" + L"a_1}{a_2}}}} - " + L"{{{4}}{g^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_2}{i_1}}_{{a_1}{a_" + L"2}}}}\\bigr) }"); + } + }); + + // 2-body ^ 1-body ^ 1-body, with/without using topology + SEQUANT_PROFILE_SINGLE("wick(H2*T1*T1)", { + for (auto&& use_nop_partitions : {false}) { + for (auto&& use_op_partitions : {true, false}) { + std::wostringstream oss; + oss << "use_op_partitions=" << use_op_partitions << "}: H2*T1*T1 = "; + + auto opseq = FNOperatorSeq( + {FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({L"a_4"}, {L"i_4"}), FNOperator({L"a_5"}, {L"i_5"})}); + auto wick = FWickTheorem{opseq}; + wick.use_topology(use_nop_partitions || use_op_partitions); + // if (use_nop_partitions) wick.set_nop_partitions({{1, 2}}); + if (use_op_partitions) wick.set_op_partitions({{0, 1}, {2, 3}}); + auto wick_result = wick.compute(); + // print(oss.str() + L" (nopseq only) ", wick_result); + if (use_op_partitions) { + REQUIRE(wick_result->is()); + REQUIRE(wick_result->size() == 4 /* factors */); + } else { + REQUIRE(wick_result->is()); + REQUIRE(wick_result->size() == 4 /* summands */); + } + + // multiply tensor factors and expand + auto wick_result_2 = + ex(L"g", WstrList{L"p_1", L"p_2"}, + WstrList{L"p_3", L"p_4"}, Symmetry::antisymm) * + ex(L"t", WstrList{L"a_4"}, WstrList{L"i_4"}, + Symmetry::antisymm) * + ex(L"t", WstrList{L"a_5"}, WstrList{L"i_5"}, + Symmetry::antisymm) * + wick_result; + expand(wick_result_2); + wick.reduce(wick_result_2); + rapid_simplify(wick_result_2); + TensorCanonicalizer::register_instance( + std::make_shared( + std::vector{})); + canonicalize(wick_result_2); + rapid_simplify(wick_result_2); + + // print(oss.str(), wick_result_2); + REQUIRE(wick_result_2->size() == 3 /* factors */); + REQUIRE(to_latex(wick_result_2) == + L"{{{4}}" + L"{\\bar{g}^{{a_1}{a_2}}_{{i_1}{i_2}}}{t^{{i_1}}_{{a_1}}}{" + L"t^{{i_" + L"2}}_{{a_" + L"2}}}}"); + } // use_op_partitions + } // use_nop_partitions + }); - // std::wcout << oss.str() << to_latex_align(wick_result_2, 20) - // << std::endl; - REQUIRE(wick_result_2->is()); - REQUIRE(wick_result_2->size() == 4); - } // use_op_partitions - } // use_nop_partitions - }); + // 2=body ^ 1-body ^ 2-body with dependent (PNO) indices + SEQUANT_PROFILE_SINGLE("wick(P2*H1*T2)", { + auto opseq = FNOperatorSeq({FNOperator(IndexList{L"i_1", L"i_2"}, + {Index(L"a_1", {L"i_1", L"i_2"}), + Index(L"a_2", {L"i_1", L"i_2"})}, + V), + FNOperator({L"p_1"}, {L"p_2"}), + FNOperator({Index(L"a_3", {L"i_3", L"i_4"}), + Index(L"a_4", {L"i_3", L"i_4"})}, + IndexList{L"i_3", L"i_4"})}); + auto wick = FWickTheorem{opseq}; + auto wick_result = wick.compute(); + REQUIRE(wick_result->is()); + REQUIRE(wick_result->size() == 16); + + // multiply tensor factors and expand + auto wick_result_2 = + ex( + L"A", IndexList{L"i_1", L"i_2"}, + IndexList{{L"a_1", {L"i_1", L"i_2"}}, {L"a_2", {L"i_1", L"i_2"}}}, + Symmetry::antisymm) * + ex(L"f", WstrList{L"p_1"}, WstrList{L"p_2"}, + Symmetry::antisymm) * + ex( + L"t", + IndexList{{L"a_3", {L"i_3", L"i_4"}}, {L"a_4", {L"i_3", L"i_4"}}}, + IndexList{L"i_3", L"i_4"}, Symmetry::antisymm) * + wick_result; + expand(wick_result_2); + wick.reduce(wick_result_2); + rapid_simplify(wick_result_2); + TensorCanonicalizer::register_instance( + std::make_shared()); + canonicalize(wick_result_2); + rapid_simplify(wick_result_2); + + // std::wcout << L"P2*H1*T2(PNO) = " << to_latex_align(wick_result_2) + // << std::endl; + // it appears that the two terms are swapped when using gcc 8 on linux + // TODO investigate why sum canonicalization seems to produce + // platform-dependent results. + // REQUIRE(to_latex(wick_result_2) == + // L"{ \\bigl( - {{{8}}" + // L"{A^{{a_1^{{i_1}{i_2}}}{a_2^{{i_1}{i_2}}}}_{{i_1}{i_2}}}{f^{{a_" + // L"3^{{i_1}{i_2}}}}_{{a_1^{{i_1}{i_2}}}}}{t^{{i_1}{i_2}}_{{a_2^{{" + // L"i_1}{i_2}}}{a_3^{{i_1}{i_2}}}}}} + {{{8}}" + // L"{A^{{a_1^{{i_1}{i_2}}}{a_2^{{i_1}{i_2}}}}_{{i_1}{i_2}}}{f^{{i_" + // L"1}}_{{i_3}}}{t^{{i_2}{i_3}}_{{a_3^{{i_2}{i_3}}}{a_4^{{i_2}{i_3}" + // L"}}}}{s^{{a_3^{{i_2}{i_3}}}}_{{a_1^{{i_1}{i_2}}}}}{s^{{a_4^{{i_" + // L"2}{i_3}}}}_{{a_2^{{i_1}{i_2}}}}}}\\bigr) }"); + }); + + // 2=body ^ 2-body ^ 2-body ^ 2-body with dependent (PNO) indices + SEQUANT_PROFILE_SINGLE("wick(P2*H2*T2*T2)", { + for (auto&& use_nop_partitions : {false}) { + for (auto&& use_op_partitions : {true, false}) { + std::wostringstream oss; + oss << "use_{nop,op}_partitions={" << use_nop_partitions << "," + << use_op_partitions << "}: P2*H2*T2*T2(PNO) = "; + + auto opseq = + FNOperatorSeq({FNOperator(IndexList{L"i_1", L"i_2"}, + {Index(L"a_1", {L"i_1", L"i_2"}), + Index(L"a_2", {L"i_1", L"i_2"})}, + V), + FNOperator({L"p_1", L"p_2"}, {L"p_3", L"p_4"}), + FNOperator({Index(L"a_3", {L"i_3", L"i_4"}), + Index(L"a_4", {L"i_3", L"i_4"})}, + IndexList{L"i_3", L"i_4"}), + FNOperator({Index(L"a_5", {L"i_5", L"i_6"}), + Index(L"a_6", {L"i_5", L"i_6"})}, + IndexList{L"i_5", L"i_6"})}); + auto wick = FWickTheorem{opseq}; + wick.set_nop_connections({{1, 2}, {1, 3}}).use_topology(true); + + if (use_nop_partitions) wick.set_nop_partitions({{2, 3}}); + if (use_op_partitions) + wick.set_op_partitions({{0, 1}, + {2, 3}, + {4, 5}, + {6, 7}, + {8, 9}, + {10, 11}, + {12, 13}, + {14, 15}}); + auto wick_result = wick.compute(); + REQUIRE(wick_result->is()); + if (use_op_partitions) { + REQUIRE(wick_result->size() == 7); + } else { + REQUIRE(wick_result->size() == 544); + } + + // multiply tensor factors and expand + auto wick_result_2 = + ex(rational{1, 256}) * + ex(L"A", IndexList{L"i_1", L"i_2"}, + IndexList{{L"a_1", {L"i_1", L"i_2"}}, + {L"a_2", {L"i_1", L"i_2"}}}, + Symmetry::antisymm) * + ex(L"g", WstrList{L"p_1", L"p_2"}, + WstrList{L"p_3", L"p_4"}, Symmetry::antisymm) * + ex(L"t", + IndexList{{L"a_3", {L"i_3", L"i_4"}}, + {L"a_4", {L"i_3", L"i_4"}}}, + IndexList{L"i_3", L"i_4"}, Symmetry::antisymm) * + ex(L"t", + IndexList{{L"a_5", {L"i_5", L"i_6"}}, + {L"a_6", {L"i_5", L"i_6"}}}, + IndexList{L"i_5", L"i_6"}, Symmetry::antisymm) * + wick_result; + expand(wick_result_2); + wick.reduce(wick_result_2); + rapid_simplify(wick_result_2); + TensorCanonicalizer::register_instance( + std::make_shared()); + canonicalize(wick_result_2); + canonicalize(wick_result_2); + canonicalize(wick_result_2); + rapid_simplify(wick_result_2); + + // std::wcout << oss.str() << to_latex_align(wick_result_2, 20) + // << std::endl; + REQUIRE(wick_result_2->is()); + REQUIRE(wick_result_2->size() == 4); + } // use_op_partitions + } // use_nop_partitions + }); #if 1 - // 3-body ^ 2-body ^ 2-body ^ 3-body - SEQUANT_PROFILE_SINGLE("wick(P3*H2*T2*T3)", { - constexpr bool connected_only = true; - constexpr bool topology = true; - auto P3 = ex(rational{1, 36}) * - ex(L"A", WstrList{L"i_1", L"i_2", L"i_3"}, - WstrList{L"a_1", L"a_2", L"a_3"}, Symmetry::antisymm) * - ex(WstrList{L"i_1", L"i_2", L"i_3"}, - WstrList{L"a_1", L"a_2", L"a_3"}); - auto H2 = - ex(rational{1, 4}) * - ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, - Symmetry::antisymm) * - ex(WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}); - auto T2 = - ex(rational{1, 4}) * - ex(L"t", WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}, - Symmetry::antisymm) * - ex(WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}); - auto T3 = ex(rational{1, 36}) * - ex(L"t", WstrList{L"a_6", L"a_7", L"a_8"}, - WstrList{L"i_6", L"i_7", L"i_8"}, Symmetry::antisymm) * - ex(WstrList{L"a_6", L"a_7", L"a_8"}, - WstrList{L"i_6", L"i_7", L"i_8"}); - FWickTheorem wick{P3 * H2 * T2 * T3}; - wick.use_topology(topology); - if (connected_only) wick.set_nop_connections({{1, 2}, {1, 3}}); - auto wick_result = wick.compute(); - - std::wcout << "P3*H2*T2*T3 = " << to_latex_align(wick_result, 20) - << std::endl; - REQUIRE(wick_result->is()); - REQUIRE( - wick_result->size() == - (connected_only ? 7 : 9)); // 9 = 2 disconnected + 7 connected terms - }); + // 3-body ^ 2-body ^ 2-body ^ 3-body + SEQUANT_PROFILE_SINGLE("wick(P3*H2*T2*T3)", { + constexpr bool connected_only = true; + constexpr bool topology = true; + auto P3 = + ex(rational{1, 36}) * + ex(L"A", WstrList{L"i_1", L"i_2", L"i_3"}, + WstrList{L"a_1", L"a_2", L"a_3"}, Symmetry::antisymm) * + ex(WstrList{L"i_1", L"i_2", L"i_3"}, + WstrList{L"a_1", L"a_2", L"a_3"}); + auto H2 = + ex(rational{1, 4}) * + ex(L"g", WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}, + Symmetry::antisymm) * + ex(WstrList{L"p_1", L"p_2"}, WstrList{L"p_3", L"p_4"}); + auto T2 = + ex(rational{1, 4}) * + ex(L"t", WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}, + Symmetry::antisymm) * + ex(WstrList{L"a_4", L"a_5"}, WstrList{L"i_4", L"i_5"}); + auto T3 = + ex(rational{1, 36}) * + ex(L"t", WstrList{L"a_6", L"a_7", L"a_8"}, + WstrList{L"i_6", L"i_7", L"i_8"}, Symmetry::antisymm) * + ex(WstrList{L"a_6", L"a_7", L"a_8"}, + WstrList{L"i_6", L"i_7", L"i_8"}); + FWickTheorem wick{P3 * H2 * T2 * T3}; + wick.use_topology(topology); + if (connected_only) wick.set_nop_connections({{1, 2}, {1, 3}}); + auto wick_result = wick.compute(); + + std::wcout << "P3*H2*T2*T3 = " << to_latex_align(wick_result, 20) + << std::endl; + REQUIRE(wick_result->is()); + REQUIRE( + wick_result->size() == + (connected_only ? 7 : 9)); // 9 = 2 disconnected + 7 connected terms + }); #endif -} + } } // TEST_CASE("WickTheorem") #endif