Advanced Usage

Indices and Equations

One of the main features of xerus is the ability to write arbitrary Tensor contraction in a Einstein-notation like form.
This allows to write very readable sourcecode that closely resembles the mathematical formulas behind the algorithms.

Indices

To write indexed equations, we first have to declare the variables that we will use as indices.

C++Python

xerus::Indexi,j,k,l;

i,j,k,l=xerus.indices(4)

Simple Equations

The most basic equations utilizing indexed expressions are reshufflings and contractions. Assuming A is a tensor of degree 2
and bof degree 1 we can write:

As python does not allow to override the = operator, we had to fall back to another one. Read the left-shift operator as assignment.

In analogy to the Einstein notation, such an expression will contract those modes on the right hand side that are indexed by the same
index and assign the result to the left hand side. If necessary it is reshuffled first to obtain the index order denoted on the
left hand side.

The left hand side (c in the above example) is not required to have the right degree or dimensions. The type of c on the
other hand does change the meaning of the equation and so has to be set correctly. E.g. if c is a xerus::TensorNetwork, no
contraction is performed as A(i,j)*b(j) is in itself a valid tensor network. See also the tutorials on TT-Tensors
and Tensor Networks for details on the respective assignments.

Unless runtime checks have explicitely been disabled during compilation of xerus (see Optimizations), invalid
indexed expressions will produce runtime errors (in the form of xerus::misc::generic_errors being thrown as exceptions).

High-Dimensional Equations

Writing high-dimensional equations in Einstein notation with individual indices can be cumbersome. What is more: in general
functions in our code we might not even know the degree of the tensors arguments before runtime. To still be able to use
indexed equations in these settings xerus uses multi-indices that can span anything between 0 and all modes of a tensor.

To denote such multi-indices, the regular indices defined as above have to be modified by operators inside the indexed expressions.

C++Python

i^d// an index that spans d modes
i&d// an index that spans all but d modes
i/n// an index that spans degree()/n modes

i^d# an index that spans d modesi**d# an index that spans d modesi&d# an index that spans all but d modesi/n# an index that spans degree()/n modes

As it might be difficult to tell how many modes the indices on the left hand side of the equation will span, it is not necessary
to declare them as multi-indices.

C++Python

// contract the last mode of A with the first of B
C(i,k)=A(i&1,j)*B(j,k&1);// C is now of degree A.degree()+B.degree()-2

# contract the last mode of A with the first of BC(i,k)<<A(i&1,j)*B(j,k&1)# C is now of degree A.degree()+B.degree()-2

The division i/n is useful for example to write equations with high dimensional operators such as TT-Operators
for which the indices are ordered per default such, that the application of the operator can be written in analogy to
matrix-vector products as:

Notice how the declaration of multi-indices with & or / are always relative to the current tensor. As such, an index (such as
j in the above example) can appear with different modifiers. The important thing is, that each occurence of every index results
in an equal number of modes spanned and that these modes are of equal dimensions (in the example the placement of j requires
that the degree of v is equal to half the degree of A and that its dimensions are equal to the second half of dimensions of A).

Blockwise Construction of Tensors

A common use for indexed expressions is to construct tensors in a blockwise fashion. In the following example we were able to
calculate the tensor comp whenever the first index was fixed, either by numerical construction (A and B) or by showing
mathematically, that it is then equal to a well known tensor (here the identity matrix). The full tensor can thus be constructed
with the help of the named constructors of xerus::Tensor (see the Tensor tutorial) as follows.