TeNPy Package

This note studies the usage of TeNPy package and the corresponding DMRG algorithms. A more detailed and complete official instruction can be found at Contents — TeNPy 1.0.2.dev2+df8818c documentation.

Get Started

In order to use TeNPy in Python, we can download the TeNPy package with pip:

pip install physics-tenpy

Once downloaded, we can import tenpy in our Python interpreter. The architechture of TeNPy is illustrated below.

This is a layered structure that constructed bottom up from vector and matrix objects in a single-body based Hilbert space (tenpy.linalg), to tensor objects (MPS, MPO) in a many-body based Hilbert space (tenpy.networks), to more complicated models with a definite many-body Hamiltonian (tenpy.models), to the sophisticated DMRG algorithms that solved a certain given model (tenpy.algorithms), to the highest-level integrated simulation interface that are called and runned on clusters (tenpy.simulations). This compact hierarchical architechture allows us to implement a certain mission neatly and efficiently at different levels.

How to Perform Linear Algebras

In TeNPy, linear algebra implementations are called within the tenpy.linalg module, and for most cases we would use the function np_conserved (dubbed as npc) inside this module.

npc is bascially a DMRG-tailored numpy module that handles array objects (generally, tensors) but with an additional symmetry constraint. It only stores non-zero number of the tensor object with a specified charge info. The charges is the conserved charges associated with certain symmetries of the models (or tensor networks). For example,

import tenpy.linalg.np_conserved as npc
chinfo = npc.ChargeInfo([1], ['2*Sz'])
p_leg = npc.LegCharge.from_qflat(chinfo, [[1], [-1]])
Sz = npc.Array.from_ndarray([[0.5, 0.], [0., -0.5]], [p_leg, p_leg.conj()], labels=['p', 'p*'])
Sp = npc.Array.from_ndarray([[0., 1.], [0., 0.]], [p_leg, p_leg.conj()], labels=['p', 'p*'])
Sm = npc.Array.from_ndarray([[0., 0.], [1., 0.]], [p_leg, p_leg.conj()], labels=['p', 'p*'])

is a standard lines to create a basic Pauli operators in a Sz conserved spin model. chinfo innitiately points out a U(1) conserved charge associated with total 2Sz number. p_leg then assigns the charge info into spin up [1] physical leg and spin down [-1] physical leg. The charges, can be simply viewed as good quantum numbers, which in TeNPy can be simultaneously stored to set a symmetry constraint of all tensor numerics as well as labels to mark the quantum states. The operators Sz, Sp and Sm, are (1,1)-tensors with two legs identified: p_leg (dubbed as ‘p’) and p_leg.conj (dubbed as ‘p*’). Now, a two-site isotropic Heisenberg Hamiltonian can be constructed as follow:

Hxy = 0.5 * (npc.outer(Sp, Sm) + npc.outer(Sm, Sp))
Hz = npc.outer(Sz, Sz)
H = Hxy + Hz
# here, H has 4 legs
H.iset_leg_labels(["s1", "t1", "s2", "t2"])

the result is a (2,2,2,2)-tensor reads:

<npc.Array shape=(2, 2, 2, 2) labels=['s1', 't1', 's2', 't2']
charge=ChargeInfo([1], [''])
 +1     | -1     | +1     | -1     
0 [[ 1] |0 [[ 1] |0 [[ 1] |0 [[ 1] 
1  [-1]]|1  [-1]]|1  [-1]]|1  [-1]]
2       |2       |2       |2       
[[[[ 0.25  0.  ]
   [ 0.   -0.25]]

  [[ 0.    0.  ]
   [ 0.5   0.  ]]]


 [[[ 0.    0.5 ]
   [ 0.    0.  ]]

  [[-0.25  0.  ]
   [ 0.    0.25]]]]
>

here, the first line tells us the basic structure of the tensor with leg labels and charges predefined, the second line returns the explicit tensor array, which is a 4-element array with each element returns a block of the final Hamiltonian. If we print out the first leg in qflat format, we would obtain:

print(H.legs[0].to_qflat().flatten())
<[1,-1]
>

This tells us the first leg of Hamiltonian is of size two, and corresponds to spin up (2Sz charge equals to +1) and spin two (2Sz charge equals to -1) states respectively.

Let us reshape it into a (4,4)-tensor by combining the first two and last two legs together:

H = H.combine_legs([["s1", "s2"], ["t1", "t2"]], qconj=[+1, -1])

through this, if we print H again, we shall obtain

<npc.Array shape=(4, 4) labels=['(s1.s2)', '(t1.t2)']
charge=ChargeInfo([1], [''])
LegPipe(shape (2, 2)->4,    |LegPipe(shape (2, 2)->4,
    qconj (+1, +1)->+1;     |    qconj (-1, -1)->-1;
    block numbers (2, 2)->3)|    block numbers (2, 2)->3)
 +1      |  +1              | -1      |  -1
0 [[ 1]  | 0 [[ 1]          |0 [[ 1]  | 0 [[ 1]
1  [-1]] | 1  [-1]]         |1  [-1]] | 1  [-1]]
2        | 2                |2        | 2
)                           |)
[[ 0.25  0.    0.    0.  ]
 [ 0.   -0.25  0.5   0.  ]
 [ 0.    0.5  -0.25  0.  ]
 [ 0.    0.    0.    0.25]]
>

We can see that now the Hamiltonian is a 4X4 matrix, with leg information added by a LegPipe structure that tells us the combination rule of legs, charges and blocks. One can use npc.Array.combine_legs() or npc.Array.split_legs() to implement combination or split of a certain legpipe. Now, if we print the first leg of the Hamiltonian again in qflat form, we would obtain:

print(H.legs[0].to_qflat().flatten())
<[-2,0,0,2]
>

This tells that the new Hamiltonian’s first leg is of size four (a legpipe combined with first two legs of the old Hamiltonian), and corresponds to spin down-spin down, spin down-spin up, spin up-spin down and spin up-spin up states.

How to Construct MPS & MPO?

After utilizing npc smoothly to construct and implement every linear algebra in the single-body quantum mechanics, the next step is to perform many-body quantum mechanics calculations embedded in the DMRG scheme. The ingredients are MPS and MPO, as illustrated in the basic consepts of DMRG part.

TeNPy manual website provides a toy code that construct a MPS class from the most original level using pure numpy numerics. This code can be found here. From this simple snippet, we can

How to Construct a Model?

If we want to run DMRG or other quantum many-body algorithms based on TeNPy to find the ground state information of a Hamiltonian, we should set up a model first. A basic structure of a model in TeNPy is constructed as follows:

class MyNewModel(MPOModel):
    def __init__(self, model_params):
        lattice = somehow_generate_lattice(model_params)
        H_MPO = somehow_generate_MPO(lattice, model_params)
        # initialize MPOModel
        MPOModel.__init__(self, lattice, H_MPO)

This snippet uses the MPOModel class in the tenpy.models.model module. The necessary ingredients are the model parameters as a input, and a lattice structure and a MPO Hamiltonian based on them.


Comments

留下评论