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}\).
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 ofConstTensorwhich 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.
|
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.