I/O

SeQuant provides different forms of I/O for its objects. Generally, they can be found in the sequant::io namespace. Different formats live in different sub-namespaces. E.g. 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 sequant/core/io/shorthands.hpp header. These shorthands live directly in the sequant namespace. The example from above could be achieved via sequant::to_latex() once that header is included.

LaTeX

The relevant function is 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 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 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 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.

Formatting and streams

Include SeQuant/core/io/format.hpp to print sequant::Expr, its subclasses, sequant::ExprPtr, and sequant::ResultExpr with std::format or stream insertion (<<). Both default to serialization and support narrow and wide characters; narrow output is UTF-8. An empty ExprPtr produces an empty string in both modes and with stream insertion, matching the LaTeX and serialization converters. Deserializing that empty string returns an empty ExprPtr.

The format specifier selects the representation: l or latex for LaTeX, and s or serialize for serialization. An empty specifier selects serialization. These are the only supported specifiers; apply string formatting to the rendered string if width, alignment, or precision is needed.

Stream insertion of sequant::Expr, sequant::ExprPtr and sequant::ResultExpr produces serialization. For explicit LaTeX, insert the result of sequant::io::latex::to_string() into a wide stream, or use std::format with l or latex for a narrow string. Wide formatting is available with wide format strings. The The Expression Tree examples demonstrate stream insertion and default formatting.

Formatting uses the existing LaTeX and serialization converters and preserves their limitations. In particular, a custom expression’s LaTeX output comes from its virtual to_latex() implementation; serialization requires support in the serialization converter. Unsupported conversions propagate their exceptions. sequant::mbpt::Operator has no serialization, so print operators and expressions containing them with l or latex. Unknown format specifiers are rejected during compile-time format-string checking, or with sequant::Exception when using runtime format strings with std::vformat. That exception does not derive from std::format_error, so a handler for std::format_error alone does not catch it.

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 sequant::io::serialization namespace. At the moment, there is only a text format implemented via to_string() and 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, from_string() is a template that takes in the type of object it is supposed to produce. The most important variants are from_string and from_string where the former also allows to deserialize any 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 SerializationSyntax when calling them or using one of the versioned function calls.

Warning

All syntax versions except 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 sequant::serialize() and sequant::deserialize.

Customizations

The exact serialization format can be customized to some extend by providing SerializationOptions and DeserializationOptions instances when calling the respective functions. Among other things, they allow for specification of a specific 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

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? )? )? ‘}’
‘^{’ IndexList? ‘}_{’ IndexList ‘}’
‘_{’ IndexList? ‘}^{’ IndexList ‘}’
Meaning is {<bra>;<ket>;<aux>}
Meaning is ^{<ket>}_{<bra>} (no aux)
Meaning is _{<bra>}^{<ket>} (no aux)

IndexList

Index ( ‘,’ Index )?

Index

BaseKey ‘_’? Integer IndexDomain?

IndexDomain

‘<’ (ProtoLabel (‘,’ ProtoLabel)*)? (‘;’ Integer)? ‘>’

‘;Integer’ (signed, 32-bit) names a non-default index basis instance; absent when BaseKey is itself the label of a registered basis instance

ProtoLabel

BaseKey ‘_’? Integer ( ‘<’ ‘;’ Integer ‘>’ )?

May carry only a domainless instance (no nested protos); same rule for a named basis

BaseKey

Letters, ⁺, ⁻, combining diacritics, arrows (e.g. ↑, ↓) and primes; the base key of a registered IndexSpace or the label of a registered basis instance (IndexBasis::base_key())

SymmetrySpec

‘:’ ( [ASN] ( ‘-’ [SCN] ( ‘-’ [SN] )? )? )

:<Symmetry>-<BraKetSymmetry>-<ColumnSymmetry>

Variable

Name

Name

Single word (may include Unicode chars)

The single-letter codes in SymmetrySpec abbreviate the corresponding enumerators, one letter per field: [ASN] is the tensor’s permutational Symmetry (A = Antisymm, S = Symm, N = Nonsymm); [SCN] is its BraKetSymmetry (S = Symm, C = Conjugate, N = Nonsymm); and the final [SN] is its ColumnSymmetry (S = Symm, N = Nonsymm — this field has no Conjugate case). A tensor’s trailing :A-C-S in the example below therefore reads “antisymmetric, conjugate bra-ket symmetric, symmetric column”.

sequant::io::serialization::from_string will start at rule Expression, whereas sequant::io::serialization::from_string will start at Result.

Examples

The following parses a residual expression R1 as a sum of four terms, the last one antisymmetric-bra-ket-symmetric-column (:A-C-S) and scaled by the constant 1/2:

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-S