## Some questions about the svd_theta

How do I use this algorithm? What does that parameter do?
QichengTang
Posts: 32
Joined: 08 Jan 2019, 03:03

### Some questions about the svd_theta

Hi everyone,

I want to write a little program to calculate the correlation length with a given "theta" (which is located in the center of the chain), so I need to do svd to theta and get the right matrice B. But there is a problem, I get the theta by using function "get_theta" for MPS object, and extract the data by using "to_ndarray", and run "np.linalg.svd(matrice)". but this gives the order by sorting the singular value, which is different from "svd_theta" in tenpy. I donot know how to spilt the physical leg out in my case and how to calculate the transfer matrix. Would anyone help me with this, or tell me how to reconstruct the matrice into a block form?

Thanks,
Qicheng
Johannes
Posts: 207
Joined: 21 Jul 2018, 12:52
Location: UC Berkeley

### Re: Some questions about the svd_theta

Hi Qicheng,

how exactly do you want to extract the correlation length from the B?
For a finite system, this is a somewhat ill-defined question, because you have no (guaranteed) periodicity. You might want to look at the correlation functions directly and fit an exponential to that.
For an infinite system, you can write down the transfermatrix and take the (second) largest eigenvalue, see e.g. the beginning of chapter 4 in our notes arXiv:1805.00055. This is exactly what the function correlation_length does.

That being said, you should probably do the svd directly with tenpy's tenpy.linalg.np_conserved.svd instead of using to_ndarray and numpy's svd. Moreover, you can also just use the MPS get_B method to get the B with the expected 3 legs 'vL', 'p', 'vR'.
QichengTang
Posts: 32
Joined: 08 Jan 2019, 03:03

### Re: Some questions about the svd_theta

Johannes,

Thanks for your reply, I know that the transfer matrix method is only well-defined in a case with translational invariance, and for the contracting of the transfer matrix we need right matrice B or left matrice A.

My question is about the implementation of the charge conservation in the calculation. For example, as said in the note, when we combine the physical leg (p) with the left leg (vL) or the right leg (vR), we map the indices for "vL" and "p" into something like "2*vL+p", so that the charge rule preserved. In this case, the inverse mapping (split combined leg) can be written simply.

In tenpy, theta with combined legs can be obtained by the following code:

Code: Select all

theta = psi.get_theta(0)
theta = theta.combine_legs([('vL', 'p0'), ('p1', 'vR')], qconj=[+1, -1])
theta = theta.to_ndarray()

this is also can be done with:

Code: Select all

theta = psi.get_theta(0).to_ndarray()
combined_theta = np.zeros((2*chi, 2*chi))
for i in range(chi):
for j in range(2):
for k in range(chi):
for l in range(2):
combined_theta[2*i+j, 2*k+l] = theta[i, j, l, k]

If we have a theta in the matrix form (legs combined), we can split physical legs from theta by using the inverse mapping:
start from a theta with combined legs

Code: Select all

theta = psi.get_theta(0)
theta = theta.combine_legs([('vL', 'p0'), ('p1', 'vR')], qconj=[+1, -1])
theta = theta.to_ndarray()

split it

Code: Select all

split_theta = np.zeros((chi, 2, 2, chi))
for i in range(chi):
for j in range(2):
for k in range(chi):
for l in range(2):
split_theta[i, j, l, k] = theta[2*i+j, 2*k+l]


But there is a question is: how to do svd for theta can preserve the block structure? If simply run "U, S, Vh = np.linalg.svd(theta)" (where theta is the wavefunction in matrix from, i.e. it's obtained by

Code: Select all

theta = psi.get_theta(0)
theta = theta.combine_legs([('vL', 'p0'), ('p1', 'vR')], qconj=[+1, -1])
theta = theta.to_ndarray()

), it will return the right matrice B (which is just Vh) but sorted by the the singular values. Also due to this reason, we cannot split combined leg back into "vR" and "p" by using the inverse mapping mentioned above. In this case, I donot know how to calculate the transfer matrix since I cannot do the tensor product of right matrice B and trace out the physical leg.

In the note, it said that we can do svd in each charge sector, I'm not sure how to do. For example, for a matrix form theta, it seems we need to do the inverse mapping first (split combined leg) to get a rank-3 tensor with shape (chi, 4, chi) and do svd in each charege sector "theta[:, 0, :]", "theta[:, 1, :]", "theta[:, 2, :]", and "theta[:, 3, :]", i.e. do svd in sector (-2, 0, 0, 2) respectively? Like in the follwing:

Code: Select all

theta = psi.get_theta(0)
theta = theta.combine_legs([('vL', 'p0'), ('p1', 'vR')], qconj=[+1, -1])
theta = theta.to_ndarray()
split_theta = np.zeros((chi, 4, chi))
for i in range(chi):
for j in range(2):
for k in range(chi):
for l in range(2):
split_theta[i, 2*j+l, k] = theta[2*i+j, 2*k+l]

There will be another question: we have two zero charge sector since (-1) + (+1) = (+1) + (-1) = 0, how to deal with this?

In fact, I think somehow we can calculate the transfer matrix without using any knowledge of the charge conservation, i.e. with only a given matrix form theta (just the data of theta, extracted by using ".to_ndarray()"), we can obtain the transfer matrix. But I really donot know how to do this.

