QMetro++ tensors

A general strategy in QMetro++ is created by defining it as a tensor network of:

  • density matrices,

  • elements of MPOs of density matrices, in particular density matrices of MPSs (see Fig. 6),

  • parameter-encoding channels,

  • control operations,

  • quantum combs,

  • measurements,

  • elements of measurement MPOs.

connected by their physical and bond spaces, as in the diagram in Fig. 12. Some input states, control operations, and quantum combs as well as all measurements, are marked as variables of the pre-QFI function (23) and they will be optimized by the ISS algorithm. Each non-measurement node is represented using its tensor representation (see a detailed explanation in Tensor network formalism and tensor representation) and measurements are represented via a tensor representation of the pre-SLD matrix \(\mathfrak{L}\).

../_images/classes.drawio.svg

Fig. 13 UML diagram of classes used for creation of strategies with their most important attributes and methods. Subclasses inherit all attributes and methods of their parent class. Instances of GeneralTensor and its subclasses can access a SpaceDict instance through their sdict attribute. Two tensors can be contracted with each other only when they have the same sdict attribute.

In practice, this means that each node of the network has to be initialized as an object of a QMetro++ tensor class (see Fig. 13). There are five kinds of tensor classes:

  • GeneralTensor — a class containing attributes and methods common to all tensors, such as the shape or the contraction operation. It is an abstract class i.e. it is only a blueprint for other classes to inherit from and it is not meant to be used in any computations directly.

  • ConstTensor — a class representing tensors that stay constant during the optimization process.

  • ParamTensor — a subclass of ConstTensor which has an additional attribute defining its derivative over the measured parameter \(\theta\). It is meant to represent parameter-encoding channels \(\Lambda_\theta\).

  • VarTensor — a class representing tensors that are optimized by the ISS algorithm. Members of this class store information specifying how they should be initialized and optimized (for example that they are CPTP channels, quantum combs or pre-SLD matrices) but they do not have any value — they are only symbols.

  • TensorNetwork — networks of constant, parametrized and variable tensors.

Additionally, there is one more class, SpaceDict, that stores the information about the names, dimensions and types of the respective spaces. For example:

import numpy as np
from qmetro import *

sd = SpaceDict()
# Adding physical spaces with names
# 'a', 'b' and dimension 2 to sd.
sd['a'] = 2
sd['b'] = 2

# Calling sd['a'] will return
# the dimension of space 'a'.
print(sd['a'])  # Prints 2.

# Adding bond space 'x':
sd.set_bond('x', 4)
print(sd['x'])  # Prints 4.

# Accessing range of appropriate index:
print(sd.irange['a'])  # Prints 4.
print(sd.irange['x'])  # Prints 4.

Note that the range of the physical index is equal to the square of the dimension of the corresponding space. This is because physical indices are created by joining two indices of a CJ operator. However, this is not the case for bond indices which are not related to any Hilbert spaces. See detailed discussion in Tensor network formalism and tensor representation.

Instances of tensors are created on spaces defined in the SpaceDict:

...

# Constant tensor can be initialized
# from a CJ matrix:
id_ab = np.identity(sd['a'] * sd['b'])
ct0 = ConstTensor(
    ['a', 'b'], choi=id_ab, sdict=sd
)

# Its tensor representation is in array
# attribute:
ct0_ten_rep = ct0.array
# Order of its indices is in spaces
# attribute:
spaces = ct0.spaces

# Constant tensor can be initialized
# from its tensor representation
ct1 = ConstTensor(spaces, array=ct0_ten_rep, sdict=sd)

# Parametrized tensor requires both its
# value and derivative. For example
# a tensor with trivial derivative will
# be:
zeros = np.zeros_like(id_ab)
pt = ParamTensor(spaces, sdict=sd, choi=id_ab, dchoi=zeros)

Variable tensors require only spaces. Their type is derived from additional attributes:

...

spaces = ['a', 'b']
bonds = ['x']

# Variable tensor:
vt0 = VarTensor(spaces, sd)

# Types:
# > state:
vt1 = VarTensor(spaces, sd, output_spaces=spaces)
# > element of a density matrix of
# an MPS:
vt1 = VarTensor(spaces + bonds, sd, output_spaces=spaces)
# > CPTP map of a channel 'a'->'b':
vt1 = VarTensor(spaces, sd, output_spaces=['b'])
# > pre-SLD:
vt2 = VarTensor(spaces, sd, is_measurement=True)
# > element of a pre-SLD MPO:
vt2 = VarTensor(spaces + bonds, sd, is_measurement=True)

Tensors can be contracted using either contr method (or function contr) or the star * symbol:

from qmetro import *

channel = par_dephasing(0.75)
choi = channel.choi()

sd = SpaceDict()
spaces = ['a', 'b', 'c']
for space in spaces:
    sd[space] = 2

ct0 = ConstTensor(['a', 'b'], choi, sd)
ct1 = ConstTensor(['b', 'c'], choi, sd)

# Contraction of 'b' space.
ct2 = ct0.contr(ct1)
# or equivalently
ct2 = contr(ct0, ct1)
# or equivalently
ct2 = ct0 * ct1

While contraction of two ConstTensors simply creates a new ConstTensor this is not the case when one of the contracted tensors is a VarTensor because it does not have any value. Contraction with a VarTensor creates a TensorNetwork consisting of the two tensors:

...

ct = ConstTensor(['a', 'b'], choi, sd)
vt = VarTensor(['b', 'c'], sd)
# Tensor network of ct and vt connected by 'b' space:
tn = ct * vt

# Elements of tensor network can be  accessed using tensor and name attributes:
print(tn.tensors[ct.name] is ct)  # True.
print(tn.tensors[vt.name] is vt)  # True.

Similarly, a contraction with a TensorNetwork creates another TensorNetwork. The results of all possible contraction combinations are presented in Table 2.

Table 2 Table of contraction types.

*

GT

CT

PT

VT

TN

GT

Error

Error

Error

Error

Error

CT

Error

CT

PT

TN

TN

PT

Error

PT

PT

TN

TN

VT

Error

TN

TN

TN

TN

TN

Error

TN

TN

TN

TN

Resulting type of a contraction of various tensor pairs: GT — GeneralTensor, CT — ConstTensor, PT — ParamTensor, VT — VarTensor, TN — TensorNetwork. Contraction with generalized tensor raises an error because instances of this class are not meant to be used in calculations. When ParamTensor is contracted with a ConstTensor or a ParamTensor the derivative of the result is computed using the Leibniz (chain) rule.