def test_split_tensor(self): # physical dimensions d0, d1 = 3, 5 # outer virtual bond dimensions D0, D2 = 14, 17 Apair = randn_complex((d0*d1, D0, D2)) / np.sqrt(d0*d1*D0*D2) # fictitious quantum numbers qd0 = np.random.randint(-2, 3, size=d0) qd1 = np.random.randint(-2, 3, size=d1) qD = [np.random.randint(-2, 3, size=D0), np.random.randint(-2, 3, size=D2)] # enforce block sparsity structure dictated by quantum numbers mask = ptn.qnumber_outer_sum([ptn.qnumber_flatten([qd0, qd1]), qD[0], -qD[1]]) Apair = np.where(mask == 0, Apair, 0) for svd_distr in ['left', 'right', 'sqrt']: (A0, A1, qbond) = ptn.split_MPS_tensor(Apair, qd0, qd1, qD, svd_distr=svd_distr, tol=0) self.assertTrue(ptn.is_qsparse(A0, [qd0, qD[0], -qbond]), msg='sparsity pattern of A0 tensors must match quantum numbers') self.assertTrue(ptn.is_qsparse(A1, [qd1, qbond, -qD[1]]), msg='sparsity pattern of A1 tensors must match quantum numbers') # merged tensor must agree with the original tensor Amrg = ptn.merge_MPS_tensor_pair(A0, A1) self.assertAlmostEqual(np.linalg.norm(Amrg - Apair), 0., delta=1e-13, msg='splitting and subsequent merging must give the same tensor')
def test_qr(self): A = randn_complex((23, 15)) # fictitious quantum numbers q0 = np.random.randint(-2, 3, size=A.shape[0]) q1 = np.random.randint(-2, 3, size=A.shape[1]) # enforce block sparsity structure dictated by quantum numbers mask = ptn.qnumber_outer_sum([q0, -q1]) A = np.where(mask == 0, A, 0) # perform QR decomposition, taking quantum numbers into account Q, R, qinterm = ptn.qr(A, q0, q1) self.assertAlmostEqual(np.linalg.norm(np.dot(Q, R) - A), 0., delta=1e-14, msg='Q.R must match A matrix') self.assertAlmostEqual( np.linalg.norm(np.dot(Q.conj().T, Q) - np.identity(Q.shape[1])), 0., delta=1e-14, msg='columns of Q matrix must be orthonormalized') self.assertTrue( ptn.is_qsparse(Q, [q0, -qinterm]), msg='sparsity pattern of Q matrix must match quantum numbers') self.assertTrue( ptn.is_qsparse(R, [qinterm, -q1]), msg='sparsity pattern of R matrix must match quantum numbers')
def test_split_matrix_svd(self): A = randn_complex((17, 26)) # fictitious quantum numbers q0 = np.random.randint(-2, 3, size=A.shape[0]) q1 = np.random.randint(-2, 3, size=A.shape[1]) # enforce block sparsity structure dictated by quantum numbers mask = ptn.qnumber_outer_sum([q0, -q1]) A = np.where(mask == 0, A, 0) # perform SVD decomposition without truncation u, s, v, qinterm = ptn.split_matrix_svd(A, q0, q1, 0.) self.assertAlmostEqual(np.linalg.norm(np.dot(u * s, v) - A), 0., delta=1e-13, msg='U.S.V must match A matrix') self.assertAlmostEqual( np.linalg.norm(np.dot(u.conj().T, u) - np.identity(u.shape[1])), 0., delta=1e-14, msg='columns of U matrix must be orthonormalized') self.assertTrue( ptn.is_qsparse(u, [q0, -qinterm]), msg='sparsity pattern of U matrix must match quantum numbers') self.assertTrue( ptn.is_qsparse(v, [qinterm, -q1]), msg='sparsity pattern of V matrix must match quantum numbers') s_norm = np.linalg.norm(s) # perform SVD decomposition with truncation u, s, v, qinterm = ptn.split_matrix_svd(A, q0, q1, 0.15) self.assertAlmostEqual( np.linalg.norm(np.dot(u * s, v) - A), np.sqrt(s_norm**2 - np.linalg.norm(s)**2), delta=1e-14, msg= 'weight of truncated singular values must agree with norm of matrix difference' ) self.assertAlmostEqual( np.linalg.norm(np.dot(u.conj().T, u) - np.identity(u.shape[1])), 0., delta=1e-14, msg='columns of U matrix must be orthonormalized') self.assertTrue( ptn.is_qsparse(u, [q0, -qinterm]), msg='sparsity pattern of U matrix must match quantum numbers') self.assertTrue( ptn.is_qsparse(v, [qinterm, -q1]), msg='sparsity pattern of V matrix must match quantum numbers')
def cast_to_MPS(mpo): """ Cast a matrix product operator into MPS form by combining the pair of local physical dimensions into one dimension. """ mps = ptn.MPS(ptn.qnumber_flatten([mpo.qd, -mpo.qd]), mpo.qD, fill=0.0) for i in range(mpo.nsites): s = mpo.A[i].shape mps.A[i] = mpo.A[i].reshape((s[0] * s[1], s[2], s[3])).copy() assert ptn.is_qsparse(mps.A[i], [mps.qd, mps.qD[i], -mps.qD[i + 1]]) return mps
def cast_to_MPO(mps, qd): """ Cast a matrix product state into MPO form by interpreting the physical dimension as Kronecker product of a pair of dimensions. """ assert np.array_equal(mps.qd, ptn.qnumber_flatten([qd, -qd])) mpo = ptn.MPO(qd, mps.qD, fill=0.0) for i in range(mps.nsites): s = mps.A[i].shape mpo.A[i] = mps.A[i].reshape((len(qd), len(qd), s[1], s[2])).copy() assert ptn.is_qsparse(mpo.A[i], [mpo.qd, -mpo.qd, mpo.qD[i], -mpo.qD[i + 1]]) return mpo
def test_from_opchains(self): # dimensions d = 4 L = 5 # physical quantum numbers qd = np.random.randint(-2, 3, size=d) # fictitious operator chains opchains = [] n = np.random.randint(20) for _ in range(n): istart = np.random.randint(L) length = np.random.randint(1, L - istart + 1) oplist = [randn_complex((d, d)) for _ in range(length)] qD = np.random.randint(-2, 3, size=length - 1) # enforce sparsity structure dictated by quantum numbers qDpad = np.pad(qD, 1, mode='constant') for i in range(length): mask = ptn.qnumber_outer_sum( [qd + qDpad[i], -(qd + qDpad[i + 1])]) oplist[i] = np.where(mask == 0, oplist[i], 0) opchains.append(ptn.OpChain(oplist, qD, istart)) # construct MPO representation corresponding to operator chains mpo0 = ptn.MPO.from_opchains(qd, L, opchains) for i in range(mpo0.nsites): self.assertTrue( ptn.is_qsparse( mpo0.A[i], [mpo0.qd, -mpo0.qd, mpo0.qD[i], -mpo0.qD[i + 1]]), msg='sparsity pattern of MPO tensors must match quantum numbers' ) # construct full Hamiltonian from operator chains, as reference Href = sum([opc.as_matrix(d, L) for opc in opchains]) # compare self.assertAlmostEqual( np.linalg.norm(mpo0.as_matrix() - Href), 0., delta=1e-10, msg= 'full merging of MPO must be equal to matrix representation of operator chains' )
def test_orthonormalization(self): # create random matrix product state d = 7 D = [1, 4, 15, 13, 7, 1] mps0 = ptn.MPS(np.random.randint(-2, 3, size=d), [np.random.randint(-2, 3, size=Di) for Di in D], fill='random') self.assertEqual(mps0.bond_dims, D, msg='virtual bond dimensions') # wavefunction on full Hilbert space psi = mps0.as_vector() # performing left-orthonormalization... cL = mps0.orthonormalize(mode='left') self.assertLessEqual(mps0.bond_dims[1], d, msg='virtual bond dimension can only increase by a factor of "d" per site') for i in range(mps0.nsites): self.assertTrue(ptn.is_qsparse(mps0.A[i], [mps0.qd, mps0.qD[i], -mps0.qD[i+1]]), msg='sparsity pattern of MPS tensors must match quantum numbers') psiL = mps0.as_vector() # wavefunction should now be normalized self.assertAlmostEqual(np.linalg.norm(psiL), 1., delta=1e-12, msg='wavefunction normalization') # wavefunctions before and after left-normalization must match # (up to normalization factor) self.assertAlmostEqual(np.linalg.norm(cL*psiL - psi), 0., delta=1e-10, msg='wavefunctions before and after left-normalization must match') # check left-orthonormalization for i in range(mps0.nsites): s = mps0.A[i].shape assert s[0] == d Q = mps0.A[i].reshape((s[0]*s[1], s[2])) QH_Q = np.dot(Q.conj().T, Q) self.assertAlmostEqual(np.linalg.norm(QH_Q - np.identity(s[2])), 0., delta=1e-12, msg='left-orthonormalization') # performing right-orthonormalization... cR = mps0.orthonormalize(mode='right') self.assertLessEqual(mps0.bond_dims[-2], d, msg='virtual bond dimension can only increase by a factor of "d" per site') for i in range(mps0.nsites): self.assertTrue(ptn.is_qsparse(mps0.A[i], [mps0.qd, mps0.qD[i], -mps0.qD[i+1]]), msg='sparsity pattern of MPS tensors must match quantum numbers') self.assertAlmostEqual(abs(cR), 1., delta=1e-12, msg='normalization factor must have magnitude 1 due to previous left-orthonormalization') psiR = mps0.as_vector() # wavefunctions must match self.assertAlmostEqual(np.linalg.norm(psiL - cR*psiR), 0., delta=1e-10, msg='wavefunctions after left- and right-orthonormalization must match') # check right-orthonormalization for i in range(mps0.nsites): s = mps0.A[i].shape assert s[0] == d Q = mps0.A[i].transpose((0, 2, 1)).reshape((s[0]*s[2], s[1])) QH_Q = np.dot(Q.conj().T, Q) self.assertAlmostEqual(np.linalg.norm(QH_Q - np.identity(s[1])), 0., delta=1e-12, msg='right-orthonormalization')
def test_orthonormalization(self): # create random matrix product operator d = 4 D = [1, 10, 13, 14, 7, 1] mpo0 = ptn.MPO(np.random.randint(-2, 3, size=d), [np.random.randint(-2, 3, size=Di) for Di in D], fill='random') self.assertEqual(mpo0.bond_dims, D, msg='virtual bond dimensions') # density matrix on full Hilbert space rho = mpo0.as_matrix() # performing left-orthonormalization... cL = mpo0.orthonormalize(mode='left') self.assertLessEqual( mpo0.bond_dims[1], d**2, msg= 'virtual bond dimension can only increase by factor of "d^2" per site' ) for i in range(mpo0.nsites): self.assertTrue( ptn.is_qsparse( mpo0.A[i], [mpo0.qd, -mpo0.qd, mpo0.qD[i], -mpo0.qD[i + 1]]), msg='sparsity pattern of MPO tensors must match quantum numbers' ) rhoL = mpo0.as_matrix() # density matrix should now be normalized self.assertAlmostEqual(np.linalg.norm(rhoL, 'fro'), 1., delta=1e-12, msg='density matrix normalization') # density matrices before and after left-normalization must match # (up to normalization factor) self.assertAlmostEqual( np.linalg.norm(cL * rhoL - rho), 0., delta=1e-10, msg= 'density matrices before and after left-normalization must match') # check left-orthonormalization for i in range(mpo0.nsites): s = mpo0.A[i].shape assert s[0] == d and s[1] == d Q = mpo0.A[i].reshape((s[0] * s[1] * s[2], s[3])) QH_Q = np.dot(Q.conj().T, Q) self.assertAlmostEqual(np.linalg.norm(QH_Q - np.identity(s[3])), 0., delta=1e-12, msg='left-orthonormalization') # performing right-orthonormalization... cR = mpo0.orthonormalize(mode='right') self.assertLessEqual( mpo0.bond_dims[-2], d**2, msg= 'virtual bond dimension can only increase by factor of "d^2" per site' ) for i in range(mpo0.nsites): self.assertTrue( ptn.is_qsparse( mpo0.A[i], [mpo0.qd, -mpo0.qd, mpo0.qD[i], -mpo0.qD[i + 1]]), msg='sparsity pattern of MPO tensors must match quantum numbers' ) self.assertAlmostEqual( abs(cR), 1., delta=1e-12, msg= 'normalization factor must have magnitude 1 due to previous left-orthonormalization' ) rhoR = mpo0.as_matrix() # density matrices must match self.assertAlmostEqual( np.linalg.norm(rhoL - cR * rhoR), 0., delta=1e-10, msg= 'density matrices after left- and right-orthonormalization must match' ) # check right-orthonormalization for i in range(mpo0.nsites): s = mpo0.A[i].shape assert s[0] == d and s[1] == d Q = mpo0.A[i].transpose((0, 1, 3, 2)).reshape( (s[0] * s[1] * s[3], s[2])) QH_Q = np.dot(Q.conj().T, Q) self.assertAlmostEqual(np.linalg.norm(QH_Q - np.identity(s[2])), 0., delta=1e-12, msg='right-orthonormalization')