Thanks,
Qicheng
QichengTang
Posts: 32
Joined: 08 Jan 2019, 03:03

### Re: Some questions about the svd_theta

There's another thing related, and I donnot understand how to deal with it.

For example, consider a spin-half chain with conserved Sz, we can write down the 3 sub-blocks with Sz "-2, 0, 2", In this case, we can do svd for each charge sector.

But in the case of, for example, TFI chain where the Sz is not conserved, the "-2, 0, 2" are coupled, and we cannot do svd for each charge sector. In this case, how to do svd for the two-site wavefunction theta to get A and B?
Johannes
Posts: 207
Joined: 21 Jul 2018, 12:52
Location: UC Berkeley

### Re: Some questions about the svd_theta

The conversion to_ndarray discards information (namely: which index corresponds to which charges? what are the charge blocks we have?). Thus it cannot be reverted (at least not after subsequent tensordot/svd/...) and should be avoided at all. Instead you should use the functions provided by tenpy.linalg.np_conserved directly.

For example, there is a tenpy.linalg.np_conserved.svd function direction in tenpy, which is aware of the charge block structure and does the svd for each block individually. Thus, this svd implementation (which at some point calls numpy's svd for the blocks) does not sort by singular values completely, but only within each block. Hence you should do something like

Code: Select all

import tenpy.linalg.np_conserved as npc
theta = psi.get_theta(0, i=2) # legs 'vL', 'p0', 'p1', 'vR'
theta = theta.combine_legs([('vL', 'p0'), ('p1', 'vR')], qconj=[+1, -1])  # labels '(vL.p0)', '(p1.vR)'
U, S, Vh = npc.svd(theta, inner_labels=['vR', 'vL'])  # U has labels '(vL.p0)', 'vR';   V has labels 'vL', '(p1.vR)'
A0 = U.split_legs(['(vL.p0)'])  # A has labels 'vL', 'p0', 'vR'  and is left-canonical
B1 = Vh.split_legs(['(p1.vR)'])  # B has labels 'vL', 'p1', 'vR' and is right-canonical
A0.ireplace_labels('p0', 'p')
B1.ireplace_labels('p1', 'p')

Take a look at examples/a_np_conserved.py for more details.

QichengTang wrote: 01 Jul 2019, 04:16 But in the case of, for example, TFI chain where the Sz is not conserved, the "-2, 0, 2" are coupled, and we cannot do svd for each charge sector. In this case, how to do svd for the two-site wavefunction theta to get A and B?
If the hamiltonian doesn't preserve the total Sz, your MPS (ground state) also doesn't, and hence you don't run into that problem. In case of the TFI chain written as $$H = Sx.Sx + g Sz$$, you can at most preserve the Sz parity, which has only two independent charge sectors. The svd can then be done in each sector individually.
QichengTang
Posts: 32
Joined: 08 Jan 2019, 03:03

### Re: Some questions about the svd_theta

Thank you, Johannes, now I'm clear with the svd process, but here appears another question.

As in the note said, the process of combination of two legs is something like mapping the indices for "vL" and "p" into something like "2*vL+p", so you can split out the physical leg by using a inverse mapping.

The question is, how to split out the physical leg after svd, i.e. how tenpy.linalg.np_conserved.split_legs works in the case of svd? It seems like, the mapping of indices we made in the process of leg combination) will be destroyed by svd, although the one-to-one correspondence of indices exists, we donot know how to write down the inverse mapping to split out the physical leg.

In the total Sz conserved case, the different total Sz sectors are individual, so the block structure preserved after we do svd. Since the 2 degrees of freedom of the physical leg are "up" and "down" and we know exactly the total Sz of each block, we can split out the physical leg.

But in the case of TFI chain, when we do svd, there's no more concept of total Sz sectors, and I donot know how to split out the physical leg in this kind of cases.
Johannes
Posts: 207
Joined: 21 Jul 2018, 12:52
Location: UC Berkeley

### Re: Some questions about the svd_theta

You can only split legs that were combined previously. In TeNPy, you can see that those legs store the charge data as tenpy.linalg.charges.LegPipe instead of tenpy.linalg.charges.LegCharge. Those pipes have the additional data needed to split the legs.

The matrices generated by SVD, say A_ij = U_ik S_k V_kl, keep the very same LegCharge/LegPipe instances for the outer legs i, l, only the inner leg k gets a new LegCharge instance.
Hence, if e.g. the i was a pipe combining the left and physical indices of the B tensor, the U will also have the Pipe for index i, such that it can be split off again.
QichengTang
Posts: 32
Joined: 08 Jan 2019, 03:03

### Re: Some questions about the svd_theta

Johannes wrote: 02 Jul 2019, 13:42 You can only split legs that were combined previously. In TeNPy, you can see that those legs store the charge data as tenpy.linalg.charges.LegPipe instead of tenpy.linalg.charges.LegCharge. Those pipes have the additional data needed to split the legs.

The matrices generated by SVD, say A_ij = U_ik S_k V_kl, keep the very same LegCharge/LegPipe instances for the outer legs i, l, only the inner leg k gets a new LegCharge instance.
Hence, if e.g. the i was a pipe combining the left and physical indices of the B tensor, the U will also have the Pipe for index i, such that it can be split off again.
It's clear now, thanks for your help!