Expectation values#

Expectation values in AKLT state#

def measure_mps_aklt(config=None, tol=1e-12):
    """ Test measuring MPS expectation values in AKLT state"""
    #
    # AKLT state with open boundary conditions and N = 31 sites.
    #
    N = 31
    psi = build_aklt_state_manually(N=N, lvec=(1, 0), rvec=(1, 0), config=config)
    #
    # We verify transfer matrix in the middle of AKLT state
    # to specify the effect of lvec and rvec
    #
    A = psi[N // 2]  # read MPS tensor
    T = yastn.ncon((A, A.conj()), ((-0, 1, -2), (-1, 1, -3)))
    T = T.fuse_legs(axes=((0, 1), (2, 3)))
    Tref = np.array([[1,  0,  0,  2],
                     [0, -1,  0,  0],
                     [0,  0, -1,  0],
                     [2,  0,  0,  1]])
    assert np.allclose(T.to_numpy(), Tref / 3, atol=tol)
    #
    # psi1 is not normalized.
    # Norm is very close to sqrt(0.5) for this system size.
    #
    norm = psi.norm()  # use canonization to calculate norm
    norm2 = mps.vdot(psi, psi)  # directly contract mps squared
    #
    assert abs(norm - np.sqrt(0.5)) < tol
    assert abs(norm2 - 0.5) < tol
    #
    # Initialize dense Spin1 operators for expectation values calculations
    #
    opts_config = {} if config is None else \
                  {'backend': config.backend,
                   'default_device': config.default_device}
    # pytest uses config to inject various backends and devices for testing
    ops = yastn.operators.Spin1(sym='dense', **opts_config)
    #
    # Measure Sz at the first and last sites
    #
    ez = mps.measure_1site(psi, {0: ops.sz(), N-1: ops.sz()}, psi)
    assert len(ez) == 2 and isinstance(ez, dict)
    assert abs(ez[0] - 1./3) < tol  # psi is not normalized
    assert abs(ez[N - 1] + 1./3) < tol
    #
    #  the same with other syntaxes
    #
    ez_bis = mps.measure_1site(psi, ops.sz(), psi, sites=[0, N-1])
    assert ez == ez_bis
    ez0 = mps.measure_1site(psi, ops.sz(), psi, sites=0)  # this return a number
    assert abs(ez0 - 1./3) < tol
    #
    #  with normalised state
    psin = psi / norm
    ez = mps.measure_1site(psin, ops.sz(), psin)  # calculate sz on all sites
    assert len(ez) == N and isinstance(ez, dict)
    assert abs(ez[0] - 2. / 3) < tol  # psin is normalized
    #
    # Calculate <Sz_i Sz_j> for (i, j) = (n, n+r) with r=1
    ezz = mps.measure_2site(psin, ops.sz(), ops.sz(), psin, bonds='r1')
    assert len(ezz) == N - 1 and isinstance(ezz, dict)
    assert all(abs(ezznn + 4. / 9) < tol for ezznn in ezz.values())
    #
    # here calculate a single bond, returning a number
    ezz12 = mps.measure_2site(psin, ops.sz(), ops.sz(), psin, bonds=(1, 2))
    assert abs(ezz12 + 4. / 9) < tol
    #
    #  Measure string operator; exp (i pi * Sz) = I - 2 * abs(Sz)
    #
    esz = ops.I() - 2 * abs(ops.sz())
    Hterm = mps.Hterm(amplitude=1,
                      positions=(10, 11, 12, 13, 14, 15),
                      operators=(ops.sz(), esz, esz, esz, esz, ops.sz()))
    I = mps.product_mpo(ops.I(), N)
    Ostring = mps.generate_mpo(I, [Hterm])  # MPO a string correlator
    Estring = mps.vdot(psin, Ostring, psin)
    assert abs(Estring + 4. / 9) < tol

Entropy and spectrum in GHZ-like state#

def mps_spectrum_ghz(sym='dense', config=None, tol=1e-12):
    """ Test measuring Schmidt spectrum and entropy of mps. """
    N = 8
    # consider 3-dimensional local Hilbert space
    opts_config = {} if config is None else \
                    {'backend': config.backend,
                    'default_device': config.default_device}
    # pytest uses config to inject various backends and devices for testing
    ops = yastn.operators.Spin1(sym=sym, **opts_config)
    #
    # take 3 orthonormal product states
    #
    psi1 = mps.product_mps(ops.vec_z(val=+0), N)
    psi2 = mps.product_mps([ops.vec_z(val=+1), ops.vec_z(val=-1)], N)
    psi3 = mps.product_mps([ops.vec_z(val=-1), ops.vec_z(val=+1)], N)
    #
    # For Z3 and U1, their sum has a well-defined
    # global charge only for even N
    #
    psi = mps.add(psi1, psi2, psi3, amplitudes=(0.5, 0.25, -0.25))
    #
    # state psi is not normalized
    #
    assert abs(psi.norm() - np.sqrt(0.375)) < tol
    #
    # calculate Schmidt values; Schmidt values are properly normalized
    #
    schmidt_spectra = psi.get_Schmidt_values()
    assert len(schmidt_spectra) == N + 1 and isinstance(schmidt_spectra, list)
    assert all(abs(sv.norm() - 1) < tol for sv in schmidt_spectra)
    #
    bds = (1,) + (3,) * (N-1) + (1,)  # (1, 3, 3, 3, 3, 3, 3, 3, 1) for N = 8
    assert psi.get_bond_dimensions() == bds
    # schmidt values are stored as diagonal yastn.Tensors
    assert all(sv.isdiag and sv.get_shape() == (bd, bd)
               for sv, bd in zip(schmidt_spectra, bds))
    #
    # mps.get_Schmidt_values is used by mps.get_entropy
    #
    entropies = psi.get_entropy()
    assert len(entropies) == N + 1 and isinstance(entropies, list)
    #
    # by default calculate von Neuman entropy (base-2 log)
    #
    assert all(abs(ent - (np.log2(6) - 4/3)) < tol for ent in entropies[1:-1])
    assert abs(entropies[0]) < tol and abs(entropies[-1]) < tol
    #
    # Renyi entropy with alpha=2 (and base-2 log)
    #
    entropies2 = psi.get_entropy(alpha=2)
    assert all(abs(ent - 1.) < tol for ent in entropies2[1:-1])
    assert abs(entropies2[0]) < tol and abs(entropies2[-1]) < tol
    #
    # the state psi is not affected by calculations
    #
    assert abs(psi.norm() - np.sqrt(0.375)) < tol