def ok(Ψ): for center in range(Ψ.size): ξ = CanonicalMPS(Ψ, center=center) # # All sites to the left and to the right are isometries # for i in range(center): self.assertTrue(approximateIsometry(ξ[i], +1)) for i in range(center + 1, ξ.size): self.assertTrue(approximateIsometry(ξ[i], -1)) # # Both states produce the same wavefunction # self.assertTrue(similar(ξ.tovector(), Ψ.tovector())) # # The norm is correct # self.assertAlmostEqual(ξ.norm2() / Ψ.norm2(), 1.0) # # Local observables give the same # O = np.array([[0, 0], [0, 1]]) nrm2 = ξ.norm2() self.assertAlmostEqual( ξ.expectation1(O) / nrm2, Ψ.expectation1(O, center) / nrm2) # # The canonical form is the same when we use the # corresponding negative indices of 'center' # χ = CanonicalMPS(Ψ, center=center - Ψ.size) for i in range(Ψ.size): self.assertTrue(similar(ξ[i], χ[i]))
def ok(Ψ): for center in range(Ψ.size): ξ1 = CanonicalMPS(Ψ, center=center, normalize=False) ξ2 = CanonicalMPS(Ψ, center=center, normalize=True) self.assertAlmostEqual(ξ2.norm2(), 1.0) self.assertTrue( similar(ξ1.tovector() / np.sqrt(ξ1.norm2()), ξ2.tovector()))
def tensor2siteok(self, aϕ, O1, O2): for center in range(aϕ.size): ϕ = CanonicalMPS(aϕ, center=center, normalize=True) for n in range(ϕ.size - 1): # # Take an MPS Φ, construct a new state ψ = O1*ϕ with a local # operator on the 'n-th' site # ψ = mps.state.MPS(ϕ) ψ[n] = np.einsum('ij,ajb->aib', O1, ψ[n]) ψ[n + 1] = np.einsum('ij,ajb->aib', O2, ψ[n + 1]) # # and make sure that the AntilinearForm provides the right tensor to # compute <ϕ|ψ> = <ϕ|O1|ϕ> # Odesired = ϕ.expectation2(O1, O2, n) LF = AntilinearForm(ϕ, ψ, center) if center + 1 < ϕ.size: D = np.einsum('ijk,klm->ijlm', ϕ[center], ϕ[center + 1]) Oestimate = np.einsum('aijb,aijb', D.conj(), LF.tensor2site(+1)) self.assertAlmostEqual(Oestimate, Odesired) if center > 0: D = np.einsum('ijk,klm->ijlm', ϕ[center - 1], ϕ[center]) Oestimate = np.einsum('aijb,aijb', D.conj(), LF.tensor2site(-1)) self.assertAlmostEqual(Oestimate, Odesired) if n >= center: self.assertTrue(almostIdentity(LF.L[center], +1)) if n + 1 <= center: self.assertTrue(almostIdentity(LF.R[center], +1))
def ok(Ψ): for center in range(Ψ.size): ξ = CanonicalMPS(Ψ, center=center) Lenv = super(CanonicalMPS, ξ).left_environment(center) Renv = super(CanonicalMPS, ξ).left_environment(center) self.assertTrue(almostIdentity(Lenv)) self.assertTrue(almostIdentity(Renv))
def inactive_test_apply_pairwise_unitaries(self): N = 2 tt = -np.pi/2 ω = np.pi dt = 0.1 # # Numerically exact solution using Scipy's exponentiation routine ψwave = random_wavefunction(N) print(mps.state.wavepacket(ψwave).tovector()) HMat = self.hopping_model_Trotter_matrix(N, tt, ω) ψwave_final = sp.linalg.expm_multiply(+1j * dt * HMat, ψwave) print(mps.state.wavepacket(ψwave_final).tovector()) print(HMat.todense()) # # Evolution using Trrotter H = self.hopping_model(N, tt, ω) U = pairwise_unitaries(H, dt) ψ = CanonicalMPS(mps.state.wavepacket(ψwave)) start = 0 direction = 1 apply_pairwise_unitaries(U, ψ, start, direction, tol=DEFAULT_TOLERANCE) print(ψ.tovector()) print(np.abs(mps.state.wavepacket(ψwave_final).tovector() - ψ.tovector())) self.assertTrue(similar(abs(mps.state.wavepacket(ψwave_final).tovector()), abs(ψ.tovector())))
def test_TEBD_evolution_second_order(self): # # # N = 21 t = 0.1 ω = 0.5 dt = 1e-6 Nt = int(1000) #ψwave = random_wavefunction(N) xx=np.arange(N) x0 = int(N//2) w0 = 5 k0 = np.pi/2 # # Approximate evolution of a wavepacket in a tight-binding model ψwave = np.exp(-(xx-x0)**2 / w0**2 + 1j * k0*xx) ψwave = ψwave / np.linalg.norm(ψwave) Hmat = self.hopping_model_matrix(N, t, ω) ψwave_final = sp.linalg.expm_multiply(-1j * dt * Nt * Hmat, ψwave) # # Trotter evolution H = self.hopping_model(N, t, ω) ψmps = CanonicalMPS(mps.state.wavepacket(ψwave)) ψmps = TEBD_evolution(ψmps, H, dt, timesteps=Nt, order=2, tol=DEFAULT_TOLERANCE).evolve() self.assertTrue(similar(abs(mps.state.wavepacket(ψwave_final).tovector()), abs(ψmps.tovector())))
def ok(Ψ): for center in range(Ψ.size): ψ = CanonicalMPS(Ψ, center=center, normalize=True) ξ = ψ.copy() self.assertEqual(ξ.size, ψ.size) self.assertEqual(ξ.center, ψ.center) for i in range(ξ.size): self.assertTrue(np.all(np.equal(ξ[i], ψ[i])))
def expected1_ok(ϕ, canonical=False): if canonical: for i in range(ϕ.size): expected1_ok(CanonicalMPS(ϕ, center=i), canonical=False) else: nrm2 = ϕ.norm2() for n in range(ϕ.size): ψ = ϕ.copy() ψ[n] = np.einsum('ij,kjl->kil', O1, ψ[n]) desired= np.vdot(ϕ.tovector(), ψ.tovector()) self.assertAlmostEqual(desired/nrm2, expectation1(ϕ, O1, n)/nrm2) self.assertAlmostEqual(desired/nrm2, ϕ.expectation1(O1, n)/nrm2)
def expected2_ok(ϕ, canonical=False): if canonical: for i in range(ϕ.size): CanonicalMPS(ϕ, center=i) nrm2 = ϕ.norm2() for n in range(1, ϕ.size): ψ = ϕ.copy() ψ[n-1] = np.einsum('ij,kjl->kil', O1, ψ[n-1]) ψ[n] = np.einsum('ij,kjl->kil', O2, ψ[n]) desired= mps.expectation.scprod(ϕ, ψ) self.assertAlmostEqual(desired/nrm2, expectation2(ϕ, O1, O2, n-1)/nrm2) self.assertAlmostEqual(desired/nrm2, ϕ.expectation2(O1, O2, n-1)/nrm2)
def ok(ψ): global foo for center in range(ψ.size): ϕ = CanonicalMPS(ψ, center=center, normalize=True) LF = AntilinearForm(ϕ, ϕ, center) for i in range(ϕ.size): if i <= center: self.assertTrue(similar(LF.L[i], ϕ.left_environment(i))) self.assertTrue(almostIdentity(LF.L[i], +1)) if i >= center: self.assertTrue( similar(LF.R[i], ϕ.right_environment(i))) self.assertTrue(almostIdentity(LF.R[i], +1))
def tensor1siteok(self, aϕ, O): for center in range(aϕ.size): ϕ = CanonicalMPS(aϕ, center=center, normalize=True) for n in range(ϕ.size): # # Take an MPS Φ, construct a new state ψ = O1*ϕ with a local # operator on the 'n-th' site # ψ = mps.state.MPS(ϕ) ψ[n] = np.einsum('ij,ajb->aib', O, ψ[n]) # # and make sure that the AntilinearForm provides the right tensor to # compute <ϕ|ψ> = <ϕ|O1|ϕ> # Odesired = expectation1(ϕ, O, n) LF = AntilinearForm(ϕ, ψ, center) Oestimate = np.einsum('aib,aib', ϕ[center].conj(), LF.tensor1site()) self.assertAlmostEqual(Oestimate, Odesired) if n >= center: self.assertTrue(almostIdentity(LF.L[center], +1)) if n <= center: self.assertTrue(almostIdentity(LF.R[center], +1))
def wavefunction_product(ψ, ξ, conjugate=False, simplify=True, **kwdargs): """Implement a nonlinear transformation that multiplies two MPS, to create a new MPS with combined bond dimensions. In other words, act with the nonlinear transformation <s|ψξ> = ψ(s)ξ(s)|s> or <s|ψ*ξ> = ψ*(s)ξ(s)|s> Arguments --------- ψ, ξ -- Two MPS or CanonicalMPS. conjugate -- Conjugate ψ or not. simplify -- Simplify the state afterwards or not. kwdargs -- Arguments to simplify() if simplify is True. Output ------ mps -- The MPS product ψξ or ψ*ξ. """ def combine(A, B): # Combine both tensors a, d, b = A.shape c, d, e = B.shape if conjugate: A = A.conj() D = np.array([ np.outer(A[:, i, :].flatten(), B[:, i, :].flatten()) for i in range(d) ]) D = np.einsum('iabce->acibe', np.array(D).reshape(d, a, b, c, e)).reshape(a * c, d, b * e) return D out = MPS([combine(A, B) for A, B in zip(ψ, ξ)]) if simplify: out = CanonicalMPS(out, center=0, **kwdargs) out, _, _ = mps.truncate.simplify(out, **kwdargs) return out