diff --git a/CMakeLists.txt b/CMakeLists.txt index cbea4cef92..adf2d948da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,9 +303,10 @@ set(SeQuant_symb_src SeQuant/core/index_space_registry.cpp SeQuant/core/index_space_registry.hpp SeQuant/core/interval.hpp - SeQuant/core/latex.cpp - SeQuant/core/latex.hpp - SeQuant/core/latex.ipp + SeQuant/core/io/concepts.hpp + SeQuant/core/io/shorthands.hpp + SeQuant/core/io/latex/latex.cpp + SeQuant/core/io/latex/latex.hpp SeQuant/core/logger.hpp SeQuant/core/math.hpp SeQuant/core/meta.hpp @@ -313,10 +314,14 @@ set(SeQuant_symb_src SeQuant/core/op.hpp SeQuant/core/options.cpp SeQuant/core/options.hpp - SeQuant/core/parse.hpp - SeQuant/core/parse/ast.cpp - SeQuant/core/parse/deparse.cpp - SeQuant/core/parse/parse.cpp + SeQuant/core/io/serialization/serialization.cpp + SeQuant/core/io/serialization/serialization.hpp + SeQuant/core/io/serialization/v1/ast.cpp + SeQuant/core/io/serialization/v1/ast.hpp + SeQuant/core/io/serialization/v1/ast_conversions.hpp + SeQuant/core/io/serialization/v1/deserialize.cpp + SeQuant/core/io/serialization/v1/semantic_actions.hpp + SeQuant/core/io/serialization/v1/serialize.cpp SeQuant/core/ranges.hpp SeQuant/core/rational.hpp SeQuant/core/runtime.cpp @@ -364,8 +369,6 @@ set(SeQuant_symb_src SeQuant/core/wick.hpp SeQuant/core/wick.impl.hpp SeQuant/core/wick.cpp - SeQuant/core/wolfram.hpp - SeQuant/core/wstring.hpp ) # MBPT domain sources diff --git a/SeQuant/core/asy_cost.cpp b/SeQuant/core/asy_cost.cpp index bfd6faa997..57a28b807e 100644 --- a/SeQuant/core/asy_cost.cpp +++ b/SeQuant/core/asy_cost.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include @@ -242,9 +242,9 @@ std::wstring AsyCost::to_latex() const { // // stream out in reverse so that more expensive terms appear first auto rev = ranges::views::reverse(cost_); - oss << sequant::to_wstring(ranges::front(rev).to_latex()); + oss << toUtf16(ranges::front(rev).to_latex()); for (auto &&c : ranges::views::tail(rev)) - oss << L" + " << sequant::to_wstring(c.to_latex()); + oss << L" + " << toUtf16(c.to_latex()); } return oss.str(); } diff --git a/SeQuant/core/attr.hpp b/SeQuant/core/attr.hpp index a83e3d4067..dea1c6f88d 100644 --- a/SeQuant/core/attr.hpp +++ b/SeQuant/core/attr.hpp @@ -36,22 +36,6 @@ enum class BraKetSymmetry { Symm, Conjugate, Nonsymm }; /// describes type of single-particle basis enum class SPBasis { Spinor, Spinfree }; -inline std::wstring to_wolfram(const Symmetry& symmetry) { - std::wstring result; - switch (symmetry) { - case Symmetry::Symm: - result = L"indexSymm[1]"; - break; - case Symmetry::Antisymm: - result = L"indexSymm[-1]"; - break; - case Symmetry::Nonsymm: - result = L"indexSymm[0]"; - break; - } - return result; -} - inline std::wstring to_wstring(Symmetry sym) { switch (sym) { case Symmetry::Symm: @@ -70,15 +54,6 @@ enum class BraKetPos { Ket, }; -inline std::wstring to_wolfram(BraKetPos a) { - switch (a) { - case BraKetPos::Bra: - return L"indexType[bra]"; - case BraKetPos::Ket: - return L"indexType[ket]"; - } -} - enum class Statistics { FermiDirac, BoseEinstein, @@ -92,11 +67,6 @@ inline Action adjoint(Action action) { return action == Action::Create ? Action::Annihilate : Action::Create; } -inline std::wstring to_wolfram(Action a) { - using namespace std::literals; - return L"indexType["s + (a == Action::Create ? L"cre" : L"ann") + L"]"; -} - enum class Vacuum { Physical, SingleProduct, MultiProduct }; inline std::wstring to_string(Vacuum V) { diff --git a/SeQuant/core/complex.hpp b/SeQuant/core/complex.hpp index 8b98711308..8929d9800f 100644 --- a/SeQuant/core/complex.hpp +++ b/SeQuant/core/complex.hpp @@ -8,10 +8,9 @@ #include #include -#include +#include #include #include -#include namespace sequant { @@ -41,28 +40,18 @@ struct Complex { constexpr bool is_identity() const { return real() == 1 && imag() == 0; } std::wstring to_latex() const { - using ::sequant::to_latex; std::wstring result = L"{"; - result += to_latex(this->real()); + result += io::latex::to_string(this->real()); if (this->imag() > 0) { - result = - L"\\bigl(" + result + L" + i " + to_latex(this->imag()) + L"\\bigr)"; + result = L"\\bigl(" + result + L" + i " + + io::latex::to_string(this->imag()) + L"\\bigr)"; } else if (this->imag() < 0) - result = - L"\\bigl(" + result + L" - i " + to_latex(-this->imag()) + L"\\bigr)"; + result = L"\\bigl(" + result + L" - i " + + io::latex::to_string(-this->imag()) + L"\\bigr)"; result += L"}"; return result; } - std::wstring to_wolfram() const { - using ::sequant::to_wolfram; - if (this->imag() == 0) - return to_wolfram(this->real()); - else - return std::wstring(L"Complex[") + to_wolfram(this->real()) + L"," + - to_wolfram(this->imag()) + L"]"; - } - std::size_t hash_value() const { auto v = hash::value(this->real()); hash::combine(v, this->imag()); diff --git a/SeQuant/core/eval/eval.hpp b/SeQuant/core/eval/eval.hpp index 5da4fb0c0c..91ff31d6dd 100644 --- a/SeQuant/core/eval/eval.hpp +++ b/SeQuant/core/eval/eval.hpp @@ -8,10 +8,11 @@ #include #include #include +#include #include #include -#include #include +#include #include #include @@ -354,7 +355,7 @@ ResultPtr evaluate(Node const& node, // std::string xpr; if constexpr (trace(EvalTrace)) { - xpr = to_string(deparse(to_expr(node))); + xpr = toUtf8(io::serialization::to_string(to_expr(node))); log::term(log::TermMode::Begin, xpr); } diff --git a/SeQuant/core/eval/eval_expr.cpp b/SeQuant/core/eval/eval_expr.cpp index d8483765f1..f098a7f366 100644 --- a/SeQuant/core/eval/eval_expr.cpp +++ b/SeQuant/core/eval/eval_expr.cpp @@ -7,12 +7,11 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #include @@ -98,9 +97,9 @@ std::string to_label_annotation(const Index& idx) { using namespace ranges::views; using ranges::to; - return sequant::to_string(idx.label()) + + return toUtf8(idx.label()) + (idx.proto_indices() | transform(&Index::label) | - transform([](auto&& str) { return sequant::to_string(str); }) | + transform([](auto&& str) { return toUtf8(str); }) | ranges::views::join | to); } @@ -225,12 +224,12 @@ Variable const& EvalExpr::as_variable() const { return expr().as(); } std::string EvalExpr::label() const noexcept { if (is_tensor()) - return to_string(as_tensor().label()) + "(" + indices_annot() + ")"; + return toUtf8(as_tensor().label()) + "(" + indices_annot() + ")"; else if (is_constant()) { - return sequant::to_string(sequant::deparse(as_constant())); + return toUtf8(io::serialization::to_string(as_constant())); } else { SEQUANT_ASSERT(is_variable()); - return to_string(as_variable().label()); + return toUtf8(as_variable().label()); } } diff --git a/SeQuant/core/export/generation_optimizer.hpp b/SeQuant/core/export/generation_optimizer.hpp index 4b09d4a42b..8853be351a 100644 --- a/SeQuant/core/export/generation_optimizer.hpp +++ b/SeQuant/core/export/generation_optimizer.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/SeQuant/core/export/itf.hpp b/SeQuant/core/export/itf.hpp index f7deb35e3f..ea11d5d9bb 100644 --- a/SeQuant/core/export/itf.hpp +++ b/SeQuant/core/export/itf.hpp @@ -1,8 +1,6 @@ #ifndef SEQUANT_CORE_EXPORT_ITF_HPP #define SEQUANT_CORE_EXPORT_ITF_HPP -#include - #include #include #include @@ -408,7 +406,6 @@ class ItfGenerator : public Generator { const Product &product = expr.as(); if (product.factors().size() > 2) { - std::wcerr << deparse(product) << std::endl; throw std::runtime_error("ITF can only handle binary contractions"); } diff --git a/SeQuant/core/expressions/abstract_tensor.hpp b/SeQuant/core/expressions/abstract_tensor.hpp index a41699d465..61bb889549 100644 --- a/SeQuant/core/expressions/abstract_tensor.hpp +++ b/SeQuant/core/expressions/abstract_tensor.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -415,7 +415,6 @@ inline auto column_symmetry(const AbstractTensor& t) { inline auto color(const AbstractTensor& t) { return t._color(); } inline auto is_cnumber(const AbstractTensor& t) { return t._is_cnumber(); } inline auto label(const AbstractTensor& t) { return t._label(); } -inline auto to_latex(const AbstractTensor& t) { return t._to_latex(); } /// produces LaTeX representation typeset using tensor package @@ -455,7 +454,7 @@ inline std::wstring to_latex_tensor( unpaired_type == SlotType::Bra) || (bkt == BraKetTypesetting::KetSuper && unpaired_type == SlotType::Ket)) { - result += to_latex(unpaired_indices[col]); + result += io::latex::to_string(unpaired_indices[col]); } else result += L"{}"; result += L"_"; @@ -463,7 +462,7 @@ inline std::wstring to_latex_tensor( unpaired_type == SlotType::Bra) || (bkt == BraKetTypesetting::KetSub && unpaired_type == SlotType::Ket)) { - result += to_latex(unpaired_indices[col]); + result += io::latex::to_string(unpaired_indices[col]); } else result += L"{}"; } @@ -477,11 +476,13 @@ inline std::wstring to_latex_tensor( const auto paired_fence = col + num_paired; for (; col != paired_fence; ++col, ++paired_bra, ++paired_ket) { result += L"*^"; - result += (bkt == BraKetTypesetting::BraSuper) ? to_latex(bra[paired_bra]) - : to_latex(ket[paired_ket]); + result += (bkt == BraKetTypesetting::BraSuper) + ? io::latex::to_string(bra[paired_bra]) + : io::latex::to_string(ket[paired_ket]); result += L"_"; - result += (bkt == BraKetTypesetting::BraSub) ? to_latex(bra[paired_bra]) - : to_latex(ket[paired_ket]); + result += (bkt == BraKetTypesetting::BraSub) + ? io::latex::to_string(bra[paired_bra]) + : io::latex::to_string(ket[paired_ket]); } // loop over right-aligned unpaired slots, if left_align==true @@ -495,7 +496,7 @@ inline std::wstring to_latex_tensor( unpaired_type == SlotType::Bra) || (bkt == BraKetTypesetting::KetSuper && unpaired_type == SlotType::Ket)) { - result += to_latex(unpaired_indices[col]); + result += io::latex::to_string(unpaired_indices[col]); } else result += L"{}"; result += L"_"; @@ -503,7 +504,7 @@ inline std::wstring to_latex_tensor( unpaired_type == SlotType::Bra) || (bkt == BraKetTypesetting::KetSub && unpaired_type == SlotType::Ket)) { - result += to_latex(unpaired_indices[col]); + result += io::latex::to_string(unpaired_indices[col]); } else result += L"{}"; } @@ -515,7 +516,7 @@ inline std::wstring to_latex_tensor( result += L"["; const auto aux_rank = aux.size(); for (std::size_t i = 0; i < aux_rank; ++i) { - result += sequant::to_latex(aux[i]); + result += io::latex::to_string(aux[i]); if (i + 1 < aux_rank) { result += L","; } @@ -552,28 +553,21 @@ inline std::wstring to_latex_tensor( /// colors are, for now, always assumed to commute) /// - @c label(t) is a valid expression and its return is convertible to /// a std::wstring; -/// - @c to_latex(t) is a valid expression and its return is convertible -/// to a std::wstring. template -struct is_tensor - : std::bool_constant< - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v && - std::is_invocable_v< - decltype(static_cast(to_latex)), T>> { +concept is_tensor = requires(const T& obj) { + { braket(obj) } -> std::ranges::range; + { braketaux(obj) } -> std::ranges::range; + { bra_rank(obj) } -> std::convertible_to; + { ket_rank(obj) } -> std::convertible_to; + { aux_rank(obj) } -> std::convertible_to; + { symmetry(obj) } -> std::convertible_to; + { braket_symmetry(obj) } -> std::convertible_to; + { column_symmetry(obj) } -> std::convertible_to; + { color(obj) } -> std::convertible_to; + { is_cnumber(obj) } -> std::convertible_to; + { label(obj) } -> std::constructible_from; }; -template -constexpr bool is_tensor_v = is_tensor::value; -static_assert(is_tensor_v, +static_assert(is_tensor, "The AbstractTensor class does not fulfill the requirements of " "the Tensor interface"); diff --git a/SeQuant/core/expressions/constant.hpp b/SeQuant/core/expressions/constant.hpp index f02e6cc897..33049ee3c7 100644 --- a/SeQuant/core/expressions/constant.hpp +++ b/SeQuant/core/expressions/constant.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -66,11 +67,7 @@ class Constant : public Expr { } std::wstring to_latex() const override { - return L"{" + sequant::to_latex(value()) + L"}"; - } - - std::wstring to_wolfram() const override { - return sequant::to_wolfram(value()); + return L"{" + io::latex::to_string(value()) + L"}"; } type_id_type type_id() const override { return get_type_id(); } diff --git a/SeQuant/core/expressions/expr.cpp b/SeQuant/core/expressions/expr.cpp index 9902050d8e..9ce13bb4de 100644 --- a/SeQuant/core/expressions/expr.cpp +++ b/SeQuant/core/expressions/expr.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -115,8 +116,6 @@ std::logic_error Expr::not_implemented(const char *fn) const { std::wstring Expr::to_latex() const { throw not_implemented("to_latex"); } -std::wstring Expr::to_wolfram() const { throw not_implemented("to_wolfram"); } - ExprPtr Expr::clone() const { throw not_implemented("clone"); } void Expr::adjoint() { throw not_implemented("adjoint"); } @@ -152,7 +151,7 @@ void Variable::conjugate() { conjugated_ = !conjugated_; } bool Variable::conjugated() const { return conjugated_; } std::wstring Variable::to_latex() const { - std::wstring result = L"{" + utf_to_latex(label_) + L"}"; + std::wstring result = L"{" + io::latex::utf_to_string(label_) + L"}"; if (conjugated_) result = L"{" + result + L"^*" + L"}"; return result; } diff --git a/SeQuant/core/expressions/expr.hpp b/SeQuant/core/expressions/expr.hpp index d6f7c6ef5b..6e568a5613 100644 --- a/SeQuant/core/expressions/expr.hpp +++ b/SeQuant/core/expressions/expr.hpp @@ -76,10 +76,6 @@ class Expr : public std::enable_shared_from_this, /// @return the string representation of @c this in the LaTeX format virtual std::wstring to_latex() const; - /// @return the string representation of @c this in the Wolfram Language - /// format - virtual std::wstring to_wolfram() const; - /// @return a clone of this object, i.e. an object that is equal to @c this /// @note - must be overridden in the derived class. /// - the default implementation throws an exception diff --git a/SeQuant/core/expressions/expr_algorithms.cpp b/SeQuant/core/expressions/expr_algorithms.cpp index 82f4c14d34..0dfe14934f 100644 --- a/SeQuant/core/expressions/expr_algorithms.cpp +++ b/SeQuant/core/expressions/expr_algorithms.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -17,11 +18,9 @@ namespace sequant { -std::wstring to_latex(const ExprPtr& exprptr) { return exprptr->to_latex(); } - std::wstring to_latex_align(const ExprPtr& exprptr, size_t max_lines_per_align, size_t max_terms_per_line) { - std::wstring result = to_latex(exprptr); + std::wstring result = io::latex::to_string(exprptr); if (exprptr->is()) { result.erase(0, 7); // remove leading "{ \bigl" result.replace(result.size() - 8, 8, @@ -79,10 +78,6 @@ std::wstring to_latex_align(const ExprPtr& exprptr, size_t max_lines_per_align, return result; } -std::wstring to_wolfram(const ExprPtr& exprptr) { - return exprptr->to_wolfram(); -} - std::size_t size(const Expr& expr) { return ranges::size(expr); } std::size_t size(const ExprPtr& exprptr) { @@ -122,15 +117,17 @@ ResultExpr& canonicalize(ResultExpr&& expr, CanonicalizeOptions opts) { struct ExpandVisitor { void operator()(ExprPtr& expr) { if (Logger::instance().expand) - std::wcout << "expand_visitor received " << to_latex(expr) << std::endl; + std::wcout << "expand_visitor received " << io::latex::to_string(expr) + << std::endl; // apply expand() iteratively until done while (expand(expr)) { if (Logger::instance().expand) - std::wcout << "after 1 round of expansion have " << to_latex(expr) - << std::endl; + std::wcout << "after 1 round of expansion have " + << io::latex::to_string(expr) << std::endl; } if (Logger::instance().expand) - std::wcout << "expansion result = " << to_latex(expr) << std::endl; + std::wcout << "expansion result = " << io::latex::to_string(expr) + << std::endl; // simplification and canonicalization are to be done by other visitors } @@ -177,7 +174,8 @@ struct ExpandVisitor { result; // will keep the result if one or more summands is expanded const auto nsubexpr = size(expr); if (Logger::instance().expand) - std::wcout << "in expand_sum: expr = " << to_latex(expr) << std::endl; + std::wcout << "in expand_sum: expr = " << io::latex::to_string(expr) + << std::endl; for (std::size_t i = 0; i != nsubexpr; ++i) { // if summand is a Product, expand it if (expr_ref[i]->is()) { @@ -193,7 +191,8 @@ struct ExpandVisitor { if (Logger::instance().expand) std::wcout << "in expand_sum: after expand_product(" << (this_term_expanded ? "true)" : "false)") - << " result = " << to_latex(result ? result : expr) + << " result = " + << io::latex::to_string(result ? result : expr) << std::endl; } // if summand is a Sum, flatten it @@ -207,7 +206,8 @@ struct ExpandVisitor { if (result) result->append(expr_ref[i]); if (Logger::instance().expand) std::wcout << "in expand_sum: after flattening Sum summand result = " - << to_latex(result ? result : expr) << std::endl; + << io::latex::to_string(result ? result : expr) + << std::endl; } else { // nothing to expand? if expanded previously (i.e. result is // nonnull) append to result if (result) result->append(expr_ref[i]); @@ -316,16 +316,17 @@ struct RapidSimplifyVisitor { void operator()(ExprPtr& expr) { if (Logger::instance().simplify) - std::wcout << "rapid_simplify_visitor received " << to_latex(expr) - << std::endl; + std::wcout << "rapid_simplify_visitor received " + << io::latex::to_string(expr) << std::endl; // apply simplify() iteratively until done while (simplify(expr, opts)) { if (Logger::instance().simplify) - std::wcout << "after 1 round of simplification have " << to_latex(expr) - << std::endl; + std::wcout << "after 1 round of simplification have " + << io::latex::to_string(expr) << std::endl; } if (Logger::instance().simplify) - std::wcout << "simplification result = " << to_latex(expr) << std::endl; + std::wcout << "simplification result = " << io::latex::to_string(expr) + << std::endl; } /// simplifies a Product by: diff --git a/SeQuant/core/expressions/expr_algorithms.hpp b/SeQuant/core/expressions/expr_algorithms.hpp index 8d2c304ee3..820f2d64a5 100644 --- a/SeQuant/core/expressions/expr_algorithms.hpp +++ b/SeQuant/core/expressions/expr_algorithms.hpp @@ -16,8 +16,6 @@ namespace sequant { -std::wstring to_latex(const ExprPtr& exprptr); - /// splits long outer sum into a multiline align /// @param exprptr the expression to be converted to a string /// @param max_lines_per_align the maximum number of lines in the align before @@ -27,8 +25,6 @@ std::wstring to_latex_align(const ExprPtr& exprptr, size_t max_lines_per_align = 0, size_t max_terms_per_line = 1); -std::wstring to_wolfram(const ExprPtr& exprptr); - template std::decay_t clone(Sequence&& exprseq) { auto cloned_seq = exprseq | ranges::views::transform([](const ExprPtr& ptr) { diff --git a/SeQuant/core/expressions/product.hpp b/SeQuant/core/expressions/product.hpp index 0bd3c065bb..7b52de1648 100644 --- a/SeQuant/core/expressions/product.hpp +++ b/SeQuant/core/expressions/product.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -305,7 +306,7 @@ class Product : public Expr { if (!scal.is_identity()) { // replace -1 prefactor by - if (!(negate ? scalar() : -scalar()).is_identity()) { - result += sequant::to_latex(scal); + result += io::latex::to_string(scal); } else { result += L"{-}"; } @@ -321,22 +322,6 @@ class Product : public Expr { return result; } - std::wstring to_wolfram() const override { - std::wstring result = - is_commutative() ? L"Times[" : L"NonCommutativeMultiply["; - if (scalar() != decltype(scalar_)(1)) { - result += sequant::to_wolfram(scalar()) + L","; - } - const auto nfactors = factors().size(); - size_t factor_count = 1; - for (const auto &i : factors()) { - result += i->to_wolfram() + (factor_count == nfactors ? L"" : L","); - ++factor_count; - } - result += L"]"; - return result; - } - type_id_type type_id() const override { return get_type_id(); }; /// @return an identical clone of this Product (a deep copy allocated on the diff --git a/SeQuant/core/expressions/sum.hpp b/SeQuant/core/expressions/sum.hpp index b7a352fc67..ed0d3a523a 100644 --- a/SeQuant/core/expressions/sum.hpp +++ b/SeQuant/core/expressions/sum.hpp @@ -240,19 +240,6 @@ class Sum : public Expr { return result; } - std::wstring to_wolfram() const override { - std::wstring result; - result = L"Plus["; - std::size_t counter = 0; - for (const auto &i : summands()) { - result += i->to_wolfram(); - ++counter; - if (counter != summands().size()) result += L","; - } - result += L"]"; - return result; - } - Expr::type_id_type type_id() const override { return Expr::get_type_id(); }; diff --git a/SeQuant/core/expressions/tensor.hpp b/SeQuant/core/expressions/tensor.hpp index 34a77ae2a6..00a414634e 100644 --- a/SeQuant/core/expressions/tensor.hpp +++ b/SeQuant/core/expressions/tensor.hpp @@ -13,9 +13,10 @@ #include #include #include -#include +#include #include #include +#include #include #include @@ -250,7 +251,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { Symmetry s = Symmetry::Nonsymm, BraKetSymmetry bks = get_default_context().braket_symmetry(), ColumnSymmetry ps = ColumnSymmetry::Symm) - : label_(to_wstring(std::forward(label))), + : label_(toUtf16(std::forward(label))), bra_(make_indices(bra_indices)), ket_(make_indices(ket_indices)), aux_(make_indices(aux_indices)), @@ -273,7 +274,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { Symmetry s = Symmetry::Nonsymm, BraKetSymmetry bks = get_default_context().braket_symmetry(), ColumnSymmetry ps = ColumnSymmetry::Symm) - : label_(to_wstring(std::forward(label))), + : label_(toUtf16(std::forward(label))), bra_(std::move(bra_indices)), ket_(std::move(ket_indices)), aux_(std::move(aux_indices)), @@ -525,7 +526,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { std::wstring core_label; if ((this->symmetry() == Symmetry::Antisymm) && add_bar) core_label += L"\\bar{"; - core_label += utf_to_latex(this->label()); + core_label += io::latex::utf_to_string(this->label()); if ((this->symmetry() == Symmetry::Antisymm) && add_bar) core_label += L"}"; switch (bkst) { @@ -537,7 +538,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { result += (bkt == BraKetTypesetting::KetSub ? L"_" : L"^"); result += L"{"; for (const auto &i : this->ket()) { - result += i ? sequant::to_latex(i) : L"\\textvisiblespace"; + result += i ? io::latex::to_string(i) : L"\\textvisiblespace"; } result += L"}"; @@ -545,7 +546,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { result += (bkt == BraKetTypesetting::BraSub ? L"_" : L"^"); result += L"{"; for (const auto &i : this->bra()) { - result += i ? sequant::to_latex(i) : L"\\textvisiblespace"; + result += i ? io::latex::to_string(i) : L"\\textvisiblespace"; } result += L"}"; @@ -554,7 +555,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { result += L"["; const index_container_type &__aux = this->aux(); for (std::size_t i = 0; i < aux_rank(); ++i) { - result += sequant::to_latex(__aux[i]); + result += io::latex::to_string(__aux[i]); if (i + 1 < aux_rank()) { result += L","; @@ -806,7 +807,7 @@ class Tensor : public Expr, public AbstractTensor, public MutatableLabeled { }; // class Tensor -static_assert(is_tensor_v, +static_assert(is_tensor, "The Tensor class does not fulfill the requirements of the " "Tensor interface"); diff --git a/SeQuant/core/index.cpp b/SeQuant/core/index.cpp index 24efc88612..c21a0cd484 100644 --- a/SeQuant/core/index.cpp +++ b/SeQuant/core/index.cpp @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -32,8 +32,8 @@ std::wstring Index::to_latex() const noexcept { sfx = std::format(L"_{}", *ordinal_ < 10 ? std::to_wstring(*ordinal_) : std::format(L"{{{}}}", *ordinal_)); - return std::format(L"{{{}{}{}}}", utf_to_latex(space().base_key()), sfx, - protos); + return std::format(L"{{{}{}{}}}", + io::latex::utf_to_string(space().base_key()), sfx, protos); } std::string Index::ascii_label() const { @@ -67,9 +67,7 @@ std::string Index::ascii_label() const { return label_ascii; } -std::string Index::to_string() const { - return sequant::to_string(this->label()); -} +std::string Index::to_string() const { return toUtf8(this->label()); } std::shared_ptr Index::obtain_default_index_registry() { diff --git a/SeQuant/core/index.hpp b/SeQuant/core/index.hpp index 35980d840f..f47472e094 100644 --- a/SeQuant/core/index.hpp +++ b/SeQuant/core/index.hpp @@ -532,15 +532,14 @@ class Index : public Taggable { static std::wstring base_label(std::string_view label) { auto underscore_position = label.find('_'); if (underscore_position == std::string::npos) - return to_wstring(std::string({label.begin(), label.end()})); + return toUtf16(label); else - return to_wstring( - std::string({label.data(), label.data() + underscore_position})); + return toUtf16(label.substr(0, underscore_position)); } template >> static std::wstring base_label(Char label) { - return to_wstring(label); + return toUtf16(label); } /// @return the memoized label as a UTF-8 encoded wide-character string @@ -575,7 +574,6 @@ class Index : public Taggable { /// @return A UTF-8 encoded narrow-character string label /// @warning not to be used with proto indices - /// @note equivalent to `sequant::to_string(this->label())` std::string to_string() const; /// @return the full label as a UTF-8 encoded wide-character string @@ -716,37 +714,6 @@ class Index : public Taggable { std::wstring to_latex() const noexcept; - /*template - std::wstring to_wolfram(Attrs &&...attrs) const { - auto protect_subscript = [](const std::wstring_view str) { - auto subsc_pos = str.rfind(L'_'); - if (subsc_pos == std::wstring_view::npos) - return std::wstring(str); - else { - SEQUANT_ASSERT(subsc_pos + 1 < str.size()); - std::wstring result = L"\\!\\(\\*SubscriptBox[\\("; - result += std::wstring(str.substr(0, subsc_pos)); - result += L"\\), \\("; - result += std::wstring(str.substr(subsc_pos + 1)); - result += L"\\)]\\)"; - return result; - } - }; - - using namespace std::literals; - std::wstring result = - L"particleIndex[\""s + protect_subscript(this->label()) + L"\""; - if (this->has_proto_indices()) { - SEQUANT_ASSERT(false && "not yet supported"); - } - using namespace std::literals; - result += L","s + ::sequant::to_wolfram(space()); - ((result += ((L","s + ::sequant::to_wolfram(std::forward(attrs))))), - ...); - result += L"]"; - return result; - }*/ - /// @param protoindex_range a range of Index objects /// @return the color of the protoindices /// @sa Index::color() diff --git a/SeQuant/core/index_space_registry.hpp b/SeQuant/core/index_space_registry.hpp index 3b9fb26b6e..65cae50190 100644 --- a/SeQuant/core/index_space_registry.hpp +++ b/SeQuant/core/index_space_registry.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -296,7 +296,7 @@ class IndexSpaceRegistry { throw std::invalid_argument( (std::string("IndexSpaceRegistry::add(index_space): space with " "index_space.base_key()=") + - to_string(IS.base_key()) + + toUtf8(IS.base_key()) + " already in the registry; if you are trying to replace the " "IndexSpace use " "IndexSpaceRegistry::replace(is)")); @@ -571,12 +571,12 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string("IndexSpaceRegistry::valid_intersection(s1,s2): space " "with key s1=") + - to_string(space1_key) + " must be added to the registry first"); + toUtf8(space1_key) + " must be added to the registry first"); if (!contains(space2_key)) throw std::invalid_argument( std::string( "IndexSpaceRegistry::valid_intersection(s1,s2): space key s2=") + - to_string(space2_key) + " must be added to the registry first"); + toUtf8(space2_key) + " must be added to the registry first"); return this->valid_intersection( *(this->retrieve_ptr(std::forward(space1_key))), *(this->retrieve_ptr(std::forward(space2_key)))); @@ -630,12 +630,12 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::intersection(s1,s2): space with key s1=") + - to_string(space1_key) + " must be added to the registry first"); + toUtf8(space1_key) + " must be added to the registry first"); if (!contains(space2_key)) throw std::invalid_argument( std::string( "IndexSpaceRegistry::intersection(s1,s2): space key s2=") + - to_string(space2_key) + " must be added to the registry first"); + toUtf8(space2_key) + " must be added to the registry first"); return this->intersection( *(this->retrieve_ptr(std::forward(space1_key))), *(this->retrieve_ptr(std::forward(space2_key)))); @@ -683,11 +683,11 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::valid_unIon(s1,s2): space with key s1=") + - to_string(space1_key) + " must be added to the registry first"); + toUtf8(space1_key) + " must be added to the registry first"); if (!contains(space2_key)) throw std::invalid_argument( std::string("IndexSpaceRegistry::valid_unIon(s1,s2): space key s2=") + - to_string(space2_key) + " must be added to the registry first"); + toUtf8(space2_key) + " must be added to the registry first"); return this->valid_unIon( *(this->retrieve_ptr(std::forward(space1_key))), *(this->retrieve_ptr(std::forward(space2_key)))); @@ -749,11 +749,11 @@ class IndexSpaceRegistry { if (!contains(space1_key)) throw std::invalid_argument( std::string("IndexSpaceRegistry::unIon(s1,s2): space with key s1=") + - to_string(space1_key) + " must be added to the registry first"); + toUtf8(space1_key) + " must be added to the registry first"); if (!contains(space2_key)) throw std::invalid_argument( std::string("IndexSpaceRegistry::unIon(s1,s2): space key s2=") + - to_string(space2_key) + " must be added to the registry first"); + toUtf8(space2_key) + " must be added to the registry first"); return this->unIon(*(this->retrieve_ptr(std::forward(space1_key))), *(this->retrieve_ptr(std::forward(space2_key)))); } @@ -867,7 +867,7 @@ class IndexSpaceRegistry { if (!contains(space_key)) throw std::invalid_argument( std::string("IndexSpaceRegistry::is_base(s): space with key s=") + - to_string(space_key) + " must be added to the registry first"); + toUtf8(space_key) + " must be added to the registry first"); return this->is_base(*(this->retrieve_ptr(std::forward(space_key)))); } @@ -907,7 +907,7 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::is_pure_occupied(s): space with key s=") + - to_string(space_key) + " must be added to the registry first"); + toUtf8(space_key) + " must be added to the registry first"); return this->is_pure_occupied( *(this->retrieve_ptr(std::forward(space_key)))); } @@ -937,7 +937,7 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::is_pure_unoccupied(s): space with key s=") + - to_string(space_key) + " must be added to the registry first"); + toUtf8(space_key) + " must be added to the registry first"); return this->is_pure_unoccupied( *(this->retrieve_ptr(std::forward(space_key)))); } @@ -958,7 +958,7 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::contains_occupied(s): space with key s=") + - to_string(space_key) + " must be added to the registry first"); + toUtf8(space_key) + " must be added to the registry first"); return this->contains_occupied( *(this->retrieve_ptr(std::forward(space_key)))); } @@ -979,7 +979,7 @@ class IndexSpaceRegistry { throw std::invalid_argument( std::string( "IndexSpaceRegistry::contains_unoccupied(s): space with key s=") + - to_string(space_key) + " must be added to the registry first"); + toUtf8(space_key) + " must be added to the registry first"); return this->contains_unoccupied( *(this->retrieve_ptr(std::forward(space_key)))); } diff --git a/SeQuant/core/io/concepts.hpp b/SeQuant/core/io/concepts.hpp new file mode 100644 index 0000000000..22eebc6cde --- /dev/null +++ b/SeQuant/core/io/concepts.hpp @@ -0,0 +1,22 @@ +#ifndef SEQUANT_CORE_IO_CONCEPTS_HPP +#define SEQUANT_CORE_IO_CONCEPTS_HPP + +#include +#include + +namespace sequant::io { + +template +concept convertible_to_latex = + requires(const T &t) { io::latex::to_string(t); }; + +template +concept serializable = + requires(const T &t) { io::serialization::to_string(t); }; + +template +concept deserializable = requires { io::serialization::from_string(""); }; + +} // namespace sequant::io + +#endif // SEQUANT_CORE_IO_CONCEPTS_HPP diff --git a/SeQuant/core/latex.ipp b/SeQuant/core/io/latex/latex.cpp similarity index 81% rename from SeQuant/core/latex.ipp rename to SeQuant/core/io/latex/latex.cpp index 5d951395ea..3f061c7c5b 100644 --- a/SeQuant/core/latex.ipp +++ b/SeQuant/core/io/latex/latex.cpp @@ -1,23 +1,46 @@ // -// Created by Eduard Valeyev on 3/30/18. +// Created by Eduard Valeyev on 7/18/23. // -#ifndef SEQUANT_CORE_LATEX_IPP -#define SEQUANT_CORE_LATEX_IPP - #include -#include -#include +#include +#include +#include + #include #include -#include #include -namespace sequant { +namespace sequant::io::latex { + +std::wstring to_string(const rational& t) { + // n.b. skip enclosing braces to make Constant::to_latex to produce same + // output as before std::wstring result = L"{"; + std::wstring result; + if (denominator(t) == 1) + result += L"{" + to_wstring(numerator(t)) + L"}"; + else { + const auto num = numerator(t); + // n.b. extra braces around \frac and use of to_wstring instead of to_latex + // to avoid extra braces around args to \frac + if (num > 0) { + result += L"{\\frac{" + to_wstring(numerator(t)) + L"}{" + + to_wstring(denominator(t)) + L"}}"; + } else if (num < 0) { + result += L"{-\\frac{" + to_wstring(-num) + L"}{" + + to_wstring(denominator(t)) + L"}}"; + } else + result += L"0"; + } + // n.b. + // result += L"}"; + return result; +} + namespace detail { template -std::basic_string greek_characters_to_latex_impl( +std::basic_string greek_characters_to_string_impl( std::basic_string_view str) { // lower-case greek characters in the order of their appearance in Unicode // chart https://www.unicode.org/charts/PDF/U0370.pdf @@ -75,7 +98,7 @@ std::basic_string greek_characters_to_latex_impl( const Char ch = *it; if (sizeof(Char) == 1 && !is_ascii(ch)) throw std::invalid_argument( - "greek_characters_to_latex(str): currently only supports " + "greek_characters_to_string(str): currently only supports " "non-ASCII characters in str if Char is a wide character (wchar_t, " "char16_t, or char32_t)"); @@ -110,7 +133,7 @@ std::basic_string greek_characters_to_latex_impl( } template -std::basic_string diactrics_to_latex_impl( +std::basic_string diactrics_to_string_impl( std::basic_string_view str) { using str_t = std::basic_string; @@ -131,7 +154,7 @@ std::basic_string diactrics_to_latex_impl( if (sizeof(Char) == 1 && ((it == begin && !is_ascii(ch)) || (next_ch && !is_ascii(*next_ch)))) { throw std::invalid_argument( - "diactrics_to_latex(str): currently only supports " + "diactrics_to_string(str): currently only supports " "non-ASCII characters in str if Char is a wide character (wchar_t, " "char16_t, or char32_t)"); } @@ -234,8 +257,27 @@ std::basic_string diactrics_to_latex_impl( return decltype(result)(str); } -} // namespace detail +#define SQ_IMPL1(CHAR) \ + template std::basic_string greek_characters_to_string_impl< \ + CHAR, std::char_traits, std::allocator>( \ + std::basic_string_view); + +SQ_IMPL1(char); +SQ_IMPL1(wchar_t); +SQ_IMPL1(char8_t); +SQ_IMPL1(char16_t); +SQ_IMPL1(char32_t); -} // namespace sequant +#define SQ_IMPL2(CHAR) \ + template std::basic_string diactrics_to_string_impl< \ + CHAR, std::char_traits, std::allocator>( \ + std::basic_string_view); -#endif // SEQUANT_CORE_LATEX_IPP +SQ_IMPL2(char); +SQ_IMPL2(wchar_t); +SQ_IMPL2(char8_t); +SQ_IMPL2(char16_t); +SQ_IMPL2(char32_t); + +} // namespace detail +} // namespace sequant::io::latex diff --git a/SeQuant/core/latex.hpp b/SeQuant/core/io/latex/latex.hpp similarity index 65% rename from SeQuant/core/latex.hpp rename to SeQuant/core/io/latex/latex.hpp index 5bf5f4bcaa..dfba00b2ff 100644 --- a/SeQuant/core/latex.hpp +++ b/SeQuant/core/io/latex/latex.hpp @@ -2,11 +2,12 @@ // Created by Eduard Valeyev on 3/30/18. // -#ifndef SEQUANT_CORE_LATEX_HPP -#define SEQUANT_CORE_LATEX_HPP +#ifndef SEQUANT_CORE_IO_LATEX_LATEX_HPP +#define SEQUANT_CORE_IO_LATEX_LATEX_HPP #include -#include +#include +#include #include #include @@ -15,19 +16,38 @@ #include #include -namespace sequant { +namespace sequant::io::latex { template -std::enable_if_t>, std::wstring> -to_latex(T&& t) { +concept has_to_latex_member = requires(const T& t) { t.to_latex(); }; + +template +concept pointer_can_call_to_latex = requires(const T& t) { t->to_latex(); }; + +template + requires(has_to_latex_member) +std::wstring to_string(T&& t) { return t.to_latex(); } +template + requires(!has_to_latex_member && pointer_can_call_to_latex) +std::wstring to_string(T&& t) { + return t->to_latex(); +} + +template + requires(!has_to_latex_member && !pointer_can_call_to_latex && + requires(const T& t) { t._to_latex(); }) +std::wstring to_string(const T& t) { + return t._to_latex(); +} + template std::enable_if_t> && !std::is_floating_point_v>, std::wstring> -to_latex(T&& t) { +to_string(T&& t) { std::wstring result = L"{"; using ::sequant::to_wstring; result += to_wstring(t) + L"}"; @@ -36,9 +56,10 @@ to_latex(T&& t) { template std::enable_if_t> && - std::is_floating_point_v>, + std::is_floating_point_v> && + !std::is_same_v, rational>, std::wstring> -to_latex(T&& t) { +to_string(T&& t) { using Real = std::decay_t; static const auto eps_sqrt = std::sqrt(std::numeric_limits::epsilon()); @@ -48,7 +69,7 @@ to_latex(T&& t) { const long round_t = std::lround(t); if (std::abs(round_t - t) < eps_sqrt) // exact integer result += to_wstring(round_t) + L"}"; - else { // TODO detect rationals + else { const auto inv_t = Real(1) / t; const long round_inv_t = std::lround(inv_t); if (std::abs(round_inv_t - inv_t) < eps_sqrt) { // exact inverse of an @@ -64,26 +85,27 @@ to_latex(T&& t) { } template -std::wstring to_latex(const std::complex& t) { +std::wstring to_string(const std::complex& t) { std::wstring result = L"{"; - result += to_latex(t.real()); + result += to_string(t.real()); if (t.imag() > 0) { - result += L" + i " + to_latex(t.imag()); + result += L" + i " + to_string(t.imag()); } else if (t.imag() < 0) - result += L" - i " + to_latex(-t.imag()); + result += L" - i " + to_string(-t.imag()); result += L"}"; return result; } +std::wstring to_string(const rational& num); + namespace detail { template -std::basic_string greek_characters_to_latex_impl( +std::basic_string greek_characters_to_string_impl( std::basic_string_view str); } // namespace detail -// chang-format off /// replaces certain greek characters in a string with their (math-mode) LaTeX /// equivalents /// @tparam Char character type @@ -95,29 +117,27 @@ std::basic_string greek_characters_to_latex_impl( /// @warning if @p str contains non-ASCII characters `Char` must be `wchar_t` /// @throw std::invalid_argument if @p Char is narrow and @p str contains /// non-ASCII characters -// chang-format on template -std::basic_string greek_characters_to_latex( +std::basic_string greek_characters_to_string( const std::basic_string_view& str) { - return detail::greek_characters_to_latex_impl>(str); + return detail::greek_characters_to_string_impl>(str); } template -std::basic_string greek_characters_to_latex( +std::basic_string greek_characters_to_string( const std::basic_string& str) { - return detail::greek_characters_to_latex_impl(str); + return detail::greek_characters_to_string_impl(str); } namespace detail { template -std::basic_string diactrics_to_latex_impl( +std::basic_string diactrics_to_string_impl( std::basic_string_view str); } // namespace detail -// chang-format off /// replaces certain diactric marks with their (math-mode) LaTeX equivalents /// @tparam Char character type /// @tparam Traits character traits type @@ -127,21 +147,19 @@ std::basic_string diactrics_to_latex_impl( /// @throw std::invalid_argument if @p Char is narrow and @p str contains /// non-ASCII characters /// @note only some combined Unicode characters are currently supported -// chang-format on template -std::basic_string diactrics_to_latex( +std::basic_string diactrics_to_string( const std::basic_string_view& str) { - return detail::diactrics_to_latex_impl>( + return detail::diactrics_to_string_impl>( str); } template -std::basic_string diactrics_to_latex( +std::basic_string diactrics_to_string( const std::basic_string& str) { - return detail::diactrics_to_latex_impl(str); + return detail::diactrics_to_string_impl(str); } -// chang-format off /// replaces certain Unicode characters with their (math-mode) LaTeX equivalents /// @tparam Char character type /// @tparam Traits character traits type @@ -155,22 +173,21 @@ std::basic_string diactrics_to_latex( /// @internal useful resources /// - https://milde.users.sourceforge.net/LUCR/Math/unimathsymbols.pdf /// - https://www.unicode.org/charts/PDF/U1D400.pdf -// chang-format on template -std::basic_string utf_to_latex( +std::basic_string utf_to_string( const std::basic_string_view& str) { // replace diacritics first since it relies on wide character structure - auto tmp = diactrics_to_latex(str); - return greek_characters_to_latex(tmp); + auto tmp = diactrics_to_string(str); + return greek_characters_to_string(tmp); } template -std::basic_string utf_to_latex( +std::basic_string utf_to_string( const std::basic_string& str) { - auto tmp = diactrics_to_latex(str); - return greek_characters_to_latex(tmp); + auto tmp = diactrics_to_string(str); + return greek_characters_to_string(tmp); } -} // namespace sequant +} // namespace sequant::io::latex -#endif // SEQUANT_CORE_LATEX_HPP +#endif // SEQUANT_CORE_IO_LATEX_LATEX_HPP diff --git a/SeQuant/core/io/serialization/serialization.cpp b/SeQuant/core/io/serialization/serialization.cpp new file mode 100644 index 0000000000..fc14fac930 --- /dev/null +++ b/SeQuant/core/io/serialization/serialization.cpp @@ -0,0 +1,52 @@ +#include +#include +#include + +#include + +namespace sequant::io::serialization { + +SerializationError::SerializationError(std::size_t offset, std::size_t length, + std::string message) + : Exception(std::move(message)), offset(offset), length(length) {} + +#define SEQUANT_RESOLVE_DESERIALIZATION_FUNC(stringType, ExprType) \ + template <> \ + ExprType from_string(stringType input, \ + const DeserializationOptions &options) { \ + switch (options.syntax) { \ + case SerializationSyntax::V1: \ + return serialization::v1::from_string(input, options); \ + } \ + \ + SEQUANT_UNREACHABLE; \ + } + +SEQUANT_RESOLVE_DESERIALIZATION_FUNC(std::wstring_view, ExprPtr) +SEQUANT_RESOLVE_DESERIALIZATION_FUNC(std::string_view, ExprPtr) +SEQUANT_RESOLVE_DESERIALIZATION_FUNC(std::wstring_view, ResultExpr) +SEQUANT_RESOLVE_DESERIALIZATION_FUNC(std::string_view, ResultExpr) + +#define SEQUANT_RESOLVE_SERIALIZE_FUNC(argType) \ + std::wstring to_string(const argType &arg, \ + const SerializationOptions &options) { \ + switch (options.syntax) { \ + case SerializationSyntax::V1: \ + return serialization::v1::to_string(arg, options); \ + } \ + \ + SEQUANT_UNREACHABLE; \ + } + +SEQUANT_RESOLVE_SERIALIZE_FUNC(ResultExpr) +SEQUANT_RESOLVE_SERIALIZE_FUNC(ExprPtr) +SEQUANT_RESOLVE_SERIALIZE_FUNC(Expr) +SEQUANT_RESOLVE_SERIALIZE_FUNC(AbstractTensor) +SEQUANT_RESOLVE_SERIALIZE_FUNC(Index) +SEQUANT_RESOLVE_SERIALIZE_FUNC(NormalOperator) +SEQUANT_RESOLVE_SERIALIZE_FUNC(NormalOperator) + +#undef SEQUANT_RESOLVE_SERIALIZE_FUNC +#undef SEQUANT_RESOLVE_DESERIALIZATION_FUNC + +} // namespace sequant::io::serialization diff --git a/SeQuant/core/io/serialization/serialization.hpp b/SeQuant/core/io/serialization/serialization.hpp new file mode 100644 index 0000000000..58f3737431 --- /dev/null +++ b/SeQuant/core/io/serialization/serialization.hpp @@ -0,0 +1,134 @@ +#ifndef SEQUANT_CORE_IO_SERIALIZATION_HPP +#define SEQUANT_CORE_IO_SERIALIZATION_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sequant::io::serialization { + +struct SerializationError : Exception { + std::size_t offset; + std::size_t length; + + SerializationError(std::size_t offset, std::size_t length, + std::string message); +}; + +/// Specifies the syntax of the textual input/representation to use. All +/// potential changes made to the syntax within a given version is understood to +/// be backwards compatible in the from_string(…) sense. That is older inputs +/// will continue to work as before. However, representations generated via +/// to_string(…) may not necessarily be deserializable by older version of +/// SeQuant. +/// +/// @note Everything but Latest is considered to be deprecated by default +/// and support for it may be removed in future versions. +enum class SerializationSyntax { + V1, + + Latest = V1 +}; + +struct DeserializationOptions { + /// The Symmetry (within bra and ket) to use, if none is specified in the + /// input explicitly. The Context is queried in case this is not provided + /// explicitly. + std::optional def_perm_symm = {}; + /// The BraKetSymmetry to use, if none is specified in the input explicitly. + /// The Context is queried in case this is not provided explicitly. + std::optional def_braket_symm = {}; + /// The ColumnSymmetry to use, if none is specified in the input explicitly. + /// The Context is queried in case this is not provided explicitly. + std::optional def_col_symm = {}; + /// The expected syntax version of the input + SerializationSyntax syntax = SerializationSyntax::Latest; +}; + +struct SerializationOptions { + /// Whether to explicitly annotate tensor symmetries + bool annot_symm = true; + /// The syntax version of the produced output + SerializationSyntax syntax = SerializationSyntax::Latest; +}; + +#define SEQUANT_DECLARE_DESERIALIZATION_FUNC \ + template \ + T from_string(std::string_view input, \ + const DeserializationOptions &options = {}) = delete; \ + template \ + T from_string(std::wstring_view input, \ + const DeserializationOptions &options = {}) = delete; + +#define SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION(ExprType) \ + template <> \ + ExprType from_string(std::string_view input, \ + const DeserializationOptions &options); \ + template <> \ + ExprType from_string(std::wstring_view input, \ + const DeserializationOptions &options); + +/// \brief Construct expressions from string representations +/// +/// \param input The input to deserialize +/// \param options Customization options +/// \return SeQuant expression. +SEQUANT_DECLARE_DESERIALIZATION_FUNC; + +SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION(ExprPtr); +SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION(ResultExpr); + +#define SEQUANT_DECLARE_SERIALIZATION_FUNC \ + std::wstring to_string(const ResultExpr &expr, \ + const SerializationOptions &options = {}); \ + std::wstring to_string(const ExprPtr &expr, \ + const SerializationOptions &options = {}); \ + std::wstring to_string(const Expr &expr, \ + const SerializationOptions &options = {}); \ + std::wstring to_string(const AbstractTensor &expr, \ + const SerializationOptions &options = {}); \ + std::wstring to_string(const Index &index, \ + const SerializationOptions &options = {}); \ + template \ + std::wstring to_string(const NormalOperator &nop, \ + const SerializationOptions &options = {}); + +/// +/// Get a serialized string from an expression. +/// +/// An expression that has a flat structure (ie. product does not contain +/// sum or product subexpressions) is guaranteed to be reparsable to itself so +/// that equality comparison holds: x == from_string(to_string(x)). For nested +/// expressions the equality comparison is not guaranteed, however, for such +/// an expression x, its corresponding evaluation node, eval_node(x), and +/// the deserialized evaluation node, eval_node(from_string(to_string(x))) are +/// equivalent. +/// +/// \param expr Expression to serialize +/// \param options Customization options +/// \return wstring of the expression. +SEQUANT_DECLARE_SERIALIZATION_FUNC + +// Versioned variants +namespace v1 { +SEQUANT_DECLARE_DESERIALIZATION_FUNC; + +SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION(ExprPtr); +SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION(ResultExpr); + +SEQUANT_DECLARE_SERIALIZATION_FUNC +} // namespace v1 + +#undef SEQUANT_DECLARE_DESERIALIZATION_FUNC +#undef SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION +#undef SEQUANT_DECLARE_SERIALIZATION_FUNC + +} // namespace sequant::io::serialization + +#endif // SEQUANT_CORE_IO_SERIALIZATION_HPP diff --git a/SeQuant/core/parse/ast.cpp b/SeQuant/core/io/serialization/v1/ast.cpp similarity index 58% rename from SeQuant/core/parse/ast.cpp rename to SeQuant/core/io/serialization/v1/ast.cpp index 6ab6190091..39431fbf3c 100644 --- a/SeQuant/core/parse/ast.cpp +++ b/SeQuant/core/io/serialization/v1/ast.cpp @@ -2,13 +2,13 @@ // Created by Robert Adam on 2023-09-21 // -#include +#include -namespace sequant::parse::ast { +namespace sequant::io::serialization::v1::ast { Product::Product(std::vector factors) : factors(std::move(factors)) {} Sum::Sum(std::vector summands) : summands(std::move(summands)) {} -} // namespace sequant::parse::ast +} // namespace sequant::io::serialization::v1::ast diff --git a/SeQuant/core/parse/ast.hpp b/SeQuant/core/io/serialization/v1/ast.hpp similarity index 71% rename from SeQuant/core/parse/ast.hpp rename to SeQuant/core/io/serialization/v1/ast.hpp index 8c93c544c2..55d5c6124a 100644 --- a/SeQuant/core/parse/ast.hpp +++ b/SeQuant/core/io/serialization/v1/ast.hpp @@ -2,8 +2,8 @@ // Created by Robert Adam on 2023-09-21 // -#ifndef SEQUANT_CORE_PARSE_AST_HPP -#define SEQUANT_CORE_PARSE_AST_HPP +#ifndef SEQUANT_CORE_PARSE_V1_AST_HPP +#define SEQUANT_CORE_PARSE_V1_AST_HPP #define BOOST_SPIRIT_X3_UNICODE #include @@ -12,12 +12,11 @@ #include #include -#include #include #include #include -namespace sequant::parse::ast { +namespace sequant::io::serialization::v1::ast { struct IndexLabel : boost::spirit::x3::position_tagged { std::wstring label; @@ -132,20 +131,27 @@ struct ResultExpr : boost::spirit::x3::position_tagged { : lhs(std::move(tensor)), rhs(std::move(expr)) {} }; -} // namespace sequant::parse::ast - -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::IndexLabel, label, id); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Index, label, protoLabels); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Number, numerator, denominator); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Variable, name, conjugated); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::IndexGroups, bra, ket, - auxiliaries, reverse_bra_ket); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::SymmetrySpec, perm_symm, - braket_symm, column_symm); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Tensor, name, indices, symmetry); - -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Product, factors); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::Sum, summands); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::ResultExpr, lhs, rhs); - -#endif // SEQUANT_CORE_PARSE_AST_HPP +} // namespace sequant::io::serialization::v1::ast + +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::IndexLabel, + label, id); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Index, label, + protoLabels); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Number, + numerator, denominator); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Variable, name, + conjugated); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::IndexGroups, bra, + ket, auxiliaries, reverse_bra_ket); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::SymmetrySpec, + perm_symm, braket_symm, column_symm); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Tensor, name, + indices, symmetry); + +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Product, + factors); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::Sum, summands); +BOOST_FUSION_ADAPT_STRUCT(sequant::io::serialization::v1::ast::ResultExpr, lhs, + rhs); + +#endif // SEQUANT_CORE_PARSE_AST_V1_HPP diff --git a/SeQuant/core/parse/ast_conversions.hpp b/SeQuant/core/io/serialization/v1/ast_conversions.hpp similarity index 79% rename from SeQuant/core/parse/ast_conversions.hpp rename to SeQuant/core/io/serialization/v1/ast_conversions.hpp index c2591d8798..02e23bb99d 100644 --- a/SeQuant/core/parse/ast_conversions.hpp +++ b/SeQuant/core/io/serialization/v1/ast_conversions.hpp @@ -8,9 +8,8 @@ #include #include #include +#include #include -#include -#include #include #include #include @@ -21,7 +20,7 @@ #include #include -namespace sequant::parse::transform { +namespace sequant::io::serialization::v1::transform { using DefaultSymmetries = std::tuple; @@ -36,12 +35,13 @@ std::tuple get_pos(const AST &ast, } template -Index to_index(const parse::ast::Index &index, +Index to_index(const io::serialization::v1::ast::Index &index, const PositionCache &position_cache, const Iterator &begin) { container::vector protoIndices; protoIndices.reserve(index.protoLabels.size()); - for (const parse::ast::IndexLabel ¤t : index.protoLabels) { + for (const io::serialization::v1::ast::IndexLabel ¤t : + index.protoLabels) { try { std::wstring label = current.label + L"_" + std::to_wstring(current.id); IndexSpace space = @@ -49,14 +49,15 @@ Index to_index(const parse::ast::Index &index, protoIndices.push_back(Index(std::move(label), std::move(space))); } catch (const IndexSpace::bad_key &) { auto [offset, length] = get_pos(current, position_cache, begin); - throw ParseError(offset, length, - "Unknown index space '" + toUtf8(current.label) + - "' in proto index specification"); + throw SerializationError(offset, length, + "Unknown index space '" + toUtf8(current.label) + + "' in proto index specification"); } catch (const std::invalid_argument &e) { auto [offset, length] = get_pos(current, position_cache, begin); - throw ParseError(offset, length, - "Invalid index '" + toUtf8(current.label) + "_" + - std::to_string(current.id) + ": " + e.what()); + throw SerializationError(offset, length, + "Invalid index '" + toUtf8(current.label) + "_" + + std::to_string(current.id) + ": " + + e.what()); } } @@ -66,21 +67,23 @@ Index to_index(const parse::ast::Index &index, return Index(std::move(space), index.label.id, std::move(protoIndices)); } catch (const IndexSpace::bad_key &e) { auto [offset, length] = get_pos(index.label, position_cache, begin); - throw ParseError(offset, length, - "Unknown index space '" + toUtf8(index.label.label) + - "' in index specification"); + throw SerializationError(offset, length, + "Unknown index space '" + + toUtf8(index.label.label) + + "' in index specification"); } catch (const std::invalid_argument &e) { auto [offset, length] = get_pos(index.label, position_cache, begin); - throw ParseError(offset, length, - "Invalid index '" + toUtf8(index.label.label) + "_" + - std::to_string(index.label.id) + ": " + e.what()); + throw SerializationError(offset, length, + "Invalid index '" + toUtf8(index.label.label) + + "_" + std::to_string(index.label.id) + ": " + + e.what()); } } template std::tuple, container::vector, container::vector> -make_indices(const parse::ast::IndexGroups &groups, +make_indices(const io::serialization::v1::ast::IndexGroups &groups, const PositionCache &position_cache, const Iterator &begin) { container::vector braIndices; container::vector ketIndices; @@ -101,13 +104,13 @@ make_indices(const parse::ast::IndexGroups &groups, braIndices.reserve(bra->size()); ketIndices.reserve(ket->size()); - for (const parse::ast::Index ¤t : *bra) { + for (const io::serialization::v1::ast::Index ¤t : *bra) { braIndices.push_back(to_index(current, position_cache, begin)); } - for (const parse::ast::Index ¤t : *ket) { + for (const io::serialization::v1::ast::Index ¤t : *ket) { ketIndices.push_back(to_index(current, position_cache, begin)); } - for (const parse::ast::Index ¤t : groups.auxiliaries) { + for (const io::serialization::v1::ast::Index ¤t : groups.auxiliaries) { auxiliaries.push_back(to_index(current, position_cache, begin)); } @@ -117,7 +120,7 @@ make_indices(const parse::ast::IndexGroups &groups, template Symmetry to_perm_symmetry(char c, std::size_t offset, const Iterator &, Symmetry default_symmetry) { - if (c == parse::ast::SymmetrySpec::unspecified) { + if (c == io::serialization::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -133,14 +136,14 @@ Symmetry to_perm_symmetry(char c, std::size_t offset, const Iterator &, return Symmetry::Nonsymm; } - throw ParseError(offset, 1, - std::string("Invalid symmetry specifier '") + c + "'"); + throw SerializationError( + offset, 1, std::string("Invalid symmetry specifier '") + c + "'"); } template BraKetSymmetry to_braket_symmetry(char c, std::size_t offset, const Iterator &, BraKetSymmetry default_symmetry) { - if (c == parse::ast::SymmetrySpec::unspecified) { + if (c == io::serialization::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -156,14 +159,14 @@ BraKetSymmetry to_braket_symmetry(char c, std::size_t offset, const Iterator &, return BraKetSymmetry::Nonsymm; } - throw ParseError( + throw SerializationError( offset, 1, std::string("Invalid BraKet symmetry specifier '") + c + "'"); } template ColumnSymmetry to_column_symmetry(char c, std::size_t offset, const Iterator &, ColumnSymmetry default_symmetry) { - if (c == parse::ast::SymmetrySpec::unspecified) { + if (c == io::serialization::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -176,14 +179,14 @@ ColumnSymmetry to_column_symmetry(char c, std::size_t offset, const Iterator &, return ColumnSymmetry::Nonsymm; } - throw ParseError( + throw SerializationError( offset, 1, std::string("Invalid particle symmetry specifier '") + c + "'"); } template -Constant to_constant(const parse::ast::Number &number, const PositionCache &, - const Iterator &) { +Constant to_constant(const io::serialization::v1::ast::Number &number, + const PositionCache &, const Iterator &) { if (static_cast(number.numerator) == number.numerator && static_cast(number.denominator) == number.denominator) { // Integer fraction @@ -198,7 +201,7 @@ Constant to_constant(const parse::ast::Number &number, const PositionCache &, template std::tuple to_symmetries( - const boost::optional &symm_spec, + const boost::optional &symm_spec, const DefaultSymmetries &default_symms, const PositionCache &cache, const Iterator &begin) { if (!symm_spec.has_value()) { @@ -223,11 +226,11 @@ std::tuple to_symmetries( } template -ExprPtr ast_to_expr(const parse::ast::Product &product, +ExprPtr ast_to_expr(const io::serialization::v1::ast::Product &product, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms); template -ExprPtr ast_to_expr(const parse::ast::Sum &sum, +ExprPtr ast_to_expr(const io::serialization::v1::ast::Sum &sum, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms); @@ -237,17 +240,17 @@ struct Transformer { std::reference_wrapper begin; std::reference_wrapper default_symms; - ExprPtr operator()(const parse::ast::Product &product) const { + ExprPtr operator()(const io::serialization::v1::ast::Product &product) const { return ast_to_expr(product, position_cache.get(), begin.get(), default_symms.get()); } - ExprPtr operator()(const parse::ast::Sum &sum) const { + ExprPtr operator()(const io::serialization::v1::ast::Sum &sum) const { return ast_to_expr(sum, position_cache.get(), begin.get(), default_symms.get()); } - ExprPtr operator()(const parse::ast::Tensor &tensor) const { + ExprPtr operator()(const io::serialization::v1::ast::Tensor &tensor) const { auto [braIndices, ketIndices, auxiliaries] = make_indices(tensor.indices, position_cache.get(), begin.get()); @@ -295,7 +298,8 @@ struct Transformer { perm_symm, braket_symm, column_symm); } - ExprPtr operator()(const parse::ast::Variable &variable) const { + ExprPtr operator()( + const io::serialization::v1::ast::Variable &variable) const { ExprPtr var = ex(variable.name); if (variable.conjugated) { @@ -305,13 +309,13 @@ struct Transformer { return var; } - ExprPtr operator()(const parse::ast::Number &number) const { + ExprPtr operator()(const io::serialization::v1::ast::Number &number) const { return ex(to_constant(number, position_cache.get(), begin.get())); } }; template -ExprPtr ast_to_expr(const parse::ast::NullaryValue &value, +ExprPtr ast_to_expr(const io::serialization::v1::ast::NullaryValue &value, const PositionCache &position_cache, const Iterator &begin, DefaultSymmetries default_symms) { return boost::apply_visitor( @@ -326,7 +330,7 @@ bool holds_alternative(const boost::variant &v) noexcept { } template -ExprPtr ast_to_expr(const parse::ast::Product &product, +ExprPtr ast_to_expr(const io::serialization::v1::ast::Product &product, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms) { if (product.factors.empty()) { @@ -343,10 +347,12 @@ ExprPtr ast_to_expr(const parse::ast::Product &product, Constant prefactor(1); // We perform constant folding - for (const parse::ast::NullaryValue &value : product.factors) { - if (holds_alternative(value)) { - prefactor *= to_constant(boost::get(value), - position_cache, begin); + for (const io::serialization::v1::ast::NullaryValue &value : + product.factors) { + if (holds_alternative(value)) { + prefactor *= + to_constant(boost::get(value), + position_cache, begin); } else { factors.push_back( ast_to_expr(value, position_cache, begin, default_symms)); @@ -367,7 +373,7 @@ ExprPtr ast_to_expr(const parse::ast::Product &product, } template -ExprPtr ast_to_expr(const parse::ast::Sum &sum, +ExprPtr ast_to_expr(const io::serialization::v1::ast::Sum &sum, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms) { if (sum.summands.empty()) { @@ -382,7 +388,7 @@ ExprPtr ast_to_expr(const parse::ast::Sum &sum, summands.reserve(sum.summands.size()); std::transform( sum.summands.begin(), sum.summands.end(), std::back_inserter(summands), - [&](const parse::ast::Product &product) { + [&](const io::serialization::v1::ast::Product &product) { return ast_to_expr(product, position_cache, begin, default_symms); }); @@ -390,7 +396,7 @@ ExprPtr ast_to_expr(const parse::ast::Sum &sum, } template -ResultExpr ast_to_result(const parse::ast::ResultExpr &result, +ResultExpr ast_to_result(const io::serialization::v1::ast::ResultExpr &result, const PositionCache &position_cache, const Iterator &begin, DefaultSymmetries default_symms) { @@ -406,11 +412,11 @@ ResultExpr ast_to_result(const parse::ast::ResultExpr &result, return {std::move(lhs.as()), std::move(rhs)}; } else { auto [offset, length] = get_pos(result.lhs, position_cache, begin); - throw ParseError(offset, length, - "LHS of a ResultExpr must be a Tensor or a Variable"); + throw SerializationError( + offset, length, "LHS of a ResultExpr must be a Tensor or a Variable"); } } -} // namespace sequant::parse::transform +} // namespace sequant::io::serialization::v1::transform #endif // SEQUANT_CORE_PARSE_AST_CONVERSIONS_HPP diff --git a/SeQuant/core/parse/parse.cpp b/SeQuant/core/io/serialization/v1/deserialize.cpp similarity index 67% rename from SeQuant/core/parse/parse.cpp rename to SeQuant/core/io/serialization/v1/deserialize.cpp index dc71dcee4d..9b55ae7512 100644 --- a/SeQuant/core/parse/parse.cpp +++ b/SeQuant/core/io/serialization/v1/deserialize.cpp @@ -6,10 +6,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #define BOOST_SPIRIT_X3_UNICODE @@ -21,24 +21,15 @@ #include #include #include -#include #include -#include #include -#include #include -#include #include #include -#include #include #include -namespace sequant { - -ParseError::ParseError(std::size_t offset, std::size_t length, - std::string message) - : std::runtime_error(std::move(message)), offset(offset), length(length) {} +namespace sequant::io::serialization::v1 { namespace x3 = boost::spirit::x3; @@ -185,8 +176,6 @@ struct IndexRule : helpers::annotate_position, helpers::error_handler {}; struct IndexGroupRule : helpers::annotate_position, helpers::error_handler {}; struct SymmetrySpecRule : helpers::annotate_position, helpers::error_handler {}; -} // namespace parse - template struct ErrorHandler { Iterator begin; @@ -195,24 +184,24 @@ struct ErrorHandler { void operator()(Iterator where, std::string expected) const { std::size_t offset = std::distance(begin, where); - throw ParseError(offset, 1, - std::string("Parse failure at offset ") + - std::to_string(offset) + ": expected " + expected); + throw SerializationError(offset, 1, + std::string("Parse failure at offset ") + + std::to_string(offset) + ": expected " + + expected); } }; template -AST do_parse(const StartRule &start, std::wstring_view input, - PositionCache &positions) { +AST parse(const StartRule &start, std::wstring_view input, + PositionCache &positions) { using iterator_type = decltype(input)::iterator; ErrorHandler error_handler(input.begin()); AST ast; - const auto parser = x3::with( - std::ref(error_handler))[x3::with( - std::ref(positions))[start]]; + const auto parser = x3::with(std::ref( + error_handler))[x3::with(std::ref(positions))[start]]; auto begin = input.begin(); try { @@ -221,15 +210,15 @@ AST do_parse(const StartRule &start, std::wstring_view input, if (!success) { // Normally, this shouldn't happen as any error should itself throw a - // ParseError already - throw ParseError(0, input.size(), - "Parsing was unsuccessful for an unknown reason"); + // SerializationError already + throw SerializationError( + 0, input.size(), "Parsing was unsuccessful for an unknown reason"); } if (begin != input.end()) { // This should also not happen as the parser requires matching EOI - throw ParseError(std::distance(input.begin(), begin), - std::distance(begin, input.end()), - "Couldn't parse the entire input"); + throw SerializationError(std::distance(input.begin(), begin), + std::distance(begin, input.end()), + "Couldn't parse the entire input"); } } catch ([[maybe_unused]] const boost::spirit::x3::expectation_failure< iterator_type> &e) { @@ -242,74 +231,61 @@ AST do_parse(const StartRule &start, std::wstring_view input, return ast; } -parse::transform::DefaultSymmetries to_default_symms( - const std::optional &perm_symm, - const std::optional &braket_symm, - const std::optional &column_symm) { +} // namespace parse + +transform::DefaultSymmetries to_default_symms( + const DeserializationOptions &options) { const Context &ctx = get_default_context(); - parse::transform::DefaultSymmetries symms{ - Symmetry::Nonsymm, ctx.braket_symmetry(), ColumnSymmetry::Symm}; + transform::DefaultSymmetries symms{Symmetry::Nonsymm, ctx.braket_symmetry(), + ColumnSymmetry::Symm}; - if (perm_symm.has_value()) { - std::get<0>(symms) = perm_symm.value(); + if (options.def_perm_symm.has_value()) { + std::get<0>(symms) = options.def_perm_symm.value(); } - if (braket_symm.has_value()) { - std::get<1>(symms) = braket_symm.value(); + if (options.def_braket_symm.has_value()) { + std::get<1>(symms) = options.def_braket_symm.value(); } - if (column_symm.has_value()) { - std::get<2>(symms) = column_symm.value(); + if (options.def_col_symm.has_value()) { + std::get<2>(symms) = options.def_col_symm.value(); } - if (std::get<0>(symms) != - Symmetry::Nonsymm) { // antisymmetry/symmetric bra and ket imply particle - // symmetry + if (std::get<0>(symms) != Symmetry::Nonsymm) { + // antisymmetry/symmetric bra and ket imply particle symmetry std::get<2>(symms) = ColumnSymmetry::Symm; } return symms; } -ResultExpr parse_result_expr(std::wstring_view input, - std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { - using iterator_type = decltype(input)::iterator; - x3::position_cache> positions(input.begin(), - input.end()); - auto ast = - do_parse(parse::resultExpr, input, positions); - - return parse::transform::ast_to_result( - ast, positions, input.begin(), - to_default_symms(perm_symm, braket_symm, column_symm)); -} - -ExprPtr parse_expr(std::wstring_view input, std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { - using iterator_type = decltype(input)::iterator; - x3::position_cache> positions(input.begin(), - input.end()); - auto ast = do_parse(parse::expr, input, positions); +#define SEQUANT_DESERIALIZATION_SPECIALIZATION(ExprType, StartRule, ASTType, \ + transformFunc) \ + template <> \ + ExprType from_string(std::wstring_view input, \ + const DeserializationOptions &options) { \ + using iterator_type = decltype(input)::iterator; \ + x3::position_cache> positions(input.begin(), \ + input.end()); \ + auto ast = parse::parse(StartRule, input, positions); \ + \ + return transformFunc(ast, positions, input.begin(), \ + to_default_symms(options)); \ + } - return parse::transform::ast_to_expr( - ast, positions, input.begin(), - to_default_symms(perm_symm, braket_symm, column_symm)); -} +SEQUANT_DESERIALIZATION_SPECIALIZATION(ResultExpr, parse::resultExpr, + ast::ResultExpr, + transform::ast_to_result) +SEQUANT_DESERIALIZATION_SPECIALIZATION(ExprPtr, parse::expr, ast::Sum, + transform::ast_to_expr) -ExprPtr parse_expr(std::string_view input, std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { - return parse_expr(sequant::to_wstring(input), perm_symm, braket_symm, - column_symm); +template <> +ResultExpr from_string(std::string_view input, + const DeserializationOptions &options) { + return v1::from_string(toUtf16(input), options); } - -ResultExpr parse_result_expr(std::string_view input, - std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { - return parse_result_expr(sequant::to_wstring(input), perm_symm, braket_symm, - column_symm); +template <> +ExprPtr from_string(std::string_view input, + const DeserializationOptions &options) { + return v1::from_string(toUtf16(input), options); } -} // namespace sequant +} // namespace sequant::io::serialization::v1 diff --git a/SeQuant/core/parse/semantic_actions.hpp b/SeQuant/core/io/serialization/v1/semantic_actions.hpp similarity index 84% rename from SeQuant/core/parse/semantic_actions.hpp rename to SeQuant/core/io/serialization/v1/semantic_actions.hpp index 8733110b12..26dab219a7 100644 --- a/SeQuant/core/parse/semantic_actions.hpp +++ b/SeQuant/core/io/serialization/v1/semantic_actions.hpp @@ -2,10 +2,10 @@ // Created by Robert Adam on 2023-09-21 // -#ifndef SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_HPP -#define SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_HPP +#ifndef SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_V1_HPP +#define SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_V1_HPP -#include +#include #include #define BOOST_SPIRIT_X3_UNICODE @@ -15,7 +15,7 @@ #include #include -namespace sequant::parse::actions { +namespace sequant::io::serialization::v1::actions { namespace x3 = boost::spirit::x3; @@ -70,6 +70,6 @@ struct process_addend { } }; -} // namespace sequant::parse::actions +} // namespace sequant::io::serialization::v1::actions -#endif // SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_HPP +#endif // SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_V1_HPP diff --git a/SeQuant/core/io/serialization/v1/serialize.cpp b/SeQuant/core/io/serialization/v1/serialize.cpp new file mode 100644 index 0000000000..199a527acd --- /dev/null +++ b/SeQuant/core/io/serialization/v1/serialize.cpp @@ -0,0 +1,316 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace sequant::io::serialization::v1 { + +namespace details { + +template +std::wstring serialize_indices(Range&& indices, + const SerializationOptions& options) { + std::wstring serialized; + + for (std::size_t i = 0; i < indices.size(); ++i) { + serialized += v1::to_string(indices[i], options); + + if (i + 1 < indices.size()) { + serialized += L","; + } + } + + return serialized; +} + +template +std::wstring serialize_ops(const Range& ops, + const SerializationOptions& options) { + std::wstring serialized; + + for (std::size_t i = 0; i < ops.size(); ++i) { + serialized += v1::to_string(ops[i].index(), options); + + if (i + 1 < ops.size()) { + serialized += L","; + } + } + + return serialized; +} + +std::wstring serialize_symm(Symmetry symm, const SerializationOptions&) { + switch (symm) { + case Symmetry::Symm: + return L"S"; + case Symmetry::Antisymm: + return L"A"; + case Symmetry::Nonsymm: + return L"N"; + } + + SEQUANT_UNREACHABLE; +} + +std::wstring serialize_symm(BraKetSymmetry symm, const SerializationOptions&) { + switch (symm) { + case BraKetSymmetry::Conjugate: + return L"C"; + case BraKetSymmetry::Symm: + return L"S"; + case BraKetSymmetry::Nonsymm: + return L"N"; + } + + SEQUANT_UNREACHABLE; +} + +std::wstring serialize_symm(ColumnSymmetry symm, const SerializationOptions&) { + switch (symm) { + case ColumnSymmetry::Symm: + return L"S"; + case ColumnSymmetry::Nonsymm: + return L"N"; + } + + SEQUANT_UNREACHABLE; +} + +std::wstring serialize_scalar(const Constant::scalar_type& scalar, + const SerializationOptions&) { + if (scalar == 0) { + return L"0"; + } + + const auto& real = scalar.real(); + const auto& realNumerator = numerator(real); + const auto& realDenominator = denominator(real); + const auto& imag = scalar.imag(); + auto imagNumerator = numerator(imag); + const auto& imagDenominator = denominator(imag); + + std::string serialized; + if (realNumerator != 0) { + serialized += realNumerator.str(); + + if (realDenominator != 1) { + serialized += "/" + realDenominator.str(); + } + } + if (imagNumerator != 0) { + if (!serialized.empty()) { + if (imagNumerator < 0) { + serialized += " - i "; + imagNumerator *= -1; + } else { + serialized += " + i "; + } + } + + serialized += imagNumerator.str(); + + if (imagDenominator != 1) { + serialized += "/" + imagDenominator.str(); + } + } + + SEQUANT_ASSERT(!serialized.empty()); + + return toUtf16(serialized); +} + +std::wstring to_string(Tensor const& tensor, + const SerializationOptions& options) { + return to_string(static_cast(tensor), options); +} + +std::wstring to_string(const Constant& constant, + const SerializationOptions& options) { + return details::serialize_scalar(constant.value(), options); +} + +std::wstring to_string(const Variable& variable, const SerializationOptions&) { + return std::wstring(variable.label()) + (variable.conjugated() ? L"^*" : L""); +} + +std::wstring to_string(Product const& prod, + const SerializationOptions& options) { + std::wstring serialized; + + const auto& scal = prod.scalar(); + if (scal != Product::scalar_type{1}) { + serialized += details::serialize_scalar(scal, options) + L" "; + } + + for (std::size_t i = 0; i < prod.size(); ++i) { + const ExprPtr& current = prod[i]; + bool parenthesize = false; + if (current->is() || current->is()) { + parenthesize = true; + serialized += L"("; + } + + serialized += to_string(current, options); + + if (parenthesize) { + serialized += L")"; + } + + if (i + 1 < prod.size()) { + serialized += L" * "; + } + } + + return serialized; +} + +std::wstring to_string(Sum const& sum, const SerializationOptions& options) { + std::wstring serialized; + + for (std::size_t i = 0; i < sum.size(); ++i) { + ExprPtr& current = sum[i]; + + const bool parenthesize = current->is(); + + std::wstring current_serialized = to_string(current, options); + + bool is_negative = false; + if (parenthesize) { + current_serialized += L"(" + current_serialized + L")"; + } else { + is_negative = current_serialized.front() == L'-'; + } + + if (i > 0) { + if (is_negative) { + serialized += L" - " + current_serialized.substr(1); + } else { + serialized += L" + " + current_serialized; + } + } else { + serialized = std::move(current_serialized); + } + } + return serialized; +} + +} // namespace details + +std::wstring to_string(const ExprPtr& expr, + const SerializationOptions& options) { + if (!expr) return {}; + + return v1::to_string(*expr, options); +} + +std::wstring to_string(const Expr& expr, const SerializationOptions& options) { + using namespace details; + if (expr.is()) + return details::to_string(expr.as(), options); + else if (expr.is()) + return v1::to_string(expr.as(), options); + else if (expr.is()) + return v1::to_string(expr.as(), options); + else if (expr.is()) + return details::to_string(expr.as(), options); + else if (expr.is()) + return details::to_string(expr.as(), options); + else if (expr.is()) + return details::to_string(expr.as(), options); + else if (expr.is()) + return details::to_string(expr.as(), options); + else + throw std::runtime_error("Unsupported expr type for serialize!"); +} + +std::wstring to_string(const ResultExpr& result, + const SerializationOptions& options) { + std::wstring serialized; + if (result.produces_tensor()) { + serialized = details::to_string(result.result_as_tensor(L"?"), options); + } else { + serialized = details::to_string(result.result_as_variable(L"?"), options); + } + + return serialized + L" = " + v1::to_string(result.expression(), options); +} + +std::wstring to_string(const Index& index, const SerializationOptions&) { + std::wstring serialized(index.label()); + + if (index.has_proto_indices()) { + serialized += L"<"; + const auto& protos = index.proto_indices(); + for (std::size_t i = 0; i < protos.size(); ++i) { + serialized += protos[i].label(); + + if (i + 1 < protos.size()) { + serialized += L","; + } + } + serialized += L">"; + } + + return serialized; +} + +std::wstring to_string(AbstractTensor const& tensor, + const SerializationOptions& options) { + std::wstring serialized(tensor._label()); + serialized += L"{" + details::serialize_indices(tensor._bra(), options); + if (tensor._ket_rank() > 0) { + serialized += L";" + details::serialize_indices(tensor._ket(), options); + } + if (tensor._aux_rank() > 0) { + if (tensor._ket_rank() == 0) { + serialized += L";"; + } + serialized += L";" + details::serialize_indices(tensor._aux(), options); + } + serialized += L"}"; + + if (options.annot_symm) { + serialized += L":" + details::serialize_symm(tensor._symmetry(), options); + serialized += + L"-" + details::serialize_symm(tensor._braket_symmetry(), options); + serialized += + L"-" + details::serialize_symm(tensor._column_symmetry(), options); + } + + return serialized; +} + +template +std::wstring to_string(NormalOperator const& nop, + const SerializationOptions& options) { + std::wstring serialized(nop.label()); + serialized += L"{" + details::serialize_ops(nop.annihilators(), options); + if (nop.ncreators() > 0) { + serialized += L";" + details::serialize_ops(nop.creators(), options); + } + serialized += L"}"; + + return serialized; +} + +template std::wstring to_string( + NormalOperator const& nop, + const SerializationOptions& options); +template std::wstring to_string( + NormalOperator const& nop, + const SerializationOptions& options); + +} // namespace sequant::io::serialization::v1 diff --git a/SeQuant/core/io/shorthands.hpp b/SeQuant/core/io/shorthands.hpp new file mode 100644 index 0000000000..4c7571da14 --- /dev/null +++ b/SeQuant/core/io/shorthands.hpp @@ -0,0 +1,51 @@ +#ifndef SEQUANT_CORE_IO_SHORTHANDS_HPP +#define SEQUANT_CORE_IO_SHORTHANDS_HPP + +/// @file This header includes a couple of shorthands that essentially consist +/// of simple wrapper functions directly in the sequant namespace which delegate +/// to the respective functions in the sequant::io namespace. + +#include +#include +#include +#include + +#include + +namespace sequant { + +/// Shorthand for io::latex::to_string +template + requires(io::convertible_to_latex) +decltype(auto) to_latex(T &&expr) { + return io::latex::to_string(std::forward(expr)); +} + +/// Shorthand for io::serialization::to_string +template + requires(io::serializable) +decltype(auto) serialize( + T &&t, const io::serialization::SerializationOptions &options = {}) { + return io::serialization::to_string(std::forward(t), options); +} + +/// Shorthand for io::serialization::from_string +template + requires(io::deserializable) +decltype(auto) deserialize( + std::string_view input, + const io::serialization::DeserializationOptions &options = {}) { + return io::serialization::from_string(input, options); +} + +template + requires(io::deserializable) +decltype(auto) deserialize( + std::wstring_view input, + const io::serialization::DeserializationOptions &options = {}) { + return io::serialization::from_string(input, options); +} + +} // namespace sequant + +#endif // SEQUANT_CORE_IO_SHORTHANDS_HPP diff --git a/SeQuant/core/latex.cpp b/SeQuant/core/latex.cpp deleted file mode 100644 index c83f07d2de..0000000000 --- a/SeQuant/core/latex.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by Eduard Valeyev on 7/18/23. -// - -#include - -namespace sequant::detail { - -#define SQ_IMPL1(CHAR) \ - template std::basic_string greek_characters_to_latex_impl< \ - CHAR, std::char_traits, std::allocator>( \ - std::basic_string_view); - -SQ_IMPL1(char); -SQ_IMPL1(wchar_t); -SQ_IMPL1(char8_t); -SQ_IMPL1(char16_t); -SQ_IMPL1(char32_t); - -#define SQ_IMPL2(CHAR) \ - template std::basic_string diactrics_to_latex_impl< \ - CHAR, std::char_traits, std::allocator>( \ - std::basic_string_view); - -SQ_IMPL2(char); -SQ_IMPL2(wchar_t); -SQ_IMPL2(char8_t); -SQ_IMPL2(char16_t); -SQ_IMPL2(char32_t); - -} // namespace sequant::detail diff --git a/SeQuant/core/meta.hpp b/SeQuant/core/meta.hpp index 23b20f76bb..84a1f36e78 100644 --- a/SeQuant/core/meta.hpp +++ b/SeQuant/core/meta.hpp @@ -315,19 +315,6 @@ struct has_memfn_to_latex( template static constexpr bool has_memfn_to_latex_v = has_memfn_to_latex::value; -///////// has_memfn_to_wolfram ///////// - -template > -struct has_memfn_to_wolfram : public std::false_type {}; - -template -struct has_memfn_to_wolfram( - std::declval().to_wolfram()))>> - : public std::true_type {}; - -template -static constexpr bool has_memfn_to_wolfram_v = has_memfn_to_wolfram::value; - /// is_range namespace is_range_impl { // detects presence of std::{begin,end} diff --git a/SeQuant/core/op.hpp b/SeQuant/core/op.hpp index 02067b0326..13169b71e8 100644 --- a/SeQuant/core/op.hpp +++ b/SeQuant/core/op.hpp @@ -931,11 +931,11 @@ class NormalOperator : public Operator, }; static_assert( - is_tensor_v>, + is_tensor>, "The NormalOperator class does not fulfill the " "requirements of the Tensor interface"); static_assert( - is_tensor_v>, + is_tensor>, "The NormalOperator class does not fulfill the " "requirements of the Tensor interface"); @@ -1100,16 +1100,6 @@ inline ExprPtr fannx(Index i, Attr &&...attr) { return ex(cre(), ann({fann(i, std::forward(attr)...)})); } -template -std::wstring to_latex(const NormalOperator &op) { - return op.to_latex(); -} - -template -std::wstring to_latex(const NormalOperatorSequence &opseq) { - return opseq.to_latex(); -} - namespace detail { struct OpIdRegistrar { OpIdRegistrar(); diff --git a/SeQuant/core/parse.hpp b/SeQuant/core/parse.hpp deleted file mode 100644 index 88550b830d..0000000000 --- a/SeQuant/core/parse.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef SEQUANT_PARSE_HPP -#define SEQUANT_PARSE_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include - -/// -/// Create SeQuant expression from string input. -/// -/// @author: Bimal Gaudel -/// version: 21 July, 2021 -/// - -namespace sequant { - -struct ParseError : std::runtime_error { - std::size_t offset; - std::size_t length; - - ParseError(std::size_t offset, std::size_t length, std::string message); -}; - -// clang-format off -/// \brief Construct expressions from string representations -/// -/// \param raw A tensor algebra expression. A valid expression is -/// a product, a sum or a mix of those including parentheses. -/// eg. 'A{i1, a1; i2, a2}' A is the label (non-space), i1, a1 are bra indices, i2, a2 are ket indices. -/// 'A{i_1, a_1; i_2, a_2}' same as above with alternate notation for indices -/// 'A_{i1, a1}^{i2, a2}' same as above with alternate notation for bra and ket -/// 'A^{i2, a2}_{i1, a1}' same as above with alternate notation for bra and ket -/// 'A{i1,i2; a1,a2} + B{i2,i1; a1,a2}' a sum of tensors -/// 'A{i1,i2; a3,a4} * B{a3,a4; a1,a2}' a product of tensors -/// 'A{i1,i2; a3,a4} * B{a3,a4; a1,a2} + C{i1,i2;a1,a2}' a sum and a product of tensors -/// 'A{i1,i2; a3,a4} * (B{a3,a4; a1,a2} + C{a3,a4; a1,a2}) a parenthesized expression -/// '0.5 * t{i1;a1} * f{i1; a1}' tensor product with a scalar -/// '1/2 * t{i1;a1} * f{i1; a1}' same as above (fractions supported) -/// '1./2. * t{i1;a1} * f{i1; a1}' same as above num. and denom. are automatically cast to double -/// '1.0/2.0 * t{i1;a1} * f{i1; a1}' same as above -/// 't{i1,i2; a1, a2}' a tensor having indices with proto indices. -/// a1 is an index with i1 and i2 as proto-indices. -/// Every tensor may optionally be annoted with index symmetry specifications. The general syntax is -/// ` [: [- [-]]]` -/// (no whitespace is allowed at this place). Examples are -/// 't{i1;i2}:A', 't{i1;i2}:A-S', 't{i1;i2}:N-C-S' -/// Possible values for `` are -/// - 'A' for antisymmetry (sequant::Symmetry::Antisymm) -/// - 'S' for symmetric (sequant::Symmetry::Symm) -/// - 'N' for non-symmetric (sequant::Symmetry::Nonsymm) -/// Possible values for `` are -/// - 'C' for antisymmetry (sequant::BraKetSymmetry::Conjugate) -/// - 'S' for symmetric (sequant::BraKetSymmetry::Symm) -/// - 'N' for non-symmetric (sequant::BraKetSymmetry::Nonsymm) -/// Possible values for `` are -/// - 'S' for symmetric (sequant::ColumnSymmetry::Symm) -/// - 'N' for non-symmetric (sequant::ColumnSymmetry::Nonsymm) -/// \param perm_symm Default index permutation symmetry to be used if tensors don't specify a permutation -/// symmetry explicitly. -/// \param braket_symm Default BraKet symmetry to be used if tensors don't specify a BraKet symmetry explicitly. -/// \param column_symm Default particle symmetry to be used if tensors don't specify a particle symmetry explicitly. -/// @c raw expression. Explicit tensor symmetry can -/// be annotated in the expression itself. In that case, the -/// annotated symmetry will be used. -/// eg. 'g{i1, a1; i2, a2}:A' tensor with 'sequant::Symmetry::Antisymm' annotation -/// 'g{i1, a1; i2, a2}:S' tensor with 'sequant::Symmetry::Symm' annotation -/// 'g{i1, a1; i2, a2}:N' tensor with 'sequant::Symmetry::Nonsymm' annotation -/// \return SeQuant expression. -// clang-format on -ExprPtr parse_expr(std::wstring_view raw, - std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); - -/// \sa parse_expr -ExprPtr parse_expr(std::string_view raw, std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); - -/// \sa parse_expr -ResultExpr parse_result_expr(std::wstring_view raw, - std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); - -/// \sa parse_expr -ResultExpr parse_result_expr(std::string_view raw, - std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); - -/// -/// Get a parsable string from an expression. -/// -/// An expression that has a flat structure (ie. product does not contain -/// sum or product subexpressions) is guaranteed to be reparsable to itself so -/// that equality comparison holds: x == parse_expr(deparse_expr(x)). For nested -/// expressions the equality comparison is not guaranteed, however, for such -/// an expression x, its corresponding evaluation node, eval_node(x), and -/// the parsed evaluation node, eval_node(parse_expr(deparse_expr(x))) are -/// equivalent. -/// -/// \param expr Expression to stringify that can be re-parsed to itself. -/// \param annot_sym Whether to add sequant::Symmetry annotation -/// to each Tensor string. -/// \return wstring of the expression. -std::wstring deparse(const ResultExpr &expr, bool annot_sym = true); -std::wstring deparse(const ExprPtr &expr, bool annot_sym = true); -std::wstring deparse(const Expr &expr, bool annot_sym = true); -std::wstring deparse(const Product &product, bool annot_sym); -std::wstring deparse(const Sum &sum, bool annot_sym); -std::wstring deparse(const Tensor &tensor, bool annot_sym = true); -std::wstring deparse(const AbstractTensor &tensor, bool annot_sym = true); -template -std::wstring deparse(const NormalOperator &nop); -std::wstring deparse(const Variable &variable); -std::wstring deparse(const Constant &constant); -std::wstring deparse(const Index &index); - -} // namespace sequant - -#endif // SEQUANT_PARSE_HPP diff --git a/SeQuant/core/parse/deparse.cpp b/SeQuant/core/parse/deparse.cpp deleted file mode 100644 index 69209fe1a8..0000000000 --- a/SeQuant/core/parse/deparse.cpp +++ /dev/null @@ -1,316 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace sequant { - -namespace details { - -template -std::wstring deparse_indices(Range&& indices) { - std::wstring deparsed; - - for (std::size_t i = 0; i < indices.size(); ++i) { - deparsed += deparse(indices[i]); - - if (i + 1 < indices.size()) { - deparsed += L","; - } - } - - return deparsed; -} - -template -std::wstring deparse_ops(const Range& ops) { - std::wstring deparsed; - - for (std::size_t i = 0; i < ops.size(); ++i) { - deparsed += deparse(ops[i].index()); - - if (i + 1 < ops.size()) { - deparsed += L","; - } - } - - return deparsed; -} - -std::wstring deparse_symm(Symmetry symm) { - switch (symm) { - case Symmetry::Symm: - return L"S"; - case Symmetry::Antisymm: - return L"A"; - case Symmetry::Nonsymm: - return L"N"; - } - - SEQUANT_UNREACHABLE; -} - -std::wstring deparse_symm(BraKetSymmetry symm) { - switch (symm) { - case BraKetSymmetry::Conjugate: - return L"C"; - case BraKetSymmetry::Symm: - return L"S"; - case BraKetSymmetry::Nonsymm: - return L"N"; - } - - SEQUANT_UNREACHABLE; -} - -std::wstring deparse_symm(ColumnSymmetry symm) { - switch (symm) { - case ColumnSymmetry::Symm: - return L"S"; - case ColumnSymmetry::Nonsymm: - return L"N"; - } - - SEQUANT_UNREACHABLE; -} - -std::wstring deparse_scalar(const Constant::scalar_type& scalar) { - if (scalar == 0) { - return L"0"; - } - - const auto& real = scalar.real(); - const auto& realNumerator = boost::multiprecision::numerator(real); - const auto& realDenominator = boost::multiprecision::denominator(real); - const auto& imag = scalar.imag(); - const auto& imagNumerator = boost::multiprecision::numerator(imag); - const auto& imagDenominator = boost::multiprecision::denominator(imag); - - std::string deparsed; - if (realNumerator != 0) { - deparsed += realNumerator.str(); - - if (realDenominator != 1) { - deparsed += "/" + realDenominator.str(); - } - } - if (imagNumerator != 0) { - if (!deparsed.empty()) { - deparsed += imagNumerator < 0 ? " + i " : " - i "; - } - - deparsed += imagNumerator.str(); - - if (imagDenominator != 1) { - deparsed += "/" + imagDenominator.str(); - } - } - - SEQUANT_ASSERT(!deparsed.empty()); - - return to_wstring(deparsed); -} - -} // namespace details - -std::wstring deparse(const ExprPtr& expr, bool annot_sym) { - if (!expr) return {}; - - return deparse(*expr, annot_sym); -} - -std::wstring deparse(const Expr& expr, bool annot_sym) { - using namespace details; - if (expr.is()) - return deparse(expr.as(), annot_sym); - else if (expr.is()) - return deparse(expr.as()); - else if (expr.is()) - return deparse(expr.as()); - else if (expr.is()) - return deparse(expr.as(), annot_sym); - else if (expr.is()) - return deparse(expr.as(), annot_sym); - else if (expr.is()) - return deparse(expr.as()); - else if (expr.is()) - return deparse(expr.as()); - else - throw std::runtime_error("Unsupported expr type for deparse!"); -} - -std::wstring deparse(const ResultExpr& result, bool annot_sym) { - std::wstring deparsed; - if (result.produces_tensor()) { - deparsed = deparse(result.result_as_tensor(L"?"), annot_sym); - } else { - deparsed = deparse(result.result_as_variable(L"?"), annot_sym); - } - - return deparsed + L" = " + deparse(result.expression(), annot_sym); -} - -std::wstring deparse(const Index& index) { - std::wstring deparsed(index.label()); - - if (index.has_proto_indices()) { - deparsed += L"<"; - const auto& protos = index.proto_indices(); - for (std::size_t i = 0; i < protos.size(); ++i) { - deparsed += protos[i].label(); - - if (i + 1 < protos.size()) { - deparsed += L","; - } - } - deparsed += L">"; - } - - return deparsed; -} - -std::wstring deparse(Tensor const& tensor, bool annot_sym) { - std::wstring deparsed(tensor.label()); - deparsed += L"{" + details::deparse_indices(tensor.bra()); - if (tensor.ket_rank() > 0) { - deparsed += L";" + details::deparse_indices(tensor.ket()); - } - if (tensor.aux_rank() > 0) { - if (tensor.ket_rank() == 0) { - deparsed += L";"; - } - deparsed += L";" + details::deparse_indices(tensor.aux()); - } - deparsed += L"}"; - - if (annot_sym) { - deparsed += L":" + details::deparse_symm(tensor.symmetry()); - deparsed += L"-" + details::deparse_symm(tensor.braket_symmetry()); - deparsed += L"-" + details::deparse_symm(tensor.column_symmetry()); - } - - return deparsed; -} - -std::wstring deparse(AbstractTensor const& tensor, bool annot_sym) { - std::wstring deparsed(tensor._label()); - deparsed += L"{" + details::deparse_indices(tensor._bra()); - if (tensor._ket_rank() > 0) { - deparsed += L";" + details::deparse_indices(tensor._ket()); - } - if (tensor._aux_rank() > 0) { - if (tensor._ket_rank() == 0) { - deparsed += L";"; - } - deparsed += L";" + details::deparse_indices(tensor._aux()); - } - deparsed += L"}"; - - if (annot_sym) { - deparsed += L":" + details::deparse_symm(tensor._symmetry()); - deparsed += L"-" + details::deparse_symm(tensor._braket_symmetry()); - deparsed += L"-" + details::deparse_symm(tensor._column_symmetry()); - } - - return deparsed; -} - -template -std::wstring deparse(NormalOperator const& nop) { - std::wstring deparsed(nop.label()); - deparsed += L"{" + details::deparse_ops(nop.annihilators()); - if (nop.ncreators() > 0) { - deparsed += L";" + details::deparse_ops(nop.creators()); - } - deparsed += L"}"; - - return deparsed; -} - -template std::wstring deparse( - NormalOperator const& nop); -template std::wstring deparse( - NormalOperator const& nop); - -std::wstring deparse(const Constant& constant) { - return details::deparse_scalar(constant.value()); -} - -std::wstring deparse(const Variable& variable) { - return std::wstring(variable.label()) + (variable.conjugated() ? L"^*" : L""); -} - -std::wstring deparse(Product const& prod, bool annot_sym) { - std::wstring deparsed; - - const auto& scal = prod.scalar(); - if (scal != Product::scalar_type{1}) { - deparsed += details::deparse_scalar(scal) + L" "; - } - - for (std::size_t i = 0; i < prod.size(); ++i) { - const ExprPtr& current = prod[i]; - bool parenthesize = false; - if (current->is() || current->is()) { - parenthesize = true; - deparsed += L"("; - } - - deparsed += deparse(current, annot_sym); - - if (parenthesize) { - deparsed += L")"; - } - - if (i + 1 < prod.size()) { - deparsed += L" * "; - } - } - - return deparsed; -} - -std::wstring deparse(Sum const& sum, bool annot_sym) { - std::wstring deparsed; - - for (std::size_t i = 0; i < sum.size(); ++i) { - ExprPtr& current = sum[i]; - - const bool parenthesize = current->is(); - - std::wstring current_deparsed = deparse(current, annot_sym); - - bool is_negative = false; - if (parenthesize) { - current_deparsed += L"(" + current_deparsed + L")"; - } else { - is_negative = current_deparsed.front() == L'-'; - } - - if (i > 0) { - if (is_negative) { - deparsed += L" - " + current_deparsed.substr(1); - } else { - deparsed += L" + " + current_deparsed; - } - } else { - deparsed = std::move(current_deparsed); - } - } - return deparsed; -} - -} // namespace sequant diff --git a/SeQuant/core/rational.hpp b/SeQuant/core/rational.hpp index c2213d170f..567d93d591 100644 --- a/SeQuant/core/rational.hpp +++ b/SeQuant/core/rational.hpp @@ -2,7 +2,7 @@ #define SEQUANT_CORE_RATIONAL_H #include -#include +#include #include #include @@ -125,62 +125,13 @@ inline std::string to_string(const boost::multiprecision::number& i) { } inline std::wstring to_wstring(const rational& i) { - return ::sequant::to_wstring(boost::lexical_cast(i)); + return toUtf16(boost::lexical_cast(i)); } template inline std::wstring to_wstring( const boost::multiprecision::number& i) { - return ::sequant::to_wstring(boost::lexical_cast(i)); -} - -template -inline std::wstring to_latex(const boost::multiprecision::number& t) { - std::wstring result = L"{"; - using ::sequant::to_wstring; - result += to_wstring(t) + L"}"; - return result; -} - -inline std::wstring to_latex(const rational& t) { - // n.b. skip enclosing braces to make Constant::to_latex to produce same - // output as before std::wstring result = L"{"; - std::wstring result; - if (denominator(t) == 1) - result += to_latex(numerator(t)); - else { - const auto num = numerator(t); - // n.b. extra braces around \frac and use of to_wstring instead of to_latex - // to avoid extra braces around args to \frac - if (num > 0) { - result += L"{\\frac{" + to_wstring(numerator(t)) + L"}{" + - to_wstring(denominator(t)) + L"}}"; - } else if (num < 0) { - result += L"{-\\frac{" + to_wstring(-num) + L"}{" + - to_wstring(denominator(t)) + L"}}"; - } else - result += L"0"; - } - // n.b. - // result += L"}"; - return result; -} - -template -inline std::wstring to_wolfram( - const boost::multiprecision::number& t) { - return ::sequant::to_wstring(t); -} - -inline std::wstring to_wolfram(const rational& t) { - using ::sequant::to_wstring; - if (denominator(t) == 1) { - // n.b. use to_string to skip extra braces so that output agrees with code - // that used scalars return to_wolfram(t.numerator()); - return to_wstring(numerator(t)); - } else - return std::wstring(L"Rational[") + to_wolfram(numerator(t)) + L"," + - to_wolfram(denominator(t)) + L"]"; + return toUtf16(boost::lexical_cast(i)); } } // namespace sequant diff --git a/SeQuant/core/space.cpp b/SeQuant/core/space.cpp index ae267ce1c3..22fb718b23 100644 --- a/SeQuant/core/space.cpp +++ b/SeQuant/core/space.cpp @@ -48,7 +48,7 @@ std::string to_string(const IndexSpace& space) { std::ostringstream oss; oss << "{attr=" << to_string(space.attr()); if (space.base_key().empty() == false) { - oss << ",base_key=" << to_string(space.base_key()); + oss << ",base_key=" << toUtf8(space.base_key()); } oss << ",approximate_size=" << std::to_string(space.approximate_size()) << "}"; diff --git a/SeQuant/core/space.hpp b/SeQuant/core/space.hpp index 824d57c5d9..e0d4891dea 100644 --- a/SeQuant/core/space.hpp +++ b/SeQuant/core/space.hpp @@ -12,7 +12,6 @@ #include #include #include -#include #include @@ -381,8 +380,7 @@ class IndexSpace { bad_key() : std::invalid_argument("bad key") {} template bad_key(S &&key) - : std::invalid_argument(std::string("bad key: ") + - sequant::to_string(key)) {} + : std::invalid_argument(std::string("bad key: ") + toUtf8(key)) {} }; struct KeyCompare { @@ -448,7 +446,7 @@ class IndexSpace { QuantumNumbersAttr qnattr = QuantumNumbersAttr{0}, unsigned long approximate_size = 10) : attr_(typeattr, qnattr), - base_key_(sequant::to_wstring(std::forward(type_label))), + base_key_(toUtf16(std::forward(type_label))), approximate_size_(approximate_size) {} explicit IndexSpace(std::string_view label); @@ -488,9 +486,9 @@ class IndexSpace { static std::wstring reduce_key(std::string_view key) { const auto underscore_position = key.rfind(L'_'); if (underscore_position != std::string::npos) { // key can be reduced - return sequant::to_wstring(key.substr(0, underscore_position)); + return toUtf16(key.substr(0, underscore_position)); } else { - return sequant::to_wstring(key); + return toUtf16(key); } } @@ -504,10 +502,6 @@ class IndexSpace { Attr attr_ = {}; std::wstring base_key_ = {}; std::size_t approximate_size_ = {}; - - static std::wstring to_wstring(std::wstring_view key) { - return std::wstring(key.begin(), key.end()); - } }; // class IndexSpace inline const IndexSpace IndexSpace::null; diff --git a/SeQuant/core/tensor_canonicalizer.hpp b/SeQuant/core/tensor_canonicalizer.hpp index a4370714fa..55c9f8c96c 100644 --- a/SeQuant/core/tensor_canonicalizer.hpp +++ b/SeQuant/core/tensor_canonicalizer.hpp @@ -173,8 +173,6 @@ class DefaultTensorCanonicalizer : public TensorCanonicalizer { template ExprPtr apply(AbstractTensor& t, const IndexComp& idxcmp, const IndexPairComp& paircmp) const { - // std::wcout << "abstract tensor: " << to_latex(t) << "\n"; - // nothing to do for non-particle-symmetric tensors if (t._column_symmetry() == ColumnSymmetry::Nonsymm) return nullptr; @@ -200,7 +198,6 @@ class DefaultTensorCanonicalizer : public TensorCanonicalizer { case Symmetry::Symm: { auto _bra = mutable_bra_range(t); auto _ket = mutable_ket_range(t); - // std::wcout << "canonicalizing " << to_latex(t); reset_ts_swap_counter(); // std::{stable_}sort does not necessarily use swap! so must implement // sort ourselves .. thankfully ranks will be low so can stick with @@ -208,8 +205,6 @@ class DefaultTensorCanonicalizer : public TensorCanonicalizer { bubble_sort(begin(_bra), end(_bra), idxcmp); bubble_sort(begin(_ket), end(_ket), idxcmp); if (is_antisymm) even = ts_swap_counter_is_even(); - // std::wcout << " is " << (even ? "even" : "odd") << " and - // produces " << to_latex(t) << std::endl; } break; case Symmetry::Nonsymm: { diff --git a/SeQuant/core/tensor_network/v1.cpp b/SeQuant/core/tensor_network/v1.cpp index 29f8b7c5cf..df986c72b9 100644 --- a/SeQuant/core/tensor_network/v1.cpp +++ b/SeQuant/core/tensor_network/v1.cpp @@ -10,14 +10,15 @@ #include #include #include -#include +#include +#include #include #include #include #include #include +#include #include -#include #include #include @@ -608,7 +609,8 @@ TensorNetworkV1::GraphData TensorNetworkV1::make_bliss_graph( vertex_labels.emplace_back(tlabel); } if (options.make_texlabels) { - vertex_texlabels.emplace_back(L"$" + utf_to_latex(tlabel) + L"$"); + vertex_texlabels.emplace_back(L"$" + io::latex::utf_to_string(tlabel) + + L"$"); } vertex_type.emplace_back(VertexType::TensorCore); vertex_color.push_back(colorizer(*t)); diff --git a/SeQuant/core/tensor_network/v2.cpp b/SeQuant/core/tensor_network/v2.cpp index 1aa8226a52..c2221827ed 100644 --- a/SeQuant/core/tensor_network/v2.cpp +++ b/SeQuant/core/tensor_network/v2.cpp @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -18,9 +19,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -754,7 +755,8 @@ TensorNetworkV2::Graph TensorNetworkV2::create_graph( ++nvertex; if (options.make_labels) graph.vertex_labels.emplace_back(tlabel); if (options.make_texlabels) - graph.vertex_texlabels.emplace_back(L"$" + utf_to_latex(tlabel) + L"$"); + graph.vertex_texlabels.emplace_back( + L"$" + io::latex::utf_to_string(tlabel) + L"$"); graph.vertex_types.emplace_back(VertexType::TensorCore); graph.vertex_colors.push_back(colorizer(tensor)); diff --git a/SeQuant/core/tensor_network/v3.cpp b/SeQuant/core/tensor_network/v3.cpp index e621d869c6..b51573b811 100644 --- a/SeQuant/core/tensor_network/v3.cpp +++ b/SeQuant/core/tensor_network/v3.cpp @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -939,7 +941,7 @@ TensorNetworkV3::Graph TensorNetworkV3::create_graph( if (options.make_labels) make_label(std::wstring{tlabel}); if (options.make_xlabels) make_xlabel(); if (options.make_texlabels) - make_texlabel(L"$" + utf_to_latex(tlabel) + L"$"); + make_texlabel(L"$" + io::latex::utf_to_string(tlabel) + L"$"); graph.vertex_types.emplace_back(VertexType::TensorCore); const auto tensor_color = colorizer.apply_shade(tensor); // subsequent vertices will be shaded by diff --git a/SeQuant/core/utility/expr.cpp b/SeQuant/core/utility/expr.cpp index add339fd7a..eb4a84f7d5 100644 --- a/SeQuant/core/utility/expr.cpp +++ b/SeQuant/core/utility/expr.cpp @@ -51,7 +51,7 @@ std::string toplevel_diff(const Variable &lhs, const Variable &rhs) { } if (lhs.label() != rhs.label()) { - return to_string(lhs.label()) + " vs. " + to_string(rhs.label()); + return toUtf8(lhs.label()) + " vs. " + toUtf8(rhs.label()); } return (lhs.conjugated() ? "conjugated" @@ -115,8 +115,8 @@ std::string diff_spaces(const IndexSpace &lhs, const IndexSpace &rhs) { stream << "QNs differ: " << AttrSet(lhs.qns().to_int32()) << " vs. " << AttrSet(rhs.qns().to_int32()); } else if (lhs.base_key() != rhs.base_key()) { - stream << "Base key differs: " << to_string(lhs.base_key()) << " vs. " - << to_string(rhs.base_key()); + stream << "Base key differs: " << toUtf8(lhs.base_key()) << " vs. " + << toUtf8(rhs.base_key()); } else if (lhs.approximate_size() != rhs.approximate_size()) { stream << "Size differs: " << std::to_string(lhs.approximate_size()) << " vs. " << std::to_string(rhs.approximate_size()); @@ -134,7 +134,7 @@ std::string toplevel_diff(const Index &lhs, const Index &rhs) { } if (lhs.full_label() != rhs.full_label()) { - return to_string(lhs.full_label()) + " vs. " + to_string(rhs.full_label()); + return toUtf8(lhs.full_label()) + " vs. " + toUtf8(rhs.full_label()); } if (lhs.space() != rhs.space()) { @@ -167,8 +167,8 @@ std::string toplevel_diff(const Tensor &lhs, const Tensor &rhs) { } if (lhs.label() != rhs.label()) { - return "Names differ: " + to_string(lhs.label()) + " vs. " + - to_string(rhs.label()); + return "Names differ: " + toUtf8(lhs.label()) + " vs. " + + toUtf8(rhs.label()); } if (lhs.slots().size() != rhs.slots().size()) { @@ -177,18 +177,18 @@ std::string toplevel_diff(const Tensor &lhs, const Tensor &rhs) { } if (lhs.symmetry() != rhs.symmetry()) { - return "Symmetry differs: " + to_string(to_wstring(lhs.symmetry())) + - " vs. " + to_string(to_wstring(rhs.symmetry())); + return "Symmetry differs: " + toUtf8(to_wstring(lhs.symmetry())) + " vs. " + + toUtf8(to_wstring(rhs.symmetry())); } if (lhs.column_symmetry() != rhs.column_symmetry()) { return "Particle-Symmetry differs: " + - to_string(to_wstring(lhs.column_symmetry())) + " vs. " + - to_string(to_wstring(rhs.column_symmetry())); + toUtf8(to_wstring(lhs.column_symmetry())) + " vs. " + + toUtf8(to_wstring(rhs.column_symmetry())); } if (lhs.braket_symmetry() != rhs.braket_symmetry()) { return "BraKet-Symmetry differs: " + - to_string(to_wstring(lhs.braket_symmetry())) + " vs. " + - to_string(to_wstring(rhs.braket_symmetry())); + toUtf8(to_wstring(lhs.braket_symmetry())) + " vs. " + + toUtf8(to_wstring(rhs.braket_symmetry())); } if (lhs.bra() != rhs.bra()) { diff --git a/SeQuant/core/utility/indices.hpp b/SeQuant/core/utility/indices.hpp index 504ca00661..c2b008ab07 100644 --- a/SeQuant/core/utility/indices.hpp +++ b/SeQuant/core/utility/indices.hpp @@ -413,7 +413,7 @@ std::string csv_labels(Rng&& idxs) { auto v = concat(single(i.label()), // i.proto_indices() | transform(&Index::label)) // | join; - return sequant::to_string(v | ranges::to); + return toUtf8(v | ranges::to); }; return std::forward(idxs) // diff --git a/SeQuant/core/utility/string.cpp b/SeQuant/core/utility/string.cpp index 893144c038..83ae2c8bfc 100644 --- a/SeQuant/core/utility/string.cpp +++ b/SeQuant/core/utility/string.cpp @@ -21,6 +21,8 @@ std::string toUtf8(const std::wstring_view str) { return std::string(stream.begin(), stream.end()); } +std::string toUtf8(std::string_view str) { return std::string(str); } + std::wstring toUtf16(const std::string_view str) { auto stream = utf8::utf8to16(str); @@ -29,4 +31,6 @@ std::wstring toUtf16(const std::string_view str) { return std::wstring(stream.begin(), stream.end()); } +std::wstring toUtf16(std::wstring_view str) { return std::wstring(str); } + } // namespace sequant diff --git a/SeQuant/core/utility/string.hpp b/SeQuant/core/utility/string.hpp index 252e6177c4..f64632f69b 100644 --- a/SeQuant/core/utility/string.hpp +++ b/SeQuant/core/utility/string.hpp @@ -6,6 +6,8 @@ #define SEQUANT_CORE_UTILITY_STRING_HPP #include +#include + #include #include @@ -65,10 +67,28 @@ concept basic_string_convertible = /// Converts the given wide-string to a UTF-8 encoded narrow string std::string toUtf8(std::wstring_view str); +/// Pass-through (only converts to std::string) +std::string toUtf8(std::string_view str); + +inline std::string toUtf8(char c) { return toUtf8(std::string_view(&c, 1)); } + +inline std::string toUtf8(wchar_t c) { + return toUtf8(std::wstring_view(&c, 1)); +} + /// Converts the given UTF-8 encoded narrow-string to a UTF-16 encoded /// wide-string std::wstring toUtf16(std::string_view str); +/// Pass-through (only converts to std::wstring) +std::wstring toUtf16(std::wstring_view str); + +inline std::wstring toUtf16(char c) { return toUtf16(std::string_view(&c, 1)); } + +inline std::wstring toUtf16(wchar_t c) { + return toUtf16(std::wstring_view(&c, 1)); +} + template std::basic_string_view> to_basic_string_view(S &&str) { if constexpr (meta::is_char_v>) @@ -77,6 +97,52 @@ std::basic_string_view> to_basic_string_view(S &&str) { return str; } +/// Converts integral type to its std::wstring representation +std::wstring to_wstring(meta::integral auto t) { return std::to_wstring(t); } + +/// Converts real type to its std::wstring representation, converting to integer +/// if possible +std::wstring to_wstring(std::floating_point auto t) { + if (std::floor(t) == t) + return std::to_wstring(static_cast(t)); + else + return std::to_wstring(t); +} + +namespace detail { + +/// selects the string literal matching the code unit given by @p Char +template +constexpr decltype(auto) select_string_literal( + const char (&str)[NChar], const wchar_t (&wstr)[NWChar], + const char8_t (&u8str)[NChar8], const char16_t (&u16str)[NChar16], + const char32_t (&u32str)[NChar32]) { + if constexpr (std::is_same_v) + return str; + else if constexpr (std::is_same_v) + return wstr; + else if constexpr (std::is_same_v) + return u8str; + else if constexpr (std::is_same_v) + return u16str; + else if constexpr (std::is_same_v) + return u32str; + + SEQUANT_ABORT("Unhandled character type"); +} +} // namespace detail + +/// @brief Converts a base string literal to the desired code unit type +/// @param code_unit The desired code unit type; either `char` or `wchar_t` +/// @param char_string_literal `char`-based string literal +#define SQ_STRLIT(code_unit, char_string_literal) \ + ::sequant::detail::select_string_literal( \ + char_string_literal, SEQUANT_CONCAT(L, char_string_literal), \ + SEQUANT_CONCAT(u8, char_string_literal), \ + SEQUANT_CONCAT(u, char_string_literal), \ + SEQUANT_CONCAT(U, char_string_literal)) + } // namespace sequant #endif // SEQUANT_CORE_UTILITY_STRING_HPP diff --git a/SeQuant/core/wick.hpp b/SeQuant/core/wick.hpp index 712fac780e..21340706bd 100644 --- a/SeQuant/core/wick.hpp +++ b/SeQuant/core/wick.hpp @@ -10,12 +10,14 @@ #include #include +#include #include #include #include #include #include #include +#include namespace sequant { @@ -161,8 +163,8 @@ class WickTheorem { std::wstringstream ss; ss << L"WickTheorem::set_external_indices: " L"external index " + - to_latex(Index(v)) + L" repeated"; - throw std::invalid_argument(to_string(ss.str())); + io::latex::to_string(Index(v)) + L" repeated"; + throw std::invalid_argument(toUtf8(ss.str())); } }); } @@ -1354,12 +1356,12 @@ class WickTheorem { ranges::get_cursor(op_right_iter))) { if (Logger::instance().wick_contract) { std::wcout << "level " << state.level << ":contracting " - << to_latex(*op_left_iter) << " with " - << to_latex(*op_right_iter) + << io::latex::to_string(*op_left_iter) << " with " + << io::latex::to_string(*op_right_iter) << " (nop_top_degen=" << nop_top_degen << ")" << std::endl; - std::wcout << " current nopseq = " << to_latex(state.nopseq) - << std::endl; + std::wcout << " current nopseq = " + << io::latex::to_string(state.nopseq) << std::endl; } // update the phase, if needed @@ -1392,7 +1394,7 @@ class WickTheorem { --state.nopseq_size; // std::wcout << " nopseq after contraction = " << - // to_latex(state.nopseq) << std::endl; + // io::latex::to_string(state.nopseq) << std::endl; // if have a nonzero result ... if (state.sp.size() != state.sp_initial_size) { @@ -1430,7 +1432,7 @@ class WickTheorem { result.second->lock(); // std::wcout << "got " << - // to_latex(state.sp) + // io::latex::to_string(state.sp) // << std::endl; result.first->append(std::move(prefactor)); // std::wcout << "now up to " << @@ -1508,7 +1510,7 @@ class WickTheorem { ranges::get_cursor(op_left_iter), ranges::get_cursor(op_right_iter)); // std::wcout << " restored nopseq = " << - // to_latex(state.opseq) << std::endl; + // io::latex::to_string(state.opseq) << std::endl; } // connect succeeded } // topologically-unique contraction } // can_contract diff --git a/SeQuant/core/wick.impl.hpp b/SeQuant/core/wick.impl.hpp index 5ff151c048..6a0b9ba2b7 100644 --- a/SeQuant/core/wick.impl.hpp +++ b/SeQuant/core/wick.impl.hpp @@ -6,6 +6,7 @@ #define SEQUANT_WICK_IMPL_HPP #include +#include #include #include #include @@ -620,8 +621,8 @@ bool reduce_wick_impl(std::shared_ptr &expr, }); sequant::wprintf("\n replrules = "); ranges::for_each(replacement_rules, [](auto &index) { - sequant::wprintf(to_latex(index.first), "\\to", to_latex(index.second), - "\\,"); + sequant::wprintf(io::latex::to_string(index.first), "\\to", + io::latex::to_string(index.second), "\\,"); }); } @@ -834,7 +835,7 @@ ExprPtr WickTheorem::compute(const bool count_only, if (Logger::instance().wick_topology) std::wcout << "WickTheorem::compute: input to topology computation = " - << to_latex(expr_input_) << std::endl; + << io::latex::to_string(expr_input_) << std::endl; // construct graph representation of the tensor product using TN = TensorNetwork; @@ -1233,7 +1234,8 @@ ExprPtr WickTheorem::compute(const bool count_only, if (Logger::instance().wick_contract) { std::wcout << "WickTheorem::compute: input to compute_nopseq = {\n"; - for (auto &&nop : input_) std::wcout << to_latex(nop) << "\n"; + for (auto &&nop : input_) + std::wcout << io::latex::to_string(nop) << "\n"; std::wcout << "}" << std::endl; } diff --git a/SeQuant/core/wolfram.hpp b/SeQuant/core/wolfram.hpp deleted file mode 100644 index 43ee615ecb..0000000000 --- a/SeQuant/core/wolfram.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by Eduard Valeyev on 2/8/19. -// - -#ifndef SEQUANT_WOLFRAM_HPP -#define SEQUANT_WOLFRAM_HPP - -#include -#include - -#include -#include - -namespace sequant { - -template -std::enable_if_t>, std::wstring> -to_wolfram(T &&t) { - return t.to_wolfram(); -} - -template -std::enable_if_t>, std::wstring> -to_wolfram(T &&t) { - using ::sequant::to_wstring; - return to_wstring(t); -} - -template -std::wstring to_wolfram(const std::complex &t) { - using ::sequant::to_wstring; - if (t.imag() == 0) - return to_wolfram(t.real()); - else - return std::wstring(L"Complex[") + to_wstring(t.real()) + L"," + - to_wstring(t.imag()) + L"]"; -} - -} // namespace sequant - -#endif // SEQUANT_WOLFRAM_HPP diff --git a/SeQuant/core/wstring.hpp b/SeQuant/core/wstring.hpp deleted file mode 100644 index 9d5b4f58ce..0000000000 --- a/SeQuant/core/wstring.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// -// Created by Eduard Valeyev on 2019-02-11. -// - -#ifndef SEQUANT_WSTRING_HPP -#define SEQUANT_WSTRING_HPP - -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace sequant { - -/// Converts integral type to its std::wstring representation -std::wstring to_wstring(meta::integral auto t) { return std::to_wstring(t); } - -/// Converts real type to its std::wstring representation, converting to integer -/// if possible -std::wstring to_wstring(std::floating_point auto t) { - if (std::floor(t) == t) - return std::to_wstring(static_cast(t)); - else - return std::to_wstring(t); -} - -/// @brief (potentially) narrowing character converter. -/// -/// Converts a UTF-8 encoded string (C or C++) to a UTF-8 encoded -/// std::string -template -std::string to_string(S&& str_utf8) { - auto str_utf8_view = to_basic_string_view(std::forward(str_utf8)); - using boost::locale::conv::utf_to_utf; - return utf_to_utf(str_utf8_view.data(), - str_utf8_view.data() + str_utf8_view.size()); -} - -/// Optimized to_string for std::string -inline std::string to_string(std::string&& str_utf8) { - return std::move(str_utf8); -} - -/// @brief (potentially) narrowing character converter. -/// -/// Converts a UTF-8 encoded std::basic_string_view to a UTF-8 encoded -/// std::wstring -template -std::wstring to_wstring(S&& str_utf8) { - auto str_utf8_view = to_basic_string_view(std::forward(str_utf8)); - using boost::locale::conv::utf_to_utf; - return utf_to_utf(str_utf8_view.data(), - str_utf8_view.data() + str_utf8_view.size()); -} - -/// Optimized to_wstring for std::wstring -inline std::wstring to_wstring(std::wstring&& str_utf8) { - return std::move(str_utf8); -} - -namespace detail { - -/// selects the string literal matching the code unit given by @p Char -template -constexpr decltype(auto) select_string_literal( - const char (&str)[NChar], const wchar_t (&wstr)[NWChar], - const char8_t (&u8str)[NChar8], const char16_t (&u16str)[NChar16], - const char32_t (&u32str)[NChar32]) { - if constexpr (std::is_same_v) - return str; - else if constexpr (std::is_same_v) - return wstr; - else if constexpr (std::is_same_v) - return u8str; - else if constexpr (std::is_same_v) - return u16str; - else if constexpr (std::is_same_v) - return u32str; - - SEQUANT_ABORT("Unhandled character type"); -} -} // namespace detail - -/// @brief Converts a base string literal to the desired code unit type -/// @param code_unit The desired code unit type; either `char` or `wchar_t` -/// @param char_string_literal `char`-based string literal -#define SQ_STRLIT(code_unit, char_string_literal) \ - detail::select_string_literal( \ - char_string_literal, SEQUANT_CONCAT(L, char_string_literal), \ - SEQUANT_CONCAT(u8, char_string_literal), \ - SEQUANT_CONCAT(u, char_string_literal), \ - SEQUANT_CONCAT(U, char_string_literal)) - -} // namespace sequant - -#endif // SEQUANT_WSTRING_HPP diff --git a/SeQuant/domain/mbpt/op.cpp b/SeQuant/domain/mbpt/op.cpp index 4f676975e9..62597d1d7f 100644 --- a/SeQuant/domain/mbpt/op.cpp +++ b/SeQuant/domain/mbpt/op.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -306,7 +307,7 @@ std::wstring to_latex(const mbpt::Operator& op) { // base_lbl is used for registry check, it should not have adjoint label or // perturbation order - auto base_lbl = sequant::to_wstring(op.label()); + auto base_lbl = std::wstring(op.label()); SEQUANT_ASSERT(!base_lbl.empty()); bool is_adjoint = false; if (base_lbl.back() == adjoint_label) { @@ -338,7 +339,7 @@ std::wstring to_latex(const mbpt::Operator& op) { base_lbl == reserved::symm_label(); // now start building the output - std::wstring label = utf_to_latex(op.label()); + std::wstring label = io::latex::utf_to_string(op.label()); auto result = has_hat ? L"{" + label : L"{\\hat{" + label + L"}"; auto op_qns = op(); // operator action i.e. quantum number change @@ -1386,8 +1387,8 @@ ExprPtr expectation_value_impl(ExprPtr expr, }); std::wcout << "\n replrules = "; ranges::for_each(replacement_rules, [](auto& index) { - std::wcout << to_latex(index.first) << "\\to" - << to_latex(index.second) << "\\,"; + std::wcout << io::latex::to_string(index.first) << "\\to" + << io::latex::to_string(index.second) << "\\,"; }); std::wcout.flush(); } diff --git a/SeQuant/domain/mbpt/op_registry.cpp b/SeQuant/domain/mbpt/op_registry.cpp index 1c80a8b57d..1f2dec7ac3 100644 --- a/SeQuant/domain/mbpt/op_registry.cpp +++ b/SeQuant/domain/mbpt/op_registry.cpp @@ -2,18 +2,19 @@ // Created by Ajay Melekamburath on 12/14/25. // -#include +#include #include +#include + namespace sequant::mbpt { void OpRegistry::validate_op(const std::wstring& op) const { if (!reserved::is_nonreserved(op)) { - throw std::runtime_error("mbpt::OpRegistry::add: operator " + - sequant::to_string(op) + " uses a reserved label"); + throw std::runtime_error("mbpt::OpRegistry::add: operator " + toUtf8(op) + + " uses a reserved label"); } if (this->contains(op)) { - throw std::runtime_error("mbpt::OpRegistry::add: operator " + - sequant::to_string(op) + + throw std::runtime_error("mbpt::OpRegistry::add: operator " + toUtf8(op) + " already exists in registry"); } } @@ -37,8 +38,7 @@ OpRegistry& OpRegistry::add(const std::wstring& op, OpClass action) { OpRegistry& OpRegistry::remove(const std::wstring& op) { if (!this->contains(op)) { throw std::runtime_error("mbpt::OpRegistry::remove: operator " + - sequant::to_string(op) + - " does not exist in registry"); + toUtf8(op) + " does not exist in registry"); } ops_->erase(op); return *this; @@ -52,8 +52,7 @@ OpClass OpRegistry::to_class(const std::wstring& op) const { auto it = ops_->find(op); if (it == ops_->end()) { throw std::runtime_error("mbpt::OpRegistry::to_class: operator " + - sequant::to_string(op) + - " does not exist in registry"); + toUtf8(op) + " does not exist in registry"); } return it->second; } diff --git a/SeQuant/domain/mbpt/spin.hpp b/SeQuant/domain/mbpt/spin.hpp index 8d10e22b98..faba48636e 100644 --- a/SeQuant/domain/mbpt/spin.hpp +++ b/SeQuant/domain/mbpt/spin.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -80,11 +81,11 @@ std::wstring spinannotation_add(WS&& label, Spin s) { SEQUANT_ASSERT(view.back() != L'↑' && view.back() != L'↓'); switch (s) { case Spin::any: - return to_wstring(std::forward(label)); + return std::wstring(std::forward(label)); case Spin::alpha: - return to_wstring(std::forward(label)) + L'↑'; + return std::wstring(std::forward(label)) + L'↑'; case Spin::beta: - return to_wstring(std::forward(label)) + L'↓'; + return std::wstring(std::forward(label)) + L'↓'; case Spin::null: SEQUANT_ABORT("Invalid spin quantum number"); } diff --git a/benchmarks/canonicalize.cpp b/benchmarks/canonicalize.cpp index fac0049e81..4a34171a18 100644 --- a/benchmarks/canonicalize.cpp +++ b/benchmarks/canonicalize.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include using namespace sequant; @@ -10,21 +10,22 @@ static constexpr std::size_t nInputs = 6; static ExprPtr get_expression(std::size_t i) { switch (i) { case 1: - return parse_expr(L"g{a1,i5;i6,p12}:A"); + return deserialize(L"g{a1,i5;i6,p12}:A"); case 2: - return parse_expr(L"g{a1,i5;i6,p12}:A t{i6,i7;a1,a2}:A"); + return deserialize(L"g{a1,i5;i6,p12}:A t{i6,i7;a1,a2}:A"); case 3: - return parse_expr( + return deserialize( L"Â{i1,i2;a1,a2}:A g{i3,i4;a3,a4}:A t{a1,a3;i3,i4}:A " L"t{a2,a4;i1,i2}:A"); case 4: - return parse_expr( + return deserialize( L"Â{i1,i2;a1,a2}:A DF{i3;a3;p1} DF{i4;a4;p1} t{a1;i3} t{a3;i4} " L"t{a2;i1} t{a4;i2}"); case 5: - return parse_expr(L"g{i3,i4;a3,a4} s{a1;a5}"); + return deserialize( + L"g{i3,i4;a3,a4} s{a1;a5}"); case 6: - return parse_expr( + return deserialize( L"s{a2;a6} g{i3,i4;a3,a4} " L"t{a3,a6;i4,i2}"); } diff --git a/benchmarks/simplify.cpp b/benchmarks/simplify.cpp index 94dfb5accc..677efd40f6 100644 --- a/benchmarks/simplify.cpp +++ b/benchmarks/simplify.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include using namespace sequant; @@ -10,13 +10,13 @@ static constexpr std::size_t nInputs = 2; static ExprPtr get_expression(std::size_t i) { switch (i) { case 1: - return parse_expr( + return deserialize( L"1/2 Â{i1,i2;a1,a2}:A g{i3,i4;a3,a4}:A t{a1,a3;i3,i4}:A " L"t{a2,a4;i1,i2}:A" L"- 1/2 Â{i2,i1;a1,a2}:A g{i3,i4;a4,a3}:A t{a2,a3;i2,i1}:A " L"t{a4,a1;i3,i4}:A "); case 2: - return parse_expr( + return deserialize( L"Â{p4;p1;}:A 1/3 g{p1,p2;p3,p4}:A t{p3;p2} " L"+ Â{p1;p4;}:A 1/3 g{p4,p2;p3,p1}:A t{p3;p2} " L"+ Â{p3;p1;}:A 1/3 g{p2,p1;p3,p4}:A t{p4;p2} "); diff --git a/benchmarks/spintrace.cpp b/benchmarks/spintrace.cpp index 369287e0ee..6a6e6ae133 100644 --- a/benchmarks/spintrace.cpp +++ b/benchmarks/spintrace.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include using namespace sequant; @@ -11,9 +11,9 @@ static constexpr std::size_t nInputs = 2; static ExprPtr get_expression(std::size_t i) { switch (i) { case 1: - return parse_expr(L"2 t{i1;a1} F{a1;i1}"); + return deserialize(L"2 t{i1;a1} F{a1;i1}"); case 2: - return parse_expr( + return deserialize( L"f{i1;a1} t{a1;i1} + 1/2 g{i1,i2;a1,a2}:A t{a1;i1} t{a2;i2} " L"+ 1/4 g{i1,i2;a1,a2}:A t{a1,a2;i1,i2}:A"); } diff --git a/benchmarks/tensor_block_compare.cpp b/benchmarks/tensor_block_compare.cpp index 922a3688f9..42e1f9a70d 100644 --- a/benchmarks/tensor_block_compare.cpp +++ b/benchmarks/tensor_block_compare.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include @@ -10,13 +10,13 @@ using namespace sequant; std::vector get_tensors() { static std::vector tensors = { - parse_expr(L"t{a1}")->as(), - parse_expr(L"K{i1,a4,a2;i2,i3}")->as(), - parse_expr(L"t{i1,a4,a2;i2,i3;i5,i6}")->as(), - parse_expr(L"K{i2,a3,a2;i4,i1}")->as(), - parse_expr(L"t{a1,a2;i1,i2}")->as(), - parse_expr(L"t{a4,a5;i3,i7}")->as(), - parse_expr(L"t{a4,a5;i3,i7}")->as(), + deserialize(L"t{a1}")->as(), + deserialize(L"K{i1,a4,a2;i2,i3}")->as(), + deserialize(L"t{i1,a4,a2;i2,i3;i5,i6}")->as(), + deserialize(L"K{i2,a3,a2;i4,i1}")->as(), + deserialize(L"t{a1,a2;i1,i2}")->as(), + deserialize(L"t{a4,a5;i3,i7}")->as(), + deserialize(L"t{a4,a5;i3,i7}")->as(), }; return tensors; diff --git a/benchmarks/wick.cpp b/benchmarks/wick.cpp index 4bc274c305..6db769e726 100644 --- a/benchmarks/wick.cpp +++ b/benchmarks/wick.cpp @@ -1,8 +1,8 @@ #include #include +#include #include -#include #include #include #include @@ -22,20 +22,20 @@ ExprPtr get_op_sequence(std::size_t i) { switch (i) { case 1: - return parse_expr(L"f{p1;p2} t{p3;p4}") * + return deserialize(L"f{p1;p2} t{p3;p4}") * ex(cre({L"p_1"}), ann({L"p_2"})) * ex(cre({L"p_3"}), ann({L"p_4"})); case 2: - return parse_expr(L"Â{p1,p2;p3,p4}:A g{p5,p6;p7,p8}:A") * + return deserialize(L"Â{p1,p2;p3,p4}:A g{p5,p6;p7,p8}:A") * ex(cre({L"p_1", L"p_2"}), ann({L"p_3", L"p_4"})) * ex(cre({L"p_5", L"p_6"}), ann({L"p_7", L"p_8"})); case 3: - return parse_expr(L"Â{p1,p2,p3;p4,p5,p6}:A g{p7,p8;p9,p10}:A") * + return deserialize(L"Â{p1,p2,p3;p4,p5,p6}:A g{p7,p8;p9,p10}:A") * ex(cre({L"p_1", L"p_2", L"p_3"}), ann({L"p_4", L"p_5", L"p_6"})) * ex(cre({L"p_7", L"p_8"}), ann({L"p_9", L"p_10"})); case 4: - return parse_expr( + return deserialize( L"Â{p1,p2,p3;p4,p5,p6}:A g{p7,p8;p9,p10}:A " L"t{p11,p12,p13;p14,p15,p16}:A") * ex(cre({L"p_1", L"p_2", L"p_3"}), @@ -44,7 +44,7 @@ ExprPtr get_op_sequence(std::size_t i) { ex(cre({L"p_11", L"p_12", L"p_13"}), ann({L"p_14", L"p_15", L"p_16"})); case 5: - return parse_expr( + return deserialize( L"Â{p1,p2;p3,p4}:A g{p5,p6;p7,p8}:A " L"t{p9,p10;p11,p12}:A t{p13,p14;p15,p16}:A") * ex(cre({L"p_1", L"p_2"}), ann({L"p_3", L"p_4"})) * diff --git a/bin/admin/clang-format.sh b/bin/admin/clang-format.sh index 45dfc47b8d..c013a62fb4 100755 --- a/bin/admin/clang-format.sh +++ b/bin/admin/clang-format.sh @@ -79,10 +79,16 @@ if [[ $have_supported_clang_format_version -eq 0 ]]; then args="$args $i" continue fi + if [[ ! -e "$i" ]]; then + # Skip non-existent files/directories + continue + fi args="$args /hostHOME/$(realpath --relative-to="$mount_path" $i )" done - "$path_to_docker" run --platform linux/x86_64 -u $(id -u $USER):$(id -g $USER) -v $mount_path:/hostHOME xianpengshen/clang-tools:$preferred_clang_format_version clang-format $args + if [[ -n "$args" ]]; then + "$path_to_docker" run --platform linux/x86_64 -u $(id -u $USER):$(id -g $USER) -v $mount_path:/hostHOME xianpengshen/clang-tools:$preferred_clang_format_version clang-format $args + fi else #echo "found $path_to_clang_format with required version $clang_format_version" "$path_to_clang_format" $* diff --git a/doc/examples/synopsis/synopsis3.cpp b/doc/examples/synopsis/synopsis3.cpp index 7f13917303..2af51ba32c 100644 --- a/doc/examples/synopsis/synopsis3.cpp +++ b/doc/examples/synopsis/synopsis3.cpp @@ -1,4 +1,5 @@ #include +#include #include int main() { diff --git a/doc/examples/synopsis/synopsis6.cpp b/doc/examples/synopsis/synopsis6.cpp index c6fe15e0e2..29669515f4 100644 --- a/doc/examples/synopsis/synopsis6.cpp +++ b/doc/examples/synopsis/synopsis6.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/doc/examples/user/cc.cpp b/doc/examples/user/cc.cpp index d795747a01..08f8b16374 100644 --- a/doc/examples/user/cc.cpp +++ b/doc/examples/user/cc.cpp @@ -3,6 +3,7 @@ // #include +#include #include #include #include diff --git a/doc/examples/user/getting_started/ccd.cpp b/doc/examples/user/getting_started/ccd.cpp index 4cfcd38761..85118ede29 100644 --- a/doc/examples/user/getting_started/ccd.cpp +++ b/doc/examples/user/getting_started/ccd.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/doc/examples/user/getting_started/index_spaces_wick.cpp b/doc/examples/user/getting_started/index_spaces_wick.cpp index 7f13917303..2af51ba32c 100644 --- a/doc/examples/user/getting_started/index_spaces_wick.cpp +++ b/doc/examples/user/getting_started/index_spaces_wick.cpp @@ -1,4 +1,5 @@ #include +#include #include int main() { diff --git a/doc/examples/user/getting_started/wick.cpp b/doc/examples/user/getting_started/wick.cpp index c3cf3599ac..e42993838c 100644 --- a/doc/examples/user/getting_started/wick.cpp +++ b/doc/examples/user/getting_started/wick.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/doc/examples/user/operator.cpp b/doc/examples/user/operator.cpp index 6f7ee66d70..a3009715c0 100644 --- a/doc/examples/user/operator.cpp +++ b/doc/examples/user/operator.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/doc/user/guide/io.rst b/doc/user/guide/io.rst new file mode 100644 index 0000000000..bc624c6b06 --- /dev/null +++ b/doc/user/guide/io.rst @@ -0,0 +1,118 @@ +*** +I/O +*** +SeQuant provides different forms of I/O for its objects. Generally, they can be found in the :code:`sequant::io` namespace. Different formats live in +different sub-namespaces. E.g. :func:`sequant::io::latex::to_string` yields a LaTeX representation of the respective object. + +.. note:: + Frequently used I/O functions also have shorthands that can be brought in by including the :file:`sequant/core/io/shorthands.hpp` header. These + shorthands live directly in the :code:`sequant` namespace. The example from above could be achieved via :func:`sequant::to_latex` once that + header is included. + +LaTeX +===== + +The relevant function is :func:`sequant::io::latex::to_string`. SeQuant provides support for LaTeX conversion on most object types. If the shorthands +header is included, this functionality is also exposed as :func:`sequant::to_latex`. + +The produced LaTeX code is not self-contained. It needs to be embedded in a suitable math environment. Furthermore, it assumes that the +`tensor `_ has been loaded. + +.. warning:: + Some objects also have a :func:`to_latex` member function. However, we don't provide any guarantees about which types do and they may also be + removed in future SeQuant versions. Hence, you should always prefer using :func:`sequant::io::latex::to_string`. + +.. note:: + The produced LaTeX code is typically not human-friendly (that is, hard to read) but should compile successfully once embedded in a suitable LaTeX + document. + + + +Serialization +============= + +SeQuant provides `serialization `_ support. This means that SeQuant objects can be transformed into some +intermediary storable format (*serialization*), which can later be converted back (*deserialization*) to yield the original SeQuant object. + +Functions associated with this live in the :code:`sequant::io::serialization` namespace. At the moment, there is only a text format implemented via +:func:`to_string` and :func:`from_string`. More formats may be added at a later point. It is important to note that in order to work with C++'s +type system, :func:`from_string` is a template that takes in the type of object it is supposed to produce. The most important variants are +:func:`from_string` and :func:`from_string` where the former also allows to deserialize any :class:`Expr` subclass. +Depending on the used template type, the expected format of the input will change accordingly. + +As SeQuant's capabilities develop over time, it can be necessary to adapt the syntax of the serialization format. +By default, the abovementioned functions will always adhere to the latest parse syntax specification. However, they can be +instructed to work with a different version by explicitly specifying a :class:`SerializationSyntax` when calling them or using one of the versioned +function calls. + +.. warning:: + All syntax versions except :class:`SerializationSyntax::Latest` are considered deprecated. Support for them will remain available for some time but + might get removed in future versions of SeQuant. + +If the shorthands header is included, text-based serialization and deserialization is available as :func:`sequant::serialize` and +:func:`sequant::deserialize`. + + +Customizations +-------------- + +The exact serialization format can be customized to some extend by providing :class:`SerializationOptions` and :class:`DeserializationOptions` +instances when calling the respective functions. Among other things, they allow for specification of a specific :class:`SerializationSyntax`. For an +overview of all customization options, please refer to the documentation of those classes in the API reference. + +.. note:: + Typically, you should leave these at their defaults to yield the most reliable behavior. + + +SerializationSyntax +------------------- + +We will loosely follow `EBNF `_ syntax for specifying the rules of the grammar +describing the parse syntax. However, we don't define a full formal grammar here as minor details and precedence issues may be left out/simplified in +order to increase readability. + +V1 +^^ + +.. note:: + Known limitations: + + - No support for second-quantized (normal-ordered) operators + - No support for complex numbers + - No support for representing operators (e.g. symmetrizers) explicitly + +.. table:: + :widths: auto + + ============== =============================================================== =========================================== + Component Rule Note + ============== =============================================================== =========================================== + Result (Tensor | Variable) ('=' | '<-') Expression + Expression Sum? + Sum Product ( ('+' | '-') Product)* + Product Nullary ( '*'? Nullary )* Explict '*' use is optional + Nullary '(' Sum ')' | Number | Tensor | Variable + Number Integer, Floating point or fraction + Tensor Name IndexGroup SymmetrySpec? + IndexGroup | '{' IndexList? ( ';' IndexList? ( ';' IndexList? )? )? '}' | Meaning is {;;} + | '^{' IndexList? '}_{' IndexList '}' | Meaning is ^{}_{} (no aux) + | '_{' IndexList? '}^{' IndexList '}' | Meaning is _{}^{} (no aux) + IndexList Index ( ',' Index )? + Index IndexSpaceName '_'? Integer + IndexSpaceName Name but no undersore allowed + SymmetrySpec ':' ( [ASN] ( '-' [SCN] ( '-' [SN] )? )? ) :-- + Variable Name + Name Single word (may include Unicode chars) + ============== =============================================================== =========================================== + +:func:`sequant::io::serialization::from_string` will start at rule :code:`Expression`, whereas +:func:`sequant::io::serialization::from_string` will start at :code:`Result`. + + +Examples +"""""""" + +:: + + R1{u1;i1} = f{u1;i1} - Ym1{u1;u2} f{u2;i1} - Ym1{u3;u2} * g{u1,u2;u3,i1} + 1/2 Ym2{u1,u4;u_2,u_3} g{u2,u3;u4,i1}:A-C-N + diff --git a/python/src/sequant/mbpt.h b/python/src/sequant/mbpt.h index 6fb487ae2b..13c2785231 100644 --- a/python/src/sequant/mbpt.h +++ b/python/src/sequant/mbpt.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include "python.h" @@ -32,8 +32,7 @@ ExprPtr VacuumAverage( sequant::mbpt::OpConnections wop_connections; wop_connections.reserve(op_connections.size()); for (const auto& [op1, op2] : op_connections) { - wop_connections.emplace_back(sequant::to_wstring(op1), - sequant::to_wstring(op2)); + wop_connections.emplace_back(sequant::toUtf16(op1), sequant::toUtf16(op2)); } return sequant::mbpt::op::vac_av(e, wop_connections); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db5ae886c6..b94bbf4bfa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,10 @@ endif() option(SEQUANT_SKIP_LONG_TESTS "Whether to skip execution of long-running tests" OFF) +if (SEQUANT_SKIP_LONG_TESTS OR CMAKE_BUILD_TYPE STREQUAL "Debug") + set(SEQUANT_INTERNAL_SKIP_LONG_TESTS ON) +endif() + if (SEQUANT_TESTS) # Add a "test" that builds that "all" target, which includes the tests binaries add_test("sequant/build" "${CMAKE_COMMAND}" --build "${PROJECT_BINARY_DIR}") diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 759619bda0..897b095fba 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -1,7 +1,7 @@ set(TEST_CASES "antisymmetrizer.cpp" ) -if (NOT SEQUANT_SKIP_LONG_TESTS) +if (NOT SEQUANT_INTERNAL_SKIP_LONG_TESTS) list(APPEND TEST_CASES # Single-Reference Coupled-Cluster equation generation (spin-orbital and spin-free) "srcc.cpp -> |3 t std so|3 t csv so|3 lambda std so|3 lambda csv so|2 t std sf|2 t csv sf|2 lambda std sf|2 lambda csv sf" @@ -44,7 +44,7 @@ foreach(current IN LISTS TEST_CASES) add_executable(${test_name} ${BUILD_BY_DEFAULT} ${test_source}) target_link_libraries(${test_name} PRIVATE SeQuant) - if (SEQUANT_SKIP_LONG_TESTS) + if (SEQUANT_INTERNAL_SKIP_LONG_TESTS) target_compile_definitions(${test_name} PRIVATE SEQUANT_SKIP_LONG_TESTS=1) endif() diff --git a/tests/integration/eomcc.cpp b/tests/integration/eomcc.cpp index 9ae2b7f2eb..fe882c28e9 100644 --- a/tests/integration/eomcc.cpp +++ b/tests/integration/eomcc.cpp @@ -102,7 +102,7 @@ class compute_eomcc { std::wcout << std::boolalpha << "EOM-CC Equations [type=" << type2wstr.at(type) << ", CC rank=" << N - << ", manifold=" << sequant::to_wstring(manifold) << "]" + << ", manifold=" << sequant::toUtf16(manifold) << "]" << " computed in " << timer_pool.read(N) << " s\n"; for (auto i = 0; i < eqvec.size(); i++) { if (eqvec[i] == nullptr) continue; diff --git a/tests/integration/eval/btas/scf_btas.hpp b/tests/integration/eval/btas/scf_btas.hpp index 3528bacc7b..3141ee18c1 100644 --- a/tests/integration/eval/btas/scf_btas.hpp +++ b/tests/integration/eval/btas/scf_btas.hpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -36,14 +36,16 @@ class SequantEvalScfBTAS final : public SequantEvalScf { DataWorldBTAS data_world_; Tensor_t const& f_vo() const { - static Tensor_t tnsr = - data_world_(parse_expr(L"f{a1;i1}", Symmetry::Nonsymm)->as()); + static Tensor_t tnsr = data_world_( + deserialize(L"f{a1;i1}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } Tensor_t const& g_vvoo() const { static Tensor_t tnsr = data_world_( - parse_expr(L"g{a1,a2;i1,i2}", Symmetry::Nonsymm)->as()); + deserialize(L"g{a1,a2;i1,i2}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } @@ -51,8 +53,8 @@ class SequantEvalScfBTAS final : public SequantEvalScf { static const std::wstring_view energy_expr = L"f{i1;a1} * t{a1;i1} + g{i1,i2;a1,a2} * " L"(1/4 * t{a1,a2;i1,i2} + 1/2 t{a1;i1} * t{a2;i2})"; - static auto const node = - binarize(parse_expr(energy_expr, Symmetry::Antisymm)); + static auto const node = binarize( + deserialize(energy_expr, {.def_perm_symm = Symmetry::Antisymm})); return evaluate(node, data_world_)->template get(); } diff --git a/tests/integration/eval/ta/scf_ta.hpp b/tests/integration/eval/ta/scf_ta.hpp index e2d554afff..eaa36caa90 100644 --- a/tests/integration/eval/ta/scf_ta.hpp +++ b/tests/integration/eval/ta/scf_ta.hpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -33,14 +33,16 @@ class SequantEvalScfTA final : public SequantEvalScf { DataWorldTA data_world_; Tensor_t const& f_vo() const { - static Tensor_t tnsr = - data_world_(parse_expr(L"f{a1;i1}", Symmetry::Nonsymm)->as()); + static Tensor_t tnsr = data_world_( + deserialize(L"f{a1;i1}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } Tensor_t const& g_vvoo() const { static Tensor_t tnsr = data_world_( - parse_expr(L"g{a1,a2;i1,i2}", Symmetry::Nonsymm)->as()); + deserialize(L"g{a1,a2;i1,i2}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } @@ -48,8 +50,8 @@ class SequantEvalScfTA final : public SequantEvalScf { static const std::wstring_view energy_expr = L"f{i1;a1} * t{a1;i1} + g{i1,i2;a1,a2} * " L"(1/4 * t{a1,a2;i1,i2} + 1/2 t{a1;i1} * t{a2;i2})"; - static auto const node = - binarize(parse_expr(energy_expr, Symmetry::Antisymm)); + static auto const node = binarize( + deserialize(energy_expr, {.def_perm_symm = Symmetry::Antisymm})); return evaluate(node, data_world_)->template get(); } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index bc3b9f67b0..70944524d9 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -21,7 +21,6 @@ set(symb_test_sources "test_parse.cpp" "test_runtime.cpp" "test_space.cpp" - "test_string.cpp" "test_tensor.cpp" "test_tensor_network.cpp" "test_utilities.cpp" @@ -210,7 +209,7 @@ else() message(STATUS "Python not found - PythonEinsumGenerator validation tests will be disabled") endif() -if (SEQUANT_SKIP_LONG_TESTS) +if (SEQUANT_INTERAL_SKIP_LONG_TESTS) target_compile_definitions(unit_tests-sequant-symb-obj PRIVATE SEQUANT_SKIP_LONG_TESTS=1) target_compile_definitions(unit_tests-sequant-mbpt-obj PRIVATE SEQUANT_SKIP_LONG_TESTS=1) endif() diff --git a/tests/unit/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index f491558d91..73d8c45b41 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -6,11 +6,10 @@ #include #include -#include +#include #include #include -#include -#include +#include #include #include @@ -35,11 +34,11 @@ struct StringMaker { bool include_canonical = true) { std::string str; try { - str = sequant::to_string(sequant::deparse(expr, true)); + str = sequant::toUtf8(sequant::serialize(expr, {.annot_symm = true})); } catch (const std::exception &) { - // deparse doesn't support all kinds of expressions -> fall back to LaTeX - // representation - str = sequant::to_string(sequant::to_latex(expr)); + // serialize doesn't support all kinds of expressions -> fall back to + // LaTeX representation + str = sequant::toUtf8(sequant::to_latex(expr)); } if (include_canonical) { @@ -73,7 +72,7 @@ struct StringMaker { template <> struct StringMaker { static std::string convert(const sequant::Index &idx) { - return sequant::to_string(idx.full_label()); + return sequant::toUtf8(idx.full_label()); } }; @@ -81,7 +80,8 @@ template <> struct StringMaker { static std::string convert(const sequant::ResultExpr &res, bool include_canonical = true) { - std::string str = sequant::to_string(sequant::deparse(res, true)); + std::string str = + sequant::toUtf8(sequant::serialize(res, {.annot_symm = true})); if (include_canonical) { sequant::ResultExpr clone = res.clone(); @@ -177,7 +177,7 @@ using ExprVar = std::variant; /// Converts the given expression-like object into an actual ExprPtr. /// It accepts either an actual expression object (as Expr & or ExprPtr) or -/// a (w)string-like object which will then be parsed to yield the actual +/// a (w)string-like object which will then be deserialized to yield the actual /// expression object. template ExprVar to_expression(T &&expression) { @@ -185,26 +185,27 @@ ExprVar to_expression(T &&expression) { using std::end; if constexpr (std::is_convertible_v) { - std::wstring string = sequant::to_wstring(std::forward(expression)); + std::wstring string = sequant::toUtf16(std::forward(expression)); if (std::find(begin(string), end(string), L'=') != end(string)) { - return sequant::parse_result_expr( - sequant::to_wstring(std::string(std::forward(expression))), - sequant::Symmetry::Nonsymm); + return sequant::deserialize( + std::string(std::forward(expression)), + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { - return sequant::parse_expr( - sequant::to_wstring(std::string(std::forward(expression))), - sequant::Symmetry::Nonsymm); + return sequant::deserialize( + std::string(std::forward(expression)), + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } } else if constexpr (std::is_convertible_v) { if (std::find(begin(expression), end(expression), L'=') != end(expression)) { - return sequant::parse_result_expr( + return sequant::deserialize( std::wstring(std::forward(expression)), - sequant::Symmetry::Nonsymm); + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { - return sequant::parse_expr(std::wstring(std::forward(expression)), - sequant::Symmetry::Nonsymm); + return sequant::deserialize( + std::wstring(std::forward(expression)), + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } } else if constexpr (std::is_convertible_v) { return expression; diff --git a/tests/unit/test_biorthogonalization.cpp b/tests/unit/test_biorthogonalization.cpp index 42a34c2d87..e62ef56ac1 100644 --- a/tests/unit/test_biorthogonalization.cpp +++ b/tests/unit/test_biorthogonalization.cpp @@ -3,7 +3,7 @@ #include "catch2_sequant.hpp" -#include +#include #include #include @@ -39,7 +39,7 @@ TEST_CASE("biorthogonalization", "[Biorthogonalization]") { for (std::size_t i = 0; i < inputs.size(); ++i) { CAPTURE(i); - ExprPtr input_expr = parse_expr(inputs.at(i)); + ExprPtr input_expr = deserialize(inputs.at(i)); auto externals = external_indices(input_expr); @@ -80,10 +80,11 @@ TEST_CASE("biorthogonalization", "[Biorthogonalization]") { container::svector expressions; container::svector expected; for (std::size_t k = 0; k < inputs.at(i).size(); ++k) { - ResultExpr parsed = parse_result_expr(inputs.at(i).at(k)); + ResultExpr parsed = deserialize(inputs.at(i).at(k)); expressions.push_back(parsed); - expected.push_back(parse_result_expr(expected_outputs.at(i).at(k))); + expected.push_back( + deserialize(expected_outputs.at(i).at(k))); } mbpt::biorthogonal_transform(expressions); @@ -107,7 +108,7 @@ TEST_CASE("biorthogonalization", "[Biorthogonalization]") { container::svector expressions; for (const std::wstring &str : current_inputs) { - expressions.push_back(parse_result_expr(str)); + expressions.push_back(deserialize(str)); } REQUIRE_THROWS_WITH( diff --git a/tests/unit/test_canonicalize.cpp b/tests/unit/test_canonicalize.cpp index 1207624d24..165c0b6093 100644 --- a/tests/unit/test_canonicalize.cpp +++ b/tests/unit/test_canonicalize.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -140,11 +139,11 @@ TEST_CASE("canonicalization", "[algorithms]") { // accounted. for (auto ignore_named_index_labels : {true, false}) { auto input1 = - parse_expr(L"1/2 t{a3,a1,a2;i4,i5,i2}:N-C-S g{i4,i5;i3,i1}:N-C-S"); - // auto input1 = parse_expr(L"1/2 t{a1,a2,a3;i5,i2,i4}:N-C-S - // g{i4,i5;i3,i1}:N-C-S"); + deserialize(L"1/2 t{a3,a1,a2;i4,i5,i2}:N-C-S g{i4,i5;i3,i1}:N-C-S"); + // auto input1 = deserialize(L"1/2 + // t{a1,a2,a3;i5,i2,i4}:N-C-S g{i4,i5;i3,i1}:N-C-S"); auto input2 = - parse_expr(L"1/2 t{a1,a3,a2;i5,i4,i2}:N-C-S g{i5,i4;i1,i3}:N-C-S"); + deserialize(L"1/2 t{a1,a3,a2;i5,i4,i2}:N-C-S g{i5,i4;i1,i3}:N-C-S"); canonicalize( input1, {.method = CanonicalizationMethod::Topological, diff --git a/tests/unit/test_eval_btas.cpp b/tests/unit/test_eval_btas.cpp index a406b50b5e..86acb55276 100644 --- a/tests/unit/test_eval_btas.cpp +++ b/tests/unit/test_eval_btas.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -31,12 +31,14 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { return (mo[1].str() == L"i" ? L"o" : L"v") + mo[2].str(); }; - auto const tnsr_deparsed = sequant::deparse(tnsr.clone(), false); + auto const tnsr_deparsed = + sequant::serialize(tnsr.clone(), {.annot_symm = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } [[maybe_unused]] auto tensor_to_key(std::wstring_view spec) { - return tensor_to_key(sequant::parse_expr(spec, sequant::Symmetry::Nonsymm) + return tensor_to_key(sequant::deserialize( + spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) ->as()); } @@ -220,7 +222,8 @@ TEST_CASE("eval_with_btas", "[eval_btas]") { }; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, sequant::Symmetry::Antisymm); + return deserialize( + xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); }; SECTION("Summation") { diff --git a/tests/unit/test_eval_expr.cpp b/tests/unit/test_eval_expr.cpp index 6e858378c5..b53e7fe7b8 100644 --- a/tests/unit/test_eval_expr.cpp +++ b/tests/unit/test_eval_expr.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -21,12 +21,14 @@ #include namespace sequant { -Tensor parse_tensor(std::wstring_view tnsr, Symmetry s = Symmetry::Nonsymm) { - return parse_expr(tnsr, s)->as(); +Tensor parse_tensor( + std::wstring_view tnsr, + const io::serialization::DeserializationOptions& options = {}) { + return deserialize(tnsr, options)->as(); } Constant parse_constant(std::wstring_view c) { - return parse_expr(c)->as(); + return deserialize(c)->as(); } EvalExpr result_expr(EvalExpr const& left, EvalExpr const& right, EvalOp op) { @@ -50,7 +52,7 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE_NOTHROW(EvalExpr{t1}); - auto p1 = parse_expr(L"g_{i3,a1}^{i1,i2} * t_{a2}^{a3}"); + auto p1 = deserialize(L"g_{i3,a1}^{i1,i2} * t_{a2}^{a3}"); const auto& c2 = EvalExpr{p1->at(0)->as()}; const auto& c3 = EvalExpr{p1->at(1)->as()}; @@ -67,12 +69,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE(!x1.op_type()); - auto p1 = parse_expr(L"g_{i3,a1}^{i1,i2} * t_{a2}^{a3}"); + auto p1 = deserialize(L"g_{i3,a1}^{i1,i2} * t_{a2}^{a3}"); const auto& c2 = EvalExpr{p1->at(0)->as()}; const auto& c3 = EvalExpr{p1->at(1)->as()}; - auto x2 = EvalExpr(parse_expr(L"1/2")->as()); + auto x2 = EvalExpr(deserialize(L"1/2")->as()); REQUIRE(!x2.op_type()); REQUIRE(!EvalExpr{Variable{L"λ"}}.op_type()); @@ -129,24 +131,26 @@ TEST_CASE("eval_expr", "[EvalExpr]") { } SECTION("result expr") { - ExprPtr expr = parse_expr(L"2 var"); + ExprPtr expr = deserialize(L"2 var"); ExprPtr root_expr = binarize(expr)->expr(); REQUIRE(root_expr->is()); REQUIRE(*root_expr != *expr); - expr = parse_expr(L"2 t{a1;i1}"); + expr = deserialize(L"2 t{a1;i1}"); root_expr = binarize(expr)->expr(); REQUIRE(root_expr->is()); REQUIRE(*root_expr != *expr); // The binarized tree shall respect the label of the ResultExpr - ResultExpr res = parse_result_expr(L"E = g{i1,i2;a1,a2} t{a1,a2;i1,i2}"); + ResultExpr res = + deserialize(L"E = g{i1,i2;a1,a2} t{a1,a2;i1,i2}"); root_expr = binarize(res)->expr(); REQUIRE(root_expr.is()); REQUIRE(root_expr.as().label() == L"E"); // The binarized tree shall respect the indexing of the ResultExpr - res = parse_result_expr(L"Result{a2;i2}:A-S-S = g{i1,i2;a1,a2} t{a1;i1}"); + res = deserialize( + L"Result{a2;i2}:A-S-S = g{i1,i2;a1,a2} t{a1;i1}"); root_expr = binarize(res)->expr(); REQUIRE(root_expr.is()); REQUIRE(root_expr.as() == @@ -156,7 +160,8 @@ TEST_CASE("eval_expr", "[EvalExpr]") { // continued -> check that changing indexing in result changes indexing in // tree - res = parse_result_expr(L"Result{i2;a2}:A-S-S = g{i1,i2;a1,a2} t{a1;i1}"); + res = deserialize( + L"Result{i2;a2}:A-S-S = g{i1,i2;a1,a2} t{a1;i1}"); root_expr = binarize(res)->expr(); REQUIRE(root_expr.is()); REQUIRE(root_expr.as() == @@ -165,12 +170,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { ColumnSymmetry::Symm)); // The name-respecting property shall also hold for terminals - res = parse_result_expr(L"Other = Var"); + res = deserialize(L"Other = Var"); root_expr = binarize(res)->expr(); REQUIRE(root_expr.is()); REQUIRE(root_expr.as().label() == L"Other"); - res = parse_result_expr(L"Amplitude{i1;a1} = t{a1;i1}"); + res = deserialize(L"Amplitude{i1;a1} = t{a1;i1}"); root_expr = binarize(res)->expr(); REQUIRE(root_expr.is()); REQUIRE(root_expr.as() == Tensor(L"Amplitude", @@ -181,9 +186,9 @@ TEST_CASE("eval_expr", "[EvalExpr]") { SECTION("Sequant expression") { const auto& str_t1 = L"g_{a1,a2}^{a3,a4}"; const auto& str_t2 = L"t_{a3,a4}^{i1,i2}"; - const auto& t1 = parse_expr(str_t1); + const auto& t1 = deserialize(str_t1); - const auto& t2 = parse_expr(str_t2); + const auto& t2 = deserialize(str_t2); const auto& x1 = EvalExpr{t1->as()}; const auto& x2 = EvalExpr{t2->as()}; @@ -215,14 +220,17 @@ TEST_CASE("eval_expr", "[EvalExpr]") { const auto& x45 = result_expr(EvalExpr{t4}, EvalExpr{t5}, EvalOp::Product); const auto& x54 = result_expr(EvalExpr{t5}, EvalExpr{t4}, EvalOp::Product); - REQUIRE(x45.to_latex() == parse_expr(L"I_{a1,a2}^{i1,i2}")->to_latex()); + REQUIRE(x45.to_latex() == deserialize(L"I_{a1,a2}^{i1,i2}")->to_latex()); REQUIRE(x45.to_latex() == x54.to_latex()); } SECTION("Hash value") { - const auto t1 = parse_tensor(L"t_{i1}^{a1}", Symmetry::Antisymm); - const auto t2 = parse_tensor(L"t_{i2}^{a2}", Symmetry::Antisymm); - const auto t3 = parse_tensor(L"t_{i1,i2}^{a1,a2}", Symmetry::Antisymm); + const auto t1 = + parse_tensor(L"t_{i1}^{a1}", {.def_perm_symm = Symmetry::Antisymm}); + const auto t2 = + parse_tensor(L"t_{i2}^{a2}", {.def_perm_symm = Symmetry::Antisymm}); + const auto t3 = parse_tensor(L"t_{i1,i2}^{a1,a2}", + {.def_perm_symm = Symmetry::Antisymm}); const auto& x1 = EvalExpr{t1}; const auto& x2 = EvalExpr{t2}; @@ -237,16 +245,18 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE_FALSE(x1.hash_value() == x3.hash_value()); REQUIRE_FALSE(x12.hash_value() == x3.hash_value()); - auto tree1 = binarize(parse_expr(L"A C")); - auto tree2 = binarize(parse_expr(L"A t{a1;i1}")); + auto tree1 = binarize(deserialize(L"A C")); + auto tree2 = binarize(deserialize(L"A t{a1;i1}")); REQUIRE(tree1->hash_value() != tree2->hash_value()); } SECTION("Symmetry of product") { // whole bra <-> ket contraction between two antisymmetric tensors - const auto t1 = parse_tensor(L"g_{i3,i4}^{i1,i2}", Symmetry::Antisymm); - const auto t2 = parse_tensor(L"t_{a1,a2}^{i3,i4}", Symmetry::Antisymm); + const auto t1 = parse_tensor(L"g_{i3,i4}^{i1,i2}", + {.def_perm_symm = Symmetry::Antisymm}); + const auto t2 = parse_tensor(L"t_{a1,a2}^{i3,i4}", + {.def_perm_symm = Symmetry::Antisymm}); const auto x12 = result_expr(EvalExpr{t1}, EvalExpr{t2}, EvalOp::Product); @@ -256,9 +266,11 @@ TEST_CASE("eval_expr", "[EvalExpr]") { // whole bra <-> ket contraction between two symmetric tensors const auto t3 = - parse_expr(L"g_{i3,i4}^{i1,i2}", Symmetry::Symm)->as(); + deserialize(L"g_{i3,i4}^{i1,i2}", {.def_perm_symm = Symmetry::Symm}) + ->as(); const auto t4 = - parse_expr(L"t_{a1,a2}^{i3,i4}", Symmetry::Symm)->as(); + deserialize(L"t_{a1,a2}^{i3,i4}", {.def_perm_symm = Symmetry::Symm}) + ->as(); const auto x34 = result_expr(EvalExpr{t3}, EvalExpr{t4}, EvalOp::Product); @@ -267,8 +279,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE(x34.expr()->as().symmetry() == Symmetry::Nonsymm); // outer product of the same tensor - const auto t5 = parse_expr(L"f_{i1}^{a1}", Symmetry::Nonsymm)->as(); - const auto t6 = parse_expr(L"f_{i2}^{a2}", Symmetry::Nonsymm)->as(); + const auto t5 = + deserialize(L"f_{i1}^{a1}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as(); + const auto t6 = + deserialize(L"f_{i2}^{a2}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as(); const auto& x56 = result_expr(EvalExpr{t5}, EvalExpr{t6}, EvalOp::Product); @@ -277,17 +293,21 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE(x56.expr()->as().symmetry() == Symmetry::Nonsymm); // contraction of some indices from a bra to a ket - const auto t7 = parse_tensor(L"g_{a1,a2}^{i1,a3}", Symmetry::Antisymm); - const auto t8 = parse_tensor(L"t_{a3}^{i2}", Symmetry::Antisymm); + const auto t7 = parse_tensor(L"g_{a1,a2}^{i1,a3}", + {.def_perm_symm = Symmetry::Antisymm}); + const auto t8 = + parse_tensor(L"t_{a3}^{i2}", {.def_perm_symm = Symmetry::Antisymm}); const auto x78 = result_expr(EvalExpr{t7}, EvalExpr{t8}, EvalOp::Product); REQUIRE(x78.expr()->as().symmetry() == Symmetry::Nonsymm); // whole bra <-> ket contraction between symmetric and antisymmetric tensors auto const t9 = - parse_expr(L"g_{a1,a2}^{a3,a4}", Symmetry::Antisymm)->as(); + deserialize(L"g_{a1,a2}^{a3,a4}", {.def_perm_symm = Symmetry::Antisymm}) + ->as(); auto const t10 = - parse_expr(L"t_{a3,a4}^{i1,i2}", Symmetry::Symm)->as(); + deserialize(L"t_{a3,a4}^{i1,i2}", {.def_perm_symm = Symmetry::Symm}) + ->as(); auto const x910 = result_expr(EvalExpr{t9}, EvalExpr{t10}, EvalOp::Product); // todo: // REQUIRE(x910.expr()->as().symmetry() == Symmetry::Symm); @@ -297,7 +317,7 @@ TEST_CASE("eval_expr", "[EvalExpr]") { #if 0 SECTION("Symmetry of sum") { auto tensor = [](Symmetry s) { - return parse_expr(L"I_{i1,i2}^{a1,a2}", s)->as(); + return deserialize(L"I_{i1,i2}^{a1,a2}", s)->as(); }; auto symmetry = [](const EvalExpr& x) { @@ -338,12 +358,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { #endif SECTION("Debug") { - auto t1 = - EvalExpr{parse_expr(L"O{a_1;a_1}", Symmetry::Nonsymm) - ->as()}; - auto t2 = - EvalExpr{parse_expr(L"O{a_2;a_2}", Symmetry::Nonsymm) - ->as()}; + auto t1 = EvalExpr{deserialize(L"O{a_1;a_1}", + {.def_perm_symm = Symmetry::Nonsymm}) + ->as()}; + auto t2 = EvalExpr{deserialize(L"O{a_2;a_2}", + {.def_perm_symm = Symmetry::Nonsymm}) + ->as()}; REQUIRE_NOTHROW(result_expr(t1, t2, EvalOp::Product)); } diff --git a/tests/unit/test_eval_node.cpp b/tests/unit/test_eval_node.cpp index 71c1f4319a..20085e1764 100644 --- a/tests/unit/test_eval_node.cpp +++ b/tests/unit/test_eval_node.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -56,7 +56,7 @@ TEST_CASE("eval_node", "[EvalNode]") { auto R = Npos::R; auto parse_expr_antisymm = [](auto const& xpr) { - return parse_expr(xpr, Symmetry::Antisymm); + return deserialize(xpr, {.def_perm_symm = Symmetry::Antisymm}); }; SECTION("terminals") { @@ -67,14 +67,14 @@ TEST_CASE("eval_node", "[EvalNode]") { // In order to represent unary computations, i.e. assignments, we have to // represent them as a multiplication with 1 in order for the tree to remain // a full binary tree. - node = binarize(parse_result_expr(L"R{a1;i1} = f{a1;i1}")); + node = binarize(deserialize(L"R{a1;i1} = f{a1;i1}")); REQUIRE(!node.leaf()); REQUIRE(node->op_type() == EvalOp::Product); REQUIRE(node->as_tensor().label() == L"R"); REQUIRE(((node.left()->is_tensor() && node.right()->is_scalar()) || (node.right()->is_tensor() && node.left()->is_scalar()))); - node = binarize(parse_result_expr(L"R = Var")); + node = binarize(deserialize(L"R = Var")); REQUIRE(!node.leaf()); REQUIRE(node->op_type() == EvalOp::Product); REQUIRE(node->as_variable().label() == L"R"); @@ -168,7 +168,7 @@ TEST_CASE("eval_node", "[EvalNode]") { } SECTION("variable") { - auto prod1 = parse_expr(L"a * b * c"); + auto prod1 = deserialize(L"a * b * c"); auto node1 = eval_node(prod1); REQUIRE(node1->op_type() == EvalOp::Product); @@ -181,7 +181,7 @@ TEST_CASE("eval_node", "[EvalNode]") { REQUIRE(node(node1, {L, R}).as_variable() == Variable{L"b"}); REQUIRE(node(node1, {L, L}).as_variable() == Variable{L"a"}); - auto sum1 = parse_expr(L"a + b + c"); + auto sum1 = deserialize(L"a + b + c"); auto node2 = eval_node(sum1); REQUIRE(node2->op_type() == EvalOp::Sum); @@ -194,7 +194,7 @@ TEST_CASE("eval_node", "[EvalNode]") { REQUIRE(node(node2, {L, R}).as_variable() == Variable{L"b"}); REQUIRE(node(node2, {L, L}).as_variable() == Variable{L"a"}); - auto prod2 = parse_expr(L"a * t{i1;a1}"); + auto prod2 = deserialize(L"a * t{i1;a1}"); auto node3 = eval_node(prod2); REQUIRE_THAT(node(node3, {}).as_tensor(), EquivalentTo("I{i1;a1}:N-N-N")); REQUIRE_THAT(node(node3, {R}).as_tensor(), EquivalentTo("t{i1;a1}")); @@ -284,48 +284,48 @@ TEST_CASE("eval_node", "[EvalNode]") { #if 0 auto const s1 = - parse_expr(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Symm); + deserialize(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Symm); auto const ns1 = eval_node(s1); REQUIRE(asy_cost(ns1) == AsyCost{{1, 4}, 2, 2}); // 1/4 * O^2V^2 auto const s2 = - parse_expr(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Antisymm); + deserialize(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Antisymm); auto const ns2 = eval_node(s2); REQUIRE(asy_cost(ns2) == AsyCost{{1, 2}, 2, 2}); // 1/2 * O^2V^2 auto const s3 = - parse_expr(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Nonsymm); + deserialize(L"I{i1,i2;a1,a2} + I{i1,i2;a1,a2}", Symmetry::Nonsymm); auto const ns3 = eval_node(s3); REQUIRE(asy_cost(ns3) == AsyCost{2, 2}); // O^2V^2 auto const p4 = - parse_expr(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Symm); + deserialize(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Symm); auto const np4 = eval_node(p4); REQUIRE(asy_cost(np4) == AsyCost{{1, 2}, 2, 4}); // 1/4 * 2 * O^2V^4 auto const p5 = - parse_expr(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Antisymm); + deserialize(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Antisymm); auto const np5 = eval_node(p5); REQUIRE(asy_cost(np5) == AsyCost{{1, 2}, 2, 4}); // 1/4 * 2 * O^2V^4 auto const p6 = - parse_expr(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Antisymm); + deserialize(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Antisymm); auto const np6 = eval_node(p6); REQUIRE(asy_cost(np6) == AsyCost{{1, 2}, 2, 4}); // 1/4 * 2 * O^2V^4 - auto const p7 = parse_expr(L"I{i1;a1} * I{i2;a2}", Symmetry::Nonsymm); + auto const p7 = deserialize(L"I{i1;a1} * I{i2;a2}", Symmetry::Nonsymm); auto const np7 = eval_node(p7); REQUIRE(asy_cost(np7) == AsyCost{{1, 2}, 2, 2}); // 1/2 * O^2V^2 auto const p8 = - parse_expr(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Nonsymm); + deserialize(L"I{i1,i2;a3,a4} * I{a3,a4;a1,a2}", Symmetry::Nonsymm); auto const np8 = eval_node(p8); REQUIRE(asy_cost(np8) == AsyCost{2, 2, 4}); // 2 * O^2V^4 #endif } SECTION("minimum storage") { - auto p1 = parse_expr(L"2 * g{i2,a1;a2,a3} * t{a2,a3;i2,i1}"); + auto p1 = deserialize(L"2 * g{i2,a1;a2,a3} * t{a2,a3;i2,i1}"); auto const n1 = eval_node(p1); // evaluation happens in two steps. // g and t are contracted to give an intermediate I{a1;i1} @@ -337,7 +337,7 @@ TEST_CASE("eval_node", "[EvalNode]") { // storage requirement. REQUIRE(min_storage(n1) == AsyCost{1, 3} + AsyCost{2, 2} + AsyCost{1, 1}); - auto p2 = parse_expr(L"1/2 * (g{a1,a2; a3,a4} t{a3;i1}) t{a4;i2}"); + auto p2 = deserialize(L"1/2 * (g{a1,a2; a3,a4} t{a3;i1}) t{a4;i2}"); auto const n2 = eval_node(p2); REQUIRE(min_storage(n2) == AsyCost{0, 4} + AsyCost{1, 3} + AsyCost{1, 1}); } diff --git a/tests/unit/test_eval_ta.cpp b/tests/unit/test_eval_ta.cpp index 21aa82ddb6..97447371ab 100644 --- a/tests/unit/test_eval_ta.cpp +++ b/tests/unit/test_eval_ta.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -86,7 +86,8 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { NestedTensorIndices oixs{tnsr}; if (oixs.inner.empty()) { - auto const tnsr_deparsed = sequant::deparse(tnsr.clone(), false); + auto const tnsr_deparsed = + sequant::serialize(tnsr.clone(), {.annot_symm = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } else { using ranges::views::intersperse; @@ -111,7 +112,8 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { } auto tensor_to_key(std::wstring_view spec) { - return tensor_to_key(sequant::parse_expr(spec, sequant::Symmetry::Nonsymm) + return tensor_to_key(sequant::deserialize( + spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) ->as()); } @@ -286,7 +288,8 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { using TA::TArrayD; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, sequant::Symmetry::Antisymm); + return sequant::deserialize( + xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); }; // tnsr is assumed to be single-tiled @@ -379,14 +382,15 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(prod2_man) == Catch::Approx(norm(prod2_eval))); - auto expr3 = sequant::parse_expr(L"R_{a1}^{i1,i3} * f_{i3}^{i2}"); + auto expr3 = sequant::deserialize( + L"R_{a1}^{i1,i3} * f_{i3}^{i2}"); auto prod3_eval = eval(expr3, "a_1,i_1,i_2"); auto prod3_man = TArrayD{}; prod3_man("a1,i1,i2") = yield(L"R{a1;i1,i3}")("a1,i1,i3") * yield(L"f{i3;i2}")("i3,i2"); REQUIRE(norm(prod3_man) == Catch::Approx(norm(prod3_eval))); - auto expr4 = sequant::parse_expr( + auto expr4 = sequant::deserialize( L"1/4 * R_{a1,a2,a3}^{i2,i3} * g_{i2,i3}^{i1,a3}"); auto prod4_eval = eval(expr4, "i_1,a_1,a_2"); auto prod4_man = TArrayD{}; @@ -413,7 +417,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(man1) == Catch::Approx(norm(eval1))); - auto expr2 = sequant::parse_expr( + auto expr2 = sequant::deserialize( L"1/4 * R_{a1,a2,a3}^{i2,i3} * g_{i2,i3}^{i1,a3} + R_{a1,a3}^{i1} * " L"f_{i2}^{a3} * t_{a2}^{i2}"); auto eval2 = eval(expr2, "i_1,a_1,a_2"); @@ -646,7 +650,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { using namespace std::string_literals; SECTION("summation") { - auto expr1 = parse_expr(L"t_{a1}^{i1} + f_{i1}^{a1}"); + auto expr1 = deserialize(L"t_{a1}^{i1} + f_{i1}^{a1}"); auto sum1_eval = eval(expr1, "i_1,a_1"); @@ -657,7 +661,8 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { // todo: REQUIRE(norm(sum1_man) == Catch::Approx(norm(sum1_eval))); - auto expr2 = parse_expr(L"2 * t_{a1}^{i1} + 3/2 * f_{i1}^{a1}"); + auto expr2 = + deserialize(L"2 * t_{a1}^{i1} + 3/2 * f_{i1}^{a1}"); auto sum2_eval = eval(expr2, "i_1,a_1"); @@ -670,7 +675,8 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { } SECTION("product") { - auto expr1 = parse_expr(L"1/2 * g_{i2,i4}^{a2,a4} * t_{a1,a2}^{i1,i2}"); + auto expr1 = deserialize( + L"1/2 * g_{i2,i4}^{a2,a4} * t_{a1,a2}^{i1,i2}"); auto prod1_eval = eval(expr1, "i_4,a_1,a_4,i_1"); TArrayC prod1_man{}; @@ -680,7 +686,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(prod1_man) == Catch::Approx(norm(prod1_eval))); - auto expr2 = parse_expr( + auto expr2 = deserialize( L"-1/4 * g_{i3,i4}^{a3,a4} * t_{a2,a4}^{i1,i2} * t_{a1,a3}^{ i3, " L"i4}"); auto prod2_eval = eval(expr2, "a_1,a_2,i_1,i_2"); @@ -693,14 +699,15 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(prod2_man) == Catch::Approx(norm(prod2_eval))); - auto expr3 = sequant::parse_expr(L"R_{a1}^{i1,i3} * f_{i3}^{i2}"); + auto expr3 = sequant::deserialize( + L"R_{a1}^{i1,i3} * f_{i3}^{i2}"); auto prod3_eval = eval(expr3, "a_1,i_1,i_2"); auto prod3_man = TArrayC{}; prod3_man("a1,i1,i2") = yield(L"R{a1;i1,i3}")("a1,i1,i3") * yield(L"f{i3;i2}")("i3,i2"); REQUIRE(norm(prod3_man) == Catch::Approx(norm(prod3_eval))); - auto expr4 = sequant::parse_expr( + auto expr4 = sequant::deserialize( L"1/4 * R_{a1,a2,a3}^{i2,i3} * g_{i2,i3}^{i1,a3}"); auto prod4_eval = eval(expr4, "i_1,a_1,a_2"); auto prod4_man = TArrayC{}; @@ -711,7 +718,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { } SECTION("sum and product") { - auto expr1 = parse_expr( + auto expr1 = deserialize( L"-1/4 * g_{i3,i4}^{a3,a4} * t_{a2,a4}^{i1,i2} * t_{a1,a3}^{i3,i4}" " + " " 1/16 * g_{i3,i4}^{a3,a4} * t_{a1,a2}^{i3,i4} * t_{a3,a4}^{i1,i2}"); @@ -729,7 +736,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(man1) == Catch::Approx(norm(eval1))); - auto expr2 = sequant::parse_expr( + auto expr2 = sequant::deserialize( L"1/4 * R_{a1,a2,a3}^{i2,i3} * g_{i2,i3}^{i1,a3} + R_{a1,a3}^{i1} * " L"f_{i2}^{a3} * t_{a2}^{i2}"); auto eval2 = eval(expr2, "i_1,a_1,a_2"); @@ -744,7 +751,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { } SECTION("Antisymmetrization") { - auto expr1 = parse_expr(L"g_{i1, i2}^{a1, a2}"); + auto expr1 = deserialize(L"g_{i1, i2}^{a1, a2}"); auto eval1 = eval_antisymm(expr1, "i_1,i_2,a_1,a_2"); auto const& arr1 = yield(L"g{i1,i2;a1,a2}"); @@ -763,7 +770,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { // REQUIRE(Approx(norm(zero1)) == 0); // odd-ranked tensor - auto expr2 = parse_expr(L"g_{i1, i2, i3}^{a1, a2}"); + auto expr2 = deserialize(L"g_{i1, i2, i3}^{a1, a2}"); auto eval2 = eval_antisymm(expr2, "i_1,i_2,i_3,a_1,a_2"); auto const& arr2 = yield(L"g{i1,i2,i3;a1,a2}"); @@ -778,7 +785,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(zero2) == Catch::Approx(0).margin( 100 * std::numeric_limits::epsilon())); - auto expr3 = parse_expr(L"R_{a1,a2}^{}"); + auto expr3 = deserialize(L"R_{a1,a2}^{}"); auto eval3 = eval_antisymm(expr3, "a_1,a_2"); auto const& arr3 = yield(L"R{a1,a2;}"); auto man3 = TArrayC{}; @@ -792,7 +799,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { } SECTION("Symmetrization") { - auto expr1 = parse_expr(L"g_{i1, i2}^{a1, a2}"); + auto expr1 = deserialize(L"g_{i1, i2}^{a1, a2}"); auto eval1 = eval_symm(expr1, "i_1,i_2,a_1,a_2"); auto const& arr1 = yield(L"g{i1,i2;a1,a2}"); @@ -802,7 +809,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { REQUIRE(norm(man1) == Catch::Approx(norm(eval1))); - auto expr2 = parse_expr(L"g_{i1,i2,i3}^{a1,a2,a3}"); + auto expr2 = deserialize(L"g_{i1,i2,i3}^{a1,a2,a3}"); auto eval2 = eval_symm(expr2, "i_1,i_2,i_3,a_1,a_2,a_3"); auto const& arr2 = yield(L"g{i1,i2,i3;a1,a2,a3}"); @@ -817,7 +824,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { SECTION("Others") { using namespace std::string_literals; - auto expr1 = parse_expr( + auto expr1 = deserialize( L"-1/4 * g_{i3,i4}^{a3,a4} * t_{a2,a4}^{i1,i2} * t_{a1,a3}^{i3,i4}" " + " " 1/16 * g_{i3,i4}^{a3,a4} * t_{a1,a2}^{i3,i4} * t_{a3,a4}^{i1,i2}"); @@ -872,7 +879,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { L"f{i3;i1}" L" * " L"t{a3,a4;i2,i3}"; - auto const node = eval_node(parse_expr(expr_str)); + auto const node = eval_node(deserialize(expr_str)); std::string const target_layout{"i_1,i_2,i_3;a_3i_2i_3,a_4i_2i_3"}; auto result = evaluate(node, target_layout, yield)->get(); ArrayToT ref; @@ -893,7 +900,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { L" * " L"s{a2;a4}"; - auto const node = eval_node(parse_expr(expr_str)); + auto const node = eval_node(deserialize(expr_str)); std::string const target_layout{"i_2,i_1;a_1i_1i_2,a_2i_1i_2"}; auto result = evaluate(node, target_layout, yield)->get(); @@ -914,7 +921,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { L"I{a1,a2;i1,i2}" L" * " L"g{i1,i2;a2,a1}"; - auto const node = eval_node(parse_expr(expr_str)); + auto const node = eval_node(deserialize(expr_str)); auto result = evaluate(node, yield)->get(); diff --git a/tests/unit/test_export.cpp b/tests/unit/test_export.cpp index b0f07c5895..736cdfca91 100644 --- a/tests/unit/test_export.cpp +++ b/tests/unit/test_export.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -251,14 +251,16 @@ std::vector> parse_expression_spec(const std::string &spec) { } try { - ResultExpr res = - parse_result_expr(to_wstring(line), Symmetry::Nonsymm, - BraKetSymmetry::Nonsymm, ColumnSymmetry::Nonsymm); + ResultExpr res = deserialize( + line, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}); groups.back().add(to_export_tree(res)); } catch (...) { ExprPtr expr = - parse_expr(to_wstring(line), Symmetry::Nonsymm, - BraKetSymmetry::Nonsymm, ColumnSymmetry::Nonsymm); + deserialize(line, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}); groups.back().add(to_export_tree(expr)); } } @@ -429,8 +431,9 @@ TEST_CASE("export", "[export]") { candidates.at(static_cast(layout)); Tensor tensor = - parse_expr(input, Symmetry::Nonsymm, BraKetSymmetry::Nonsymm, - ColumnSymmetry::Nonsymm) + deserialize(input, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}) ->as(); bool rewritten = ctx.rewrite(tensor); REQUIRE_THAT(tensor, EquivalentTo(expected)); @@ -449,7 +452,7 @@ TEST_CASE("export", "[export]") { Variable v3{L"v3"}; SECTION("unchanged") { - export_expression(to_export_tree(parse_result_expr(L"v1 = 2 v2")), + export_expression(to_export_tree(deserialize(L"v1 = 2 v2")), generator, ctx); REQUIRE_THAT(generator.get_generated_code(), @@ -465,7 +468,7 @@ TEST_CASE("export", "[export]") { SECTION("elided load/unload") { SECTION("single") { export_expression( - to_export_tree(parse_result_expr(L"v1 = 2 v2 + 4 v2 v3")), + to_export_tree(deserialize(L"v1 = 2 v2 + 4 v2 v3")), generator, ctx); REQUIRE_THAT(generator.get_generated_code(), @@ -483,7 +486,7 @@ TEST_CASE("export", "[export]") { "Persist v1\n")); } SECTION("multiple") { - export_expression(to_export_tree(parse_result_expr( + export_expression(to_export_tree(deserialize( L"ECC = 2 g{i1,i2;a1,a2} t{a1,a2;i1,i2} " "- g{i1,i2;a1,a2} t{a2,a1;i1,i2}")), generator, ctx); @@ -522,7 +525,7 @@ TEST_CASE("export", "[export]") { // needs to be moved before the load of B in order to retain // compatibility to frameworks with stack-based memory models export_expression( - to_export_tree(parse_result_expr( + to_export_tree(deserialize( L"R{a1;i1} = 2 B{a1;i1} + B{a1;i1} C - C D{a1;i1}")), generator, ctx); @@ -550,7 +553,7 @@ TEST_CASE("export", "[export]") { "Persist R[a_1, i_1]\n")); } SECTION("reused intermediates") { - export_expression(to_export_tree(parse_result_expr( + export_expression(to_export_tree(deserialize( L"ECC = 2 K{i1,i2;a1,a2} t{a1;i1} t{a2;i2} - " L"K{i1,i2;a1,a2} t{a1;i2} t{a2;i1}")), generator, ctx); @@ -588,7 +591,7 @@ TEST_CASE("export", "[export]") { } SECTION("requires caution") { export_expression( - to_export_tree(parse_result_expr( + to_export_tree(deserialize( L"R{a1,a2;i1,i2} = g{i3,i4;a3,a4} t{a4;i2} t{a1,a3;i1,i4} " L"t{a2;i3} " "- 2 g{i3,i4;a3,a4} t{a4;i2} t{a1,a3;i1,i3} t{a2;i4} ")), @@ -653,7 +656,7 @@ TEST_CASE("export", "[export]") { } SECTION("tbd2") { export_expression( - to_export_tree(parse_result_expr( + to_export_tree(deserialize( L"R1{u_1;i_1;} = " L"+ 2 g{u_2, i_2;a_1, a_2;} (GAM0{u_3, u_4;u_5, u_2;} T2g{a_2, " L"u_5;u_3, i_2;}) T2g{a_1, u_1;i_1, u_4;} " @@ -673,12 +676,12 @@ TEST_CASE("export", "[export]") { SECTION("remap_integrals") { SECTION("Unchanged") { - Tensor tensor = parse_expr(L"t{i1;a1}:N-N-N")->as(); + Tensor tensor = deserialize(L"t{i1;a1}:N-N-N")->as(); bool rewritten = ctx.rewrite(tensor); REQUIRE_THAT(tensor, EquivalentTo("t{i1;a1}:N-N-N")); REQUIRE_FALSE(rewritten); - tensor = parse_expr(int_label + L"{a1;i1}:N-N-N")->as(); + tensor = deserialize(int_label + L"{a1;i1}:N-N-N")->as(); rewritten = ctx.rewrite(tensor); REQUIRE_THAT(tensor, EquivalentTo("g{a1;i1}:N-N-N")); } diff --git a/tests/unit/test_expr.cpp b/tests/unit/test_expr.cpp index b6ade55fd3..65767d82ea 100644 --- a/tests/unit/test_expr.cpp +++ b/tests/unit/test_expr.cpp @@ -11,9 +11,8 @@ #include #include #include -#include +#include #include -#include #include #include @@ -34,7 +33,6 @@ struct Dummy : public sequant::Expr { virtual ~Dummy() = default; std::wstring to_latex() const override { return L"{\\text{Dummy}}"; } - std::wstring to_wolfram() const override { return L"Dummy[]"; } type_id_type type_id() const override { return get_type_id(); }; sequant::ExprPtr clone() const override { return sequant::ex(); } bool static_equal(const sequant::Expr &) const override { return true; } @@ -65,21 +63,6 @@ struct VecExpr : public std::vector, public sequant::Expr { result += L"\\}}"; return result; } - std::wstring to_wolfram() const override { - std::wstring result = L"VecExpr["; - size_t count = 1; - for (const auto &e : *this) { - const auto last_it = count == this->std::vector::size(); - if constexpr (sequant::Expr::is_shared_ptr_of_expr_or_derived::value) { - result += e->to_wolfram() + (last_it ? L"" : L","); - } else { - result += std::to_wstring(e) + (last_it ? L"" : L","); - } - ++count; - } - result += L"]"; - return result; - } type_id_type type_id() const override { return get_type_id>(); }; @@ -124,9 +107,6 @@ struct Adjointable : public sequant::Expr { std::wstring to_latex() const override { return L"{\\text{Adjointable}{" + std::to_wstring(v) + L"}}"; } - std::wstring to_wolfram() const override { - return L"Adjointable[" + std::to_wstring(v) + L"]"; - } type_id_type type_id() const override { return get_type_id(); }; sequant::ExprPtr clone() const override { return sequant::ex(v); @@ -409,7 +389,7 @@ TEST_CASE("expr", "[elements]") { e->append(ex(3)); e->append(ex(4)); // std::wcout << "to_latex(e) = " << to_latex(e) << std::endl; - REQUIRE(to_latex(e) == + REQUIRE(to_latex(*e) == L"{ \\bigl({\\text{Adjointable}{1}} + {\\text{Adjointable}{2}} + " L"{\\text{Adjointable}{3}} + {\\text{Adjointable}{4}}\\bigr) }"); // std::wcout << "to_latex_align(e) = " << to_latex_align(e) << std::endl; @@ -440,22 +420,6 @@ TEST_CASE("expr", "[elements]") { } } // SECTION("latex") - SECTION("wolfram") { - Product sp0{}; - sp0.append(2, std::make_shared()); - REQUIRE(to_wolfram(sp0) == L"Times[2,Dummy[]]"); - - // VecExpr - { - const auto ex5_init = std::vector>{ - std::make_shared(1), std::make_shared(2), - std::make_shared(3)}; - auto ex6 = - std::make_shared>(begin(ex5_init), end(ex5_init)); - REQUIRE(ex6->to_wolfram() == L"VecExpr[1,2,3]"); - } - } - SECTION("visitor") { // read-only visitor { @@ -888,33 +852,34 @@ TEST_CASE("expr", "[elements]") { SECTION("ResultExpr") { SECTION("accessors") { SECTION("as_variable") { - REQUIRE_THAT(parse_result_expr(L"R = Var").result_as_variable(), + REQUIRE_THAT(deserialize(L"R = Var").result_as_variable(), EquivalentTo(L"R")); - REQUIRE_THAT(parse_result_expr(L"R = Var").result_as_tensor(), + REQUIRE_THAT(deserialize(L"R = Var").result_as_tensor(), EquivalentTo(L"R{}:N-N-N")); } SECTION("as_tensor") { - REQUIRE_THAT( - parse_result_expr(L"R{a1;i2;p3} = T{a1;i2;p3}").result_as_tensor(), - EquivalentTo(L"R{a1;i2;p3}")); + REQUIRE_THAT(deserialize(L"R{a1;i2;p3} = T{a1;i2;p3}") + .result_as_tensor(), + EquivalentTo(L"R{a1;i2;p3}")); } } SECTION("particle pairings") { std::vector> expected = {{L"a_1", L"i_1"}}; - auto pairings = parse_result_expr(L"R{a1;i1} = t{a1;i1}") + auto pairings = deserialize(L"R{a1;i1} = t{a1;i1}") .index_particle_grouping>(); REQUIRE_THAT(pairings, ::Catch::Matchers::UnorderedRangeEquals(expected)); expected = {{L"a_1", L"i_1"}, {L"a_2", L"i_2"}}; - pairings = parse_result_expr(L"R{a1,a2;i1,i2} = t{a1,a2;i1,i2}") + pairings = deserialize(L"R{a1,a2;i1,i2} = t{a1,a2;i1,i2}") .index_particle_grouping>(); REQUIRE_THAT(pairings, ::Catch::Matchers::UnorderedRangeEquals(expected)); // aux indices are ignored expected = {{L"a_1", L"i_1"}, {L"a_2", L"i_2"}}; - pairings = parse_result_expr(L"R{a1,a2;i1,i2;p1} = t{a1,a2;i1,i2;p1}") - .index_particle_grouping>(); + pairings = + deserialize(L"R{a1,a2;i1,i2;p1} = t{a1,a2;i1,i2;p1}") + .index_particle_grouping>(); REQUIRE_THAT(pairings, ::Catch::Matchers::UnorderedRangeEquals(expected)); } } diff --git a/tests/unit/test_fusion.cpp b/tests/unit/test_fusion.cpp index 73e8668f65..7712945ba7 100644 --- a/tests/unit/test_fusion.cpp +++ b/tests/unit/test_fusion.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -43,9 +43,9 @@ TEST_CASE("fusion", "[optimize]") { }; for (auto&& [l, r, f] : fused_terms) { - auto const le = parse_expr(l); - auto const re = parse_expr(r); - auto const fe = parse_expr(f); + auto const le = deserialize(l); + auto const re = deserialize(r); + auto const fe = deserialize(f); auto fu = Fusion{le->as(), re->as()}; REQUIRE((fu.left() || fu.right())); diff --git a/tests/unit/test_index.cpp b/tests/unit/test_index.cpp index ea1428b90e..454585855a 100644 --- a/tests/unit/test_index.cpp +++ b/tests/unit/test_index.cpp @@ -7,7 +7,7 @@ #include "catch2_sequant.hpp" #include -#include +#include #include #include @@ -455,41 +455,4 @@ TEST_CASE("index", "[elements][index]") { REQUIRE(j11.to_latex() == L"{j_{11}}"); } } - - /*SECTION("wolfram") { - Index i1(L"i_1"); - std::wstring i1_str; - REQUIRE_NOTHROW(i1_str = i1.to_wolfram()); - REQUIRE(i1_str == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(i\\), " - L"\\(1\\)]\\)\",particleSpace[occupied]]"); - REQUIRE(i1.to_wolfram(Action::Create) == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(i\\), " - L"\\(1\\)]\\)\",particleSpace[occupied],indexType[cre]]"); - REQUIRE(i1.to_wolfram(BraKetPos::ket) == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(i\\), " - L"\\(1\\)]\\)\",particleSpace[occupied],indexType[ket]]"); - REQUIRE(i1.to_wolfram(Action::Annihilate) == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(i\\), " - L"\\(1\\)]\\)\",particleSpace[occupied],indexType[ann]]"); - REQUIRE(i1.to_wolfram(BraKetPos::bra) == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(i\\), " - L"\\(1\\)]\\)\",particleSpace[occupied],indexType[bra]]"); - - REQUIRE(Index(L"a_1").to_wolfram() == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(a\\), " - L"\\(1\\)]\\)\",particleSpace[virtual]]"); - REQUIRE(Index(L"p_1").to_wolfram() == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(p\\), " - L"\\(1\\)]\\)\",particleSpace[occupied,virtual]]"); - REQUIRE(Index(L"α'_1").to_wolfram() == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(α'\\), " - L"\\(1\\)]\\)\",particleSpace[othervirtual]]"); - REQUIRE(Index(L"α_1").to_wolfram() == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(α\\), " - L"\\(1\\)]\\)\",particleSpace[virtual,othervirtual]]"); - REQUIRE(Index(L"κ_1").to_wolfram() == - L"particleIndex[\"\\!\\(\\*SubscriptBox[\\(κ\\), " - L"\\(1\\)]\\)\",particleSpace[occupied,virtual,othervirtual]]"); - }*/ } diff --git a/tests/unit/test_latex.cpp b/tests/unit/test_latex.cpp index 7c12161ea3..d44bd8225b 100644 --- a/tests/unit/test_latex.cpp +++ b/tests/unit/test_latex.cpp @@ -6,7 +6,7 @@ #include "catch2_sequant.hpp" -#include +#include #include #include @@ -17,73 +17,84 @@ TEST_CASE("latex", "[util]") { SECTION("greek->latex") { using namespace std::string_literals; - REQUIRE(greek_characters_to_latex("alpha"s) == "alpha"); - REQUIRE_THROWS_AS(greek_characters_to_latex("α"s) == "\\alpha", + REQUIRE(io::latex::greek_characters_to_string("alpha"s) == "alpha"); + REQUIRE_THROWS_AS(io::latex::greek_characters_to_string("α"s) == "\\alpha", std::invalid_argument); - REQUIRE(greek_characters_to_latex(std::wstring(L"alpha")) == L"alpha"); - REQUIRE(greek_characters_to_latex(std::wstring(L"α")) == L"\\alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::wstring(L"alpha")) == + L"alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::wstring(L"α")) == + L"\\alpha"); - REQUIRE(greek_characters_to_latex(std::wstring(L"Alpha")) == L"Alpha"); - REQUIRE(greek_characters_to_latex(std::wstring(L"Α")) == L"A"); - REQUIRE(greek_characters_to_latex(std::wstring(L"Γ")) == L"\\Gamma"); + REQUIRE(io::latex::greek_characters_to_string(std::wstring(L"Alpha")) == + L"Alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::wstring(L"Α")) == L"A"); + REQUIRE(io::latex::greek_characters_to_string(std::wstring(L"Γ")) == + L"\\Gamma"); - REQUIRE(greek_characters_to_latex(std::u8string(u8"alpha")) == u8"alpha"); - REQUIRE_THROWS_AS( - greek_characters_to_latex(std::u8string(u8"α")) == u8"\\alpha", - std::invalid_argument); - REQUIRE_THROWS_AS( - greek_characters_to_latex(std::u8string(u8"Γ")) == u8"\\Gamma", - std::invalid_argument); + REQUIRE(io::latex::greek_characters_to_string(std::u8string(u8"alpha")) == + u8"alpha"); + REQUIRE_THROWS_AS(io::latex::greek_characters_to_string( + std::u8string(u8"α")) == u8"\\alpha", + std::invalid_argument); + REQUIRE_THROWS_AS(io::latex::greek_characters_to_string( + std::u8string(u8"Γ")) == u8"\\Gamma", + std::invalid_argument); - REQUIRE(greek_characters_to_latex(std::u16string(u"alpha")) == u"alpha"); - REQUIRE(greek_characters_to_latex(std::u16string(u"α")) == u"\\alpha"); - REQUIRE(greek_characters_to_latex(std::u16string(u"Γ")) == u"\\Gamma"); + REQUIRE(io::latex::greek_characters_to_string(std::u16string(u"alpha")) == + u"alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::u16string(u"α")) == + u"\\alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::u16string(u"Γ")) == + u"\\Gamma"); - REQUIRE(greek_characters_to_latex(std::u32string(U"alpha")) == U"alpha"); - REQUIRE(greek_characters_to_latex(std::u32string(U"α")) == U"\\alpha"); - REQUIRE(greek_characters_to_latex(std::u32string(U"Γ")) == U"\\Gamma"); + REQUIRE(io::latex::greek_characters_to_string(std::u32string(U"alpha")) == + U"alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::u32string(U"α")) == + U"\\alpha"); + REQUIRE(io::latex::greek_characters_to_string(std::u32string(U"Γ")) == + U"\\Gamma"); } SECTION("diactrids->latex") { std::wstring tilde__a = L"ã"; REQUIRE(tilde__a.size() == 2); - REQUIRE(diactrics_to_latex(tilde__a) == L"\\tilde{a}"); + REQUIRE(io::latex::diactrics_to_string(tilde__a) == L"\\tilde{a}"); std::wstring tilde_a = L"ã"; REQUIRE(tilde_a.size() == 1); - REQUIRE(diactrics_to_latex(tilde_a) == L"\\tilde{a}"); + REQUIRE(io::latex::diactrics_to_string(tilde_a) == L"\\tilde{a}"); std::wstring tilde_A = L"Ã"; REQUIRE(tilde_A.size() == 1); - REQUIRE(diactrics_to_latex(tilde_A) == L"\\tilde{A}"); + REQUIRE(io::latex::diactrics_to_string(tilde_A) == L"\\tilde{A}"); std::wstring tilde__f = L"f̃"; REQUIRE(tilde__f.size() == 2); - REQUIRE(diactrics_to_latex(tilde__f) == L"\\tilde{f}"); + REQUIRE(io::latex::diactrics_to_string(tilde__f) == L"\\tilde{f}"); std::wstring grave__f = L"f̀"; REQUIRE(grave__f.size() == 2); - REQUIRE(diactrics_to_latex(grave__f) == L"\\grave{f}"); + REQUIRE(io::latex::diactrics_to_string(grave__f) == L"\\grave{f}"); std::wstring acute__f = L"f́"; REQUIRE(acute__f.size() == 2); - REQUIRE(diactrics_to_latex(acute__f) == L"\\acute{f}"); + REQUIRE(io::latex::diactrics_to_string(acute__f) == L"\\acute{f}"); std::wstring caron__f = L"f̌"; REQUIRE(caron__f.size() == 2); - REQUIRE(diactrics_to_latex(caron__f) == L"\\check{f}"); + REQUIRE(io::latex::diactrics_to_string(caron__f) == L"\\check{f}"); // notice the size is 1 since these are precomposed characters std::wstring hat_A = L"Â"; REQUIRE(hat_A.size() == 1); - REQUIRE(diactrics_to_latex(hat_A) == L"\\hat{A}"); + REQUIRE(io::latex::diactrics_to_string(hat_A) == L"\\hat{A}"); std::wstring hat_S = L"Ŝ"; REQUIRE(hat_S.size() == 1); - REQUIRE(diactrics_to_latex(hat_S) == L"\\hat{S}"); + REQUIRE(io::latex::diactrics_to_string(hat_S) == L"\\hat{S}"); } SECTION("UTF->latex") { std::wstring tilde__alpha = L"α̃"; REQUIRE(tilde__alpha.size() == 2); - REQUIRE(utf_to_latex(tilde__alpha) == L"\\tilde{\\alpha}"); + REQUIRE(io::latex::utf_to_string(tilde__alpha) == L"\\tilde{\\alpha}"); std::wstring hat__alpha = L"α̂"; REQUIRE(hat__alpha.size() == 2); - REQUIRE(utf_to_latex(hat__alpha) == L"\\hat{\\alpha}"); + REQUIRE(io::latex::utf_to_string(hat__alpha) == L"\\hat{\\alpha}"); } } diff --git a/tests/unit/test_math.cpp b/tests/unit/test_math.cpp index ee5cc56c09..14ad4ed0cc 100644 --- a/tests/unit/test_math.cpp +++ b/tests/unit/test_math.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/unit/test_mbpt.cpp b/tests/unit/test_mbpt.cpp index b4251a7ada..18d348732a 100644 --- a/tests/unit/test_mbpt.cpp +++ b/tests/unit/test_mbpt.cpp @@ -6,9 +6,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -983,7 +982,7 @@ SECTION("SRSF") { } } SECTION("S operator action") { - auto expr = tensor::S(2) * parse_expr(L"f{a1,a2;i1,i2}"); + auto expr = tensor::S(2) * deserialize(L"f{a1,a2;i1,i2}"); simplify(expr); auto scalar = expr->as().scalar(); REQUIRE(scalar == 1); @@ -1094,7 +1093,7 @@ SECTION("rules") { for (std::size_t i = 0; i < inputs.size(); ++i) { CAPTURE(inputs.at(i)); - ExprPtr input_expr = parse_expr(inputs.at(i)); + ExprPtr input_expr = deserialize(inputs.at(i)); const IndexSpace aux_space = get_default_context().index_space_registry()->retrieve(L"x"); @@ -1125,7 +1124,7 @@ SECTION("rules") { for (std::size_t i = 0; i < inputs.size(); ++i) { CAPTURE(inputs.at(i)); - ExprPtr input_expr = parse_expr(inputs.at(i)); + ExprPtr input_expr = deserialize(inputs.at(i)); const IndexSpace aux_space = get_default_context().index_space_registry()->retrieve(L"x"); diff --git a/tests/unit/test_optimize.cpp b/tests/unit/test_optimize.cpp index 41b906a539..bdac9058e2 100644 --- a/tests/unit/test_optimize.cpp +++ b/tests/unit/test_optimize.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -61,7 +61,7 @@ TEST_CASE("optimize", "[optimize]") { }; auto parse_expr_antisymm = [](auto const& xpr) { - return parse_expr(xpr, Symmetry::Antisymm); + return deserialize(xpr, {.def_perm_symm = Symmetry::Antisymm}); }; SECTION("Single term optimization") { @@ -141,7 +141,7 @@ TEST_CASE("optimize", "[optimize]") { // // single-term optimization when sequant::Variables appear in a product // - auto prod6 = parse_expr( + auto prod6 = deserialize( L"α * β * γ * " "g_{i3,i4}^{a3,a4}" // T1 " * t_{a1,a2}^{i3,i4}" // T2 @@ -161,7 +161,7 @@ TEST_CASE("optimize", "[optimize]") { // // single-term optimization including tensors with auxiliary indices // - auto prod7 = parse_expr( + auto prod7 = deserialize( L"DF{a_1;a_3;x_1} " // T1 "DF{a_2;i_1;x_1} " // T2 "t{a_3;i_2}" // T3 @@ -177,7 +177,7 @@ TEST_CASE("optimize", "[optimize]") { REQUIRE(extract(res7, {1}) == prod7.at(1)); auto prod8 = - parse_expr( + deserialize( L"T1{i_1;i_2;x_1,x_2,x_3,x_4} T2{i_2;i_1;x_5,x_6,x_7,x_8} " L"T3{i_3;;x_1,x_2,x_3,x_4} T4{i_4;;x_5,x_6,x_7,x_8}") ->as(); @@ -194,7 +194,7 @@ TEST_CASE("optimize", "[optimize]") { SECTION("Ensure single-value sums/products are not discarded") { auto sum = ex(); sum->as().append( - ex(ExprPtrList{parse_expr(L"f{a_1;i_1}")})); + ex(ExprPtrList{deserialize(L"f{a_1;i_1}")})); REQUIRE(sum->as().summand(0).as().factors().size() == 1); auto optimized = optimize(sum); REQUIRE(optimized->is()); @@ -232,14 +232,16 @@ TEST_CASE("optimize", "[optimize]") { std::vector expected; for (const std::wstring& current : inputs) { - expressions.push_back(binarize(parse_result_expr( - current, Symmetry::Nonsymm, BraKetSymmetry::Nonsymm, - ColumnSymmetry::Nonsymm))); + expressions.push_back(binarize(deserialize( + current, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}))); } for (const std::wstring& current : outputs) { - expected.push_back(parse_result_expr(current, Symmetry::Nonsymm, - BraKetSymmetry::Nonsymm, - ColumnSymmetry::Nonsymm)); + expected.push_back(deserialize( + current, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm})); } auto binarizer = [](auto&& expr) { return binarize(expr); }; diff --git a/tests/unit/test_parse.cpp b/tests/unit/test_parse.cpp index e3d8ee1073..eeef461ebe 100644 --- a/tests/unit/test_parse.cpp +++ b/tests/unit/test_parse.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -28,27 +28,32 @@ namespace Catch { template <> -struct StringMaker { - static std::string convert(const sequant::ParseError& error) { - return "ParseError{offset: " + std::to_string(error.offset) + +struct StringMaker { + static std::string convert( + const sequant::io::serialization::SerializationError& error) { + return "io::serialization::SerializationError{offset: " + + std::to_string(error.offset) + ", length: " + std::to_string(error.length) + ", what(): '" + error.what() + "'}"; } }; } // namespace Catch -struct ParseErrorMatcher : Catch::Matchers::MatcherBase { +struct SerializationErrorMatcher + : Catch::Matchers::MatcherBase< + sequant::io::serialization::SerializationError> { std::size_t offset; std::size_t length; std::string messageFragment; - ParseErrorMatcher(std::size_t offset, std::size_t length, - std::string messageFragment = "") + SerializationErrorMatcher(std::size_t offset, std::size_t length, + std::string messageFragment = "") : offset(offset), length(length), messageFragment(std::move(messageFragment)) {} - bool match(const sequant::ParseError& exception) const override { + bool match(const sequant::io::serialization::SerializationError& exception) + const override { if (exception.offset != offset) { return false; } @@ -73,13 +78,13 @@ struct ParseErrorMatcher : Catch::Matchers::MatcherBase { } }; -ParseErrorMatcher parseErrorMatches(std::size_t offset, std::size_t length, - std::string messageFragment = "") { - return ParseErrorMatcher{offset, length, std::move(messageFragment)}; +SerializationErrorMatcher serializationErrorMatches( + std::size_t offset, std::size_t length, std::string messageFragment = "") { + return SerializationErrorMatcher{offset, length, std::move(messageFragment)}; } -TEST_CASE("parsing", "[parse]") { - SECTION("parse_expr") { +TEST_CASE("serialization", "[serialization]") { + SECTION("deserialize") { using namespace sequant; auto ctx = get_default_context(); @@ -87,19 +92,19 @@ TEST_CASE("parsing", "[parse]") { auto ctx_resetter = set_scoped_default_context(ctx); SECTION("Scalar tensor") { - auto expr = parse_expr(L"t{}"); + auto expr = deserialize(L"t{}"); REQUIRE(expr->is()); REQUIRE(expr->as().bra().empty()); REQUIRE(expr->as().ket().empty()); REQUIRE(expr->as().aux().empty()); - REQUIRE(expr == parse_expr(L"t{;}")); - REQUIRE(expr == parse_expr(L"t{;;}")); - REQUIRE(expr == parse_expr(L"t^{}_{}")); - REQUIRE(expr == parse_expr(L"t_{}^{}")); + REQUIRE(expr == deserialize(L"t{;}")); + REQUIRE(expr == deserialize(L"t{;;}")); + REQUIRE(expr == deserialize(L"t^{}_{}")); + REQUIRE(expr == deserialize(L"t_{}^{}")); } SECTION("Tensor") { - auto expr = parse_expr(L"t{i1;a1}"); + auto expr = deserialize(L"t{i1;a1}"); REQUIRE(expr->is()); REQUIRE(expr->as().label() == L"t"); REQUIRE(expr->as().bra().size() == 1); @@ -108,13 +113,13 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(expr->as().ket().at(0) == L"a_1"); REQUIRE(expr->as().aux().empty()); - REQUIRE(expr == parse_expr(L"t_{i1}^{a1}")); - REQUIRE(expr == parse_expr(L"t^{a1}_{i1}")); - REQUIRE(expr == parse_expr(L"t{i_1; a_1}")); - REQUIRE(expr == parse_expr(L"t{i_1; a_1;}")); - REQUIRE(expr == parse_expr(L"t_{i_1}^{a_1}")); + REQUIRE(expr == deserialize(L"t_{i1}^{a1}")); + REQUIRE(expr == deserialize(L"t^{a1}_{i1}")); + REQUIRE(expr == deserialize(L"t{i_1; a_1}")); + REQUIRE(expr == deserialize(L"t{i_1; a_1;}")); + REQUIRE(expr == deserialize(L"t_{i_1}^{a_1}")); - expr = parse_expr(L"t{i1,i2;a1,a2}"); + expr = deserialize(L"t{i1,i2;a1,a2}"); REQUIRE(expr->as().bra().size() == 2); REQUIRE(expr->as().bra().at(0).label() == L"i_1"); REQUIRE(expr->as().bra().at(1).label() == L"i_2"); @@ -123,33 +128,44 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(expr->as().ket().at(1).label() == L"a_2"); REQUIRE(expr->as().aux().empty()); - REQUIRE(expr == parse_expr(L"+t{i1, i2; a1, a2}")); - REQUIRE(parse_expr(L"-t{i1;a1}")->is()); - REQUIRE(expr == parse_expr(L"t{\ti1, \ti2; \na1,\t a2 \t}")); + REQUIRE(expr == deserialize(L"+t{i1, i2; a1, a2}")); + REQUIRE(deserialize(L"-t{i1;a1}")->is()); + REQUIRE(expr == deserialize(L"t{\ti1, \ti2; \na1,\t a2 \t}")); // Tensor labels including underscores - REQUIRE(parse_expr(L"T_1{i_1;a_1}")->as().label() == L"T_1"); + REQUIRE(deserialize(L"T_1{i_1;a_1}")->as().label() == + L"T_1"); // "Non-standard" tensor labels - REQUIRE(parse_expr(L"α{a1;i1}")->as().label() == L"α"); - REQUIRE(parse_expr(L"γ_1{a1;i1}")->as().label() == L"γ_1"); - REQUIRE(parse_expr(L"t⁔1{a1;i1}")->as().label() == L"t⁔1"); - REQUIRE(parse_expr(L"t¹{a1;i1}")->as().label() == L"t¹"); - REQUIRE(parse_expr(L"t⁸{a1;i1}")->as().label() == L"t⁸"); - REQUIRE(parse_expr(L"t⁻{a1;i1}")->as().label() == L"t⁻"); - REQUIRE(parse_expr(L"tₐ{a1;i1}")->as().label() == L"tₐ"); - REQUIRE(parse_expr(L"t₋{a1;i1}")->as().label() == L"t₋"); - REQUIRE(parse_expr(L"t₌{a1;i1}")->as().label() == L"t₌"); - REQUIRE(parse_expr(L"t↓{a1;i1}")->as().label() == L"t↓"); - REQUIRE(parse_expr(L"t↑{a1;i1}")->as().label() == L"t↑"); + REQUIRE(deserialize(L"α{a1;i1}")->as().label() == L"α"); + REQUIRE(deserialize(L"γ_1{a1;i1}")->as().label() == + L"γ_1"); + REQUIRE(deserialize(L"t⁔1{a1;i1}")->as().label() == + L"t⁔1"); + REQUIRE(deserialize(L"t¹{a1;i1}")->as().label() == + L"t¹"); + REQUIRE(deserialize(L"t⁸{a1;i1}")->as().label() == + L"t⁸"); + REQUIRE(deserialize(L"t⁻{a1;i1}")->as().label() == + L"t⁻"); + REQUIRE(deserialize(L"tₐ{a1;i1}")->as().label() == + L"tₐ"); + REQUIRE(deserialize(L"t₋{a1;i1}")->as().label() == + L"t₋"); + REQUIRE(deserialize(L"t₌{a1;i1}")->as().label() == + L"t₌"); + REQUIRE(deserialize(L"t↓{a1;i1}")->as().label() == + L"t↓"); + REQUIRE(deserialize(L"t↑{a1;i1}")->as().label() == + L"t↑"); // "Non-standard" index names - auto expr1 = parse_expr(L"t{a↓1;i↑1}"); + auto expr1 = deserialize(L"t{a↓1;i↑1}"); REQUIRE(expr1->as().bra().at(0).label() == L"a↓_1"); REQUIRE(expr1->as().ket().at(0).label() == L"i↑_1"); // Auxiliary indices - expr = parse_expr(L"t{;;i1}"); + expr = deserialize(L"t{;;i1}"); REQUIRE(expr->is()); REQUIRE(expr->as().bra().empty()); REQUIRE(expr->as().ket().empty()); @@ -157,7 +173,7 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(expr->as().aux()[0].label() == L"i_1"); // All index groups at once - expr = parse_expr(L"t{i1,i2;a1;x1,x2}"); + expr = deserialize(L"t{i1,i2;a1;x1,x2}"); REQUIRE(expr->is()); REQUIRE(expr->as().bra().size() == 2); REQUIRE(expr->as().bra().at(0).label() == L"i_1"); @@ -170,9 +186,9 @@ TEST_CASE("parsing", "[parse]") { } SECTION("Tensor with symmetry annotation") { - auto expr1 = parse_expr(L"t{a1;i1}:A"); - auto expr2 = parse_expr(L"t{a1;i1}:S-C"); - auto expr3 = parse_expr(L"t{a1;i1}:N-S-N"); + auto expr1 = deserialize(L"t{a1;i1}:A"); + auto expr2 = deserialize(L"t{a1;i1}:S-C"); + auto expr3 = deserialize(L"t{a1;i1}:N-S-N"); const Tensor& t1 = expr1->as(); const Tensor& t2 = expr2->as(); @@ -191,7 +207,7 @@ TEST_CASE("parsing", "[parse]") { SECTION("NormalOperator") { { using NOp = FNOperator; - auto expr = parse_expr(L"a{i1;a1}"); + auto expr = deserialize(L"a{i1;a1}"); REQUIRE(expr->is()); REQUIRE(expr->as().label() == NOp::labels()[0]); REQUIRE(expr->as().creators().size() == 1); @@ -202,7 +218,7 @@ TEST_CASE("parsing", "[parse]") { } { using NOp = FNOperator; - auto expr = parse_expr(L"ã{i1;}"); + auto expr = deserialize(L"ã{i1;}"); REQUIRE(expr->is()); REQUIRE(expr->as().label() == NOp::labels()[1]); REQUIRE(expr->as().creators().size() == 0); @@ -213,7 +229,7 @@ TEST_CASE("parsing", "[parse]") { { using NOp = BNOperator; - auto expr = parse_expr(L"b{i1;a1}"); + auto expr = deserialize(L"b{i1;a1}"); REQUIRE(expr->is()); REQUIRE(expr->as().label() == NOp::labels()[0]); REQUIRE(expr->as().creators().size() == 1); @@ -224,7 +240,7 @@ TEST_CASE("parsing", "[parse]") { } { using NOp = BNOperator; - auto expr = parse_expr(L"b̃{;a1}"); + auto expr = deserialize(L"b̃{;a1}"); REQUIRE(expr->is()); REQUIRE(expr->as().label() == NOp::labels()[1]); REQUIRE(expr->as().creators().size() == 1); @@ -235,47 +251,49 @@ TEST_CASE("parsing", "[parse]") { } SECTION("Constant") { - REQUIRE(parse_expr(L"1/2")->is()); - REQUIRE(parse_expr(L"0/2")->is()); - REQUIRE(parse_expr(L"-1/2")->is()); - REQUIRE(parse_expr(L"-0/2")->is()); - REQUIRE(parse_expr(L"1")->is()); - REQUIRE(parse_expr(L"123")->is()); - REQUIRE(parse_expr(L"1.")->is()); - REQUIRE(parse_expr(L"01.00")->is()); - REQUIRE(parse_expr(L"0 / 10")->is()); - REQUIRE(parse_expr(L"0.5/0.25")->is()); - REQUIRE(parse_expr(L".4")->is()); + REQUIRE(deserialize(L"1/2")->is()); + REQUIRE(deserialize(L"0/2")->is()); + REQUIRE(deserialize(L"-1/2")->is()); + REQUIRE(deserialize(L"-0/2")->is()); + REQUIRE(deserialize(L"1")->is()); + REQUIRE(deserialize(L"123")->is()); + REQUIRE(deserialize(L"1.")->is()); + REQUIRE(deserialize(L"01.00")->is()); + REQUIRE(deserialize(L"0 / 10")->is()); + REQUIRE(deserialize(L"0.5/0.25")->is()); + REQUIRE(deserialize(L".4")->is()); } SECTION("Variable") { // SeQuant variable is just a label followed by an optional ^* // to denote if the variable is conjugated - REQUIRE(parse_expr(L"a")->is()); - REQUIRE(parse_expr(L"α")->is()); - REQUIRE(parse_expr(L"β")->is()); - REQUIRE(parse_expr(L"γ")->is()); - REQUIRE(parse_expr(L"λ")->is()); - REQUIRE(parse_expr(L"δ")->is()); - REQUIRE(parse_expr(L"a^*")->is()); - REQUIRE(parse_expr(L"α^*")->is()); - REQUIRE(parse_expr(L"β^*")->is()); - REQUIRE(parse_expr(L"b^*")->is()); - REQUIRE(parse_expr(L"b^*")->as().conjugated()); - REQUIRE(parse_expr(L"b^*")->as().label() == L"b"); + REQUIRE(deserialize(L"a")->is()); + REQUIRE(deserialize(L"α")->is()); + REQUIRE(deserialize(L"β")->is()); + REQUIRE(deserialize(L"γ")->is()); + REQUIRE(deserialize(L"λ")->is()); + REQUIRE(deserialize(L"δ")->is()); + REQUIRE(deserialize(L"a^*")->is()); + REQUIRE(deserialize(L"α^*")->is()); + REQUIRE(deserialize(L"β^*")->is()); + REQUIRE(deserialize(L"b^*")->is()); + REQUIRE(deserialize(L"b^*")->as().conjugated()); + REQUIRE(deserialize(L"b^*")->as().label() == L"b"); } SECTION("Product") { - auto expr = parse_expr(L"-1/2 g{i2,i3; i1,a2} t{a1,a2; i2,i3}"); + auto expr = deserialize(L"-1/2 g{i2,i3; i1,a2} t{a1,a2; i2,i3}"); REQUIRE(expr->is()); auto const& prod = expr->as(); REQUIRE(prod.scalar() == rational{-1, 2}); - REQUIRE(prod.factor(0) == parse_expr(L"g_{i_2, i_3}^{i_1, a_2}")); - REQUIRE(prod.factor(1) == parse_expr(L"t^{i2, i3}_{a1, a2}")); - REQUIRE(parse_expr(L"-1/2 * δ * t{i1;a1}") == - parse_expr(L"-1/2 δ t{i1;a1}")); - auto const prod2 = parse_expr(L"-1/2 * δ * γ * t{i1;a1}")->as(); + REQUIRE(prod.factor(0) == + deserialize(L"g_{i_2, i_3}^{i_1, a_2}")); + REQUIRE(prod.factor(1) == deserialize(L"t^{i2, i3}_{a1, a2}")); + REQUIRE(deserialize(L"-1/2 * δ * t{i1;a1}") == + deserialize(L"-1/2 δ t{i1;a1}")); + auto const prod2 = + deserialize(L"-1/2 * δ * γ * t{i1;a1}")->as(); REQUIRE(prod2.scalar() == rational{-1, 2}); REQUIRE(prod2.factor(0) == ex(L"δ")); REQUIRE(prod2.factor(1) == ex(L"γ")); @@ -283,27 +301,28 @@ TEST_CASE("parsing", "[parse]") { } SECTION("Sum") { - auto expr1 = parse_expr( + auto expr1 = deserialize( L"f{a1;i1}" "- 1/2*g{i2,a1; a2,a3}t{a2,a3; i1,i2}"); REQUIRE(expr1->is()); auto const& sum1 = expr1->as(); - REQUIRE(sum1.summand(0) == parse_expr(L"f{a1;i1}")); - REQUIRE(sum1.summand(1) == - parse_expr(L"- 1/2 * g{i2,a1; a2,a3} * t{a2,a3; i1,i2}")); + REQUIRE(sum1.summand(0) == deserialize(L"f{a1;i1}")); + REQUIRE( + sum1.summand(1) == + deserialize(L"- 1/2 * g{i2,a1; a2,a3} * t{a2,a3; i1,i2}")); - auto expr2 = parse_expr(L"a - 4"); + auto expr2 = deserialize(L"a - 4"); REQUIRE(expr2->is()); auto const& sum2 = expr2->as(); - REQUIRE(sum2.summand(0) == parse_expr(L"a")); - REQUIRE(sum2.summand(1) == parse_expr(L"-4")); + REQUIRE(sum2.summand(0) == deserialize(L"a")); + REQUIRE(sum2.summand(1) == deserialize(L"-4")); } SECTION("Parentheses") { - auto expr1 = - parse_expr(L"-1/2 g{i2,i3; a2,a3} * ( t{a1,a3; i2,i3} * t{a2;i1} )"); + auto expr1 = deserialize( + L"-1/2 g{i2,i3; a2,a3} * ( t{a1,a3; i2,i3} * t{a2;i1} )"); REQUIRE(expr1->is()); auto const& prod1 = expr1->as(); @@ -313,7 +332,7 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(prod1.factor(1)->is()); REQUIRE(prod1.factor(1)->size() == 2); - auto expr2 = parse_expr( + auto expr2 = deserialize( L"(-1/2) ( g{i2,i3; a2,a3} * t{a1,a3; i2,i3} ) * (t{a2;i1})"); REQUIRE(expr2->is()); @@ -321,11 +340,13 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(prod2.size() == 2); REQUIRE(prod2.scalar() == rational{-1, 2}); REQUIRE(prod2.factor(0)->is()); - REQUIRE(prod2.factor(0)->at(0) == parse_expr(L"g{i2,i3; a2,a3}")); - REQUIRE(prod2.factor(0)->at(1) == parse_expr(L"t{a1,a3; i2,i3}")); - REQUIRE(prod2.factor(1) == parse_expr(L"t{a2;i1}")); + REQUIRE(prod2.factor(0)->at(0) == + deserialize(L"g{i2,i3; a2,a3}")); + REQUIRE(prod2.factor(0)->at(1) == + deserialize(L"t{a1,a3; i2,i3}")); + REQUIRE(prod2.factor(1) == deserialize(L"t{a2;i1}")); - auto expr3 = parse_expr( + auto expr3 = deserialize( L"(-1/2) ( g{i2,i3; a2,a3} * t{a1,a3; i2,i3} ) * (1/2) * " L"((t{a2;i1}))"); REQUIRE(expr3->is()); @@ -334,11 +355,13 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(prod3.size() == 2); REQUIRE(prod3.scalar() == rational{-1, 4}); REQUIRE(prod3.factor(0)->is()); - REQUIRE(prod3.factor(0)->at(0) == parse_expr(L"g{i2,i3; a2,a3}")); - REQUIRE(prod3.factor(0)->at(1) == parse_expr(L"t{a1,a3; i2,i3}")); - REQUIRE(prod3.factor(1) == parse_expr(L"t{a2;i1}")); + REQUIRE(prod3.factor(0)->at(0) == + deserialize(L"g{i2,i3; a2,a3}")); + REQUIRE(prod3.factor(0)->at(1) == + deserialize(L"t{a1,a3; i2,i3}")); + REQUIRE(prod3.factor(1) == deserialize(L"t{a2;i1}")); - auto expr4 = parse_expr(L"1/2 (a + b) * c"); + auto expr4 = deserialize(L"1/2 (a + b) * c"); REQUIRE(expr4->is()); const auto& prod4 = expr4->as(); @@ -356,7 +379,7 @@ TEST_CASE("parsing", "[parse]") { } SECTION("Mixed") { - auto expr = parse_expr( + auto expr = deserialize( L"0.25 g{a1,a2; i1,i2}" "+ 1/4 g{i3,i4; a3,a4} (t{a3;i1} * t{a4;i2}) * (t{a1;i3} * " "t{a2;i4})"); @@ -368,25 +391,26 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(sum.summand(0)->is()); REQUIRE(sum.summand(0)->as().scalar() == rational{1, 4}); REQUIRE(sum.summand(0)->size() == 1); - REQUIRE(sum.summand(0)->at(0) == parse_expr(L"g{a1,a2; i1,i2}")); + REQUIRE(sum.summand(0)->at(0) == + deserialize(L"g{a1,a2; i1,i2}")); REQUIRE(sum.summand(1)->is()); auto const& prod = sum.summand(1)->as(); REQUIRE(prod.scalar() == rational{1, 4}); REQUIRE(prod.size() == 3); - REQUIRE(prod.factor(0) == parse_expr(L"g{i3,i4; a3,a4}")); + REQUIRE(prod.factor(0) == deserialize(L"g{i3,i4; a3,a4}")); REQUIRE(prod.factor(1)->is()); - REQUIRE(prod.factor(1)->at(0) == parse_expr(L"t{a3;i1}")); - REQUIRE(prod.factor(1)->at(1) == parse_expr(L"t{a4;i2}")); + REQUIRE(prod.factor(1)->at(0) == deserialize(L"t{a3;i1}")); + REQUIRE(prod.factor(1)->at(1) == deserialize(L"t{a4;i2}")); REQUIRE(prod.factor(2)->is()); - REQUIRE(prod.factor(2)->at(0) == parse_expr(L"t{a1;i3}")); - REQUIRE(prod.factor(2)->at(1) == parse_expr(L"t{a2;i4}")); + REQUIRE(prod.factor(2)->at(0) == deserialize(L"t{a1;i3}")); + REQUIRE(prod.factor(2)->at(1) == deserialize(L"t{a2;i4}")); } - SECTION("Empty input") { REQUIRE(parse_expr(L"") == nullptr); } + SECTION("Empty input") { REQUIRE(deserialize(L"") == nullptr); } SECTION("Error handling") { SECTION("Exception type") { @@ -397,30 +421,35 @@ TEST_CASE("parsing", "[parse]") { L"T^{i1}{a1}"}; for (const std::wstring& current : inputs) { - REQUIRE_THROWS_AS(parse_expr(current), ParseError); + REQUIRE_THROWS_AS(deserialize(current), + io::serialization::SerializationError); } } SECTION("Invalid index") { - REQUIRE_THROWS_MATCHES(parse_expr(L"t{i1;}"), ParseError, - parseErrorMatches(5, 3, "proto")); - REQUIRE_THROWS_MATCHES(parse_expr(L"t{i1;az3}"), ParseError, - parseErrorMatches(5, 3, "Unknown index space")); + REQUIRE_THROWS_MATCHES(deserialize(L"t{i1;}"), + io::serialization::SerializationError, + serializationErrorMatches(5, 3, "proto")); + REQUIRE_THROWS_MATCHES( + deserialize(L"t{i1;az3}"), + io::serialization::SerializationError, + serializationErrorMatches(5, 3, "Unknown index space")); } SECTION("Invalid symmetry") { REQUIRE_THROWS_MATCHES( - parse_expr(L"t{i1;a3}:P"), ParseError, - parseErrorMatches(9, 1, "Invalid symmetry specifier")); + deserialize(L"t{i1;a3}:P"), + io::serialization::SerializationError, + serializationErrorMatches(9, 1, "Invalid symmetry specifier")); } } } - SECTION("parse_result_expr") { + SECTION("deserialize") { using namespace sequant; SECTION("constant") { - ResultExpr result = parse_result_expr(L"A = 3"); + ResultExpr result = deserialize(L"A = 3"); REQUIRE(result.has_label()); REQUIRE(result.label() == L"A"); @@ -434,8 +463,8 @@ TEST_CASE("parsing", "[parse]") { REQUIRE(result.expression().as().value() == 3); } SECTION("contraction") { - ResultExpr result = - parse_result_expr(L"R{i1,i2;e1,e2}:A = f{e2;e3} t{e1,e3;i1,i2}"); + ResultExpr result = deserialize( + L"R{i1,i2;e1,e2}:A = f{e2;e3} t{e1,e3;i1,i2}"); REQUIRE(result.has_label()); REQUIRE(result.label() == L"R"); @@ -460,7 +489,7 @@ TEST_CASE("parsing", "[parse]") { } } - SECTION("deparse") { + SECTION("serialize") { using namespace sequant; std::vector expressions = { @@ -471,16 +500,15 @@ TEST_CASE("parsing", "[parse]") { L"a + b - 4 specialVariable", L"variable + A{a_1;i_1}:N-N-S * B{i_1;a_1}:A-C-S", L"1/2 (a + b) * c", - L"T1{}:N-N-N + T2{;;x_1}:N-N-N * T3{;;x_1}:N-N-N + T4{a_1;;x_2}:S-C-S " - L"* " - L"T5{;a_1;x_2}:S-S-S", + L"T1{}:N-N-N + T2{;;x_1}:N-N-N * T3{;;x_1}:N-N-N " + L"+ T4{a_1;;x_2}:S-C-S * T5{;a_1;x_2}:S-S-S", L"q1 * q2^* * q3", L"1/2 ã{i_1;i_2} * b̃{i_3;i_4}"}; for (const std::wstring& current : expressions) { - ExprPtr expression = parse_expr(current); + ExprPtr expression = deserialize(current); - REQUIRE(deparse(expression, true) == current); + REQUIRE(serialize(expression, {.annot_symm = true}) == current); } SECTION("result_expressions") { @@ -493,9 +521,9 @@ TEST_CASE("parsing", "[parse]") { }; for (const std::wstring& current : expressions) { - ResultExpr result = parse_result_expr(current); + ResultExpr result = deserialize(current); - REQUIRE(deparse(result, true) == current); + REQUIRE(serialize(result, {.annot_symm = true}) == current); } } } diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index 5cf8a082c7..ffeb274dc3 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -10,8 +10,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -274,7 +273,7 @@ TEST_CASE("spin", "[spin]") { // as this forces the code to take a path where it traces a product which // ends up as a single tensor (due to an additional factor of -1 induces // by the chosen pairing of external indices) - ExprPtr expr = parse_expr(L"- g{p1,p2;p3,p4}:A"); + ExprPtr expr = deserialize(L"- g{p1,p2;p3,p4}:A"); ExprPtr result = spintrace(expr, {{L"p_1", L"p_4"}, {L"p_2", L"p_3"}}); REQUIRE_THAT(result, @@ -322,7 +321,7 @@ TEST_CASE("spin", "[spin]") { } SECTION("Scaled Product with variable") { - ExprPtr expr = parse_expr(L"1/2 Var g{i1,i2;a1,a2}:A t{a1;i1} t{a2;i2}"); + ExprPtr expr = deserialize(L"1/2 Var g{i1,i2;a1,a2}:A t{a1;i1} t{a2;i2}"); auto result = spintrace(expr); canonicalize(result); REQUIRE_THAT( @@ -333,7 +332,7 @@ TEST_CASE("spin", "[spin]") { } SECTION("Tensor times variable") { - ResultExpr expr = parse_result_expr( + ResultExpr expr = deserialize( L"R2{a1,a2;i1,i2}:A = Â{i1,i2;a1,a2}:A INTkx{a1,a2;i1,i2}:A H"); auto results = closed_shell_spintrace(expr); REQUIRE_THAT( @@ -767,16 +766,18 @@ SECTION("Closed-shell spintrace CCD") { { { // standard = v1 , expression can be parsed directly without requiring // cast to Sum - const auto input = 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); + const auto input = + deserialize(L"1/4 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}", + {.def_perm_symm = Symmetry::Antisymm}); auto result = closed_shell_CC_spintrace_v1(input); REQUIRE_THAT(result, EquivalentTo(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}")); } { // compact = v2 - 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)}); + const auto input = ex( + ExprPtrList{deserialize(L"1/4 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}", + {.def_perm_symm = Symmetry::Antisymm})}); auto result = closed_shell_CC_spintrace_v2(input); REQUIRE_THAT(result, @@ -784,7 +785,7 @@ SECTION("Closed-shell spintrace CCD") { L"2 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}")); } { // CSV (aka PNO) for regular cs - const auto pno_ccd_energy_so = parse_expr( + const auto pno_ccd_energy_so = deserialize( L"1/4 g{a1, a2;i1, i2}:A-C t{i1,i2;a1, " L"a2}:A"); @@ -800,7 +801,7 @@ SECTION("Closed-shell spintrace CCD") { "t{i1,i2;a2,a1}:N-C")); } { // CSV (aka PNO) for more compact equations - const auto pno_ccd_energy_so = parse_expr( + const auto pno_ccd_energy_so = deserialize( L"1/4 g{a1, a2;i1, i2}:A-C t{i1,i2;a1, " L"a2}:A"); @@ -1016,8 +1017,8 @@ SECTION("Closed-shell spintrace CCSD") { SECTION("Closed-shell CC spintrace for variable, constant, product") { { // test variable * tensors - auto expr1 = sequant::parse_expr(L"-ω Â{i1,i2;a1,a2} t{a1,a2;i1,i2}", - Symmetry::Antisymm); + auto expr1 = sequant::deserialize(L"-ω Â{i1,i2;a1,a2} t{a1,a2;i1,i2}", + {.def_perm_symm = Symmetry::Antisymm}); auto result_v1 = mbpt::closed_shell_CC_spintrace_v1(expr1); REQUIRE_THAT(result_v1, EquivalentTo(L"-ω Ŝ{i1,i2;a1,a2} t{a1,a2;i1,i2}")); @@ -1026,7 +1027,7 @@ SECTION("Closed-shell CC spintrace for variable, constant, product") { REQUIRE_THAT(result_v2, EquivalentTo(L"-ω Ŝ{i1,i2;a1,a2} t{a1,a2;i1,i2}")); } { // test a single variable - auto expr1 = sequant::parse_expr(L"ω"); + auto expr1 = sequant::deserialize(L"ω"); auto result_v1 = mbpt::closed_shell_CC_spintrace_v1(expr1); REQUIRE_THAT(result_v1, EquivalentTo(L"ω")); @@ -1035,7 +1036,7 @@ SECTION("Closed-shell CC spintrace for variable, constant, product") { REQUIRE_THAT(result_v2, EquivalentTo(L"ω")); } { // test a single constant - auto expr1 = sequant::parse_expr(L"1/4"); + auto expr1 = sequant::deserialize(L"1/4"); auto result_v1 = mbpt::closed_shell_CC_spintrace_v1(expr1); REQUIRE_THAT(result_v1, EquivalentTo(L"1/4")); @@ -1044,8 +1045,8 @@ SECTION("Closed-shell CC spintrace for variable, constant, product") { REQUIRE_THAT(result_v2, EquivalentTo(L"1/4")); } { // test a product of tensors - const auto input = 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); + const auto input = deserialize(L"1/4 g{i_1,i_2;a_1,a_2} t{a_1,a_2;i_1,i_2}", + {.def_perm_symm = Symmetry::Antisymm}); auto result_v1 = closed_shell_CC_spintrace_v1(input); REQUIRE_THAT(result_v1, @@ -1162,9 +1163,9 @@ SECTION("Closed-shell spintrace CCSDT terms") { SECTION("the most expensive terms in CCSDT in v2") { // results in 1 term const auto input = ex(ExprPtrList{ - parse_expr(L" 3/2 Â{i_1,i_2,i_3;a_1,a_2,a_3} * " - L"g{a_1,a_2;a_4,a_5} * t{a_3,a_4,a_5;i_1,i_2,i_3}", - Symmetry::Antisymm)}); + deserialize(L" 3/2 Â{i_1,i_2,i_3;a_1,a_2,a_3} * " + L"g{a_1,a_2;a_4,a_5} * t{a_3,a_4,a_5;i_1,i_2,i_3}", + {.def_perm_symm = Symmetry::Antisymm})}); auto result = closed_shell_CC_spintrace_v2(input); REQUIRE_THAT( @@ -1176,9 +1177,9 @@ SECTION("Closed-shell spintrace CCSDT terms") { SECTION("most expensive CCSDT term in v1") { // results in 4 terms const auto input = ex(ExprPtrList{ - parse_expr(L"3/2 Â{i_1,i_2,i_3;a_1,a_2,a_3} * g{a_1,a_2;a_4,a_5} * " - "t{a_3,a_4,a_5;i_1,i_2,i_3}", - Symmetry::Antisymm)}); + deserialize(L"3/2 Â{i_1,i_2,i_3;a_1,a_2,a_3} * g{a_1,a_2;a_4,a_5} * " + "t{a_3,a_4,a_5;i_1,i_2,i_3}", + {.def_perm_symm = Symmetry::Antisymm})}); auto result = closed_shell_CC_spintrace_v1(input); REQUIRE(result->size() == 4); @@ -1703,11 +1704,11 @@ SECTION("ResultExpr") { for (std::size_t i = 0; i < inputs.size(); ++i) { CAPTURE(inputs.at(i)); - const ResultExpr input = parse_result_expr(inputs.at(i)); + const ResultExpr input = deserialize(inputs.at(i)); container::svector expected; for (std::size_t k = 0; k < expected_outputs.at(i).size(); ++k) { - expected.push_back(parse_result_expr(expected_outputs.at(i).at(k))); + expected.push_back(deserialize(expected_outputs.at(i).at(k))); } SECTION("closed_shell" + std::to_string(i)) { diff --git a/tests/unit/test_string.cpp b/tests/unit/test_string.cpp deleted file mode 100644 index c19ce10fe4..0000000000 --- a/tests/unit/test_string.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// Created by Eduard Valeyev on 7/19/23. -// - -#include - -#include "catch2_sequant.hpp" - -#include - -#include -#include - -TEST_CASE("string", "[util]") { - using namespace sequant; - - SECTION("SQ_STRLIT") { - STATIC_REQUIRE( - std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - - STATIC_REQUIRE( - std::is_same_v); - STATIC_REQUIRE( - std::is_same_v); - STATIC_REQUIRE( - std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - } -} diff --git a/tests/unit/test_tensor.cpp b/tests/unit/test_tensor.cpp index 8f19973d86..ee18e0497a 100644 --- a/tests/unit/test_tensor.cpp +++ b/tests/unit/test_tensor.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -291,7 +291,7 @@ TEST_CASE("tensor", "[elements]") { REQUIRE(!t.ket()[1].tag().has_value()); SECTION("proto indices") { - Tensor tensor = parse_expr(L"g{i2,a1;a2,i1}")->as(); + Tensor tensor = deserialize(L"g{i2,a1;a2,i1}")->as(); const auto* slot_b0 = &(tensor.bra()[0]); const auto* slot_b1 = &(tensor.bra()[1]); @@ -307,7 +307,7 @@ TEST_CASE("tensor", "[elements]") { }; const Tensor expected = - parse_expr(L"g{a1,i2;i1,a2}")->as(); + deserialize(L"g{a1,i2;i1,a2}")->as(); tensor.transform_indices(idxmap); REQUIRE(tensor == expected); diff --git a/tests/unit/test_tensor_network.cpp b/tests/unit/test_tensor_network.cpp index 6521722668..b2e8b50046 100644 --- a/tests/unit/test_tensor_network.cpp +++ b/tests/unit/test_tensor_network.cpp @@ -14,9 +14,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -176,8 +176,8 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, CAPTURE(toUtf8(input1)); CAPTURE(toUtf8(input2)); - auto ex1 = parse_expr(input1); - auto ex2 = parse_expr(input2); + auto ex1 = deserialize(input1); + auto ex2 = deserialize(input2); TN tn1(ex1); auto cbp1 = tn1.canonicalize_slots( @@ -215,10 +215,10 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, SECTION("phase_difference") { const Product prod1 = - parse_expr(L"g{i_2,i_3;a_2,a_3}:A-C-S * t{a_2;i_2}:A-C-S") + deserialize(L"g{i_2,i_3;a_2,a_3}:A-C-S * t{a_2;i_2}:A-C-S") ->as(); const Product prod2 = - parse_expr(L"g{i_2,i_3;a_2,a_3}:A-C-S * t{a_2;i_3}:A-C-S") + deserialize(L"g{i_2,i_3;a_2,a_3}:A-C-S * t{a_2;i_3}:A-C-S") ->as(); TN tn1(prod1.factors()); @@ -240,12 +240,12 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, .first_dummy_index_ordinal = 20000}); const Product prod1 = - parse_expr( + deserialize( L"g{i_2,i_3;a_19592,a_19593}:N-C-S * C{a_19592;a_2}:N-C-S * " L"C{a_19593;a_3}:N-C-S") ->as(); const Product prod2 = - parse_expr( + deserialize( L"g{i_2,i_3;a_19594,a_19595}:N-C-S * C{a_19594;a_2}:N-C-S * " L"C{a_19595;a_3}:N-C-S") ->as(); @@ -285,7 +285,7 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, }) | ranges::to(); - Product prod = parse_expr(input)->as(); + Product prod = deserialize(input)->as(); TN tn(prod.factors()); auto meta = tn.canonicalize_slots( @@ -302,11 +302,11 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, SECTION("need particle reorder?") { { Index::reset_tmp_index(); - auto input1 = parse_expr( + auto input1 = deserialize( L"t{p1,p2;p3,p4}:N-C-S t{p4,p5;p6,p7}:N-C-S t{p7,p8;p9,p1}:N-C-S"); // N.B. renaming external index changes local canonical order produced // by TNV1 (due to the use of DefaultTensorCanonicalizer) - auto input2 = parse_expr( + auto input2 = deserialize( L"t{p1,p2;p3,p4}:N-C-S t{p4,p5;p11,p7}:N-C-S t{p7,p8;p9,p1}:N-C-S"); CAPTURE(input1); @@ -351,8 +351,8 @@ TEMPLATE_TEST_CASE("tensor_network_shared", "[elements]", TensorNetworkV1, {Index{L"p_11"}, Index{L"p_6"}}, {Index{L"p_6"}, Index{L"p_7"}}}; apply_index_replacements(*t2_i, idxrepl, false); - REQUIRE(deparse(*(tn1.tensors()[i]), true) == - deparse(*t2_i, true)); + REQUIRE(serialize(*(tn1.tensors()[i]), {.annot_symm = true}) == + serialize(*t2_i, {.annot_symm = true})); } } } @@ -1039,8 +1039,8 @@ TEST_CASE("tensor_network_v2", "[elements]") { } SECTION("particle non-conserving") { - const auto input1 = parse_expr(L"P{;a1,a3}"); - const auto input2 = parse_expr(L"P{a1,a3;}"); + const auto input1 = deserialize(L"P{;a1,a3}"); + const auto input2 = deserialize(L"P{a1,a3;}"); const std::wstring expected1 = L"{{P^{{a_1}{a_3}}_{}}}"; const std::wstring expected2 = L"{{P^{}_{{a_1}{a_3}}}}"; @@ -1058,7 +1058,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { SECTION("non-symmetric") { const auto input = - parse_expr(L"Â{i9,i12;i7,i3}:A I1{i7,i3;;x5}:N I2{;i9,i12;x5}:N") + deserialize(L"Â{i9,i12;i7,i3}:A I1{i7,i3;;x5}:N I2{;i9,i12;x5}:N") .as() .factors(); const std::wstring expected = @@ -1083,8 +1083,8 @@ TEST_CASE("tensor_network_v2", "[elements]") { L"Γ{o_2,o_4;o_1,o_3}:N * g{i_1,o_3;o_4,e_1}:N * " L"t{o_1,e_1;o_2,i_1}:N"}}; for (const auto& pair : pairs) { - const auto first = parse_expr(pair.first).as().factors(); - const auto second = parse_expr(pair.second).as().factors(); + const auto first = deserialize(pair.first).as().factors(); + const auto second = deserialize(pair.second).as().factors(); TensorNetworkV2Accessor accessor; auto [first_graph, first_labels] = @@ -1106,7 +1106,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { .texlabels = graph.vertex_texlabels, .display_colors = true}); - FAIL(to_string(stream.str())); + FAIL(toUtf8(stream.str())); } TensorNetworkV2 tn1(first); @@ -1140,7 +1140,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { }; for (const auto& [input, expected] : inputs) { - const auto input_tensors = parse_expr(input).as().factors(); + const auto input_tensors = deserialize(input).as().factors(); TensorNetworkV2 tn(input_tensors); ExprPtr factor = tn.canonicalize( @@ -1158,7 +1158,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { SECTION("special") { auto factors = - parse_expr( + deserialize( L"Ŝ{i_1;a_1}:N-C-S g{i_2,a_1;a_2,i_1}:N-C-S " L"t{a_2;i_2}:N-C-S") ->as() @@ -1182,9 +1182,9 @@ TEST_CASE("tensor_network_v2", "[elements]") { // and doesn't actually matter What does, is that all equivalent ways of // writing it down, canonicalizes to the same exact form const Product expectedExpr = - parse_expr( + deserialize( L"Â{i1,i2;a1,a2} g{i3,i4;a3,a4} t{a1,a3;i1,i2} t{a2,a4;i3,i4}", - Symmetry::Antisymm) + {.def_perm_symm = Symmetry::Antisymm}) .as(); const auto expected = expectedExpr.factors(); @@ -1261,8 +1261,8 @@ TEST_CASE("tensor_network_v2", "[elements]") { const auto [current_graph, current_graph_labels] = accessor.get_canonical_bliss_graph(tn); if (current_graph->cmp(*canonical_graph) != 0) { - std::wcout << "Canonical graph for " << deparse(ex(copy)) - << ":\n"; + std::wcout << "Canonical graph for " + << serialize(ex(copy)) << ":\n"; current_graph->write_dot(std::wcout, {.labels = current_graph_labels}); std::wcout << std::endl; @@ -1295,12 +1295,12 @@ TEST_CASE("tensor_network_v2", "[elements]") { std::wstring equality = actual[i] == expected[i] ? L" == " : L" != "; - sstream << deparse(actual[i]) << equality - << deparse(expected[i]) << "\n"; + sstream << serialize(actual[i]) << equality + << serialize(expected[i]) << "\n"; } - sstream << "\nInput was " << deparse(ex(factors)) + sstream << "\nInput was " << serialize(ex(factors)) << "\n"; - FAIL(to_string(sstream.str())); + FAIL(toUtf8(sstream.str())); } } while (std::next_permutation(indices.begin() + 4, indices.end())); } while (std::next_permutation(indices.begin(), indices.begin() + 4)); @@ -1321,8 +1321,8 @@ TEST_CASE("tensor_network_v2", "[elements]") { }; for (const std::wstring& current : inputs) { - auto factors1 = parse_expr(current).as().factors(); - auto factors2 = parse_expr(current).as().factors(); + auto factors1 = deserialize(current).as().factors(); + auto factors2 = deserialize(current).as().factors(); TensorNetworkV2 reference_tn(factors1); reference_tn.canonicalize(TensorCanonicalizer::cardinal_tensor_labels(), @@ -1683,8 +1683,8 @@ TEST_CASE("tensor_network_v3", "[elements]") { } SECTION("particle non-conserving") { - const auto input1 = parse_expr(L"P{;a1,a3}"); - const auto input2 = parse_expr(L"P{a1,a3;}"); + const auto input1 = deserialize(L"P{;a1,a3}"); + const auto input2 = deserialize(L"P{a1,a3;}"); const std::wstring expected1 = L"{{P^{{a_1}{a_3}}_{}}}"; const std::wstring expected2 = L"{{P^{}_{{a_1}{a_3}}}}"; @@ -1703,7 +1703,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { SECTION("non-symmetric") { const auto input = - parse_expr(L"Â{i9,i12;i7,i3}:A I1{i7,i3;;x5}:N I2{;i9,i12;x5}:N") + deserialize(L"Â{i9,i12;i7,i3}:A I1{i7,i3;;x5}:N I2{;i9,i12;x5}:N") .as() .factors(); const std::wstring expected = @@ -1730,8 +1730,8 @@ TEST_CASE("tensor_network_v3", "[elements]") { L"Γ{o_2,o_4;o_1,o_3}:N * g{i_1,o_3;o_4,e_1}:N * " L"t{o_1,e_1;o_2,i_1}:N"}}; for (const auto& pair : pairs) { - const auto first = parse_expr(pair.first).as().factors(); - const auto second = parse_expr(pair.second).as().factors(); + const auto first = deserialize(pair.first).as().factors(); + const auto second = deserialize(pair.second).as().factors(); TensorNetworkV3Accessor accessor; auto [first_graph, first_labels] = @@ -1754,7 +1754,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { .texlabels = graph.vertex_texlabels, .display_colors = true}); - FAIL(to_string(stream.str())); + FAIL(toUtf8(stream.str())); } TN tn1(first); @@ -1788,7 +1788,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { }; for (const auto& [input, expected] : inputs) { - const auto input_tensors = parse_expr(input).as().factors(); + const auto input_tensors = deserialize(input).as().factors(); TN tn(input_tensors); ExprPtr factor = @@ -1807,7 +1807,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { SECTION("special") { auto factors = - parse_expr( + deserialize( L"Ŝ{i_1;a_1}:N-C-S g{i_2,a_1;a_2,i_1}:N-C-S " L"t{a_2;i_2}:N-C-S") ->as() @@ -1881,9 +1881,9 @@ TEST_CASE("tensor_network_v3", "[elements]") { // and doesn't actually matter What does, is that all equivalent ways of // writing it down, canonicalizes to the same exact form const Product expectedExpr = - parse_expr( + deserialize( L"Â{i1,i2;a1,a2} g{i3,i4;a3,a4} t{a1,a3;i1,i2} t{a2,a4;i3,i4}", - Symmetry::Antisymm) + {.def_perm_symm = Symmetry::Antisymm}) .as(); const auto expected = expectedExpr.factors(); @@ -1960,8 +1960,8 @@ TEST_CASE("tensor_network_v3", "[elements]") { const auto [current_graph, current_graph_labels] = accessor.get_canonical_bliss_graph(tn); if (current_graph->cmp(*canonical_graph) != 0) { - std::wcout << "Canonical graph for " << deparse(ex(copy)) - << ":\n"; + std::wcout << "Canonical graph for " + << serialize(ex(copy)) << ":\n"; current_graph->write_dot(std::wcout, {.labels = current_graph_labels}); std::wcout << std::endl; @@ -1994,12 +1994,12 @@ TEST_CASE("tensor_network_v3", "[elements]") { std::wstring equality = actual[i] == expected[i] ? L" == " : L" != "; - sstream << deparse(actual[i]) << equality - << deparse(expected[i]) << "\n"; + sstream << serialize(actual[i]) << equality + << serialize(expected[i]) << "\n"; } - sstream << "\nInput was " << deparse(ex(factors)) + sstream << "\nInput was " << serialize(ex(factors)) << "\n"; - FAIL(to_string(sstream.str())); + FAIL(toUtf8(sstream.str())); } } while (std::next_permutation(indices.begin() + 4, indices.end())); } while (std::next_permutation(indices.begin(), indices.begin() + 4)); @@ -2020,8 +2020,8 @@ TEST_CASE("tensor_network_v3", "[elements]") { }; for (const std::wstring& current : inputs) { - auto factors1 = parse_expr(current).as().factors(); - auto factors2 = parse_expr(current).as().factors(); + auto factors1 = deserialize(current).as().factors(); + auto factors2 = deserialize(current).as().factors(); TN reference_tn(factors1); reference_tn.canonicalize(TensorCanonicalizer::cardinal_tensor_labels(), diff --git a/tests/unit/test_utilities.cpp b/tests/unit/test_utilities.cpp index 26cc79a190..51d942235c 100644 --- a/tests/unit/test_utilities.cpp +++ b/tests/unit/test_utilities.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,7 +46,7 @@ class S : public sequant::Singleton> { } // namespace sequant::singleton sequant::Tensor parse_tensor(std::wstring_view str) { - return sequant::parse_expr(str)->as(); + return sequant::deserialize(str)->as(); } TEST_CASE("utilities", "[utilities]") { @@ -93,7 +93,7 @@ TEST_CASE("utilities", "[utilities]") { using namespace Catch::Matchers; SECTION("Constant") { - auto const expression = parse_expr(L"5"); + auto const expression = deserialize(L"5"); auto const indices = get_unique_indices(expression); @@ -111,7 +111,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE(indices == get_unique_indices(expression->as())); } SECTION("Tensor") { - auto expression = parse_expr(L"t{i1;a1,a2;x1}"); + auto expression = deserialize(L"t{i1;a1,a2;x1}"); auto indices = get_unique_indices(expression); @@ -121,7 +121,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE_THAT(indices.aux, UnorderedEquals(std::vector{{L"x_1"}})); REQUIRE(indices == get_unique_indices(expression->as())); - expression = parse_expr(L"t{i1,i2;a1,a2}"); + expression = deserialize(L"t{i1,i2;a1,a2}"); indices = get_unique_indices(expression); @@ -132,7 +132,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE(indices.aux.size() == 0); REQUIRE(indices == get_unique_indices(expression->as())); - expression = parse_expr(L"t{i1,i2;a1,i1}"); + expression = deserialize(L"t{i1,i2;a1,i1}"); indices = get_unique_indices(expression); @@ -141,7 +141,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE(indices == get_unique_indices(expression->as())); } SECTION("Product") { - auto expression = parse_expr(L"t{i1;a1,a2} p{a2;i2;x1}"); + auto expression = deserialize(L"t{i1;a1,a2} p{a2;i2;x1}"); auto indices = get_unique_indices(expression); @@ -151,7 +151,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE_THAT(indices.aux, UnorderedEquals(std::vector{{L"x_1"}})); REQUIRE(indices == get_unique_indices(expression->as())); - expression = parse_expr(L"1/8 g{a3,a4;i3,i4;x1} t{a1,a4;i1,i4;x1}"); + expression = deserialize(L"1/8 g{a3,a4;i3,i4;x1} t{a1,a4;i1,i4;x1}"); indices = get_unique_indices(expression); @@ -163,7 +163,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE(indices == get_unique_indices(expression->as())); } SECTION("Sum") { - auto expression = parse_expr(L"t{i1;a2;x1} + g{i1;a2;x1}"); + auto expression = deserialize(L"t{i1;a2;x1} + g{i1;a2;x1}"); auto indices = get_unique_indices(expression); @@ -172,7 +172,7 @@ TEST_CASE("utilities", "[utilities]") { REQUIRE_THAT(indices.aux, UnorderedEquals(std::vector{{L"x_1"}})); REQUIRE(indices == get_unique_indices(expression->as())); - expression = parse_expr(L"t{i1;a2} t{i1;a1} + t{i1;a1} g{i1;a2}"); + expression = deserialize(L"t{i1;a2} t{i1;a1} + t{i1;a1} g{i1;a2}"); indices = get_unique_indices(expression); @@ -285,7 +285,7 @@ TEST_CASE("utilities", "[utilities]") { SECTION("transform_expr") { { ExprPtr expr = - parse_expr(L"- g{a1,i2;a2,i1} t{a2;i2} + 2 g{a1,i2;i1,a2} t{a2;i2}"); + deserialize(L"- g{a1,i2;a2,i1} t{a2;i2} + 2 g{a1,i2;i1,a2} t{a2;i2}"); container::map idxmap = {{Index{L"i_1"}, Index{L"i_2"}}, {Index{L"i_2"}, Index{L"i_1"}}}; auto transformed_result = transform_expr(expr, idxmap); @@ -298,7 +298,7 @@ TEST_CASE("utilities", "[utilities]") { } { ExprPtr expr = - parse_expr(L"- g{i2,a1;i1,a2} + 2 g{i2,a1;a2,i1} t{a2;i2}"); + deserialize(L"- g{i2,a1;i1,a2} + 2 g{i2,a1;a2,i1} t{a2;i2}"); container::map idxmap = {{Index{L"i_1"}, Index{L"i_2"}}, {Index{L"i_2"}, Index{L"i_1"}}}; auto transformed_result = transform_expr(expr, idxmap); @@ -330,8 +330,8 @@ TEST_CASE("utilities", "[utilities]") { CAPTURE(toUtf8(lhs)); CAPTURE(toUtf8(rhs)); - const Tensor lhs_tensor = parse_expr(lhs)->as(); - const Tensor rhs_tensor = parse_expr(rhs)->as(); + const Tensor lhs_tensor = deserialize(lhs)->as(); + const Tensor rhs_tensor = deserialize(rhs)->as(); REQUIRE(cmp(lhs_tensor, rhs_tensor) == equal); } } @@ -361,8 +361,8 @@ TEST_CASE("utilities", "[utilities]") { CAPTURE(toUtf8(lhs)); CAPTURE(toUtf8(rhs)); - const Tensor lhs_tensor = parse_expr(lhs)->as(); - const Tensor rhs_tensor = parse_expr(rhs)->as(); + const Tensor lhs_tensor = deserialize(lhs)->as(); + const Tensor rhs_tensor = deserialize(rhs)->as(); REQUIRE(cmp(lhs_tensor, rhs_tensor) == less); if (equal_cmp(lhs_tensor, rhs_tensor)) { @@ -384,7 +384,7 @@ TEST_CASE("utilities", "[utilities]") { {L"t{a1;a2} - (Var * (B{p1} T{a1,a2;p1}) + t{a1;a2})", {L"a_1", L"a_2", L"p_1"}}, }) { - ExprPtr expr = parse_expr(input); + ExprPtr expr = deserialize(input); auto indices = expected | std::ranges::views::transform( [](const std::wstring& idx) { return Index(idx); }); @@ -414,9 +414,9 @@ TEST_CASE("utilities", "[utilities]") { CAPTURE(toUtf8(replacement_str)); CAPTURE(toUtf8(expected_str)); - ExprPtr input = parse_expr(input_str); - const ExprPtr target = parse_expr(target_str); - const ExprPtr replacement = parse_expr(replacement_str); + ExprPtr input = deserialize(input_str); + const ExprPtr target = deserialize(target_str); + const ExprPtr replacement = deserialize(replacement_str); replace(input, target, replacement); @@ -448,9 +448,9 @@ TEST_CASE("utilities", "[utilities]") { CAPTURE(toUtf8(replacement_str)); CAPTURE(toUtf8(expected_str)); - ResultExpr input = parse_result_expr(input_str); - const ExprPtr target = parse_expr(target_str); - const ExprPtr replacement = parse_expr(replacement_str); + ResultExpr input = deserialize(input_str); + const ExprPtr target = deserialize(target_str); + const ExprPtr replacement = deserialize(replacement_str); replace(input, target, replacement); @@ -477,7 +477,7 @@ TEST_CASE("utilities", "[utilities]") { }) { CAPTURE(toUtf8(expr_str)); - ExprPtr expr = parse_expr(expr_str); + ExprPtr expr = deserialize(expr_str); const bool expected = msg.empty(); @@ -522,7 +522,7 @@ TEST_CASE("utilities", "[utilities]") { }) { CAPTURE(toUtf8(expr_str)); - ResultExpr expr = parse_result_expr(expr_str); + ResultExpr expr = deserialize(expr_str); const bool expected = msg.empty(); @@ -568,7 +568,7 @@ TEST_CASE("utilities", "[utilities]") { }) { CAPTURE(toUtf8(input)); - ExprPtr expr = parse_expr(input); + ExprPtr expr = deserialize(input); auto actual = external_indices>(expr); @@ -632,4 +632,28 @@ TEST_CASE("utilities", "[utilities]") { } } } + + SECTION("SQ_STRLIT") { + STATIC_REQUIRE( + std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + + STATIC_REQUIRE( + std::is_same_v); + STATIC_REQUIRE( + std::is_same_v); + STATIC_REQUIRE( + std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + } } diff --git a/tests/unit/test_wick.cpp b/tests/unit/test_wick.cpp index 7e88490e47..9c57e377d5 100644 --- a/tests/unit/test_wick.cpp +++ b/tests/unit/test_wick.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -1335,7 +1335,7 @@ TEST_CASE("wick", "[algorithms][wick]") { REQUIRE(result.as().size() == 5); // clang-format off - auto expected = parse_expr( + auto expected = deserialize( "- h{;;p_3} ã{p_1,p_3;p_2,p_3}" "+ h{;;p_3} δ{p_1;a_1} δ{a_2;p_2} ã{p_3;p_3} s{a_1;a_2} " "- h{;;a_1} δ{a_2;p_2} ã{p_1;a_1} s{a_1;a_2} " @@ -1350,7 +1350,7 @@ TEST_CASE("wick", "[algorithms][wick]") { // quasi-diagonal example, with some indices in covariant expression // fixed, as in the pair-specific densities used to produce PNOs { - auto expr = sequant::parse_expr( + auto expr = sequant::deserialize( L"1/16 t{i1,i2;a3,a4}:A-C-S * ã{a3,a4;i1,i2} * ã{;a1} * ã{a2} * " L"t{a5,a6;i3,i4}:A-C-S * ã{i3,i4;a5,a6}"); auto resetter = sequant::set_scoped_default_context( @@ -1382,7 +1382,7 @@ TEST_CASE("wick", "[algorithms][wick]") { // triples variant of the previous case { - auto expr = sequant::parse_expr( + auto expr = sequant::deserialize( L"1/1296 t{i1,i2,i3;a1,a2,a3}:A-C-S * ã{a1,a2,a3;i1,i2,i3} * " L"ã{;a4} * ã{a5} * " L"t{a6,a7,a8;i4,i5,i6}:A-C-S * ã{i4,i5,i6;a6,a7,a8}"); diff --git a/utilities/external-interface/external_interface.cpp b/utilities/external-interface/external_interface.cpp index a735d6f359..7b646280a6 100644 --- a/utilities/external-interface/external_interface.cpp +++ b/utilities/external-interface/external_interface.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -218,8 +218,8 @@ void generateITF(const json &blocks, std::string_view out_file, std::ifstream in(input_file); const std::string input(std::istreambuf_iterator(in), {}); - sequant::ResultExpr result = - sequant::parse_result_expr(toUtf16(input), Symmetry::Antisymm); + sequant::ResultExpr result = sequant::deserialize( + input, {.def_perm_symm = Symmetry::Antisymm}); if (current_result.contains("name")) { result.set_label(toUtf16(current_result.at("name").get())); @@ -227,11 +227,12 @@ void generateITF(const json &blocks, std::string_view out_file, if (current_result.contains("replace")) { for (const nlohmann::json &sub : current_result.at("replace")) { - ExprPtr target = parse_expr( - toUtf16(sub.at("target").get()), Symmetry::Antisymm); + ExprPtr target = + deserialize(sub.at("target").get(), + {.def_perm_symm = Symmetry::Antisymm}); ExprPtr replacement = - parse_expr(toUtf16(sub.at("replacement").get()), - Symmetry::Antisymm); + deserialize(sub.at("replacement").get(), + {.def_perm_symm = Symmetry::Antisymm}); std::string equality_method = sub.value("tensor_equality", "identity"); diff --git a/utilities/tensor_network_graphs.cpp b/utilities/tensor_network_graphs.cpp index e5aa1a570a..315c9faf1f 100644 --- a/utilities/tensor_network_graphs.cpp +++ b/utilities/tensor_network_graphs.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -85,9 +85,9 @@ int main(int argc, char **argv) { ExprPtr expr; try { - expr = parse_expr(current); - } catch (const ParseError &e) { - std::wcout << "Failed to parse expression '" << current + expr = deserialize(current); + } catch (const io::serialization::SerializationError &e) { + std::wcout << "Failed to deserialize expression '" << current << "': " << e.what() << std::endl; return 1; }