Problems with segment boundaries for TDVP

How do I use this algorithm? What does that parameter do?
Post Reply
User avatar
JordanTaylor
Posts: 1
Joined: 19 Jan 2022, 00:38
Contact:

Problems with segment boundaries for TDVP

Post by JordanTaylor »

I'm trying to run TDVP on an L=2 MPS with segment boundary conditions. I've got it running fine (I think) with open boundaries, or with segment boundaries so long as the external dimension equals one, but I'm running into trouble for nontrivial external dimensions. Specifically, running with dt=0 doesn't seem to preserve my original state.

Here is an example as simple as I could make it:

Image

Everything is just (normalized) identities. The Hamiltonian is too (which is stupid for an actual simulation), but it's completely irrelevant since I'm only looking at dt=0. I get the same results below for any H.

Here is my code implementing single steps of TDVP on this state with dt=0:

Code: Select all

import tenpy
import numpy as np
p=2 # phys dim
e=2 # external dim (replace with 1 and set d=2 and this works)
d=4 # bond dim    
delta_t = 0.0
N_steps = 1

sites = [tenpy.networks.site.Site(tenpy.linalg.charges.LegCharge.from_trivial(p)),
         tenpy.networks.site.Site(tenpy.linalg.charges.LegCharge.from_trivial(p))]
H_MPO = tenpy.networks.mpo.MPO(sites, 
        [tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.expand_dims(np.expand_dims(np.eye(p),0),0), labels = ['wL','wR','p','p*']),
        tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.expand_dims(np.expand_dims(np.eye(p),0),0), labels = ['wR','wL','p','p*']), 
        ],
        IdL = 0,
        IdR = 2, bc = 'segment')
MPOModel = tenpy.models.model.MPOModel(tenpy.models.lattice.TrivialLattice(sites),H_MPO)

psi = tenpy.networks.mps.MPS([tenpy.networks.site.Site(tenpy.linalg.charges.LegCharge.from_trivial(p))]*2, 
                      [tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.eye(d).reshape((e,p,d)), labels = ['vL','p','vR']), 
                       tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.eye(d).reshape((d,p,e)), labels = ['vL','p','vR'])],
                      [np.ones(e), np.ones(d)/np.sqrt(d), np.ones(e)], 
                            bc = 'segment',
                            form = 'G')

print(f'psi_0 = \n{psi.get_theta(0,2)}')

#Initialize TDVP
tdvp_params = {
    'start_time': 0,
    'dt': delta_t,
}
LP = tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.expand_dims(np.eye(e),1), labels = ['vR','wR','vR*'])
RP = tenpy.linalg.np_conserved.Array.from_ndarray_trivial(np.expand_dims(np.eye(e),1), labels = ['vL','wL','vL*'])
env = tenpy.networks.mpo.MPOEnvironment(psi, H_MPO, psi, init_LP=LP, init_RP=RP)
tdvp_engine = tenpy.algorithms.tdvp.TDVPEngine(psi, MPOModel, tdvp_params, env )

print('\nRunning TDVP:')
for i in range(4):
    tdvp_engine.run_one_site(N_steps=1)
    print(f'psi_{i+1} = \n{psi.get_theta(0,2)}')

And here are the outputs:

Code: Select all

psi_0 = 
<npc.Array shape=(2, 2, 2, 2) labels=['vL', 'p0', 'p1', 'vR']
charge=ChargeInfo([], [])
 +1 | +1 | +1 | +1 
