Graphs and MPOs

Graphs

Graphs are useful to describe interactions or gates to apply.

TensorMixedStates.circle_graph — Function
circle_graph(n)

the ring of n vertices, as a vector of edges: [(1, 2), (2, 3), ..., (n-1, n), (n, 1)]. The ring of two vertices joins them twice, [(1, 2), (2, 1)], as the two bonds of a periodic chain of two sites; a ring of fewer vertices is refused.

source
TensorMixedStates.complete_graph — Function
complete_graph(n)

the complete graph of n vertices, as a vector of edges: [(1, 2), (1, 3), ..., (1, n), (2, 3), ..., (n-1, n)].

Examples

graph_state(System(10, Qubit()), complete_graph(10); limits = Limits(maxdim = 10))
source
TensorMixedStates.square_lattice — Function
square_lattice(nx, ny; xperiodic = false, yperiodic = false)
square_lattice(n; xperiodic = false, yperiodic = false)

the nx by ny square lattice, as a vector of edges, its sites numbered along a snake running down the columns: the first column top to bottom, the second bottom to top, and so on. Horizontal bonds join sites at most 2ny - 1 apart: put the short side in ny, since it bounds the bond dimension. With a single argument the lattice is n by n.

yperiodic joins the last row to the first, making a cylinder, its bonds within the same bound; xperiodic joins the last column to the first, by bonds across the whole lattice, which cost far more: put a single periodic direction in y. The bonds closing a direction come after the others, and, as in circle_graph, a direction of length 2 has its bonds twice, and one of length 1 cannot be periodic.

Examples

square_lattice(3, 2)                    # 1-2, 3-4, 5-6, 1-4, 2-3, 4-5, 3-6
square_lattice(4, 3; yperiodic = true)  # a cylinder of circumference 3

that is, for the first, the lattice

1 4 5
2 3 6
source
TensorMixedStates.graph_base_size — Function
graph_base_size(::Vector{Tuple{Int, Int}})

the largest vertex of a graph, the number of sites a system needs to host it, 0 for a graph without edges. It is not the number of vertices: a vertex may be in no edge.

Examples

graph_base_size([(1, 2), (2, 5)])     # 5
source

MPO

Matrix Product Operators (MPO) are used under the hood by TMS to act on the states, which it represents as matrix product states. Every operator is converted to an MPO internally, except in apply, which applies gates one by one, and in expect, expect1, expect2 and measure, which contract the state directly.

How the MPO is built, and what it costs

When it builds an MPO, TMS compacts the operator: its terms of several sites are gathered into blocks of channels in which they share what they have in common, and the operators of each site are compared through their matrices on the sites of the system, so that X*Y and im * Z on a qubit, or N and (1 - Z) / 2, are known to be related. The MPO keeps the triangular form: one channel runs along the chain for the terms not yet started, another for those already finished, and in between each link has as many channels as the rank of the operator across it, once its parts that are the identity on either side are taken out. No MPO of this form has fewer.

For an operator whose terms are short ranged, this is what one channel per term gives already: the Ising chain sum(Z(i)Z(i+1) for i in 1:n-1) has bond dimension 3 whatever the number of sites. For a long ranged one, the difference is large. Measured on sum(Z(i)Z(j) for i in 1:n-1 for j in i+1:n):

sitestermsone channel per termcompacted
1045273
201901023
407804023

Three channels is also what ITensorMPS' OpSum finds there, with an SVD. Each term of a time dependent evolver is compacted on its own, so that it keeps its time function, and changing the coefficients from one step to the next rebuilds the tensors of the MPO alone.

A product of sums would develop into as many terms as the product of theirs: the moment M^4 of M = sum(Z(i) for i in 1:n) has of order n⁴. When that outnumbers the terms of its factors together, TMS builds it from the MPOs of its factors instead, multiplied site by site and compacted the same way, equal factors in a row by squaring, H^4 being (H^2)^2; on a density matrix, a gate or a dissipator of an operator of many terms is made likewise of its sides Kρ and ρK†. The cost is then linear in the number of sites: expect(ψ, M^4) on 24 sites takes 0.01 s, where its 10 903 terms took 2.2 s. The same algebra is at hand on the PreMPOs themselves: pre', a * b, a ^ p, a + b, a - b, c * a and sum(v).

Two things follow from it in practice:

  • The WI and WII approximations of ApproxW need the triangular form, and their error, of order τ², depends on how the operator is split into terms, not on the operator alone. Compacting keeps the first and the last site of every term, unless an operator of a site is a combination of other ones and of the identity there, as N when Z is used too, or N with the Jordan-Wigner strings F of fermions: part of a term then goes to terms of fewer sites, and the approximations change, while remaining of the same order.
  • expect prepares the operator it measures, compacted, at each call, and a Measure keeps that preparation for the calls of measure that follow: on a long ranged operator measured many times, a Measure built once saves most of the cost, as a PreMPO does for the MPO of an evolver.
TensorMixedStates.PreMPO — Type
PreMPO(state, op; limits = Limits())
PreMPO(state, [A, B => g]; limits = Limits())

the operator op, placed on sites, laid on the system of state as the pieces of its MPO, its terms compacted into as few channels as a triangular MPO can have; make_mpo builds the MPO from it. A vector of terms, each an operator or an operator with its real function of time, is the evolver depending on time $A + g(t) B$ of evolve, each term laid apart, see make_mpo. On a pure state, a generator with no dissipator, $\rho \mapsto K\rho + \rho K^\dagger$, gives the MPO of K, which the state follows as $d\psi/dt = K\psi$. On a mixed state, a channel or a generator gives the MPO of its superoperator, and an operator K that of Evolver(K).

limits truncates the MPO in its triangular form, see Limits: across each link, the singular values of the part of the operator acting on both sides, for the Hilbert-Schmidt norm normalized on each site, are cut as those of a bond of a state, the bond dimension of the MPO being two more than its channels; by default nothing is cut but rounding.

pre' is the PreMPO of the adjoint, its time functions kept, and a * b, a ^ p, a + b, a - b, c * a and sum(v), of PreMPOs of one system each of a single term not depending on time, those of the product, of the power and of the linear combinations of their operators: built with as few channels as their rank across each link, at a cost linear in the number of sites, where the terms of a product grow as the product of theirs. PreMPO(state, op) builds so the products of sums of op that would develop into more terms than their factors have, and, on a mixed state, its gates and dissipators of operators of many terms from the sides $K\rho$ and $\rho K^\dagger$ of these.

source
TensorMixedStates.make_mpo — Function
make_mpo(::PreMPO[, coefs])
make_mpo(::State, op)

the MPO of the operator op on the system of the state, given with the state or as PreMPO(state, op). For a vector of terms, built from its PreMPO, coefs holds the value of the time function of each, one real number per term, one for all by default, which a PreMPO depending on time refuses.

Examples

mpo = make_mpo(state, sum(Z(i) * Z(i + 1) for i in 1:9))
mpo = make_mpo(ρ, -im * sum(Z(i) * Z(i + 1) for i in 1:9) +
                  sum(Dissipator(Sm)(i) for i in 1:10))
source