From 3cbd8ffb7d7f64e8ff5434ab28830eec30b92f67 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 26 Jan 2026 16:53:37 +0100 Subject: [PATCH 01/22] Make parse functions use options objects Also, remove some redundant deparse() overload declarations (they are implicitly accessible via the overload for Expr &). --- SeQuant/core/parse.hpp | 55 ++-- SeQuant/core/parse/deparse.cpp | 242 +++++++++--------- SeQuant/core/parse/parse.cpp | 53 ++-- tests/integration/eval/btas/scf_btas.hpp | 12 +- tests/integration/eval/ta/scf_ta.hpp | 12 +- tests/unit/catch2_sequant.hpp | 13 +- tests/unit/test_eval_btas.cpp | 10 +- tests/unit/test_eval_expr.cpp | 57 +++-- tests/unit/test_eval_node.cpp | 2 +- tests/unit/test_eval_ta.cpp | 10 +- tests/unit/test_export.cpp | 19 +- tests/unit/test_optimize.cpp | 14 +- tests/unit/test_parse.cpp | 9 +- tests/unit/test_spin.cpp | 18 +- tests/unit/test_tensor_network.cpp | 8 +- .../external-interface/external_interface.cpp | 11 +- 16 files changed, 278 insertions(+), 267 deletions(-) diff --git a/SeQuant/core/parse.hpp b/SeQuant/core/parse.hpp index 88550b830d..565a890834 100644 --- a/SeQuant/core/parse.hpp +++ b/SeQuant/core/parse.hpp @@ -27,6 +27,24 @@ struct ParseError : std::runtime_error { ParseError(std::size_t offset, std::size_t length, std::string message); }; +struct ParseOptions { + /// 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 = {}; +}; + +struct DeparseOptions { + /// Whether to explicitly annotate tensor symmetries + bool annot_sym = true; +}; + // clang-format off /// \brief Construct expressions from string representations /// @@ -73,27 +91,18 @@ struct ParseError : std::runtime_error { /// '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 = {}); +ExprPtr parse_expr(std::wstring_view raw, const ParseOptions &options = {}); /// \sa parse_expr -ExprPtr parse_expr(std::string_view raw, std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); +ExprPtr parse_expr(std::string_view raw, const ParseOptions &options = {}); /// \sa parse_expr ResultExpr parse_result_expr(std::wstring_view raw, - std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); + const ParseOptions &options = {}); /// \sa parse_expr ResultExpr parse_result_expr(std::string_view raw, - std::optional perm_symm = {}, - std::optional braket_symm = {}, - std::optional column_symm = {}); + const ParseOptions &options = {}); /// /// Get a parsable string from an expression. @@ -110,18 +119,16 @@ ResultExpr parse_result_expr(std::string_view raw, /// \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); +std::wstring deparse(const ResultExpr &expr, + const DeparseOptions &options = {}); +std::wstring deparse(const ExprPtr &expr, const DeparseOptions &options = {}); +std::wstring deparse(const Expr &expr, const DeparseOptions &options = {}); +std::wstring deparse(const AbstractTensor &tensor, + const DeparseOptions &options = {}); 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); +std::wstring deparse(const NormalOperator &nop, + const DeparseOptions &options = {}); +std::wstring deparse(const Index &index, const DeparseOptions &options = {}); } // namespace sequant diff --git a/SeQuant/core/parse/deparse.cpp b/SeQuant/core/parse/deparse.cpp index 69209fe1a8..475b40e246 100644 --- a/SeQuant/core/parse/deparse.cpp +++ b/SeQuant/core/parse/deparse.cpp @@ -21,11 +21,11 @@ namespace sequant { namespace details { template -std::wstring deparse_indices(Range&& indices) { +std::wstring deparse_indices(Range&& indices, const DeparseOptions& options) { std::wstring deparsed; for (std::size_t i = 0; i < indices.size(); ++i) { - deparsed += deparse(indices[i]); + deparsed += deparse(indices[i], options); if (i + 1 < indices.size()) { deparsed += L","; @@ -36,11 +36,11 @@ std::wstring deparse_indices(Range&& indices) { } template -std::wstring deparse_ops(const Range& ops) { +std::wstring deparse_ops(const Range& ops, const DeparseOptions& options) { std::wstring deparsed; for (std::size_t i = 0; i < ops.size(); ++i) { - deparsed += deparse(ops[i].index()); + deparsed += deparse(ops[i].index(), options); if (i + 1 < ops.size()) { deparsed += L","; @@ -50,7 +50,7 @@ std::wstring deparse_ops(const Range& ops) { return deparsed; } -std::wstring deparse_symm(Symmetry symm) { +std::wstring deparse_symm(Symmetry symm, const DeparseOptions&) { switch (symm) { case Symmetry::Symm: return L"S"; @@ -63,7 +63,7 @@ std::wstring deparse_symm(Symmetry symm) { SEQUANT_UNREACHABLE; } -std::wstring deparse_symm(BraKetSymmetry symm) { +std::wstring deparse_symm(BraKetSymmetry symm, const DeparseOptions&) { switch (symm) { case BraKetSymmetry::Conjugate: return L"C"; @@ -76,7 +76,7 @@ std::wstring deparse_symm(BraKetSymmetry symm) { SEQUANT_UNREACHABLE; } -std::wstring deparse_symm(ColumnSymmetry symm) { +std::wstring deparse_symm(ColumnSymmetry symm, const DeparseOptions&) { switch (symm) { case ColumnSymmetry::Symm: return L"S"; @@ -87,7 +87,8 @@ std::wstring deparse_symm(ColumnSymmetry symm) { SEQUANT_UNREACHABLE; } -std::wstring deparse_scalar(const Constant::scalar_type& scalar) { +std::wstring deparse_scalar(const Constant::scalar_type& scalar, + const DeparseOptions&) { if (scalar == 0) { return L"0"; } @@ -124,46 +125,118 @@ std::wstring deparse_scalar(const Constant::scalar_type& scalar) { return to_wstring(deparsed); } +std::wstring deparse(Tensor const& tensor, const DeparseOptions& options) { + return deparse(static_cast(tensor), options); +} + +std::wstring deparse(const Constant& constant, const DeparseOptions& options) { + return details::deparse_scalar(constant.value(), options); +} + +std::wstring deparse(const Variable& variable, const DeparseOptions&) { + return std::wstring(variable.label()) + (variable.conjugated() ? L"^*" : L""); +} + +std::wstring deparse(Product const& prod, const DeparseOptions& options) { + std::wstring deparsed; + + const auto& scal = prod.scalar(); + if (scal != Product::scalar_type{1}) { + deparsed += details::deparse_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; + deparsed += L"("; + } + + deparsed += deparse(current, options); + + if (parenthesize) { + deparsed += L")"; + } + + if (i + 1 < prod.size()) { + deparsed += L" * "; + } + } + + return deparsed; +} + +std::wstring deparse(Sum const& sum, const DeparseOptions& options) { + 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, options); + + 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 details -std::wstring deparse(const ExprPtr& expr, bool annot_sym) { +std::wstring deparse(const ExprPtr& expr, const DeparseOptions& options) { if (!expr) return {}; - return deparse(*expr, annot_sym); + return deparse(*expr, options); } -std::wstring deparse(const Expr& expr, bool annot_sym) { +std::wstring deparse(const Expr& expr, const DeparseOptions& options) { using namespace details; if (expr.is()) - return deparse(expr.as(), annot_sym); + return details::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as()); + return deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as()); + return deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as(), annot_sym); + return details::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as(), annot_sym); + return details::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as()); + return details::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as()); + return details::deparse(expr.as(), options); else throw std::runtime_error("Unsupported expr type for deparse!"); } -std::wstring deparse(const ResultExpr& result, bool annot_sym) { +std::wstring deparse(const ResultExpr& result, const DeparseOptions& options) { std::wstring deparsed; if (result.produces_tensor()) { - deparsed = deparse(result.result_as_tensor(L"?"), annot_sym); + deparsed = details::deparse(result.result_as_tensor(L"?"), options); } else { - deparsed = deparse(result.result_as_variable(L"?"), annot_sym); + deparsed = details::deparse(result.result_as_variable(L"?"), options); } - return deparsed + L" = " + deparse(result.expression(), annot_sym); + return deparsed + L" = " + deparse(result.expression(), options); } -std::wstring deparse(const Index& index) { +std::wstring deparse(const Index& index, const DeparseOptions&) { std::wstring deparsed(index.label()); if (index.has_proto_indices()) { @@ -182,58 +255,39 @@ std::wstring deparse(const Index& index) { 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 deparse(AbstractTensor const& tensor, + const DeparseOptions& options) { std::wstring deparsed(tensor._label()); - deparsed += L"{" + details::deparse_indices(tensor._bra()); + deparsed += L"{" + details::deparse_indices(tensor._bra(), options); if (tensor._ket_rank() > 0) { - deparsed += L";" + details::deparse_indices(tensor._ket()); + deparsed += L";" + details::deparse_indices(tensor._ket(), options); } if (tensor._aux_rank() > 0) { if (tensor._ket_rank() == 0) { deparsed += L";"; } - deparsed += L";" + details::deparse_indices(tensor._aux()); + deparsed += L";" + details::deparse_indices(tensor._aux(), options); } 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()); + if (options.annot_sym) { + deparsed += L":" + details::deparse_symm(tensor._symmetry(), options); + deparsed += + L"-" + details::deparse_symm(tensor._braket_symmetry(), options); + deparsed += + L"-" + details::deparse_symm(tensor._column_symmetry(), options); } return deparsed; } template -std::wstring deparse(NormalOperator const& nop) { +std::wstring deparse(NormalOperator const& nop, + const DeparseOptions& options) { std::wstring deparsed(nop.label()); - deparsed += L"{" + details::deparse_ops(nop.annihilators()); + deparsed += L"{" + details::deparse_ops(nop.annihilators(), options); if (nop.ncreators() > 0) { - deparsed += L";" + details::deparse_ops(nop.creators()); + deparsed += L";" + details::deparse_ops(nop.creators(), options); } deparsed += L"}"; @@ -241,76 +295,10 @@ std::wstring deparse(NormalOperator const& nop) { } template std::wstring deparse( - NormalOperator const& nop); + NormalOperator const& nop, + const DeparseOptions& options); 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; -} + NormalOperator const& nop, + const DeparseOptions& options); } // namespace sequant diff --git a/SeQuant/core/parse/parse.cpp b/SeQuant/core/parse/parse.cpp index dc71dcee4d..8dca850340 100644 --- a/SeQuant/core/parse/parse.cpp +++ b/SeQuant/core/parse/parse.cpp @@ -243,26 +243,23 @@ AST do_parse(const StartRule &start, std::wstring_view input, } parse::transform::DefaultSymmetries to_default_symms( - const std::optional &perm_symm, - const std::optional &braket_symm, - const std::optional &column_symm) { + const ParseOptions &options) { const Context &ctx = get_default_context(); parse::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; } @@ -270,46 +267,34 @@ parse::transform::DefaultSymmetries to_default_symms( } ResultExpr parse_result_expr(std::wstring_view input, - std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { + const ParseOptions &options) { 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)); + return parse::transform::ast_to_result(ast, positions, input.begin(), + to_default_symms(options)); } -ExprPtr parse_expr(std::wstring_view input, std::optional perm_symm, - std::optional braket_symm, - std::optional column_symm) { +ExprPtr parse_expr(std::wstring_view input, const ParseOptions &options) { using iterator_type = decltype(input)::iterator; x3::position_cache> positions(input.begin(), input.end()); auto ast = do_parse(parse::expr, input, positions); - return parse::transform::ast_to_expr( - ast, positions, input.begin(), - to_default_symms(perm_symm, braket_symm, column_symm)); + return parse::transform::ast_to_expr(ast, positions, input.begin(), + to_default_symms(options)); } -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); +ExprPtr parse_expr(std::string_view input, const ParseOptions &options) { + return parse_expr(sequant::to_wstring(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); + const ParseOptions &options) { + return parse_result_expr(sequant::to_wstring(input), options); } } // namespace sequant diff --git a/tests/integration/eval/btas/scf_btas.hpp b/tests/integration/eval/btas/scf_btas.hpp index 3528bacc7b..ffba5ac799 100644 --- a/tests/integration/eval/btas/scf_btas.hpp +++ b/tests/integration/eval/btas/scf_btas.hpp @@ -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_( + parse_expr(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()); + parse_expr(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( + parse_expr(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..491d142a98 100644 --- a/tests/integration/eval/ta/scf_ta.hpp +++ b/tests/integration/eval/ta/scf_ta.hpp @@ -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_( + parse_expr(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()); + parse_expr(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( + parse_expr(energy_expr, {.def_perm_symm = Symmetry::Antisymm})); return evaluate(node, data_world_)->template get(); } diff --git a/tests/unit/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index f491558d91..3eac0e705d 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -35,7 +35,7 @@ struct StringMaker { bool include_canonical = true) { std::string str; try { - str = sequant::to_string(sequant::deparse(expr, true)); + str = sequant::to_string(sequant::deparse(expr, {.annot_sym = true})); } catch (const std::exception &) { // deparse doesn't support all kinds of expressions -> fall back to LaTeX // representation @@ -81,7 +81,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::to_string(sequant::deparse(res, {.annot_sym = true})); if (include_canonical) { sequant::ResultExpr clone = res.clone(); @@ -190,21 +191,21 @@ ExprVar to_expression(T &&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); + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { return sequant::parse_expr( sequant::to_wstring(std::string(std::forward(expression))), - sequant::Symmetry::Nonsymm); + {.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( 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); + {.def_perm_symm = sequant::Symmetry::Nonsymm}); } } else if constexpr (std::is_convertible_v) { return expression; diff --git a/tests/unit/test_eval_btas.cpp b/tests/unit/test_eval_btas.cpp index 6a3e5afd1f..986ea3bc00 100644 --- a/tests/unit/test_eval_btas.cpp +++ b/tests/unit/test_eval_btas.cpp @@ -31,13 +31,15 @@ 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::deparse(tnsr.clone(), {.annot_sym = 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) - ->as()); + return tensor_to_key( + sequant::parse_expr(spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) + ->as()); } template @@ -220,7 +222,7 @@ TEST_CASE("eval_with_btas", "[eval_btas]") { }; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, sequant::Symmetry::Antisymm); + return parse_expr(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 48fef17f35..70dcf49b77 100644 --- a/tests/unit/test_eval_expr.cpp +++ b/tests/unit/test_eval_expr.cpp @@ -22,8 +22,8 @@ #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 ParseOptions& options = {}) { + return parse_expr(tnsr, options)->as(); } Constant parse_constant(std::wstring_view c) { @@ -221,9 +221,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { } 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}; @@ -246,8 +249,10 @@ TEST_CASE("eval_expr", "[EvalExpr]") { 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); @@ -257,9 +262,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(); + parse_expr(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(); + parse_expr(L"t_{a1,a2}^{i3,i4}", {.def_perm_symm = Symmetry::Symm}) + ->as(); const auto x34 = result_expr(EvalExpr{t3}, EvalExpr{t4}, EvalOp::Product); @@ -268,8 +275,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 = + parse_expr(L"f_{i1}^{a1}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as(); + const auto t6 = + parse_expr(L"f_{i2}^{a2}", {.def_perm_symm = Symmetry::Nonsymm}) + ->as(); const auto& x56 = result_expr(EvalExpr{t5}, EvalExpr{t6}, EvalOp::Product); @@ -278,17 +289,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(); + parse_expr(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(); + parse_expr(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); @@ -339,12 +354,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{parse_expr(L"O{a_1;a_1}", + {.def_perm_symm = Symmetry::Nonsymm}) + ->as()}; + auto t2 = EvalExpr{parse_expr(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 4b742d36a8..f96b77c62e 100644 --- a/tests/unit/test_eval_node.cpp +++ b/tests/unit/test_eval_node.cpp @@ -57,7 +57,7 @@ TEST_CASE("eval_node", "[EvalNode]") { auto R = Npos::R; auto parse_expr_antisymm = [](auto const& xpr) { - return parse_expr(xpr, Symmetry::Antisymm); + return parse_expr(xpr, {.def_perm_symm = Symmetry::Antisymm}); }; SECTION("terminals") { diff --git a/tests/unit/test_eval_ta.cpp b/tests/unit/test_eval_ta.cpp index 21aa82ddb6..19244100dd 100644 --- a/tests/unit/test_eval_ta.cpp +++ b/tests/unit/test_eval_ta.cpp @@ -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::deparse(tnsr.clone(), {.annot_sym = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } else { using ranges::views::intersperse; @@ -111,8 +112,9 @@ 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) - ->as()); + return tensor_to_key( + sequant::parse_expr(spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) + ->as()); } template @@ -286,7 +288,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { using TA::TArrayD; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, sequant::Symmetry::Antisymm); + return parse_expr(xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); }; // tnsr is assumed to be single-tiled diff --git a/tests/unit/test_export.cpp b/tests/unit/test_export.cpp index a3d1731063..78bc7c6b31 100644 --- a/tests/unit/test_export.cpp +++ b/tests/unit/test_export.cpp @@ -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 = parse_result_expr( + to_wstring(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); + ExprPtr expr = parse_expr(to_wstring(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) + parse_expr(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)); diff --git a/tests/unit/test_optimize.cpp b/tests/unit/test_optimize.cpp index 951e286aaf..9551dd721b 100644 --- a/tests/unit/test_optimize.cpp +++ b/tests/unit/test_optimize.cpp @@ -61,7 +61,7 @@ TEST_CASE("optimize", "[optimize]") { }; auto parse_expr_antisymm = [](auto const& xpr) { - return parse_expr(xpr, Symmetry::Antisymm); + return parse_expr(xpr, {.def_perm_symm = Symmetry::Antisymm}); }; SECTION("Single term optimization") { @@ -233,13 +233,15 @@ TEST_CASE("optimize", "[optimize]") { for (const std::wstring& current : inputs) { expressions.push_back(binarize(parse_result_expr( - current, Symmetry::Nonsymm, BraKetSymmetry::Nonsymm, - ColumnSymmetry::Nonsymm))); + 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(parse_result_expr( + 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..eaf3a986ea 100644 --- a/tests/unit/test_parse.cpp +++ b/tests/unit/test_parse.cpp @@ -471,16 +471,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); - REQUIRE(deparse(expression, true) == current); + REQUIRE(deparse(expression, {.annot_sym = true}) == current); } SECTION("result_expressions") { @@ -495,7 +494,7 @@ TEST_CASE("parsing", "[parse]") { for (const std::wstring& current : expressions) { ResultExpr result = parse_result_expr(current); - REQUIRE(deparse(result, true) == current); + REQUIRE(deparse(result, {.annot_sym = true}) == current); } } } diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index 5cf8a082c7..2f9cd107ed 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -767,16 +767,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 = + parse_expr(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{parse_expr(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, @@ -1017,7 +1019,7 @@ 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); + {.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}")); @@ -1045,7 +1047,7 @@ SECTION("Closed-shell CC spintrace for variable, constant, product") { } { // 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); + {.def_perm_symm = Symmetry::Antisymm}); auto result_v1 = closed_shell_CC_spintrace_v1(input); REQUIRE_THAT(result_v1, @@ -1164,7 +1166,7 @@ SECTION("Closed-shell spintrace CCSDT terms") { 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)}); + {.def_perm_symm = Symmetry::Antisymm})}); auto result = closed_shell_CC_spintrace_v2(input); REQUIRE_THAT( @@ -1178,7 +1180,7 @@ SECTION("Closed-shell spintrace CCSDT 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)}); + {.def_perm_symm = Symmetry::Antisymm})}); auto result = closed_shell_CC_spintrace_v1(input); REQUIRE(result->size() == 4); diff --git a/tests/unit/test_tensor_network.cpp b/tests/unit/test_tensor_network.cpp index 6521722668..b946c40840 100644 --- a/tests/unit/test_tensor_network.cpp +++ b/tests/unit/test_tensor_network.cpp @@ -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(deparse(*(tn1.tensors()[i]), {.annot_sym = true}) == + deparse(*t2_i, {.annot_sym = true})); } } } @@ -1184,7 +1184,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { const Product expectedExpr = parse_expr( 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(); @@ -1883,7 +1883,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { const Product expectedExpr = parse_expr( 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(); diff --git a/utilities/external-interface/external_interface.cpp b/utilities/external-interface/external_interface.cpp index 441b1896e1..8c268ea1af 100644 --- a/utilities/external-interface/external_interface.cpp +++ b/utilities/external-interface/external_interface.cpp @@ -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::parse_result_expr( + toUtf16(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 = + parse_expr(toUtf16(sub.at("target").get()), + {.def_perm_symm = Symmetry::Antisymm}); ExprPtr replacement = parse_expr(toUtf16(sub.at("replacement").get()), - Symmetry::Antisymm); + {.def_perm_symm = Symmetry::Antisymm}); std::string equality_method = sub.value("tensor_equality", "identity"); From ac22cc29405837a2fc6d83eaf2abdc7290007ed7 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 10:55:50 +0100 Subject: [PATCH 02/22] Add missing namespace qualification --- tests/unit/test_eval_ta.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_eval_ta.cpp b/tests/unit/test_eval_ta.cpp index 19244100dd..91df97ee1c 100644 --- a/tests/unit/test_eval_ta.cpp +++ b/tests/unit/test_eval_ta.cpp @@ -288,7 +288,8 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { using TA::TArrayD; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); + return sequant::parse_expr( + xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); }; // tnsr is assumed to be single-tiled From 33810e5e7c74ae73c67b648892cab25f75391c6e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 11:05:02 +0100 Subject: [PATCH 03/22] Consolidate spelling of symm --- SeQuant/core/parse.hpp | 2 +- SeQuant/core/parse/deparse.cpp | 2 +- tests/unit/catch2_sequant.hpp | 4 ++-- tests/unit/test_eval_btas.cpp | 2 +- tests/unit/test_eval_ta.cpp | 2 +- tests/unit/test_parse.cpp | 4 ++-- tests/unit/test_tensor_network.cpp | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SeQuant/core/parse.hpp b/SeQuant/core/parse.hpp index 565a890834..fb15922df1 100644 --- a/SeQuant/core/parse.hpp +++ b/SeQuant/core/parse.hpp @@ -42,7 +42,7 @@ struct ParseOptions { struct DeparseOptions { /// Whether to explicitly annotate tensor symmetries - bool annot_sym = true; + bool annot_symm = true; }; // clang-format off diff --git a/SeQuant/core/parse/deparse.cpp b/SeQuant/core/parse/deparse.cpp index 475b40e246..d278896795 100644 --- a/SeQuant/core/parse/deparse.cpp +++ b/SeQuant/core/parse/deparse.cpp @@ -270,7 +270,7 @@ std::wstring deparse(AbstractTensor const& tensor, } deparsed += L"}"; - if (options.annot_sym) { + if (options.annot_symm) { deparsed += L":" + details::deparse_symm(tensor._symmetry(), options); deparsed += L"-" + details::deparse_symm(tensor._braket_symmetry(), options); diff --git a/tests/unit/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index 3eac0e705d..bd24e080b3 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -35,7 +35,7 @@ struct StringMaker { bool include_canonical = true) { std::string str; try { - str = sequant::to_string(sequant::deparse(expr, {.annot_sym = true})); + str = sequant::to_string(sequant::deparse(expr, {.annot_symm = true})); } catch (const std::exception &) { // deparse doesn't support all kinds of expressions -> fall back to LaTeX // representation @@ -82,7 +82,7 @@ struct StringMaker { static std::string convert(const sequant::ResultExpr &res, bool include_canonical = true) { std::string str = - sequant::to_string(sequant::deparse(res, {.annot_sym = true})); + sequant::to_string(sequant::deparse(res, {.annot_symm = true})); if (include_canonical) { sequant::ResultExpr clone = res.clone(); diff --git a/tests/unit/test_eval_btas.cpp b/tests/unit/test_eval_btas.cpp index 986ea3bc00..0621c412da 100644 --- a/tests/unit/test_eval_btas.cpp +++ b/tests/unit/test_eval_btas.cpp @@ -32,7 +32,7 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { }; auto const tnsr_deparsed = - sequant::deparse(tnsr.clone(), {.annot_sym = false}); + sequant::deparse(tnsr.clone(), {.annot_symm = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } diff --git a/tests/unit/test_eval_ta.cpp b/tests/unit/test_eval_ta.cpp index 91df97ee1c..b0bf873a4d 100644 --- a/tests/unit/test_eval_ta.cpp +++ b/tests/unit/test_eval_ta.cpp @@ -87,7 +87,7 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { NestedTensorIndices oixs{tnsr}; if (oixs.inner.empty()) { auto const tnsr_deparsed = - sequant::deparse(tnsr.clone(), {.annot_sym = false}); + sequant::deparse(tnsr.clone(), {.annot_symm = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } else { using ranges::views::intersperse; diff --git a/tests/unit/test_parse.cpp b/tests/unit/test_parse.cpp index eaf3a986ea..31fcad2482 100644 --- a/tests/unit/test_parse.cpp +++ b/tests/unit/test_parse.cpp @@ -479,7 +479,7 @@ TEST_CASE("parsing", "[parse]") { for (const std::wstring& current : expressions) { ExprPtr expression = parse_expr(current); - REQUIRE(deparse(expression, {.annot_sym = true}) == current); + REQUIRE(deparse(expression, {.annot_symm = true}) == current); } SECTION("result_expressions") { @@ -494,7 +494,7 @@ TEST_CASE("parsing", "[parse]") { for (const std::wstring& current : expressions) { ResultExpr result = parse_result_expr(current); - REQUIRE(deparse(result, {.annot_sym = true}) == current); + REQUIRE(deparse(result, {.annot_symm = true}) == current); } } } diff --git a/tests/unit/test_tensor_network.cpp b/tests/unit/test_tensor_network.cpp index b946c40840..001bc45344 100644 --- a/tests/unit/test_tensor_network.cpp +++ b/tests/unit/test_tensor_network.cpp @@ -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]), {.annot_sym = true}) == - deparse(*t2_i, {.annot_sym = true})); + REQUIRE(deparse(*(tn1.tensors()[i]), {.annot_symm = true}) == + deparse(*t2_i, {.annot_symm = true})); } } } From 3d006e5435b6b584150dc56f1bac6c55f2c59cce Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 11:26:20 +0100 Subject: [PATCH 04/22] Always skip long running tests in Debug builds Without optimizations, these tests can take forever and hence hamper running tests repeatedly during development cycles. --- tests/CMakeLists.txt | 4 ++++ tests/integration/CMakeLists.txt | 4 ++-- tests/unit/CMakeLists.txt | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) 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/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 0c341696e1..e4ca5e3759 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -87,7 +87,7 @@ else() message(STATUS "Python not found - PythonEinsumGenerator validation tests will be disabled") endif() -if (SEQUANT_SKIP_LONG_TESTS) +if (SEQUANT_INTERNAL_SKIP_LONG_TESTS) target_compile_definitions(unit_tests-sequant PRIVATE SEQUANT_SKIP_LONG_TESTS=1) endif() From 2db831bb6cfe746c023234de6bfafdd7d585bbbb Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 13:24:13 +0100 Subject: [PATCH 05/22] Add docs for parse syntax --- doc/user/guide/parse.rst | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 doc/user/guide/parse.rst diff --git a/doc/user/guide/parse.rst b/doc/user/guide/parse.rst new file mode 100644 index 0000000000..89cd7854b1 --- /dev/null +++ b/doc/user/guide/parse.rst @@ -0,0 +1,84 @@ +Parsing and Deparsing +===================== + +SeQuant supports creating expression objects by parsing an appropriately formatted input string. This process is referred to as *parsing*. +Furthermore, SeQuant can also serialize expression objects into this string representation, which is referred to as *deparsing*. + +The functions for performing these tasks are :func:`parse_expr(…)`, :func:`parse_result_expr(…)` and :func:`deparse(…)` respectively. The former two +differ in that one of them produces a :class:`ExprPtr` and the other a :class:`ResultExpr` object as their output. This implies that the parsed text +in the latter case is supposed to contain a result specification (see below). + +As SeQuant's capabilities develop over time, it can be necessary to adapt the syntax of this text representation, referred to as the *parse syntax*. +By default, the abovementioned functions will always expect and produce text according to the latest parse syntax specification. However, they can be +instructed to work with a different version by explicitly specifying a :class:`SyntaxVersion` when calling them or using one of the versioned function +calls. + +.. warning:: + All syntax versions except :class:`SyntaxVersion::Latest` are considered deprecated. Support for them will remain available for some time but might + get removed in future versions of SeQuant. + +Generally speaking, parse and deparse are inverse operations. + + +Customizations +-------------- + +The parse and deparse functions accept customization options of type :class:`ParseOptions` and :class:`DeparseOptions` respectively. They allow for +specification of a specific :class:`SyntaxVersion` and things like how to deal with symmetry annotations. For parsing, the latter refers to default +symmetries for objects that don't explicitly specify their symmetries and for deparsing this refers to whether or not those annotations are included +in the output. + +For an overview of all customization options, please refer to the documentation of those classes in the API reference. + + +Syntax +------ + +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:`parse_expr` will start at rule `Expression`, whereas :func:`parse_result_expr` will start at `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 + From 2e5c1167275a0d29d4eec775b84d50c86637d0a2 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 13:27:12 +0100 Subject: [PATCH 06/22] Compactify parse function declarations --- SeQuant/core/parse.hpp | 82 +++++++++++------------------------------- 1 file changed, 20 insertions(+), 62 deletions(-) diff --git a/SeQuant/core/parse.hpp b/SeQuant/core/parse.hpp index fb15922df1..949fcb079b 100644 --- a/SeQuant/core/parse.hpp +++ b/SeQuant/core/parse.hpp @@ -45,64 +45,32 @@ struct DeparseOptions { bool annot_symm = true; }; +#define SEQUANT_DECLARE_PARSE_FUNC(name, returnType) \ + returnType name(std::wstring_view input, const ParseOptions &options = {}); \ + returnType name(std::string_view input, const ParseOptions &options = {}); + // 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 +/// \param input The input to parse +/// \param options Customization options /// \return SeQuant expression. -// clang-format on -ExprPtr parse_expr(std::wstring_view raw, const ParseOptions &options = {}); +SEQUANT_DECLARE_PARSE_FUNC(parse_expr, ExprPtr); /// \sa parse_expr -ExprPtr parse_expr(std::string_view raw, const ParseOptions &options = {}); +SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); -/// \sa parse_expr -ResultExpr parse_result_expr(std::wstring_view raw, - const ParseOptions &options = {}); +#undef SEQUANT_DECLARE_PARSE_FUNC -/// \sa parse_expr -ResultExpr parse_result_expr(std::string_view raw, - const ParseOptions &options = {}); + +#define SEQUANT_DECLARE_DEPARSE_FUNC(name) \ + std::wstring name(const ResultExpr &expr, const DeparseOptions &options = {}); \ + std::wstring name(const ExprPtr &expr, const DeparseOptions &options = {}); \ + std::wstring name(const Expr &expr, const DeparseOptions &options = {}); \ + std::wstring name(const AbstractTensor &expr, const DeparseOptions &options = {}); \ + std::wstring name(const Index &index, const DeparseOptions &options = {}); \ + template< Statistics S> \ + std::wstring name(const NormalOperator &nop, const DeparseOptions &options = {}); \ /// /// Get a parsable string from an expression. @@ -116,19 +84,9 @@ ResultExpr parse_result_expr(std::string_view raw, /// 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. +/// \param options Customization options /// \return wstring of the expression. -std::wstring deparse(const ResultExpr &expr, - const DeparseOptions &options = {}); -std::wstring deparse(const ExprPtr &expr, const DeparseOptions &options = {}); -std::wstring deparse(const Expr &expr, const DeparseOptions &options = {}); -std::wstring deparse(const AbstractTensor &tensor, - const DeparseOptions &options = {}); -template -std::wstring deparse(const NormalOperator &nop, - const DeparseOptions &options = {}); -std::wstring deparse(const Index &index, const DeparseOptions &options = {}); +SEQUANT_DECLARE_DEPARSE_FUNC(deparse) } // namespace sequant From 86efae095b4f338d59c9d6cef565c3529218bdd7 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Jan 2026 15:24:26 +0100 Subject: [PATCH 07/22] Namespace parse functions based on syntax version --- CMakeLists.txt | 8 +- SeQuant/core/parse.hpp | 41 ++- SeQuant/core/parse/parse.cpp | 323 ++---------------- SeQuant/core/parse/{ => v1}/ast.cpp | 6 +- SeQuant/core/parse/{ => v1}/ast.hpp | 32 +- .../core/parse/{ => v1}/ast_conversions.hpp | 60 ++-- SeQuant/core/parse/{ => v1}/deparse.cpp | 16 +- SeQuant/core/parse/v1/parse.cpp | 294 ++++++++++++++++ .../core/parse/{ => v1}/semantic_actions.hpp | 12 +- 9 files changed, 432 insertions(+), 360 deletions(-) rename SeQuant/core/parse/{ => v1}/ast.cpp (64%) rename SeQuant/core/parse/{ => v1}/ast.hpp (78%) rename SeQuant/core/parse/{ => v1}/ast_conversions.hpp (88%) rename SeQuant/core/parse/{ => v1}/deparse.cpp (95%) create mode 100644 SeQuant/core/parse/v1/parse.cpp rename SeQuant/core/parse/{ => v1}/semantic_actions.hpp (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33d7b0dcd5..30aba712f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,9 +323,13 @@ set(SeQuant_src SeQuant/core/optimize/fusion.hpp SeQuant/core/optimize/optimize.cpp SeQuant/core/parse.hpp - SeQuant/core/parse/ast.cpp - SeQuant/core/parse/deparse.cpp SeQuant/core/parse/parse.cpp + SeQuant/core/parse/v1/ast.cpp + SeQuant/core/parse/v1/ast.hpp + SeQuant/core/parse/v1/ast_conversions.hpp + SeQuant/core/parse/v1/deparse.cpp + SeQuant/core/parse/v1/parse.cpp + SeQuant/core/parse/v1/semantic_actions.hpp SeQuant/core/ranges.hpp SeQuant/core/rational.hpp SeQuant/core/runtime.cpp diff --git a/SeQuant/core/parse.hpp b/SeQuant/core/parse.hpp index 949fcb079b..bdb92b0bd6 100644 --- a/SeQuant/core/parse.hpp +++ b/SeQuant/core/parse.hpp @@ -11,13 +11,6 @@ #include #include -/// -/// Create SeQuant expression from string input. -/// -/// @author: Bimal Gaudel -/// version: 21 July, 2021 -/// - namespace sequant { struct ParseError : std::runtime_error { @@ -27,6 +20,20 @@ struct ParseError : std::runtime_error { ParseError(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 parse sense. That is older inputs will +/// continue to work as before. However, representations generated via +/// deparse(…) may not necessarily be parsable 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 ParseSyntax { + V1, + + Latest = V1 +}; + struct ParseOptions { /// 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 @@ -38,11 +45,15 @@ struct ParseOptions { /// 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 + ParseSyntax syntax = ParseSyntax::Latest; }; struct DeparseOptions { /// Whether to explicitly annotate tensor symmetries bool annot_symm = true; + /// The syntax version of the produced output + ParseSyntax syntax = ParseSyntax::Latest; }; #define SEQUANT_DECLARE_PARSE_FUNC(name, returnType) \ @@ -60,8 +71,6 @@ SEQUANT_DECLARE_PARSE_FUNC(parse_expr, ExprPtr); /// \sa parse_expr SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); -#undef SEQUANT_DECLARE_PARSE_FUNC - #define SEQUANT_DECLARE_DEPARSE_FUNC(name) \ std::wstring name(const ResultExpr &expr, const DeparseOptions &options = {}); \ @@ -88,6 +97,20 @@ SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); /// \return wstring of the expression. SEQUANT_DECLARE_DEPARSE_FUNC(deparse) + + +// Namespaced variants +namespace parse::v1 { + SEQUANT_DECLARE_PARSE_FUNC(parse_expr, ExprPtr); + SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); + + SEQUANT_DECLARE_DEPARSE_FUNC(deparse) +} + + +#undef SEQUANT_DECLARE_PARSE_FUNC +#undef SEQUANT_DECLARE_DEPARSE_FUNC + } // namespace sequant #endif // SEQUANT_PARSE_HPP diff --git a/SeQuant/core/parse/parse.cpp b/SeQuant/core/parse/parse.cpp index 8dca850340..f469bbfd68 100644 --- a/SeQuant/core/parse/parse.cpp +++ b/SeQuant/core/parse/parse.cpp @@ -1,38 +1,8 @@ -// -// Created by Robert Adam on 2023-09-20 -// - -#include -#include #include -#include #include -#include -#include -#include -#include - -#define BOOST_SPIRIT_X3_UNICODE -#include -#include -#include -#include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include namespace sequant { @@ -40,261 +10,40 @@ ParseError::ParseError(std::size_t offset, std::size_t length, std::string message) : std::runtime_error(std::move(message)), offset(offset), length(length) {} -namespace x3 = boost::spirit::x3; - -namespace parse { - -struct NumberRule; -struct VariableRule; -struct TensorRule; -struct ProductRule; -struct SumRule; -struct ExprRule; -struct ResultExprRule; -struct IndexLabelRule; -struct IndexRule; -struct IndexGroupRule; -struct SymmetrySpecRule; - -// Types -x3::rule number{"Number"}; -x3::rule variable{"Variable"}; -x3::rule tensor{"Tensor"}; - -// Expression structure -x3::rule product{"Product"}; -x3::rule sum{"Sum"}; -x3::rule expr{"Expression"}; -x3::rule resultExpr{"ResultExpr"}; - -// Auxiliaries -x3::rule name{"Name"}; -x3::rule index_label{"IndexLabel"}; -x3::rule index{"Index"}; -x3::rule index_groups{"IndexGroups"}; -x3::rule symmetry_spec{"SymmetrySpec"}; - -auto to_char_type = [](auto c) { - return static_cast(c); -}; - -// clang-format off -auto word_components = x3::unicode::alnum - | x3::char_('_') | x3::unicode::char_(L'⁔') - | x3::unicode::char_(to_char_type(0x0303)) // combining tilde - | x3::unicode::char_(to_char_type(0x0302)) // combining circumflex/hat - // Superscript and Subscript block - | (x3::unicode::char_(to_char_type(0x2070), to_char_type(0x209F)) - x3::unicode::unassigned) - // These are defined in the Latin-1 Supplement block and thus need to be listed explicitly - | x3::unicode::char_(L'¹') | x3::unicode::char_(L'²') | x3::unicode::char_(L'³') - // Arrow block - | (x3::unicode::char_(to_char_type(0x2190), to_char_type(0x21FF)) - x3::unicode::unassigned); -// A name begins with a letter, then can container letters, digits and -// underscores, but can not end with an underscore (to not confuse the parser -// with tensors á la t_{…}^{…}. -auto name_def = x3::lexeme[ - x3::unicode::alpha >> -( *(word_components >> &word_components) >> (word_components - '_') ) - ]; - -auto number_def = x3::double_ >> -('/' >> x3::double_); - -auto variable_def = x3::lexeme[name >> -(x3::lit('^') >> '*' >> x3::attr(true))]; - -auto index_name = +( x3::unicode::alpha | x3::unicode::char_(L'⁺') | x3::unicode::char_(L'⁻') | x3::unicode::char_(L'̃') - | x3::unicode::char_(L'↑') | x3::unicode::char_(L'↓') - ); - -auto index_label_def = x3::lexeme[ - index_name >> -x3::lit('_') >> x3::uint_ - ]; - -auto index_def = x3::lexeme[ - index_label >> -x3::skip['<' >> index_label % ',' >> ">"] - ]; - -const std::vector noIndices; -auto index_groups_def = L"_{" > -(index % ',') > L"}^{" > -(index % ',') > L"}" >> x3::attr(noIndices) >> x3::attr(false) - | L"^{" > -(index % ',') > L"}_{" > -(index % ',') > L"}" >> x3::attr(noIndices) >> x3::attr(true) - | '{' > -(index % ',') > -( ';' > -(index % ',')) > -(';' > -(index % ',')) > '}' >> x3::attr(false); - -auto symmetry_spec_def= x3::lexeme[ - ':' >> x3::upper >> -('-' >> x3::upper) >> -('-' >> x3::upper) - ]; - -auto tensor_def = x3::lexeme[ - name >> x3::skip[index_groups] >> -(symmetry_spec) - ]; - -auto nullary = number | tensor | variable; - -auto grouped = '(' > sum > ')' | nullary; - -auto product_def = grouped % -x3::lit('*'); - -auto first_addend = (('-' >> x3::attr(-1) | -x3::lit('+') >> x3::attr(1)) >> product)[actions::process_addend{}]; - -auto addend = (('+' >> x3::attr(1) | '-' >> x3::attr(-1)) > product)[actions::process_addend{}]; - -auto sum_def = first_addend >> *addend; - -auto expr_def = -sum > x3::eoi; - -auto resultExpr_def = (tensor | variable) > (L'=' | x3::lit(L"->")) >> expr; -// clang-format on - -BOOST_SPIRIT_DEFINE(name, number, variable, index_label, index, index_groups, - tensor, product, sum, expr, symmetry_spec, resultExpr); - -struct position_cache_tag; -struct error_handler_tag; - -namespace helpers { - -struct annotate_position { - template - void on_success(const Iterator &first, const Iterator &last, T &ast, - const Context &ctx) { - auto &position_cache = - boost::spirit::x3::get(ctx).get(); - position_cache.annotate(ast, first, last); - } -}; - -struct error_handler { - template - x3::error_handler_result on_error(const Iterator & /*first*/, - const Iterator & /*last*/, - const Exception &e, const Context &ctx) { - auto &error_handler = x3::get(ctx).get(); - error_handler(e.where(), boost::core::demangle(e.which().data())); - return x3::error_handler_result::fail; - } -}; - -} // namespace helpers - -struct NumberRule : helpers::annotate_position, helpers::error_handler {}; -struct VariableRule : helpers::annotate_position, helpers::error_handler {}; -struct TensorRule : helpers::annotate_position, helpers::error_handler {}; -struct ProductRule : helpers::annotate_position, helpers::error_handler {}; -struct SumRule : helpers::annotate_position, helpers::error_handler {}; -struct ExprRule : helpers::annotate_position, helpers::error_handler {}; -struct ResultRule : helpers::annotate_position, helpers::error_handler {}; -struct IndexLabelRule : helpers::annotate_position, helpers::error_handler {}; -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; - - ErrorHandler(Iterator begin) : begin(std::move(begin)) {} - - 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); - } -}; - -template -AST do_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]]; - - auto begin = input.begin(); - try { - bool success = - x3::phrase_parse(begin, input.end(), parser, x3::unicode::space, ast); - - 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"); - } - 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"); - } - } catch ([[maybe_unused]] const boost::spirit::x3::expectation_failure< - iterator_type> &e) { - // std::wcout << "Caught expectation_failure\nwhere: " << e.where() - // << "\nwhat: " << e.what() << "\nwhich: " << e.which().data() - // << std::endl; - throw; - } - - return ast; -} - -parse::transform::DefaultSymmetries to_default_symms( - const ParseOptions &options) { - const Context &ctx = get_default_context(); - - parse::transform::DefaultSymmetries symms{ - Symmetry::Nonsymm, ctx.braket_symmetry(), ColumnSymmetry::Symm}; - - if (options.def_perm_symm.has_value()) { - std::get<0>(symms) = options.def_perm_symm.value(); - } - if (options.def_braket_symm.has_value()) { - std::get<1>(symms) = options.def_braket_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 - std::get<2>(symms) = ColumnSymmetry::Symm; - } - - return symms; -} - -ResultExpr parse_result_expr(std::wstring_view input, - const ParseOptions &options) { - 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(options)); -} - -ExprPtr parse_expr(std::wstring_view input, const ParseOptions &options) { - using iterator_type = decltype(input)::iterator; - x3::position_cache> positions(input.begin(), - input.end()); - auto ast = do_parse(parse::expr, input, positions); - - return parse::transform::ast_to_expr(ast, positions, input.begin(), - to_default_symms(options)); -} - -ExprPtr parse_expr(std::string_view input, const ParseOptions &options) { - return parse_expr(sequant::to_wstring(input), options); -} - -ResultExpr parse_result_expr(std::string_view input, - const ParseOptions &options) { - return parse_result_expr(sequant::to_wstring(input), options); -} +#define SEQUANT_RESOLVE_PARSE_FUNC(name, stringType, returnType) \ + returnType name(stringType input, const ParseOptions &options) { \ + switch (options.syntax) { \ + case ParseSyntax::V1: \ + return parse::v1::name(input, options); \ + } \ + \ + SEQUANT_UNREACHABLE; \ + } + +SEQUANT_RESOLVE_PARSE_FUNC(parse_expr, std::wstring_view, ExprPtr) +SEQUANT_RESOLVE_PARSE_FUNC(parse_expr, std::string_view, ExprPtr) +SEQUANT_RESOLVE_PARSE_FUNC(parse_result_expr, std::wstring_view, ResultExpr) +SEQUANT_RESOLVE_PARSE_FUNC(parse_result_expr, std::string_view, ResultExpr) + +#define RESOLVE_DEPARSE_FUNC(argType) \ + std::wstring deparse(const argType &arg, const DeparseOptions &options) { \ + switch (options.syntax) { \ + case ParseSyntax::V1: \ + return parse::v1::deparse(arg, options); \ + } \ + \ + SEQUANT_UNREACHABLE; \ + } + +RESOLVE_DEPARSE_FUNC(ResultExpr) +RESOLVE_DEPARSE_FUNC(ExprPtr) +RESOLVE_DEPARSE_FUNC(Expr) +RESOLVE_DEPARSE_FUNC(AbstractTensor) +RESOLVE_DEPARSE_FUNC(Index) +RESOLVE_DEPARSE_FUNC(NormalOperator) +RESOLVE_DEPARSE_FUNC(NormalOperator) + +#undef SEQUANT_RESOLVE_PARSE_FUNC +#undef SEQUANT_RESOLVE_DEPARSE_FUNC } // namespace sequant diff --git a/SeQuant/core/parse/ast.cpp b/SeQuant/core/parse/v1/ast.cpp similarity index 64% rename from SeQuant/core/parse/ast.cpp rename to SeQuant/core/parse/v1/ast.cpp index 6ab6190091..2fddd02359 100644 --- a/SeQuant/core/parse/ast.cpp +++ b/SeQuant/core/parse/v1/ast.cpp @@ -2,13 +2,13 @@ // Created by Robert Adam on 2023-09-21 // -#include +#include -namespace sequant::parse::ast { +namespace sequant::parse::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::parse::v1::ast diff --git a/SeQuant/core/parse/ast.hpp b/SeQuant/core/parse/v1/ast.hpp similarity index 78% rename from SeQuant/core/parse/ast.hpp rename to SeQuant/core/parse/v1/ast.hpp index 8c93c544c2..640c2cf6e1 100644 --- a/SeQuant/core/parse/ast.hpp +++ b/SeQuant/core/parse/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 @@ -17,7 +17,7 @@ #include #include -namespace sequant::parse::ast { +namespace sequant::parse::v1::ast { struct IndexLabel : boost::spirit::x3::position_tagged { std::wstring label; @@ -132,20 +132,22 @@ struct ResultExpr : boost::spirit::x3::position_tagged { : lhs(std::move(tensor)), rhs(std::move(expr)) {} }; -} // namespace sequant::parse::ast +} // namespace sequant::parse::v1::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, +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::IndexLabel, label, id); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Index, label, protoLabels); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Number, numerator, + denominator); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Variable, name, conjugated); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::IndexGroups, bra, ket, auxiliaries, reverse_bra_ket); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::ast::SymmetrySpec, perm_symm, +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::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::v1::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); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Product, factors); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Sum, summands); +BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::ResultExpr, lhs, rhs); -#endif // SEQUANT_CORE_PARSE_AST_HPP +#endif // SEQUANT_CORE_PARSE_AST_V1_HPP diff --git a/SeQuant/core/parse/ast_conversions.hpp b/SeQuant/core/parse/v1/ast_conversions.hpp similarity index 88% rename from SeQuant/core/parse/ast_conversions.hpp rename to SeQuant/core/parse/v1/ast_conversions.hpp index c2591d8798..03b07cdf91 100644 --- a/SeQuant/core/parse/ast_conversions.hpp +++ b/SeQuant/core/parse/v1/ast_conversions.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,7 +21,7 @@ #include #include -namespace sequant::parse::transform { +namespace sequant::parse::v1::transform { using DefaultSymmetries = std::tuple; @@ -36,12 +36,12 @@ std::tuple get_pos(const AST &ast, } template -Index to_index(const parse::ast::Index &index, +Index to_index(const parse::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 parse::v1::ast::IndexLabel ¤t : index.protoLabels) { try { std::wstring label = current.label + L"_" + std::to_wstring(current.id); IndexSpace space = @@ -80,7 +80,7 @@ Index to_index(const parse::ast::Index &index, template std::tuple, container::vector, container::vector> -make_indices(const parse::ast::IndexGroups &groups, +make_indices(const parse::v1::ast::IndexGroups &groups, const PositionCache &position_cache, const Iterator &begin) { container::vector braIndices; container::vector ketIndices; @@ -101,13 +101,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 parse::v1::ast::Index ¤t : *bra) { braIndices.push_back(to_index(current, position_cache, begin)); } - for (const parse::ast::Index ¤t : *ket) { + for (const parse::v1::ast::Index ¤t : *ket) { ketIndices.push_back(to_index(current, position_cache, begin)); } - for (const parse::ast::Index ¤t : groups.auxiliaries) { + for (const parse::v1::ast::Index ¤t : groups.auxiliaries) { auxiliaries.push_back(to_index(current, position_cache, begin)); } @@ -117,7 +117,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 == parse::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -140,7 +140,7 @@ Symmetry to_perm_symmetry(char c, std::size_t offset, const Iterator &, template BraKetSymmetry to_braket_symmetry(char c, std::size_t offset, const Iterator &, BraKetSymmetry default_symmetry) { - if (c == parse::ast::SymmetrySpec::unspecified) { + if (c == parse::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -163,7 +163,7 @@ BraKetSymmetry to_braket_symmetry(char c, std::size_t offset, const Iterator &, template ColumnSymmetry to_column_symmetry(char c, std::size_t offset, const Iterator &, ColumnSymmetry default_symmetry) { - if (c == parse::ast::SymmetrySpec::unspecified) { + if (c == parse::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -182,8 +182,8 @@ ColumnSymmetry to_column_symmetry(char c, std::size_t offset, const Iterator &, } template -Constant to_constant(const parse::ast::Number &number, const PositionCache &, - const Iterator &) { +Constant to_constant(const parse::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 +198,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 +223,11 @@ std::tuple to_symmetries( } template -ExprPtr ast_to_expr(const parse::ast::Product &product, +ExprPtr ast_to_expr(const parse::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 parse::v1::ast::Sum &sum, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms); @@ -237,17 +237,17 @@ struct Transformer { std::reference_wrapper begin; std::reference_wrapper default_symms; - ExprPtr operator()(const parse::ast::Product &product) const { + ExprPtr operator()(const parse::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 parse::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 parse::v1::ast::Tensor &tensor) const { auto [braIndices, ketIndices, auxiliaries] = make_indices(tensor.indices, position_cache.get(), begin.get()); @@ -295,7 +295,7 @@ struct Transformer { perm_symm, braket_symm, column_symm); } - ExprPtr operator()(const parse::ast::Variable &variable) const { + ExprPtr operator()(const parse::v1::ast::Variable &variable) const { ExprPtr var = ex(variable.name); if (variable.conjugated) { @@ -305,13 +305,13 @@ struct Transformer { return var; } - ExprPtr operator()(const parse::ast::Number &number) const { + ExprPtr operator()(const parse::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 parse::v1::ast::NullaryValue &value, const PositionCache &position_cache, const Iterator &begin, DefaultSymmetries default_symms) { return boost::apply_visitor( @@ -326,7 +326,7 @@ bool holds_alternative(const boost::variant &v) noexcept { } template -ExprPtr ast_to_expr(const parse::ast::Product &product, +ExprPtr ast_to_expr(const parse::v1::ast::Product &product, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms) { if (product.factors.empty()) { @@ -343,9 +343,9 @@ 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), + for (const parse::v1::ast::NullaryValue &value : product.factors) { + if (holds_alternative(value)) { + prefactor *= to_constant(boost::get(value), position_cache, begin); } else { factors.push_back( @@ -367,7 +367,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 parse::v1::ast::Sum &sum, const PositionCache &position_cache, const Iterator &begin, const DefaultSymmetries &default_symms) { if (sum.summands.empty()) { @@ -382,7 +382,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 parse::v1::ast::Product &product) { return ast_to_expr(product, position_cache, begin, default_symms); }); @@ -390,7 +390,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 parse::v1::ast::ResultExpr &result, const PositionCache &position_cache, const Iterator &begin, DefaultSymmetries default_symms) { @@ -411,6 +411,6 @@ ResultExpr ast_to_result(const parse::ast::ResultExpr &result, } } -} // namespace sequant::parse::transform +} // namespace sequant::parse::v1::transform #endif // SEQUANT_CORE_PARSE_AST_CONVERSIONS_HPP diff --git a/SeQuant/core/parse/deparse.cpp b/SeQuant/core/parse/v1/deparse.cpp similarity index 95% rename from SeQuant/core/parse/deparse.cpp rename to SeQuant/core/parse/v1/deparse.cpp index d278896795..579da030d9 100644 --- a/SeQuant/core/parse/deparse.cpp +++ b/SeQuant/core/parse/v1/deparse.cpp @@ -16,7 +16,7 @@ #include #include -namespace sequant { +namespace sequant::parse::v1 { namespace details { @@ -25,7 +25,7 @@ std::wstring deparse_indices(Range&& indices, const DeparseOptions& options) { std::wstring deparsed; for (std::size_t i = 0; i < indices.size(); ++i) { - deparsed += deparse(indices[i], options); + deparsed += v1::deparse(indices[i], options); if (i + 1 < indices.size()) { deparsed += L","; @@ -40,7 +40,7 @@ std::wstring deparse_ops(const Range& ops, const DeparseOptions& options) { std::wstring deparsed; for (std::size_t i = 0; i < ops.size(); ++i) { - deparsed += deparse(ops[i].index(), options); + deparsed += v1::deparse(ops[i].index(), options); if (i + 1 < ops.size()) { deparsed += L","; @@ -202,7 +202,7 @@ std::wstring deparse(Sum const& sum, const DeparseOptions& options) { std::wstring deparse(const ExprPtr& expr, const DeparseOptions& options) { if (!expr) return {}; - return deparse(*expr, options); + return v1::deparse(*expr, options); } std::wstring deparse(const Expr& expr, const DeparseOptions& options) { @@ -210,9 +210,9 @@ std::wstring deparse(const Expr& expr, const DeparseOptions& options) { if (expr.is()) return details::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as(), options); + return v1::deparse(expr.as(), options); else if (expr.is()) - return deparse(expr.as(), options); + return v1::deparse(expr.as(), options); else if (expr.is()) return details::deparse(expr.as(), options); else if (expr.is()) @@ -233,7 +233,7 @@ std::wstring deparse(const ResultExpr& result, const DeparseOptions& options) { deparsed = details::deparse(result.result_as_variable(L"?"), options); } - return deparsed + L" = " + deparse(result.expression(), options); + return deparsed + L" = " + v1::deparse(result.expression(), options); } std::wstring deparse(const Index& index, const DeparseOptions&) { @@ -301,4 +301,4 @@ template std::wstring deparse( NormalOperator const& nop, const DeparseOptions& options); -} // namespace sequant +} // namespace sequant::parse::v1 diff --git a/SeQuant/core/parse/v1/parse.cpp b/SeQuant/core/parse/v1/parse.cpp new file mode 100644 index 0000000000..d05c4d1c9a --- /dev/null +++ b/SeQuant/core/parse/v1/parse.cpp @@ -0,0 +1,294 @@ +// +// Created by Robert Adam on 2023-09-20 +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOOST_SPIRIT_X3_UNICODE +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sequant::parse::v1 { + +namespace x3 = boost::spirit::x3; + +namespace parse { + +struct NumberRule; +struct VariableRule; +struct TensorRule; +struct ProductRule; +struct SumRule; +struct ExprRule; +struct ResultExprRule; +struct IndexLabelRule; +struct IndexRule; +struct IndexGroupRule; +struct SymmetrySpecRule; + +// Types +x3::rule number{"Number"}; +x3::rule variable{"Variable"}; +x3::rule tensor{"Tensor"}; + +// Expression structure +x3::rule product{"Product"}; +x3::rule sum{"Sum"}; +x3::rule expr{"Expression"}; +x3::rule resultExpr{"ResultExpr"}; + +// Auxiliaries +x3::rule name{"Name"}; +x3::rule index_label{"IndexLabel"}; +x3::rule index{"Index"}; +x3::rule index_groups{"IndexGroups"}; +x3::rule symmetry_spec{"SymmetrySpec"}; + +auto to_char_type = [](auto c) { + return static_cast(c); +}; + +// clang-format off +auto word_components = x3::unicode::alnum + | x3::char_('_') | x3::unicode::char_(L'⁔') + | x3::unicode::char_(to_char_type(0x0303)) // combining tilde + | x3::unicode::char_(to_char_type(0x0302)) // combining circumflex/hat + // Superscript and Subscript block + | (x3::unicode::char_(to_char_type(0x2070), to_char_type(0x209F)) - x3::unicode::unassigned) + // These are defined in the Latin-1 Supplement block and thus need to be listed explicitly + | x3::unicode::char_(L'¹') | x3::unicode::char_(L'²') | x3::unicode::char_(L'³') + // Arrow block + | (x3::unicode::char_(to_char_type(0x2190), to_char_type(0x21FF)) - x3::unicode::unassigned); +// A name begins with a letter, then can container letters, digits and +// underscores, but can not end with an underscore (to not confuse the parser +// with tensors á la t_{…}^{…}. +auto name_def = x3::lexeme[ + x3::unicode::alpha >> -( *(word_components >> &word_components) >> (word_components - '_') ) + ]; + +auto number_def = x3::double_ >> -('/' >> x3::double_); + +auto variable_def = x3::lexeme[name >> -(x3::lit('^') >> '*' >> x3::attr(true))]; + +auto index_name = +( x3::unicode::alpha | x3::unicode::char_(L'⁺') | x3::unicode::char_(L'⁻') | x3::unicode::char_(L'̃') + | x3::unicode::char_(L'↑') | x3::unicode::char_(L'↓') + ); + +auto index_label_def = x3::lexeme[ + index_name >> -x3::lit('_') >> x3::uint_ + ]; + +auto index_def = x3::lexeme[ + index_label >> -x3::skip['<' >> index_label % ',' >> ">"] + ]; + +const std::vector noIndices; +auto index_groups_def = L"_{" > -(index % ',') > L"}^{" > -(index % ',') > L"}" >> x3::attr(noIndices) >> x3::attr(false) + | L"^{" > -(index % ',') > L"}_{" > -(index % ',') > L"}" >> x3::attr(noIndices) >> x3::attr(true) + | '{' > -(index % ',') > -( ';' > -(index % ',')) > -(';' > -(index % ',')) > '}' >> x3::attr(false); + +auto symmetry_spec_def= x3::lexeme[ + ':' >> x3::upper >> -('-' >> x3::upper) >> -('-' >> x3::upper) + ]; + +auto tensor_def = x3::lexeme[ + name >> x3::skip[index_groups] >> -(symmetry_spec) + ]; + +auto nullary = number | tensor | variable; + +auto grouped = '(' > sum > ')' | nullary; + +auto product_def = grouped % -x3::lit('*'); + +auto first_addend = (('-' >> x3::attr(-1) | -x3::lit('+') >> x3::attr(1)) >> product)[actions::process_addend{}]; + +auto addend = (('+' >> x3::attr(1) | '-' >> x3::attr(-1)) > product)[actions::process_addend{}]; + +auto sum_def = first_addend >> *addend; + +auto expr_def = -sum > x3::eoi; + +auto resultExpr_def = (tensor | variable) > (L'=' | x3::lit(L"->")) >> expr; +// clang-format on + +BOOST_SPIRIT_DEFINE(name, number, variable, index_label, index, index_groups, + tensor, product, sum, expr, symmetry_spec, resultExpr); + +struct position_cache_tag; +struct error_handler_tag; + +namespace helpers { + +struct annotate_position { + template + void on_success(const Iterator &first, const Iterator &last, T &ast, + const Context &ctx) { + auto &position_cache = + boost::spirit::x3::get(ctx).get(); + position_cache.annotate(ast, first, last); + } +}; + +struct error_handler { + template + x3::error_handler_result on_error(const Iterator & /*first*/, + const Iterator & /*last*/, + const Exception &e, const Context &ctx) { + auto &error_handler = x3::get(ctx).get(); + error_handler(e.where(), boost::core::demangle(e.which().data())); + return x3::error_handler_result::fail; + } +}; + +} // namespace helpers + +struct NumberRule : helpers::annotate_position, helpers::error_handler {}; +struct VariableRule : helpers::annotate_position, helpers::error_handler {}; +struct TensorRule : helpers::annotate_position, helpers::error_handler {}; +struct ProductRule : helpers::annotate_position, helpers::error_handler {}; +struct SumRule : helpers::annotate_position, helpers::error_handler {}; +struct ExprRule : helpers::annotate_position, helpers::error_handler {}; +struct ResultRule : helpers::annotate_position, helpers::error_handler {}; +struct IndexLabelRule : helpers::annotate_position, helpers::error_handler {}; +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; + + ErrorHandler(Iterator begin) : begin(std::move(begin)) {} + + 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); + } +}; + +template +AST do_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]]; + + auto begin = input.begin(); + try { + bool success = + x3::phrase_parse(begin, input.end(), parser, x3::unicode::space, ast); + + 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"); + } + 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"); + } + } catch ([[maybe_unused]] const boost::spirit::x3::expectation_failure< + iterator_type> &e) { + // std::wcout << "Caught expectation_failure\nwhere: " << e.where() + // << "\nwhat: " << e.what() << "\nwhich: " << e.which().data() + // << std::endl; + throw; + } + + return ast; +} + +transform::DefaultSymmetries to_default_symms(const ParseOptions &options) { + const Context &ctx = get_default_context(); + + transform::DefaultSymmetries symms{Symmetry::Nonsymm, ctx.braket_symmetry(), + ColumnSymmetry::Symm}; + + if (options.def_perm_symm.has_value()) { + std::get<0>(symms) = options.def_perm_symm.value(); + } + if (options.def_braket_symm.has_value()) { + std::get<1>(symms) = options.def_braket_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 + std::get<2>(symms) = ColumnSymmetry::Symm; + } + + return symms; +} + +ResultExpr parse_result_expr(std::wstring_view input, + const ParseOptions &options) { + using iterator_type = decltype(input)::iterator; + x3::position_cache> positions(input.begin(), + input.end()); + auto ast = do_parse(parse::resultExpr, input, positions); + + return transform::ast_to_result(ast, positions, input.begin(), + to_default_symms(options)); +} + +ExprPtr parse_expr(std::wstring_view input, const ParseOptions &options) { + using iterator_type = decltype(input)::iterator; + x3::position_cache> positions(input.begin(), + input.end()); + auto ast = do_parse(parse::expr, input, positions); + + return transform::ast_to_expr(ast, positions, input.begin(), + to_default_symms(options)); +} + +ExprPtr parse_expr(std::string_view input, const ParseOptions &options) { + return v1::parse_expr(sequant::to_wstring(input), options); +} + +ResultExpr parse_result_expr(std::string_view input, + const ParseOptions &options) { + return v1::parse_result_expr(sequant::to_wstring(input), options); +} + +} // namespace sequant::parse::v1 diff --git a/SeQuant/core/parse/semantic_actions.hpp b/SeQuant/core/parse/v1/semantic_actions.hpp similarity index 86% rename from SeQuant/core/parse/semantic_actions.hpp rename to SeQuant/core/parse/v1/semantic_actions.hpp index 8733110b12..e627a00076 100644 --- a/SeQuant/core/parse/semantic_actions.hpp +++ b/SeQuant/core/parse/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::parse::v1::actions { namespace x3 = boost::spirit::x3; @@ -70,6 +70,6 @@ struct process_addend { } }; -} // namespace sequant::parse::actions +} // namespace sequant::parse::v1::actions -#endif // SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_HPP +#endif // SEQUANT_CORE_PARSE_SEMANTIC_ACTIONS_V1_HPP From ff108fede2098870bd1ef593f9c3f923d5c8eb8c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 30 Jan 2026 20:31:47 +0100 Subject: [PATCH 08/22] Remove wolfram output Support was incomplete anyway and since we don't have intentions on completing it, we remove it in order for nobody to rely on it. --- CMakeLists.txt | 1 - SeQuant/core/attr.hpp | 30 -------------- SeQuant/core/complex.hpp | 10 ----- SeQuant/core/expressions/constant.hpp | 4 -- SeQuant/core/expressions/expr.cpp | 2 - SeQuant/core/expressions/expr.hpp | 4 -- SeQuant/core/expressions/expr_algorithms.cpp | 4 -- SeQuant/core/expressions/expr_algorithms.hpp | 2 - SeQuant/core/expressions/product.hpp | 16 -------- SeQuant/core/expressions/sum.hpp | 13 ------- SeQuant/core/index.hpp | 31 --------------- SeQuant/core/meta.hpp | 13 ------- SeQuant/core/rational.hpp | 17 -------- SeQuant/core/wolfram.hpp | 41 -------------------- tests/unit/test_expr.cpp | 36 ----------------- tests/unit/test_index.cpp | 37 ------------------ 16 files changed, 261 deletions(-) delete mode 100644 SeQuant/core/wolfram.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 30aba712f8..7afd0cef90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,7 +375,6 @@ set(SeQuant_src SeQuant/core/reserved.hpp SeQuant/core/wick.hpp SeQuant/core/wick.impl.hpp - SeQuant/core/wolfram.hpp SeQuant/core/wstring.hpp SeQuant/domain/mbpt/antisymmetrizer.cpp SeQuant/domain/mbpt/antisymmetrizer.hpp 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..452561f98a 100644 --- a/SeQuant/core/complex.hpp +++ b/SeQuant/core/complex.hpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace sequant { @@ -54,15 +53,6 @@ struct Complex { 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/expressions/constant.hpp b/SeQuant/core/expressions/constant.hpp index f02e6cc897..d0a19c4ff0 100644 --- a/SeQuant/core/expressions/constant.hpp +++ b/SeQuant/core/expressions/constant.hpp @@ -69,10 +69,6 @@ class Constant : public Expr { return L"{" + sequant::to_latex(value()) + L"}"; } - std::wstring to_wolfram() const override { - return sequant::to_wolfram(value()); - } - type_id_type type_id() const override { return get_type_id(); } ExprPtr clone() const override { return ex(this->value()); } diff --git a/SeQuant/core/expressions/expr.cpp b/SeQuant/core/expressions/expr.cpp index 9902050d8e..23a9cd8fa9 100644 --- a/SeQuant/core/expressions/expr.cpp +++ b/SeQuant/core/expressions/expr.cpp @@ -115,8 +115,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"); } 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..a7724d1bd3 100644 --- a/SeQuant/core/expressions/expr_algorithms.cpp +++ b/SeQuant/core/expressions/expr_algorithms.cpp @@ -79,10 +79,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) { diff --git a/SeQuant/core/expressions/expr_algorithms.hpp b/SeQuant/core/expressions/expr_algorithms.hpp index 8d2c304ee3..7218ba428b 100644 --- a/SeQuant/core/expressions/expr_algorithms.hpp +++ b/SeQuant/core/expressions/expr_algorithms.hpp @@ -27,8 +27,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..4a878b13bf 100644 --- a/SeQuant/core/expressions/product.hpp +++ b/SeQuant/core/expressions/product.hpp @@ -321,22 +321,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/index.hpp b/SeQuant/core/index.hpp index 35980d840f..f4b6614495 100644 --- a/SeQuant/core/index.hpp +++ b/SeQuant/core/index.hpp @@ -716,37 +716,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/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/rational.hpp b/SeQuant/core/rational.hpp index c2213d170f..ffd08711d6 100644 --- a/SeQuant/core/rational.hpp +++ b/SeQuant/core/rational.hpp @@ -166,23 +166,6 @@ inline std::wstring to_latex(const rational& t) { 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"]"; -} - } // namespace sequant #endif // SEQUANT_CORE_RATIONAL_H 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/tests/unit/test_expr.cpp b/tests/unit/test_expr.cpp index b6ade55fd3..7113c96cb9 100644 --- a/tests/unit/test_expr.cpp +++ b/tests/unit/test_expr.cpp @@ -13,7 +13,6 @@ #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); @@ -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 { diff --git a/tests/unit/test_index.cpp b/tests/unit/test_index.cpp index ea1428b90e..188b5e11ee 100644 --- a/tests/unit/test_index.cpp +++ b/tests/unit/test_index.cpp @@ -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]]"); - }*/ } From bf299d82fec5218f0bfad3bfaf16d777003165e6 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 30 Jan 2026 21:31:28 +0100 Subject: [PATCH 09/22] Merge wstring.hpp into string utilities At the same time, switch to consistently using toUtf8/toUtf16 --- CMakeLists.txt | 1 - SeQuant/core/asy_cost.cpp | 6 +- SeQuant/core/eval/eval.hpp | 3 +- SeQuant/core/eval/eval_expr.cpp | 11 ++- SeQuant/core/expressions/tensor.hpp | 5 +- SeQuant/core/index.cpp | 6 +- SeQuant/core/index.hpp | 8 +- SeQuant/core/index_space_registry.hpp | 30 ++++---- SeQuant/core/latex.hpp | 2 +- SeQuant/core/latex.ipp | 2 +- SeQuant/core/parse/v1/deparse.cpp | 2 +- SeQuant/core/parse/v1/parse.cpp | 4 +- SeQuant/core/rational.hpp | 6 +- SeQuant/core/space.cpp | 2 +- SeQuant/core/space.hpp | 14 +--- SeQuant/core/tensor_network/v1.cpp | 2 +- SeQuant/core/tensor_network/v2.cpp | 2 +- SeQuant/core/tensor_network/v3.cpp | 1 + SeQuant/core/utility/expr.cpp | 24 +++--- SeQuant/core/utility/indices.hpp | 2 +- SeQuant/core/utility/string.cpp | 4 + SeQuant/core/utility/string.hpp | 102 +++++++++++++++++++++++++ SeQuant/core/wick.hpp | 3 +- SeQuant/core/wstring.hpp | 103 -------------------------- SeQuant/domain/mbpt/op.cpp | 2 +- SeQuant/domain/mbpt/op_registry.cpp | 17 ++--- SeQuant/domain/mbpt/spin.hpp | 7 +- python/src/sequant/mbpt.h | 5 +- python/test.py | 56 ++++++++++++++ tests/integration/eomcc.cpp | 2 +- tests/unit/CMakeLists.txt | 1 - tests/unit/catch2_sequant.hpp | 19 +++-- tests/unit/test_export.cpp | 16 ++-- tests/unit/test_math.cpp | 2 +- tests/unit/test_string.cpp | 40 ---------- tests/unit/test_tensor_network.cpp | 8 +- tests/unit/test_utilities.cpp | 24 ++++++ 37 files changed, 288 insertions(+), 256 deletions(-) delete mode 100644 SeQuant/core/wstring.hpp create mode 100644 python/test.py delete mode 100644 tests/unit/test_string.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7afd0cef90..8afe0f8491 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,7 +375,6 @@ set(SeQuant_src SeQuant/core/reserved.hpp SeQuant/core/wick.hpp SeQuant/core/wick.impl.hpp - SeQuant/core/wstring.hpp SeQuant/domain/mbpt/antisymmetrizer.cpp SeQuant/domain/mbpt/antisymmetrizer.hpp SeQuant/domain/mbpt/biorthogonalization.cpp 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/eval/eval.hpp b/SeQuant/core/eval/eval.hpp index 5da4fb0c0c..e6c515c54d 100644 --- a/SeQuant/core/eval/eval.hpp +++ b/SeQuant/core/eval/eval.hpp @@ -12,6 +12,7 @@ #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(deparse(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..ea79718e12 100644 --- a/SeQuant/core/eval/eval_expr.cpp +++ b/SeQuant/core/eval/eval_expr.cpp @@ -12,7 +12,6 @@ #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(sequant::deparse(as_constant())); } else { SEQUANT_ASSERT(is_variable()); - return to_string(as_variable().label()); + return toUtf8(as_variable().label()); } } diff --git a/SeQuant/core/expressions/tensor.hpp b/SeQuant/core/expressions/tensor.hpp index 34a77ae2a6..d93cfd4be5 100644 --- a/SeQuant/core/expressions/tensor.hpp +++ b/SeQuant/core/expressions/tensor.hpp @@ -16,6 +16,7 @@ #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)), diff --git a/SeQuant/core/index.cpp b/SeQuant/core/index.cpp index 24efc88612..5fbc7f39f5 100644 --- a/SeQuant/core/index.cpp +++ b/SeQuant/core/index.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -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 f4b6614495..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 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/latex.hpp b/SeQuant/core/latex.hpp index 5bf5f4bcaa..bb93b83bf7 100644 --- a/SeQuant/core/latex.hpp +++ b/SeQuant/core/latex.hpp @@ -6,7 +6,7 @@ #define SEQUANT_CORE_LATEX_HPP #include -#include +#include #include #include diff --git a/SeQuant/core/latex.ipp b/SeQuant/core/latex.ipp index 5d951395ea..dfde84d770 100644 --- a/SeQuant/core/latex.ipp +++ b/SeQuant/core/latex.ipp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/SeQuant/core/parse/v1/deparse.cpp b/SeQuant/core/parse/v1/deparse.cpp index 579da030d9..e1ce93c663 100644 --- a/SeQuant/core/parse/v1/deparse.cpp +++ b/SeQuant/core/parse/v1/deparse.cpp @@ -122,7 +122,7 @@ std::wstring deparse_scalar(const Constant::scalar_type& scalar, SEQUANT_ASSERT(!deparsed.empty()); - return to_wstring(deparsed); + return toUtf16(deparsed); } std::wstring deparse(Tensor const& tensor, const DeparseOptions& options) { diff --git a/SeQuant/core/parse/v1/parse.cpp b/SeQuant/core/parse/v1/parse.cpp index d05c4d1c9a..4fd703f127 100644 --- a/SeQuant/core/parse/v1/parse.cpp +++ b/SeQuant/core/parse/v1/parse.cpp @@ -283,12 +283,12 @@ ExprPtr parse_expr(std::wstring_view input, const ParseOptions &options) { } ExprPtr parse_expr(std::string_view input, const ParseOptions &options) { - return v1::parse_expr(sequant::to_wstring(input), options); + return v1::parse_expr(toUtf16(input), options); } ResultExpr parse_result_expr(std::string_view input, const ParseOptions &options) { - return v1::parse_result_expr(sequant::to_wstring(input), options); + return v1::parse_result_expr(toUtf16(input), options); } } // namespace sequant::parse::v1 diff --git a/SeQuant/core/rational.hpp b/SeQuant/core/rational.hpp index ffd08711d6..4b45e86897 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,13 +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)); + return toUtf16(boost::lexical_cast(i)); } template 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_network/v1.cpp b/SeQuant/core/tensor_network/v1.cpp index 29f8b7c5cf..274362af9a 100644 --- a/SeQuant/core/tensor_network/v1.cpp +++ b/SeQuant/core/tensor_network/v1.cpp @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/SeQuant/core/tensor_network/v2.cpp b/SeQuant/core/tensor_network/v2.cpp index 1aa8226a52..9d9a7b602e 100644 --- a/SeQuant/core/tensor_network/v2.cpp +++ b/SeQuant/core/tensor_network/v2.cpp @@ -18,9 +18,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/SeQuant/core/tensor_network/v3.cpp b/SeQuant/core/tensor_network/v3.cpp index e621d869c6..092260c5d3 100644 --- a/SeQuant/core/tensor_network/v3.cpp +++ b/SeQuant/core/tensor_network/v3.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include 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..3f0c9644b9 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,88 @@ 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); +} + +#if 0 +/// @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); +} +#endif + +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_CORE_UTILITY_STRING_HPP diff --git a/SeQuant/core/wick.hpp b/SeQuant/core/wick.hpp index 32fda728f0..ba8ea25d20 100644 --- a/SeQuant/core/wick.hpp +++ b/SeQuant/core/wick.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace sequant { @@ -162,7 +163,7 @@ class WickTheorem { ss << L"WickTheorem::set_external_indices: " L"external index " + to_latex(Index(v)) + L" repeated"; - throw std::invalid_argument(to_string(ss.str())); + throw std::invalid_argument(toUtf8(ss.str())); } }); } 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..60f8c8f924 100644 --- a/SeQuant/domain/mbpt/op.cpp +++ b/SeQuant/domain/mbpt/op.cpp @@ -306,7 +306,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) { 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/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/python/test.py b/python/test.py new file mode 100644 index 0000000000..5e04827e89 --- /dev/null +++ b/python/test.py @@ -0,0 +1,56 @@ +import unittest +import _sequant as sq +from _sequant import Tensor, Sum, Product, Constant, Expr, zRational + +#sq.IndexSpace.occupied = "i" + +def visit(visitor, expr): + if isinstance(expr,Sum): return visitor.Sum(*expr.summands) + if isinstance(expr,Product): return visitor.Product(expr.scalar,*expr.factors) + if isinstance(expr,Tensor): return visitor.Tensor(expr) + if isinstance(expr,Constant): return visitor.Constant(expr) + assert False, expr + +class TestSequant(unittest.TestCase): + + def _test_expressions(self): + t = Tensor("T", ["i_1","i_2"], ["i_1"]) + + s = t + t - t + self.assertEqual(type(s), Sum) + self.assertEqual([ type(e) for e in s.summands], [ Tensor, Tensor, Product ]) + self.assertFalse(s.factors) + + p = s*t + self.assertEqual(type(p), Product) + self.assertEqual([type(e) for e in p.factors], [ Sum, Tensor ]) + self.assertFalse(p.summands) + + self.assertEqual(t.latex, "{T^{{i_1}}_{{i_1}{i_2}}}") + self.assertTrue((p+s).latex) + + def test_ccsd(self): + from _sequant.mbpt import A,H,T,T_,VacuumAverage,OpType + ccd = VacuumAverage( A(-2) * H() * T_(2) * T_(2), [(OpType.h, OpType.t)] ); # explicit list of operator connections .. + ccsd = VacuumAverage( A(-2) * H() * T(2) * T(2)); # .. is not needed since H and T are connected by default + print (ccsd.latex) + + class String: + def Sum(self,*args): + return "(%s)" % " + ".join(visit(self,a) for a in args) + def Product(self,pfac,*factors): + pfac_str = (str(pfac) + " * ") if pfac != 1 else "" + return pfac_str + " * ".join(visit(self,a) for a in factors) + def Tensor(self,arg): + return '%s[%s]' % (arg.label,",".join('"%s"' % a for a in arg.braket)) + def Constant(self,arg): + return '%s' % arg + def zRational(self,arg): + return '%s' % arg + + s = visit(String(), ccd) + print (s) + + +if __name__ == '__main__': + unittest.main() 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/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index e4ca5e3759..f3908e2958 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -31,7 +31,6 @@ add_executable(unit_tests-sequant ${BUILD_BY_DEFAULT} "test_runtime.cpp" "test_space.cpp" "test_spin.cpp" - "test_string.cpp" "test_tensor.cpp" "test_tensor_network.cpp" "test_utilities.cpp" diff --git a/tests/unit/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index bd24e080b3..de9d531a17 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -35,11 +35,11 @@ struct StringMaker { bool include_canonical = true) { std::string str; try { - str = sequant::to_string(sequant::deparse(expr, {.annot_symm = true})); + str = sequant::toUtf8(sequant::deparse(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)); + str = sequant::toUtf8(sequant::to_latex(expr)); } if (include_canonical) { @@ -73,7 +73,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()); } }; @@ -82,7 +82,7 @@ struct StringMaker { static std::string convert(const sequant::ResultExpr &res, bool include_canonical = true) { std::string str = - sequant::to_string(sequant::deparse(res, {.annot_symm = true})); + sequant::toUtf8(sequant::deparse(res, {.annot_symm = true})); if (include_canonical) { sequant::ResultExpr clone = res.clone(); @@ -186,16 +186,15 @@ 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))), + std::string(std::forward(expression)), {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { - return sequant::parse_expr( - sequant::to_wstring(std::string(std::forward(expression))), - {.def_perm_symm = sequant::Symmetry::Nonsymm}); + return sequant::parse_expr(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'=') != diff --git a/tests/unit/test_export.cpp b/tests/unit/test_export.cpp index 78bc7c6b31..496a1e1533 100644 --- a/tests/unit/test_export.cpp +++ b/tests/unit/test_export.cpp @@ -251,16 +251,16 @@ std::vector> parse_expression_spec(const std::string &spec) { } try { - ResultExpr res = parse_result_expr( - to_wstring(line), {.def_perm_symm = Symmetry::Nonsymm, - .def_braket_symm = BraKetSymmetry::Nonsymm, - .def_col_symm = ColumnSymmetry::Nonsymm}); + ResultExpr res = + parse_result_expr(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), - {.def_perm_symm = Symmetry::Nonsymm, - .def_braket_symm = BraKetSymmetry::Nonsymm, - .def_col_symm = ColumnSymmetry::Nonsymm}); + ExprPtr expr = + parse_expr(line, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}); groups.back().add(to_export_tree(expr)); } } 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_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_network.cpp b/tests/unit/test_tensor_network.cpp index 001bc45344..b4b73bbb27 100644 --- a/tests/unit/test_tensor_network.cpp +++ b/tests/unit/test_tensor_network.cpp @@ -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); @@ -1300,7 +1300,7 @@ TEST_CASE("tensor_network_v2", "[elements]") { } sstream << "\nInput was " << deparse(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)); @@ -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); @@ -1999,7 +1999,7 @@ TEST_CASE("tensor_network_v3", "[elements]") { } sstream << "\nInput was " << deparse(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)); diff --git a/tests/unit/test_utilities.cpp b/tests/unit/test_utilities.cpp index 26cc79a190..48b6983584 100644 --- a/tests/unit/test_utilities.cpp +++ b/tests/unit/test_utilities.cpp @@ -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); + } } From 01818c8c2033487113f050b1abd7ae61430e2d6d Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 11:56:21 +0100 Subject: [PATCH 10/22] Use fully qualified namespace in macro --- SeQuant/core/utility/string.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SeQuant/core/utility/string.hpp b/SeQuant/core/utility/string.hpp index 3f0c9644b9..d452aa56a4 100644 --- a/SeQuant/core/utility/string.hpp +++ b/SeQuant/core/utility/string.hpp @@ -173,7 +173,7 @@ constexpr decltype(auto) select_string_literal( /// @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( \ + ::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), \ From d889ab8d8d6739ff4bc6a0c21f973084720fbb46 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 11:57:15 +0100 Subject: [PATCH 11/22] Move LaTeX conversion into dedicated namespace --- CMakeLists.txt | 7 +- SeQuant/core/complex.hpp | 2 +- SeQuant/core/expressions/abstract_tensor.hpp | 2 +- SeQuant/core/expressions/expr.cpp | 3 +- SeQuant/core/expressions/tensor.hpp | 5 +- SeQuant/core/index.cpp | 6 +- SeQuant/core/io/concepts.hpp | 14 ++++ .../core/{latex.ipp => io/latex/latex.cpp} | 47 +++++++----- SeQuant/core/{ => io/latex}/latex.hpp | 64 +++++++--------- SeQuant/core/io/shorthands.hpp | 22 ++++++ SeQuant/core/latex.cpp | 31 -------- SeQuant/core/tensor_network/v1.cpp | 6 +- SeQuant/core/tensor_network/v2.cpp | 6 +- SeQuant/core/tensor_network/v3.cpp | 5 +- SeQuant/domain/mbpt/op.cpp | 3 +- doc/examples/user/getting_started/wick.cpp | 2 +- tests/unit/catch2_sequant.hpp | 2 +- tests/unit/test_canonicalize.cpp | 1 - tests/unit/test_expr.cpp | 1 - tests/unit/test_index.cpp | 2 +- tests/unit/test_latex.cpp | 75 +++++++++++-------- tests/unit/test_mbpt.cpp | 2 +- tests/unit/test_spin.cpp | 2 +- tests/unit/test_tensor.cpp | 2 +- tests/unit/test_wick.cpp | 2 +- 25 files changed, 172 insertions(+), 142 deletions(-) create mode 100644 SeQuant/core/io/concepts.hpp rename SeQuant/core/{latex.ipp => io/latex/latex.cpp} (88%) rename SeQuant/core/{ => io/latex}/latex.hpp (75%) create mode 100644 SeQuant/core/io/shorthands.hpp delete mode 100644 SeQuant/core/latex.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8afe0f8491..ba0cd89bca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -307,9 +307,10 @@ set(SeQuant_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 diff --git a/SeQuant/core/complex.hpp b/SeQuant/core/complex.hpp index 452561f98a..2e5ee9922b 100644 --- a/SeQuant/core/complex.hpp +++ b/SeQuant/core/complex.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include diff --git a/SeQuant/core/expressions/abstract_tensor.hpp b/SeQuant/core/expressions/abstract_tensor.hpp index a41699d465..6083fdf227 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 diff --git a/SeQuant/core/expressions/expr.cpp b/SeQuant/core/expressions/expr.cpp index 23a9cd8fa9..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 @@ -150,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/tensor.hpp b/SeQuant/core/expressions/tensor.hpp index d93cfd4be5..93fd43e105 100644 --- a/SeQuant/core/expressions/tensor.hpp +++ b/SeQuant/core/expressions/tensor.hpp @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -526,7 +527,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) { diff --git a/SeQuant/core/index.cpp b/SeQuant/core/index.cpp index 5fbc7f39f5..c21a0cd484 100644 --- a/SeQuant/core/index.cpp +++ b/SeQuant/core/index.cpp @@ -4,7 +4,7 @@ #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 { diff --git a/SeQuant/core/io/concepts.hpp b/SeQuant/core/io/concepts.hpp new file mode 100644 index 0000000000..ed71df811f --- /dev/null +++ b/SeQuant/core/io/concepts.hpp @@ -0,0 +1,14 @@ +#ifndef SEQUANT_CORE_IO_CONCEPTS_HPP +#define SEQUANT_CORE_IO_CONCEPTS_HPP + +#include + +namespace sequant::io { + +template +concept convertible_to_latex = + requires(const T &t) { io::latex::to_string(t); }; + +} // 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 88% rename from SeQuant/core/latex.ipp rename to SeQuant/core/io/latex/latex.cpp index dfde84d770..4fc563da38 100644 --- a/SeQuant/core/latex.ipp +++ b/SeQuant/core/io/latex/latex.cpp @@ -1,23 +1,18 @@ // -// 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 -namespace sequant { -namespace detail { +namespace sequant::io::latex::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 +70,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 +105,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 +126,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 +229,26 @@ std::basic_string diactrics_to_latex_impl( return decltype(result)(str); } -} // namespace detail - -} // namespace sequant - -#endif // SEQUANT_CORE_LATEX_IPP +#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); + +#define SQ_IMPL2(CHAR) \ + template std::basic_string diactrics_to_string_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::io::latex::detail diff --git a/SeQuant/core/latex.hpp b/SeQuant/core/io/latex/latex.hpp similarity index 75% rename from SeQuant/core/latex.hpp rename to SeQuant/core/io/latex/latex.hpp index bb93b83bf7..dfa8f212e9 100644 --- a/SeQuant/core/latex.hpp +++ b/SeQuant/core/io/latex/latex.hpp @@ -2,8 +2,8 @@ // 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 @@ -15,11 +15,11 @@ #include #include -namespace sequant { +namespace sequant::io::latex { template std::enable_if_t>, std::wstring> -to_latex(T&& t) { +to_string(T&& t) { return t.to_latex(); } @@ -27,7 +27,7 @@ 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"}"; @@ -38,7 +38,7 @@ template std::enable_if_t> && std::is_floating_point_v>, 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()); @@ -64,13 +64,13 @@ 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; } @@ -78,12 +78,11 @@ std::wstring to_latex(const std::complex& t) { 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 +94,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 +124,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 +150,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/shorthands.hpp b/SeQuant/core/io/shorthands.hpp new file mode 100644 index 0000000000..846e627502 --- /dev/null +++ b/SeQuant/core/io/shorthands.hpp @@ -0,0 +1,22 @@ +#ifndef SEQUANT_CORE_IO_SHORTHANDS_HPP +#define SEQUANT_CORE_IO_SHORTHANDS_HPP + +#include +#include + +/// @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. + +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)); +} + +} // 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/tensor_network/v1.cpp b/SeQuant/core/tensor_network/v1.cpp index 274362af9a..df986c72b9 100644 --- a/SeQuant/core/tensor_network/v1.cpp +++ b/SeQuant/core/tensor_network/v1.cpp @@ -10,7 +10,8 @@ #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 9d9a7b602e..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 @@ -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 092260c5d3..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 @@ -940,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/domain/mbpt/op.cpp b/SeQuant/domain/mbpt/op.cpp index 60f8c8f924..caa9ce008d 100644 --- a/SeQuant/domain/mbpt/op.cpp +++ b/SeQuant/domain/mbpt/op.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -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 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/tests/unit/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index de9d531a17..50f4a67f4d 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include diff --git a/tests/unit/test_canonicalize.cpp b/tests/unit/test_canonicalize.cpp index 1207624d24..84b9cb2053 100644 --- a/tests/unit/test_canonicalize.cpp +++ b/tests/unit/test_canonicalize.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit/test_expr.cpp b/tests/unit/test_expr.cpp index 7113c96cb9..dc9c4c8802 100644 --- a/tests/unit/test_expr.cpp +++ b/tests/unit/test_expr.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/tests/unit/test_index.cpp b/tests/unit/test_index.cpp index 188b5e11ee..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 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_mbpt.cpp b/tests/unit/test_mbpt.cpp index b4251a7ada..a7cb63a0ba 100644 --- a/tests/unit/test_mbpt.cpp +++ b/tests/unit/test_mbpt.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index 2f9cd107ed..32ac8cb0e5 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/tests/unit/test_tensor.cpp b/tests/unit/test_tensor.cpp index 8f19973d86..ad6ee05b39 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 diff --git a/tests/unit/test_wick.cpp b/tests/unit/test_wick.cpp index 7e88490e47..c342c0dbd1 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 From d42a74926de6965154af0b6a6efe614da8fba495 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 12:56:12 +0100 Subject: [PATCH 12/22] Ignore non-existent paths when formatting --- bin/admin/clang-format.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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" $* From 106ac4fba07b3164c47e3a741fa5ec736f4c82cd Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 12:57:14 +0100 Subject: [PATCH 13/22] Don't use I/O shorthands in SeQuant internals --- SeQuant/core/complex.hpp | 13 ++++++------ SeQuant/core/expressions/abstract_tensor.hpp | 22 +++++++++++--------- SeQuant/core/expressions/tensor.hpp | 7 +++---- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/SeQuant/core/complex.hpp b/SeQuant/core/complex.hpp index 2e5ee9922b..8929d9800f 100644 --- a/SeQuant/core/complex.hpp +++ b/SeQuant/core/complex.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -40,15 +40,14 @@ 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; } diff --git a/SeQuant/core/expressions/abstract_tensor.hpp b/SeQuant/core/expressions/abstract_tensor.hpp index 6083fdf227..4dda7ec142 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 @@ -455,7 +455,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 +463,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 +477,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 +497,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 +505,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 +517,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","; } diff --git a/SeQuant/core/expressions/tensor.hpp b/SeQuant/core/expressions/tensor.hpp index 93fd43e105..e46c2e0c87 100644 --- a/SeQuant/core/expressions/tensor.hpp +++ b/SeQuant/core/expressions/tensor.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -539,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"}"; @@ -547,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"}"; @@ -556,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","; From bea5537e542c07775341eed99d4307a9fdd98fbb Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 13:19:41 +0100 Subject: [PATCH 14/22] Turned is_tensor into a concept --- SeQuant/core/expressions/abstract_tensor.hpp | 34 +++++++++----------- SeQuant/core/expressions/tensor.hpp | 2 +- SeQuant/core/op.hpp | 4 +-- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/SeQuant/core/expressions/abstract_tensor.hpp b/SeQuant/core/expressions/abstract_tensor.hpp index 4dda7ec142..e5bcb59344 100644 --- a/SeQuant/core/expressions/abstract_tensor.hpp +++ b/SeQuant/core/expressions/abstract_tensor.hpp @@ -554,28 +554,24 @@ 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 +/// - @c io::latex::to_string(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; + { to_latex(obj) } -> std::convertible_to; }; -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/tensor.hpp b/SeQuant/core/expressions/tensor.hpp index e46c2e0c87..00a414634e 100644 --- a/SeQuant/core/expressions/tensor.hpp +++ b/SeQuant/core/expressions/tensor.hpp @@ -807,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/op.hpp b/SeQuant/core/op.hpp index 02067b0326..0ffe6bc7a4 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"); From 7670bbc0b4c01f4c13a4563cdc5019a9da85afda Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 13:45:38 +0100 Subject: [PATCH 15/22] Remove debug print statement --- SeQuant/core/export/itf.hpp | 3 --- 1 file changed, 3 deletions(-) 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"); } From 42209ebdedb3744d9fa09fb7065f505a84c3ed2e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 13:48:12 +0100 Subject: [PATCH 16/22] More 'no shorthands internally' --- SeQuant/core/expressions/constant.hpp | 3 ++- SeQuant/core/expressions/expr_algorithms.cpp | 21 ++++++++++---------- SeQuant/core/expressions/product.hpp | 3 ++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/SeQuant/core/expressions/constant.hpp b/SeQuant/core/expressions/constant.hpp index d0a19c4ff0..77f08dbd8c 100644 --- a/SeQuant/core/expressions/constant.hpp +++ b/SeQuant/core/expressions/constant.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,7 @@ class Constant : public Expr { } std::wstring to_latex() const override { - return L"{" + sequant::to_latex(value()) + L"}"; + 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_algorithms.cpp b/SeQuant/core/expressions/expr_algorithms.cpp index a7724d1bd3..95c9fb40c4 100644 --- a/SeQuant/core/expressions/expr_algorithms.cpp +++ b/SeQuant/core/expressions/expr_algorithms.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -21,7 +22,7 @@ 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, @@ -118,15 +119,15 @@ 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::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 } @@ -173,7 +174,7 @@ 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()) { @@ -189,7 +190,7 @@ 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 @@ -203,7 +204,7 @@ 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]); @@ -312,16 +313,16 @@ struct RapidSimplifyVisitor { void operator()(ExprPtr& expr) { if (Logger::instance().simplify) - std::wcout << "rapid_simplify_visitor received " << to_latex(expr) + 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::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/product.hpp b/SeQuant/core/expressions/product.hpp index 4a878b13bf..101f85e399 100644 --- a/SeQuant/core/expressions/product.hpp +++ b/SeQuant/core/expressions/product.hpp @@ -7,6 +7,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"{-}"; } From a6b7c620460f55b56e1bd0abfc8a73e5dac852ef Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 13:52:20 +0100 Subject: [PATCH 17/22] Remove duplicated to_latex definition --- SeQuant/core/expressions/expr_algorithms.cpp | 2 -- SeQuant/core/expressions/expr_algorithms.hpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/SeQuant/core/expressions/expr_algorithms.cpp b/SeQuant/core/expressions/expr_algorithms.cpp index 95c9fb40c4..f05ff6288b 100644 --- a/SeQuant/core/expressions/expr_algorithms.cpp +++ b/SeQuant/core/expressions/expr_algorithms.cpp @@ -18,8 +18,6 @@ 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 = io::latex::to_string(exprptr); diff --git a/SeQuant/core/expressions/expr_algorithms.hpp b/SeQuant/core/expressions/expr_algorithms.hpp index 7218ba428b..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 From 0a0e69b6effcfdc88371c6eb3df0467fc424f002 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 14:20:29 +0100 Subject: [PATCH 18/22] More to_latex adaptations --- SeQuant/core/expressions/abstract_tensor.hpp | 31 +++++++++-------- SeQuant/core/expressions/constant.hpp | 2 +- SeQuant/core/expressions/expr_algorithms.cpp | 32 ++++++++++-------- SeQuant/core/expressions/product.hpp | 2 +- SeQuant/core/io/latex/latex.cpp | 33 +++++++++++++++++-- SeQuant/core/io/latex/latex.hpp | 30 ++++++++++++++--- SeQuant/core/op.hpp | 10 ------ SeQuant/core/rational.hpp | 32 ------------------ SeQuant/core/tensor_canonicalizer.hpp | 5 --- SeQuant/core/wick.hpp | 17 +++++----- SeQuant/core/wick.impl.hpp | 10 +++--- SeQuant/domain/mbpt/op.cpp | 4 +-- doc/examples/synopsis/synopsis3.cpp | 1 + doc/examples/synopsis/synopsis6.cpp | 1 + doc/examples/user/cc.cpp | 1 + doc/examples/user/getting_started/ccd.cpp | 1 + .../getting_started/index_spaces_wick.cpp | 1 + doc/examples/user/operator.cpp | 1 + tests/unit/test_expr.cpp | 3 +- 19 files changed, 118 insertions(+), 99 deletions(-) diff --git a/SeQuant/core/expressions/abstract_tensor.hpp b/SeQuant/core/expressions/abstract_tensor.hpp index e5bcb59344..2597eb43c2 100644 --- a/SeQuant/core/expressions/abstract_tensor.hpp +++ b/SeQuant/core/expressions/abstract_tensor.hpp @@ -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 @@ -554,22 +553,22 @@ 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 io::latex::to_string(t) is a valid expression and its return is convertible -/// to a std::wstring. +/// - @c io::latex::to_string(t) is a valid expression and its return is +/// convertible to a std::wstring. template -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; - { to_latex(obj) } -> std::convertible_to; +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; + { io::latex::to_string(obj) } -> std::convertible_to; }; static_assert(is_tensor, "The AbstractTensor class does not fulfill the requirements of " diff --git a/SeQuant/core/expressions/constant.hpp b/SeQuant/core/expressions/constant.hpp index 77f08dbd8c..33049ee3c7 100644 --- a/SeQuant/core/expressions/constant.hpp +++ b/SeQuant/core/expressions/constant.hpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/SeQuant/core/expressions/expr_algorithms.cpp b/SeQuant/core/expressions/expr_algorithms.cpp index f05ff6288b..0dfe14934f 100644 --- a/SeQuant/core/expressions/expr_algorithms.cpp +++ b/SeQuant/core/expressions/expr_algorithms.cpp @@ -5,9 +5,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -117,15 +117,17 @@ ResultExpr& canonicalize(ResultExpr&& expr, CanonicalizeOptions opts) { struct ExpandVisitor { void operator()(ExprPtr& expr) { if (Logger::instance().expand) - std::wcout << "expand_visitor received " << io::latex::to_string(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 " << io::latex::to_string(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 = " << io::latex::to_string(expr) << std::endl; + std::wcout << "expansion result = " << io::latex::to_string(expr) + << std::endl; // simplification and canonicalization are to be done by other visitors } @@ -172,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 = " << io::latex::to_string(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()) { @@ -188,7 +191,8 @@ struct ExpandVisitor { if (Logger::instance().expand) std::wcout << "in expand_sum: after expand_product(" << (this_term_expanded ? "true)" : "false)") - << " result = " << io::latex::to_string(result ? result : expr) + << " result = " + << io::latex::to_string(result ? result : expr) << std::endl; } // if summand is a Sum, flatten it @@ -202,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 = " - << io::latex::to_string(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]); @@ -311,16 +316,17 @@ struct RapidSimplifyVisitor { void operator()(ExprPtr& expr) { if (Logger::instance().simplify) - std::wcout << "rapid_simplify_visitor received " << io::latex::to_string(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 " << io::latex::to_string(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 = " << io::latex::to_string(expr) << std::endl; + std::wcout << "simplification result = " << io::latex::to_string(expr) + << std::endl; } /// simplifies a Product by: diff --git a/SeQuant/core/expressions/product.hpp b/SeQuant/core/expressions/product.hpp index 101f85e399..7b52de1648 100644 --- a/SeQuant/core/expressions/product.hpp +++ b/SeQuant/core/expressions/product.hpp @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/SeQuant/core/io/latex/latex.cpp b/SeQuant/core/io/latex/latex.cpp index 4fc563da38..3f061c7c5b 100644 --- a/SeQuant/core/io/latex/latex.cpp +++ b/SeQuant/core/io/latex/latex.cpp @@ -4,12 +4,40 @@ #include #include +#include #include + #include #include #include -namespace sequant::io::latex::detail { +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_string_impl( @@ -251,4 +279,5 @@ SQ_IMPL2(char8_t); SQ_IMPL2(char16_t); SQ_IMPL2(char32_t); -} // namespace sequant::io::latex::detail +} // namespace detail +} // namespace sequant::io::latex diff --git a/SeQuant/core/io/latex/latex.hpp b/SeQuant/core/io/latex/latex.hpp index dfa8f212e9..4a90ee2525 100644 --- a/SeQuant/core/io/latex/latex.hpp +++ b/SeQuant/core/io/latex/latex.hpp @@ -6,6 +6,7 @@ #define SEQUANT_CORE_IO_LATEX_LATEX_HPP #include +#include #include #include @@ -18,11 +19,29 @@ namespace sequant::io::latex { template -std::enable_if_t>, std::wstring> -to_string(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(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>, @@ -36,7 +55,8 @@ to_string(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_string(T&& t) { using Real = std::decay_t; @@ -48,7 +68,7 @@ to_string(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 @@ -75,6 +95,8 @@ std::wstring to_string(const std::complex& t) { return result; } +std::wstring to_string(const rational& num); + namespace detail { template diff --git a/SeQuant/core/op.hpp b/SeQuant/core/op.hpp index 0ffe6bc7a4..13169b71e8 100644 --- a/SeQuant/core/op.hpp +++ b/SeQuant/core/op.hpp @@ -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/rational.hpp b/SeQuant/core/rational.hpp index 4b45e86897..567d93d591 100644 --- a/SeQuant/core/rational.hpp +++ b/SeQuant/core/rational.hpp @@ -134,38 +134,6 @@ inline std::wstring to_wstring( return toUtf16(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; -} - } // namespace sequant #endif // SEQUANT_CORE_RATIONAL_H 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/wick.hpp b/SeQuant/core/wick.hpp index ba8ea25d20..176d8a647e 100644 --- a/SeQuant/core/wick.hpp +++ b/SeQuant/core/wick.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -162,7 +163,7 @@ class WickTheorem { std::wstringstream ss; ss << L"WickTheorem::set_external_indices: " L"external index " + - to_latex(Index(v)) + L" repeated"; + io::latex::to_string(Index(v)) + L" repeated"; throw std::invalid_argument(toUtf8(ss.str())); } }); @@ -1355,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 @@ -1393,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) { @@ -1431,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 " << @@ -1509,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/domain/mbpt/op.cpp b/SeQuant/domain/mbpt/op.cpp index caa9ce008d..62597d1d7f 100644 --- a/SeQuant/domain/mbpt/op.cpp +++ b/SeQuant/domain/mbpt/op.cpp @@ -1387,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/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/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/tests/unit/test_expr.cpp b/tests/unit/test_expr.cpp index dc9c4c8802..9e34ea94e1 100644 --- a/tests/unit/test_expr.cpp +++ b/tests/unit/test_expr.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -388,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; From daec09092e203b915c39af20e6a9d3115e3ae0ea Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 4 Feb 2026 14:54:26 +0100 Subject: [PATCH 19/22] Move parse functions to io & rename to serialization --- CMakeLists.txt | 16 +- SeQuant/core/eval/eval.hpp | 4 +- SeQuant/core/eval/eval_expr.cpp | 4 +- SeQuant/core/export/generation_optimizer.hpp | 1 - SeQuant/core/io/concepts.hpp | 8 + .../core/io/serialization/serialization.cpp | 52 +++ .../core/io/serialization/serialization.hpp | 133 ++++++++ .../{parse => io/serialization}/v1/ast.cpp | 6 +- .../{parse => io/serialization}/v1/ast.hpp | 42 +-- .../serialization}/v1/ast_conversions.hpp | 104 +++--- .../serialization/v1/deserialize.cpp} | 103 +++--- .../serialization}/v1/semantic_actions.hpp | 6 +- .../core/io/serialization/v1/serialize.cpp | 311 ++++++++++++++++++ SeQuant/core/io/shorthands.hpp | 34 +- SeQuant/core/parse.hpp | 116 ------- SeQuant/core/parse/parse.cpp | 49 --- SeQuant/core/parse/v1/deparse.cpp | 304 ----------------- benchmarks/canonicalize.cpp | 15 +- benchmarks/simplify.cpp | 6 +- benchmarks/spintrace.cpp | 6 +- benchmarks/tensor_block_compare.cpp | 16 +- benchmarks/wick.cpp | 12 +- tests/integration/eval/btas/scf_btas.hpp | 15 +- tests/integration/eval/ta/scf_ta.hpp | 15 +- tests/unit/catch2_sequant.hpp | 25 +- tests/unit/test_biorthogonalization.cpp | 11 +- tests/unit/test_canonicalize.cpp | 12 +- tests/unit/test_eval_btas.cpp | 13 +- tests/unit/test_eval_expr.cpp | 96 +++--- tests/unit/test_eval_node.cpp | 35 +- tests/unit/test_eval_ta.cpp | 56 ++-- tests/unit/test_export.cpp | 47 +-- tests/unit/test_expr.cpp | 21 +- tests/unit/test_fusion.cpp | 8 +- tests/unit/test_mbpt.cpp | 7 +- tests/unit/test_optimize.cpp | 16 +- tests/unit/test_parse.cpp | 271 ++++++++------- tests/unit/test_spin.cpp | 54 +-- tests/unit/test_tensor.cpp | 5 +- tests/unit/test_tensor_network.cpp | 92 +++--- tests/unit/test_utilities.cpp | 56 ++-- tests/unit/test_wick.cpp | 6 +- .../external-interface/external_interface.cpp | 14 +- utilities/tensor_network_graphs.cpp | 8 +- 44 files changed, 1181 insertions(+), 1050 deletions(-) create mode 100644 SeQuant/core/io/serialization/serialization.cpp create mode 100644 SeQuant/core/io/serialization/serialization.hpp rename SeQuant/core/{parse => io/serialization}/v1/ast.cpp (58%) rename SeQuant/core/{parse => io/serialization}/v1/ast.hpp (73%) rename SeQuant/core/{parse => io/serialization}/v1/ast_conversions.hpp (79%) rename SeQuant/core/{parse/v1/parse.cpp => io/serialization/v1/deserialize.cpp} (74%) rename SeQuant/core/{parse => io/serialization}/v1/semantic_actions.hpp (92%) create mode 100644 SeQuant/core/io/serialization/v1/serialize.cpp delete mode 100644 SeQuant/core/parse.hpp delete mode 100644 SeQuant/core/parse/parse.cpp delete mode 100644 SeQuant/core/parse/v1/deparse.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ba0cd89bca..a189206fde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,14 +323,14 @@ set(SeQuant_src SeQuant/core/optimize/fusion.cpp SeQuant/core/optimize/fusion.hpp SeQuant/core/optimize/optimize.cpp - SeQuant/core/parse.hpp - SeQuant/core/parse/parse.cpp - SeQuant/core/parse/v1/ast.cpp - SeQuant/core/parse/v1/ast.hpp - SeQuant/core/parse/v1/ast_conversions.hpp - SeQuant/core/parse/v1/deparse.cpp - SeQuant/core/parse/v1/parse.cpp - SeQuant/core/parse/v1/semantic_actions.hpp + 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 diff --git a/SeQuant/core/eval/eval.hpp b/SeQuant/core/eval/eval.hpp index e6c515c54d..91ff31d6dd 100644 --- a/SeQuant/core/eval/eval.hpp +++ b/SeQuant/core/eval/eval.hpp @@ -8,9 +8,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -355,7 +355,7 @@ ResultPtr evaluate(Node const& node, // std::string xpr; if constexpr (trace(EvalTrace)) { - xpr = toUtf8(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 ea79718e12..f098a7f366 100644 --- a/SeQuant/core/eval/eval_expr.cpp +++ b/SeQuant/core/eval/eval_expr.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -226,7 +226,7 @@ std::string EvalExpr::label() const noexcept { if (is_tensor()) return toUtf8(as_tensor().label()) + "(" + indices_annot() + ")"; else if (is_constant()) { - return toUtf8(sequant::deparse(as_constant())); + return toUtf8(io::serialization::to_string(as_constant())); } else { SEQUANT_ASSERT(is_variable()); 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/io/concepts.hpp b/SeQuant/core/io/concepts.hpp index ed71df811f..22eebc6cde 100644 --- a/SeQuant/core/io/concepts.hpp +++ b/SeQuant/core/io/concepts.hpp @@ -2,6 +2,7 @@ #define SEQUANT_CORE_IO_CONCEPTS_HPP #include +#include namespace sequant::io { @@ -9,6 +10,13 @@ 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/io/serialization/serialization.cpp b/SeQuant/core/io/serialization/serialization.cpp new file mode 100644 index 0000000000..42a6c052fe --- /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 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; \ + } + +RESOLVE_SERIALIZE_FUNC(ResultExpr) +RESOLVE_SERIALIZE_FUNC(ExprPtr) +RESOLVE_SERIALIZE_FUNC(Expr) +RESOLVE_SERIALIZE_FUNC(AbstractTensor) +RESOLVE_SERIALIZE_FUNC(Index) +RESOLVE_SERIALIZE_FUNC(NormalOperator) +RESOLVE_SERIALIZE_FUNC(NormalOperator) + +#undef SEQUANT_RESOLVE_PARSE_FUNC +#undef SEQUANT_RESOLVE_DEPARSE_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..b3509f22ab --- /dev/null +++ b/SeQuant/core/io/serialization/serialization.hpp @@ -0,0 +1,133 @@ +#ifndef SEQUANT_PARSE_HPP +#define SEQUANT_PARSE_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); + +// clang-format off +/// \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< Statistics S> \ + 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 +} + + +#undef SEQUANT_DECLARE_DESERIALIZATION_FUNC +#undef SEQUANT_DECLARE_DESERIALIZATION_FUNC_SPECIALIZATION +#undef SEQUANT_DECLARE_SERIALIZATION_FUNC + +} // namespace sequant + +#endif // SEQUANT_PARSE_HPP diff --git a/SeQuant/core/parse/v1/ast.cpp b/SeQuant/core/io/serialization/v1/ast.cpp similarity index 58% rename from SeQuant/core/parse/v1/ast.cpp rename to SeQuant/core/io/serialization/v1/ast.cpp index 2fddd02359..39431fbf3c 100644 --- a/SeQuant/core/parse/v1/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::v1::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::v1::ast +} // namespace sequant::io::serialization::v1::ast diff --git a/SeQuant/core/parse/v1/ast.hpp b/SeQuant/core/io/serialization/v1/ast.hpp similarity index 73% rename from SeQuant/core/parse/v1/ast.hpp rename to SeQuant/core/io/serialization/v1/ast.hpp index 640c2cf6e1..55d5c6124a 100644 --- a/SeQuant/core/parse/v1/ast.hpp +++ b/SeQuant/core/io/serialization/v1/ast.hpp @@ -12,12 +12,11 @@ #include #include -#include #include #include #include -namespace sequant::parse::v1::ast { +namespace sequant::io::serialization::v1::ast { struct IndexLabel : boost::spirit::x3::position_tagged { std::wstring label; @@ -132,22 +131,27 @@ struct ResultExpr : boost::spirit::x3::position_tagged { : lhs(std::move(tensor)), rhs(std::move(expr)) {} }; -} // namespace sequant::parse::v1::ast - -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::IndexLabel, label, id); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Index, label, protoLabels); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Number, numerator, - denominator); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Variable, name, conjugated); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::IndexGroups, bra, ket, - auxiliaries, reverse_bra_ket); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::SymmetrySpec, perm_symm, - braket_symm, column_symm); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Tensor, name, indices, - symmetry); - -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Product, factors); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::Sum, summands); -BOOST_FUSION_ADAPT_STRUCT(sequant::parse::v1::ast::ResultExpr, lhs, rhs); +} // 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/v1/ast_conversions.hpp b/SeQuant/core/io/serialization/v1/ast_conversions.hpp similarity index 79% rename from SeQuant/core/parse/v1/ast_conversions.hpp rename to SeQuant/core/io/serialization/v1/ast_conversions.hpp index 03b07cdf91..02e23bb99d 100644 --- a/SeQuant/core/parse/v1/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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::ast::IndexGroups &groups, braIndices.reserve(bra->size()); ketIndices.reserve(ket->size()); - for (const parse::v1::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::v1::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::v1::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::v1::ast::IndexGroups &groups, template Symmetry to_perm_symmetry(char c, std::size_t offset, const Iterator &, Symmetry default_symmetry) { - if (c == parse::v1::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::v1::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::v1::ast::SymmetrySpec::unspecified) { + if (c == io::serialization::v1::ast::SymmetrySpec::unspecified) { return default_symmetry; } @@ -176,13 +179,13 @@ 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::v1::ast::Number &number, +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) { @@ -198,7 +201,7 @@ Constant to_constant(const parse::v1::ast::Number &number, 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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::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::v1::ast::Product &product, Constant prefactor(1); // We perform constant folding - for (const parse::v1::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::v1::ast::Product &product, } template -ExprPtr ast_to_expr(const parse::v1::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::v1::ast::Sum &sum, summands.reserve(sum.summands.size()); std::transform( sum.summands.begin(), sum.summands.end(), std::back_inserter(summands), - [&](const parse::v1::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::v1::ast::Sum &sum, } template -ResultExpr ast_to_result(const parse::v1::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::v1::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::v1::transform +} // namespace sequant::io::serialization::v1::transform #endif // SEQUANT_CORE_PARSE_AST_CONVERSIONS_HPP diff --git a/SeQuant/core/parse/v1/parse.cpp b/SeQuant/core/io/serialization/v1/deserialize.cpp similarity index 74% rename from SeQuant/core/parse/v1/parse.cpp rename to SeQuant/core/io/serialization/v1/deserialize.cpp index 4fd703f127..9b55ae7512 100644 --- a/SeQuant/core/parse/v1/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,20 +21,15 @@ #include #include #include -#include #include -#include #include -#include #include -#include #include #include -#include #include #include -namespace sequant::parse::v1 { +namespace sequant::io::serialization::v1 { namespace x3 = boost::spirit::x3; @@ -181,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; @@ -191,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 { @@ -217,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) { @@ -238,7 +231,10 @@ AST do_parse(const StartRule &start, std::wstring_view input, return ast; } -transform::DefaultSymmetries to_default_symms(const ParseOptions &options) { +} // namespace parse + +transform::DefaultSymmetries to_default_symms( + const DeserializationOptions &options) { const Context &ctx = get_default_context(); transform::DefaultSymmetries symms{Symmetry::Nonsymm, ctx.braket_symmetry(), @@ -261,34 +257,35 @@ transform::DefaultSymmetries to_default_symms(const ParseOptions &options) { return symms; } -ResultExpr parse_result_expr(std::wstring_view input, - const ParseOptions &options) { - using iterator_type = decltype(input)::iterator; - x3::position_cache> positions(input.begin(), - input.end()); - auto ast = do_parse(parse::resultExpr, input, positions); - - return transform::ast_to_result(ast, positions, input.begin(), - to_default_symms(options)); -} - -ExprPtr parse_expr(std::wstring_view input, const ParseOptions &options) { - 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 transform::ast_to_expr(ast, positions, input.begin(), - to_default_symms(options)); -} +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, const ParseOptions &options) { - return v1::parse_expr(toUtf16(input), options); +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, - const ParseOptions &options) { - return v1::parse_result_expr(toUtf16(input), options); +template <> +ExprPtr from_string(std::string_view input, + const DeserializationOptions &options) { + return v1::from_string(toUtf16(input), options); } -} // namespace sequant::parse::v1 +} // namespace sequant::io::serialization::v1 diff --git a/SeQuant/core/parse/v1/semantic_actions.hpp b/SeQuant/core/io/serialization/v1/semantic_actions.hpp similarity index 92% rename from SeQuant/core/parse/v1/semantic_actions.hpp rename to SeQuant/core/io/serialization/v1/semantic_actions.hpp index e627a00076..26dab219a7 100644 --- a/SeQuant/core/parse/v1/semantic_actions.hpp +++ b/SeQuant/core/io/serialization/v1/semantic_actions.hpp @@ -5,7 +5,7 @@ #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::v1::actions { +namespace sequant::io::serialization::v1::actions { namespace x3 = boost::spirit::x3; @@ -70,6 +70,6 @@ struct process_addend { } }; -} // namespace sequant::parse::v1::actions +} // namespace sequant::io::serialization::v1::actions #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..1cbc24acb4 --- /dev/null +++ b/SeQuant/core/io/serialization/v1/serialize.cpp @@ -0,0 +1,311 @@ +#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 = 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 serialized; + if (realNumerator != 0) { + serialized += realNumerator.str(); + + if (realDenominator != 1) { + serialized += "/" + realDenominator.str(); + } + } + if (imagNumerator != 0) { + if (!serialized.empty()) { + serialized += imagNumerator < 0 ? " + i " : " - 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 index 846e627502..8afbf41680 100644 --- a/SeQuant/core/io/shorthands.hpp +++ b/SeQuant/core/io/shorthands.hpp @@ -1,13 +1,16 @@ #ifndef SEQUANT_CORE_IO_SHORTHANDS_HPP #define SEQUANT_CORE_IO_SHORTHANDS_HPP -#include -#include - /// @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 + namespace sequant { /// Shorthand for io::latex::to_string @@ -17,6 +20,31 @@ 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/parse.hpp b/SeQuant/core/parse.hpp deleted file mode 100644 index bdb92b0bd6..0000000000 --- a/SeQuant/core/parse.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef SEQUANT_PARSE_HPP -#define SEQUANT_PARSE_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include - -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); -}; - -/// 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 parse sense. That is older inputs will -/// continue to work as before. However, representations generated via -/// deparse(…) may not necessarily be parsable 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 ParseSyntax { - V1, - - Latest = V1 -}; - -struct ParseOptions { - /// 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 - ParseSyntax syntax = ParseSyntax::Latest; -}; - -struct DeparseOptions { - /// Whether to explicitly annotate tensor symmetries - bool annot_symm = true; - /// The syntax version of the produced output - ParseSyntax syntax = ParseSyntax::Latest; -}; - -#define SEQUANT_DECLARE_PARSE_FUNC(name, returnType) \ - returnType name(std::wstring_view input, const ParseOptions &options = {}); \ - returnType name(std::string_view input, const ParseOptions &options = {}); - -// clang-format off -/// \brief Construct expressions from string representations -/// -/// \param input The input to parse -/// \param options Customization options -/// \return SeQuant expression. -SEQUANT_DECLARE_PARSE_FUNC(parse_expr, ExprPtr); - -/// \sa parse_expr -SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); - - -#define SEQUANT_DECLARE_DEPARSE_FUNC(name) \ - std::wstring name(const ResultExpr &expr, const DeparseOptions &options = {}); \ - std::wstring name(const ExprPtr &expr, const DeparseOptions &options = {}); \ - std::wstring name(const Expr &expr, const DeparseOptions &options = {}); \ - std::wstring name(const AbstractTensor &expr, const DeparseOptions &options = {}); \ - std::wstring name(const Index &index, const DeparseOptions &options = {}); \ - template< Statistics S> \ - std::wstring name(const NormalOperator &nop, const DeparseOptions &options = {}); \ - -/// -/// 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 options Customization options -/// \return wstring of the expression. -SEQUANT_DECLARE_DEPARSE_FUNC(deparse) - - - -// Namespaced variants -namespace parse::v1 { - SEQUANT_DECLARE_PARSE_FUNC(parse_expr, ExprPtr); - SEQUANT_DECLARE_PARSE_FUNC(parse_result_expr, ResultExpr); - - SEQUANT_DECLARE_DEPARSE_FUNC(deparse) -} - - -#undef SEQUANT_DECLARE_PARSE_FUNC -#undef SEQUANT_DECLARE_DEPARSE_FUNC - -} // namespace sequant - -#endif // SEQUANT_PARSE_HPP diff --git a/SeQuant/core/parse/parse.cpp b/SeQuant/core/parse/parse.cpp deleted file mode 100644 index f469bbfd68..0000000000 --- a/SeQuant/core/parse/parse.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#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) {} - -#define SEQUANT_RESOLVE_PARSE_FUNC(name, stringType, returnType) \ - returnType name(stringType input, const ParseOptions &options) { \ - switch (options.syntax) { \ - case ParseSyntax::V1: \ - return parse::v1::name(input, options); \ - } \ - \ - SEQUANT_UNREACHABLE; \ - } - -SEQUANT_RESOLVE_PARSE_FUNC(parse_expr, std::wstring_view, ExprPtr) -SEQUANT_RESOLVE_PARSE_FUNC(parse_expr, std::string_view, ExprPtr) -SEQUANT_RESOLVE_PARSE_FUNC(parse_result_expr, std::wstring_view, ResultExpr) -SEQUANT_RESOLVE_PARSE_FUNC(parse_result_expr, std::string_view, ResultExpr) - -#define RESOLVE_DEPARSE_FUNC(argType) \ - std::wstring deparse(const argType &arg, const DeparseOptions &options) { \ - switch (options.syntax) { \ - case ParseSyntax::V1: \ - return parse::v1::deparse(arg, options); \ - } \ - \ - SEQUANT_UNREACHABLE; \ - } - -RESOLVE_DEPARSE_FUNC(ResultExpr) -RESOLVE_DEPARSE_FUNC(ExprPtr) -RESOLVE_DEPARSE_FUNC(Expr) -RESOLVE_DEPARSE_FUNC(AbstractTensor) -RESOLVE_DEPARSE_FUNC(Index) -RESOLVE_DEPARSE_FUNC(NormalOperator) -RESOLVE_DEPARSE_FUNC(NormalOperator) - -#undef SEQUANT_RESOLVE_PARSE_FUNC -#undef SEQUANT_RESOLVE_DEPARSE_FUNC - -} // namespace sequant diff --git a/SeQuant/core/parse/v1/deparse.cpp b/SeQuant/core/parse/v1/deparse.cpp deleted file mode 100644 index e1ce93c663..0000000000 --- a/SeQuant/core/parse/v1/deparse.cpp +++ /dev/null @@ -1,304 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace sequant::parse::v1 { - -namespace details { - -template -std::wstring deparse_indices(Range&& indices, const DeparseOptions& options) { - std::wstring deparsed; - - for (std::size_t i = 0; i < indices.size(); ++i) { - deparsed += v1::deparse(indices[i], options); - - if (i + 1 < indices.size()) { - deparsed += L","; - } - } - - return deparsed; -} - -template -std::wstring deparse_ops(const Range& ops, const DeparseOptions& options) { - std::wstring deparsed; - - for (std::size_t i = 0; i < ops.size(); ++i) { - deparsed += v1::deparse(ops[i].index(), options); - - if (i + 1 < ops.size()) { - deparsed += L","; - } - } - - return deparsed; -} - -std::wstring deparse_symm(Symmetry symm, const DeparseOptions&) { - 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, const DeparseOptions&) { - 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, const DeparseOptions&) { - 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, - const DeparseOptions&) { - 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 toUtf16(deparsed); -} - -std::wstring deparse(Tensor const& tensor, const DeparseOptions& options) { - return deparse(static_cast(tensor), options); -} - -std::wstring deparse(const Constant& constant, const DeparseOptions& options) { - return details::deparse_scalar(constant.value(), options); -} - -std::wstring deparse(const Variable& variable, const DeparseOptions&) { - return std::wstring(variable.label()) + (variable.conjugated() ? L"^*" : L""); -} - -std::wstring deparse(Product const& prod, const DeparseOptions& options) { - std::wstring deparsed; - - const auto& scal = prod.scalar(); - if (scal != Product::scalar_type{1}) { - deparsed += details::deparse_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; - deparsed += L"("; - } - - deparsed += deparse(current, options); - - if (parenthesize) { - deparsed += L")"; - } - - if (i + 1 < prod.size()) { - deparsed += L" * "; - } - } - - return deparsed; -} - -std::wstring deparse(Sum const& sum, const DeparseOptions& options) { - 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, options); - - 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 details - -std::wstring deparse(const ExprPtr& expr, const DeparseOptions& options) { - if (!expr) return {}; - - return v1::deparse(*expr, options); -} - -std::wstring deparse(const Expr& expr, const DeparseOptions& options) { - using namespace details; - if (expr.is()) - return details::deparse(expr.as(), options); - else if (expr.is()) - return v1::deparse(expr.as(), options); - else if (expr.is()) - return v1::deparse(expr.as(), options); - else if (expr.is()) - return details::deparse(expr.as(), options); - else if (expr.is()) - return details::deparse(expr.as(), options); - else if (expr.is()) - return details::deparse(expr.as(), options); - else if (expr.is()) - return details::deparse(expr.as(), options); - else - throw std::runtime_error("Unsupported expr type for deparse!"); -} - -std::wstring deparse(const ResultExpr& result, const DeparseOptions& options) { - std::wstring deparsed; - if (result.produces_tensor()) { - deparsed = details::deparse(result.result_as_tensor(L"?"), options); - } else { - deparsed = details::deparse(result.result_as_variable(L"?"), options); - } - - return deparsed + L" = " + v1::deparse(result.expression(), options); -} - -std::wstring deparse(const Index& index, const DeparseOptions&) { - 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(AbstractTensor const& tensor, - const DeparseOptions& options) { - std::wstring deparsed(tensor._label()); - deparsed += L"{" + details::deparse_indices(tensor._bra(), options); - if (tensor._ket_rank() > 0) { - deparsed += L";" + details::deparse_indices(tensor._ket(), options); - } - if (tensor._aux_rank() > 0) { - if (tensor._ket_rank() == 0) { - deparsed += L";"; - } - deparsed += L";" + details::deparse_indices(tensor._aux(), options); - } - deparsed += L"}"; - - if (options.annot_symm) { - deparsed += L":" + details::deparse_symm(tensor._symmetry(), options); - deparsed += - L"-" + details::deparse_symm(tensor._braket_symmetry(), options); - deparsed += - L"-" + details::deparse_symm(tensor._column_symmetry(), options); - } - - return deparsed; -} - -template -std::wstring deparse(NormalOperator const& nop, - const DeparseOptions& options) { - std::wstring deparsed(nop.label()); - deparsed += L"{" + details::deparse_ops(nop.annihilators(), options); - if (nop.ncreators() > 0) { - deparsed += L";" + details::deparse_ops(nop.creators(), options); - } - deparsed += L"}"; - - return deparsed; -} - -template std::wstring deparse( - NormalOperator const& nop, - const DeparseOptions& options); -template std::wstring deparse( - NormalOperator const& nop, - const DeparseOptions& options); - -} // namespace sequant::parse::v1 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/tests/integration/eval/btas/scf_btas.hpp b/tests/integration/eval/btas/scf_btas.hpp index ffba5ac799..7d44bb65bc 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 @@ -37,15 +37,16 @@ class SequantEvalScfBTAS final : public SequantEvalScf { Tensor_t const& f_vo() const { static Tensor_t tnsr = data_world_( - parse_expr(L"f{a1;i1}", {.def_perm_symm = Symmetry::Nonsymm}) + 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}", {.def_perm_symm = Symmetry::Nonsymm}) - ->as()); + static Tensor_t tnsr = + data_world_(deserialize(L"g{a1,a2;i1,i2}", + {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } @@ -53,8 +54,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, {.def_perm_symm = 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 491d142a98..cce6630e18 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 @@ -34,15 +34,16 @@ class SequantEvalScfTA final : public SequantEvalScf { Tensor_t const& f_vo() const { static Tensor_t tnsr = data_world_( - parse_expr(L"f{a1;i1}", {.def_perm_symm = Symmetry::Nonsymm}) + 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}", {.def_perm_symm = Symmetry::Nonsymm}) - ->as()); + static Tensor_t tnsr = + data_world_(deserialize(L"g{a1,a2;i1,i2}", + {.def_perm_symm = Symmetry::Nonsymm}) + ->as()); return tnsr; } @@ -50,8 +51,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, {.def_perm_symm = 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/catch2_sequant.hpp b/tests/unit/catch2_sequant.hpp index 50f4a67f4d..73d8c45b41 100644 --- a/tests/unit/catch2_sequant.hpp +++ b/tests/unit/catch2_sequant.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -35,10 +34,10 @@ struct StringMaker { bool include_canonical = true) { std::string str; try { - str = sequant::toUtf8(sequant::deparse(expr, {.annot_symm = 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 + // serialize doesn't support all kinds of expressions -> fall back to + // LaTeX representation str = sequant::toUtf8(sequant::to_latex(expr)); } @@ -82,7 +81,7 @@ struct StringMaker { static std::string convert(const sequant::ResultExpr &res, bool include_canonical = true) { std::string str = - sequant::toUtf8(sequant::deparse(res, {.annot_symm = true})); + sequant::toUtf8(sequant::serialize(res, {.annot_symm = true})); if (include_canonical) { sequant::ResultExpr clone = res.clone(); @@ -178,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) { @@ -189,22 +188,24 @@ ExprVar to_expression(T &&expression) { std::wstring string = sequant::toUtf16(std::forward(expression)); if (std::find(begin(string), end(string), L'=') != end(string)) { - return sequant::parse_result_expr( + return sequant::deserialize( std::string(std::forward(expression)), {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { - return sequant::parse_expr(std::string(std::forward(expression)), - {.def_perm_symm = 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)), {.def_perm_symm = sequant::Symmetry::Nonsymm}); } else { - return sequant::parse_expr(std::wstring(std::forward(expression)), - {.def_perm_symm = 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..26e756c39e 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 84b9cb2053..c7f14e1911 100644 --- a/tests/unit/test_canonicalize.cpp +++ b/tests/unit/test_canonicalize.cpp @@ -138,12 +138,12 @@ TEST_CASE("canonicalization", "[algorithms]") { // the context of a sum external index labels are meaningful and should be // 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"); - 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"); + auto input1 = 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 = 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 0621c412da..698d50ace6 100644 --- a/tests/unit/test_eval_btas.cpp +++ b/tests/unit/test_eval_btas.cpp @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include @@ -32,14 +32,14 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { }; auto const tnsr_deparsed = - sequant::deparse(tnsr.clone(), {.annot_symm = false}); + 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, {.def_perm_symm = sequant::Symmetry::Nonsymm}) - ->as()); + return tensor_to_key(sequant::deserialize( + spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) + ->as()); } template @@ -222,7 +222,8 @@ TEST_CASE("eval_with_btas", "[eval_btas]") { }; auto parse_antisymm = [](auto const& xpr) { - return parse_expr(xpr, {.def_perm_symm = 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 70dcf49b77..63c8ce034a 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 #include @@ -22,12 +22,14 @@ #include namespace sequant { -Tensor parse_tensor(std::wstring_view tnsr, const ParseOptions& options = {}) { - return parse_expr(tnsr, options)->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) { @@ -51,7 +53,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()}; @@ -68,12 +70,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()); @@ -130,24 +132,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() == @@ -157,7 +161,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() == @@ -166,12 +171,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", @@ -182,9 +187,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()}; @@ -216,7 +221,8 @@ 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()); } @@ -241,8 +247,8 @@ 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()); } @@ -261,12 +267,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { REQUIRE(x12.expr()->as().symmetry() == Symmetry::Nonsymm); // whole bra <-> ket contraction between two symmetric tensors - const auto t3 = - parse_expr(L"g_{i3,i4}^{i1,i2}", {.def_perm_symm = Symmetry::Symm}) - ->as(); - const auto t4 = - parse_expr(L"t_{a1,a2}^{i3,i4}", {.def_perm_symm = Symmetry::Symm}) - ->as(); + const auto t3 = deserialize(L"g_{i3,i4}^{i1,i2}", + {.def_perm_symm = Symmetry::Symm}) + ->as(); + const auto t4 = deserialize(L"t_{a1,a2}^{i3,i4}", + {.def_perm_symm = Symmetry::Symm}) + ->as(); const auto x34 = result_expr(EvalExpr{t3}, EvalExpr{t4}, EvalOp::Product); @@ -275,12 +281,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}", {.def_perm_symm = Symmetry::Nonsymm}) - ->as(); - const auto t6 = - parse_expr(L"f_{i2}^{a2}", {.def_perm_symm = 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); @@ -298,12 +304,12 @@ TEST_CASE("eval_expr", "[EvalExpr]") { 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}", {.def_perm_symm = Symmetry::Antisymm}) - ->as(); - auto const t10 = - parse_expr(L"t_{a3,a4}^{i1,i2}", {.def_perm_symm = Symmetry::Symm}) - ->as(); + auto const t9 = deserialize(L"g_{a1,a2}^{a3,a4}", + {.def_perm_symm = Symmetry::Antisymm}) + ->as(); + auto const t10 = 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); @@ -313,7 +319,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) { @@ -354,12 +360,14 @@ TEST_CASE("eval_expr", "[EvalExpr]") { #endif SECTION("Debug") { - auto t1 = EvalExpr{parse_expr(L"O{a_1;a_1}", - {.def_perm_symm = Symmetry::Nonsymm}) - ->as()}; - auto t2 = EvalExpr{parse_expr(L"O{a_2;a_2}", - {.def_perm_symm = 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 f96b77c62e..0e7c65e198 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 #include @@ -57,7 +57,7 @@ TEST_CASE("eval_node", "[EvalNode]") { auto R = Npos::R; auto parse_expr_antisymm = [](auto const& xpr) { - return parse_expr(xpr, {.def_perm_symm = Symmetry::Antisymm}); + return deserialize(xpr, {.def_perm_symm = Symmetry::Antisymm}); }; SECTION("terminals") { @@ -68,14 +68,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"); @@ -169,7 +169,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); @@ -182,7 +182,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); @@ -195,7 +195,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}")); @@ -285,48 +285,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} @@ -338,7 +338,8 @@ 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 b0bf873a4d..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 @@ -87,7 +87,7 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { NestedTensorIndices oixs{tnsr}; if (oixs.inner.empty()) { auto const tnsr_deparsed = - sequant::deparse(tnsr.clone(), {.annot_symm = false}); + sequant::serialize(tnsr.clone(), {.annot_symm = false}); return boost::regex_replace(tnsr_deparsed, idx_rgx, formatter); } else { using ranges::views::intersperse; @@ -112,9 +112,9 @@ auto tensor_to_key(sequant::Tensor const& tnsr) { } auto tensor_to_key(std::wstring_view spec) { - return tensor_to_key( - sequant::parse_expr(spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) - ->as()); + return tensor_to_key(sequant::deserialize( + spec, {.def_perm_symm = sequant::Symmetry::Nonsymm}) + ->as()); } template @@ -288,7 +288,7 @@ TEST_CASE("eval_with_tiledarray", "[eval]") { using TA::TArrayD; auto parse_antisymm = [](auto const& xpr) { - return sequant::parse_expr( + return sequant::deserialize( xpr, {.def_perm_symm = sequant::Symmetry::Antisymm}); }; @@ -382,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{}; @@ -416,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"); @@ -649,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"); @@ -660,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"); @@ -673,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{}; @@ -683,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"); @@ -696,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{}; @@ -714,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}"); @@ -732,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"); @@ -747,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}"); @@ -766,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}"); @@ -781,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{}; @@ -795,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}"); @@ -805,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}"); @@ -820,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}"); @@ -875,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; @@ -896,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(); @@ -917,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 496a1e1533..cc68e856b4 100644 --- a/tests/unit/test_export.cpp +++ b/tests/unit/test_export.cpp @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -251,16 +251,16 @@ std::vector> parse_expression_spec(const std::string &spec) { } try { - ResultExpr res = - parse_result_expr(line, {.def_perm_symm = Symmetry::Nonsymm, - .def_braket_symm = BraKetSymmetry::Nonsymm, - .def_col_symm = 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(line, {.def_perm_symm = Symmetry::Nonsymm, - .def_braket_symm = BraKetSymmetry::Nonsymm, - .def_col_symm = ColumnSymmetry::Nonsymm}); + ExprPtr expr = deserialize( + line, {.def_perm_symm = Symmetry::Nonsymm, + .def_braket_symm = BraKetSymmetry::Nonsymm, + .def_col_symm = ColumnSymmetry::Nonsymm}); groups.back().add(to_export_tree(expr)); } } @@ -430,11 +430,11 @@ TEST_CASE("export", "[export]") { const std::string &expected = candidates.at(static_cast(layout)); - Tensor tensor = - parse_expr(input, {.def_perm_symm = Symmetry::Nonsymm, - .def_braket_symm = BraKetSymmetry::Nonsymm, - .def_col_symm = ColumnSymmetry::Nonsymm}) - ->as(); + Tensor tensor = 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)); REQUIRE(rewritten == (toUtf8(input) != expected)); @@ -452,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(), @@ -468,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(), @@ -486,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); @@ -525,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); @@ -553,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); @@ -591,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} ")), @@ -656,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;} " @@ -676,12 +676,13 @@ 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 9e34ea94e1..65767d82ea 100644 --- a/tests/unit/test_expr.cpp +++ b/tests/unit/test_expr.cpp @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include #include #include @@ -852,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 b7259ccb95..fb548191b4 100644 --- a/tests/unit/test_fusion.cpp +++ b/tests/unit/test_fusion.cpp @@ -3,8 +3,8 @@ #include "catch2_sequant.hpp" #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_mbpt.cpp b/tests/unit/test_mbpt.cpp index a7cb63a0ba..85f9c2a440 100644 --- a/tests/unit/test_mbpt.cpp +++ b/tests/unit/test_mbpt.cpp @@ -8,7 +8,6 @@ #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 9551dd721b..e0e407d094 100644 --- a/tests/unit/test_optimize.cpp +++ b/tests/unit/test_optimize.cpp @@ -8,9 +8,9 @@ #include #include #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, {.def_perm_symm = 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,13 +232,13 @@ TEST_CASE("optimize", "[optimize]") { std::vector expected; for (const std::wstring& current : inputs) { - expressions.push_back(binarize(parse_result_expr( + 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( + expected.push_back(deserialize( current, {.def_perm_symm = Symmetry::Nonsymm, .def_braket_symm = BraKetSymmetry::Nonsymm, .def_col_symm = ColumnSymmetry::Nonsymm})); diff --git a/tests/unit/test_parse.cpp b/tests/unit/test_parse.cpp index 31fcad2482..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 = { @@ -477,9 +506,9 @@ TEST_CASE("parsing", "[parse]") { 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, {.annot_symm = true}) == current); + REQUIRE(serialize(expression, {.annot_symm = true}) == current); } SECTION("result_expressions") { @@ -492,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, {.annot_symm = true}) == current); + REQUIRE(serialize(result, {.annot_symm = true}) == current); } } } diff --git a/tests/unit/test_spin.cpp b/tests/unit/test_spin.cpp index 32ac8cb0e5..3f340f953f 100644 --- a/tests/unit/test_spin.cpp +++ b/tests/unit/test_spin.cpp @@ -11,7 +11,6 @@ #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,8 @@ 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 +333,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( @@ -768,17 +768,17 @@ 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}", - {.def_perm_symm = Symmetry::Antisymm}); + 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}", - {.def_perm_symm = 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, @@ -786,7 +786,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"); @@ -802,7 +802,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"); @@ -1018,8 +1018,9 @@ 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}", - {.def_perm_symm = 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}")); @@ -1028,7 +1029,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"ω")); @@ -1037,7 +1038,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")); @@ -1046,8 +1047,9 @@ 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}", - {.def_perm_symm = 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, @@ -1164,9 +1166,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}", - {.def_perm_symm = 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( @@ -1177,10 +1179,10 @@ 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}", - {.def_perm_symm = Symmetry::Antisymm})}); + const auto input = ex(ExprPtrList{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); @@ -1705,11 +1707,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_tensor.cpp b/tests/unit/test_tensor.cpp index ad6ee05b39..642bb1900e 100644 --- a/tests/unit/test_tensor.cpp +++ b/tests/unit/test_tensor.cpp @@ -291,7 +291,8 @@ 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 +308,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 b4b73bbb27..f2abe3cf8e 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]), {.annot_symm = true}) == - deparse(*t2_i, {.annot_symm = 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,8 @@ 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 +1084,10 @@ 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] = @@ -1140,7 +1143,8 @@ 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 +1162,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,7 +1186,7 @@ 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}", {.def_perm_symm = Symmetry::Antisymm}) .as(); @@ -1261,8 +1265,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,10 +1299,10 @@ 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(toUtf8(sstream.str())); } @@ -1321,8 +1325,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 +1687,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 +1707,8 @@ 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 +1735,10 @@ 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] = @@ -1788,7 +1795,8 @@ 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 +1815,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,7 +1889,7 @@ 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}", {.def_perm_symm = Symmetry::Antisymm}) .as(); @@ -1960,8 +1968,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,10 +2002,10 @@ 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(toUtf8(sstream.str())); } @@ -2020,8 +2028,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 48b6983584..37e55e817d 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,8 @@ 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 +164,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 +173,8 @@ 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); @@ -284,8 +286,8 @@ 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}"); + ExprPtr expr = 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 +300,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 +332,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 +363,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 +386,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 +416,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 +450,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 +479,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 +524,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 +570,7 @@ TEST_CASE("utilities", "[utilities]") { }) { CAPTURE(toUtf8(input)); - ExprPtr expr = parse_expr(input); + ExprPtr expr = deserialize(input); auto actual = external_indices>(expr); diff --git a/tests/unit/test_wick.cpp b/tests/unit/test_wick.cpp index c342c0dbd1..54383c000c 100644 --- a/tests/unit/test_wick.cpp +++ b/tests/unit/test_wick.cpp @@ -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 8c268ea1af..9d1c82ad07 100644 --- a/utilities/external-interface/external_interface.cpp +++ b/utilities/external-interface/external_interface.cpp @@ -10,8 +10,8 @@ #include #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), {.def_perm_symm = 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())); @@ -228,11 +228,11 @@ 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()), - {.def_perm_symm = Symmetry::Antisymm}); + deserialize(sub.at("target").get(), + {.def_perm_symm = Symmetry::Antisymm}); ExprPtr replacement = - parse_expr(toUtf16(sub.at("replacement").get()), - {.def_perm_symm = 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; } From 896c5246c7b39e4e58ea353492ea60a9866d72e4 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 5 Feb 2026 13:20:57 +0100 Subject: [PATCH 20/22] Update docs --- doc/user/guide/io.rst | 117 +++++++++++++++++++++++++++++++++++++++ doc/user/guide/parse.rst | 84 ---------------------------- 2 files changed, 117 insertions(+), 84 deletions(-) create mode 100644 doc/user/guide/io.rst delete mode 100644 doc/user/guide/parse.rst diff --git a/doc/user/guide/io.rst b/doc/user/guide/io.rst new file mode 100644 index 0000000000..f35c561f45 --- /dev/null +++ b/doc/user/guide/io.rst @@ -0,0 +1,117 @@ +*** +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:`parse_expr` will start at rule :code:`Expression`, whereas :func:`parse_result_expr` 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/doc/user/guide/parse.rst b/doc/user/guide/parse.rst deleted file mode 100644 index 89cd7854b1..0000000000 --- a/doc/user/guide/parse.rst +++ /dev/null @@ -1,84 +0,0 @@ -Parsing and Deparsing -===================== - -SeQuant supports creating expression objects by parsing an appropriately formatted input string. This process is referred to as *parsing*. -Furthermore, SeQuant can also serialize expression objects into this string representation, which is referred to as *deparsing*. - -The functions for performing these tasks are :func:`parse_expr(…)`, :func:`parse_result_expr(…)` and :func:`deparse(…)` respectively. The former two -differ in that one of them produces a :class:`ExprPtr` and the other a :class:`ResultExpr` object as their output. This implies that the parsed text -in the latter case is supposed to contain a result specification (see below). - -As SeQuant's capabilities develop over time, it can be necessary to adapt the syntax of this text representation, referred to as the *parse syntax*. -By default, the abovementioned functions will always expect and produce text according to the latest parse syntax specification. However, they can be -instructed to work with a different version by explicitly specifying a :class:`SyntaxVersion` when calling them or using one of the versioned function -calls. - -.. warning:: - All syntax versions except :class:`SyntaxVersion::Latest` are considered deprecated. Support for them will remain available for some time but might - get removed in future versions of SeQuant. - -Generally speaking, parse and deparse are inverse operations. - - -Customizations --------------- - -The parse and deparse functions accept customization options of type :class:`ParseOptions` and :class:`DeparseOptions` respectively. They allow for -specification of a specific :class:`SyntaxVersion` and things like how to deal with symmetry annotations. For parsing, the latter refers to default -symmetries for objects that don't explicitly specify their symmetries and for deparsing this refers to whether or not those annotations are included -in the output. - -For an overview of all customization options, please refer to the documentation of those classes in the API reference. - - -Syntax ------- - -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:`parse_expr` will start at rule `Expression`, whereas :func:`parse_result_expr` will start at `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 - From 4a02be798f3316c4a350627687e7264fe3caf4d2 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 5 Feb 2026 13:59:31 +0100 Subject: [PATCH 21/22] Make functions cross-referenceable --- doc/user/guide/io.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/user/guide/io.rst b/doc/user/guide/io.rst index f35c561f45..260715dd93 100644 --- a/doc/user/guide/io.rst +++ b/doc/user/guide/io.rst @@ -2,11 +2,11 @@ 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. +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 + 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 @@ -35,9 +35,9 @@ SeQuant provides `serialization `_ 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 +: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. +: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. @@ -49,8 +49,8 @@ function calls. 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(…)`. +If the shorthands header is included, text-based serialization and deserialization is available as :func:`sequant::serialize` and +:func:`sequant::deserialize`. Customizations From da0c64cbf30332b15cb289a9825822d63592e580 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 5 Feb 2026 13:59:51 +0100 Subject: [PATCH 22/22] Fix remnants of old function names --- doc/user/guide/io.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/user/guide/io.rst b/doc/user/guide/io.rst index 260715dd93..bc624c6b06 100644 --- a/doc/user/guide/io.rst +++ b/doc/user/guide/io.rst @@ -105,7 +105,8 @@ V1 Name Single word (may include Unicode chars) ============== =============================================================== =========================================== -:func:`parse_expr` will start at rule :code:`Expression`, whereas :func:`parse_result_expr` will start at :code:`Result`. +: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