0 []|0 []|0 []|0 []
2   |2   |2   |2   
[[[[0.5 0. ]
   [0.  0. ]]

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


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

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

Running TDVP:
psi_1 = 
<npc.Array shape=(2, 2, 2, 2) labels=['vL', 'p0', 'p1', 'vR']
charge=ChargeInfo([], [])
 +1 | +1 | +1 | -1 
0 []|0 []|0 []|0 []
2   |2   |2   |2   
[[[[ 0. +0.j  0. +0.j]
   [-0.5+0.j  0. +0.j]]

  [[ 0. +0.j  0. +0.j]
   [ 0. +0.j -0.5+0.j]]]


 [[[-0.5+0.j  0. +0.j]
   [ 0. +0.j  0. +0.j]]

  [[ 0. +0.j -0.5+0.j]
   [ 0. +0.j  0. +0.j]]]]
>
psi_2 = 
<npc.Array shape=(2, 2, 2, 2) labels=['vL', 'p0', 'p1', 'vR']
charge=ChargeInfo([], [])
 +1 | +1 | +1 | -1 
0 []|0 []|0 []|0 []
2   |2   |2   |2   
[[[[0. +0.j 0. +0.j]
   [0. +0.j 0.5+0.j]]

  [[0. +0.j 0. +0.j]
   [0.5+0.j 0. +0.j]]]


 [[[0. +0.j 0.5+0.j]
   [0. +0.j 0. +0.j]]

  [[0.5+0.j 0. +0.j]
   [0. +0.j 0. +0.j]]]]
>
psi_3 = 
<npc.Array shape=(2, 2, 2, 2) labels=['vL', 'p0', 'p1', 'vR']
charge=ChargeInfo([], [])
 +1 | +1 | +1 | -1 
0 []|0 []|0 []|0 []
2   |2   |2   |2   
[[[[ 0. +0.j  0. +0.j]
   [-0.5+0.j  0. +0.j]]

  [[ 0. +0.j  0. +0.j]
   [ 0. +0.j -0.5+0.j]]]


 [[[-0.5+0.j  0. +0.j]
   [ 0. +0.j  0. +0.j]]

  [[ 0. +0.j -0.5+0.j]
   [ 0. +0.j  0. +0.j]]]]
>
psi_4 = 
<npc.Array shape=(2, 2, 2, 2) labels=['vL', 'p0', 'p1', 'vR']
charge=ChargeInfo([], [])
 +1 | +1 | +1 | -1 
0 []|0 []|0 []|0 []
2   |2   |2   |2   
[[[[0. +0.j 0. +0.j]
   [0.5+0.j 0. +0.j]]

  [[0. +0.j 0. +0.j]
   [0. +0.j 0.5+0.j]]]


 [[[0.5+0.j 0. +0.j]
   [0. +0.j 0. +0.j]]

  [[0. +0.j 0.5+0.j]
   [0. +0.j 0. +0.j]]]]
>

So after one dt=0 timestep, the state has become (I think)
-Image
which is obviously not the original state.

I don't think this behaviour is just because of the non-injectivity of my example, or because of the stupidity of an identity Hamiltonian: An even worse scrambling happens when my initial state is a random normalized MPS, and my initial Hamiltonian is a random Hermitian MPO, so long as the exterior bond dimension is greater than one.

What am I missing?
User avatar
Johannes
Site Admin
Posts: 324
Joined: 21 Jul 2018, 12:52
Location: TU Munich

Re: Problems with segment boundaries for TDVP

Post by Johannes »

I'm sorry that this wasn't clear, but TeNPy's TDVP isn't intended to be used with segment boundary conditions; as of now, TeNPy only supports segment MPS for DMRG. There, the idea is to just keep the state outside of the segment (or "window" in the literature) fixed during the optimization.

In general, doing time evolution with "segment" boundary conditions is a bit more tricky: if the state outside of the segment is not an exact eigenstate, you need to evolve it as well! This is in particular what goes wrong if you just take random states/H outside!
Even if it is an exact eigenstate, you need to make sure that the boundary inside the segment matches such that the state ouside effectively doesn't evolve (up to a phase), and/or update the bond connecting the segment with the outsides.

There are 4 relevant papers, that appeared all at the same time: arXiv:1207.0652, arXiv:1207.0678, arXiv:1207.0691 and arXiv:1207.0862.

As I said, right only DMRG is implemented with segments (and we're actively working on this in the topo_excitations branch on github for general cases).
Right now I don't have the time available to work on it alone, so I can't promise that it will come anytime soon if you just wait...
However, if you want to work on implementing segments for time evolution yourself, I'll be happy to discuss and assist!
Post Reply