Ejemplo n.º 1
0
def cXY_local_terms(nr_sites, gamma):
    r"""Local terms of the cyclic XY model (MPOs)

    :param nr_sites: Number of spin one-half sites
    :param gamma: Asymmetry parameter
    :returns: List :code:`terms` of length :code:`nr_sites` (MPOs)

    The term :code:`terms[i]` acts on spins :code:`(i, i + 1)` and
    spin :code:`nr_sites` is the same as the first spin.

    The Hamiltonian of the cyclic XY model is given by
    [:any:`LSM61 <LSM61>`, Eq. 2.1]:

    .. math::

       H_\gamma = \sum_{i=1}^{N}   (1+\gamma) S^x_i S^x_{i+1}
                                 + (1-\gamma) S^y_i S^y_{i+1}

    with :math:`S^j_{N+1} = S^j_{1}`. The function :func:`cXY_E0`
    returns the exact ground state energy of this Hamiltonian.

    """
    local = ((1 + gamma) * 0.25 * mp.chain([mpo_X, mpo_X]) +
             (1 - gamma) * 0.25 * mp.chain([mpo_Y, mpo_Y]))
    return (local, ) * nr_sites
Ejemplo n.º 2
0
def cXY_local_terms(nr_sites, gamma):
    r"""Local terms of the cyclic XY model (MPOs)

    :param nr_sites: Number of spin one-half sites
    :param gamma: Asymmetry parameter
    :returns: List :code:`terms` of length :code:`nr_sites` (MPOs)

    The term :code:`terms[i]` acts on spins :code:`(i, i + 1)` and
    spin :code:`nr_sites` is the same as the first spin.

    The Hamiltonian of the cyclic XY model is given by
    [:any:`LSM61 <LSM61>`, Eq. 2.1]:

    .. math::

       H_\gamma = \sum_{i=1}^{N}   (1+\gamma) S^x_i S^x_{i+1}
                                 + (1-\gamma) S^y_i S^y_{i+1}

    with :math:`S^j_{N+1} = S^j_{1}`. The function :func:`cXY_E0`
    returns the exact ground state energy of this Hamiltonian.

    """
    local = ((1 + gamma) * 0.25 * mp.chain([mpo_X, mpo_X])
             + (1 - gamma) * 0.25 * mp.chain([mpo_Y, mpo_Y]))
    return (local,) * nr_sites
Ejemplo n.º 3
0
    def next(self):
        # generate a random unitary and convert to MPO
        mpu = mp.MPArray.from_array_global(random_U(
            self.max_size, num=1).reshape([2] * (self.max_size * 2)),
                                           ndims=2)

        # number of subsystems we need to appl
        subsys = int(self.N // 2) - self.max_size

        # iterate if still have remaining subsystems
        while subsys > 0:
            # generate another unitary
            dim = min([subsys, self.max_size])
            mpo_rand = mp.MPArray.from_array_global(random_U(
                dim, num=1).reshape([2] * (dim * 2)),
                                                    ndims=2)

            # chain
            mpu = mp.chain([mpu, mpo_rand])

            # compress matrix product
            mpu.compress("svd", relerr=relerr)

            # remove computed subsystem dimensions from count
            subsys -= dim

        # chain with identity MPO
        mpu = mp.chain([mpu, mp.chain([mpo_dict["id"]] * (self.N // 2))])
        # perform in-place compression on output operator
        mpu.compress("svd", relerr=self.relerr)

        return mpu
Ejemplo n.º 4
0
def _u_list_to_mpo_even(dims, u_even, compr):
    """
    Transforms list of matrices on even-odd sites to MPO acting on full
    state. So the list of u_even :math:`\\{u_{j,j+1} : j \\text{ even}\\}`,
    which are ``numpy.ndarrays``, is transformed into :math:`\\bigotimes_j
    u_{j,j+1} : j \\text{ even}` of the type ``mpnum.MPArray``.

    Args:
        dims (list):
            List of dimensions of each site
        u_even (list):
            List of time evolution operators acting on even adjacent sites
        compr (dict): Parameters for the compression which is executed on every
            MPA during the calculations, except for the Trotter calculation
            where trotter_compr is used

    Returns:
        mpnum.MPArray:
            The MPO for the full state acting on even-odd adjacent sites
    """
    if len(dims) % 2 == 0:
        last_h = u_even[-1]
        u_even = u_even[:-1]
    even = mp.chain(
        matrix_to_mpo(u, [[dims[2 * i + 1]] * 2, [dims[2 * i + 2]] * 2], compr)
        for i, u in enumerate(u_even[1::]))
    even = mp.chain([matrix_to_mpo(u_even[0], [[dims[0]] * 2], compr), even])
    if len(dims) % 2 == 0:
        even = mp.chain([even, matrix_to_mpo(last_h, [[dims[-1]] * 2], compr)])
    return even
Ejemplo n.º 5
0
def _u_list_to_mpo_even(dims, u_even, compr):
    """
    Transforms list of matrices on odd-even sites to MPOs acting on full state

    .. todo::
       Give explicit form of the final MPO (tensor product of input matrices)


    Args:
        dims (list):
            List of dimensions of each site
        u_even (list):
            List of time evolution operators acting on even adjacent sites
        compr (dict): Parameters for the compression which is executed on every
            MPA during the calculations, except for the Trotter calculation
            where trotter_compr is used

    Returns:
        mpnum.MPArray:
            The MPO for the full state acting on even-odd adjacent sites
    """
    if len(dims) % 2 == 0:
        last_h = u_even[-1]
        u_even = u_even[:-1]

    even = mp.chain(
        matrix_to_mpo(u, [[dims[2 * i + 1]] * 2, [dims[2 * i + 2]] * 2], compr)
        for i, u in enumerate(u_even[1::]))

    even = mp.chain([matrix_to_mpo(u_even[0], [[dims[0]] * 2], compr), even])

    if len(dims) % 2 == 0:
        even = mp.chain([even, matrix_to_mpo(last_h, [[dims[-1]] * 2], compr)])
    return even
Ejemplo n.º 6
0
def _u_list_to_mpo_odd(dims, u_odd, compr):
    """
    Transforms list of matrices on odd-even sites to MPOs acting on full stateself.

    .. todo::
       Give explicit form of the final MPO (tensor product of input matrices)

    Args:
        dims (list):
            List of dimensions of each site
        u_odd (list):
            List of time evolution operators acting on odd adjacent sites
        compr (dict): Parameters for the compression which is executed on every
            MPA during the calculations, except for the Trotter calculation
            where trotter_compr is used

    Returns:
        mpnum.MPArray:
            The MPO for the full state acting on odd-even adjacent sites
    """
    #
    if len(dims) % 2 == 1:
        last_h = u_odd[-1]
        u_odd = u_odd[:-1]

    # build tensor product of local propagators after transforming the matrices
    # to MPOs
    odd = mp.chain(
        matrix_to_mpo(u, [[dims[2 * i]] * 2, [dims[2 * i + 1]] * 2], compr)
        for i, u in enumerate(u_odd))

    if len(dims) % 2 == 1:
        odd = mp.chain([odd, matrix_to_mpo(last_h, [[dims[-1]] * 2], compr)])

    return odd
    def corr(self, site1, site2, a, b):
        #making two points observable in the form of mpo
        left = [mp.MPArray.from_kron([idm] *
                                     (site1 - 1))] if (site1 - 1) > 0 else []
        right = [mp.MPArray.from_kron([idm] * (self.sites - site2))] \
            if (self.sites - site2) > 0 else []
        mid = [mp.MPArray.from_kron([idm] * (site2 - site1 - 1))] \
            if (site2 - site1 - 1) > 0 else []

        mpo = mp.chain(left + [mp.MPArray.from_array_global(a)] + mid + \
                 [mp.MPArray.from_array_global(b)] + right) if site1 != site2 else\
                  mp.chain(left + [mp.MPArray.from_array_global(a.dot(b))]\
                           + right)
        #returning matrix element
        return mp.dot(mp.dot(self.psi.conj(), mpo), self.psi).to_array()
Ejemplo n.º 8
0
 def _build_odd_propagator(self, hi_list, tau, idL):
     """
         Builds propagator for odd bonds (even i in hi): e^(-j*H_{odd}*\tau)
         For an odd number of sites we append an identity at the end.
         Even/odd assumes indices start at 1, so we start with the first hi (python index 0)
     :param hi_list: List/Tuple of all terms in the Hamiltonian H = sum_i hi, where hi is local to one bond
     :param tau: timestep for propagator
     :param idL: Identity mpo for the last site
     :return: e^(-j*H_{odd}*\tau) as MPArray (mpo)
     """
     odd_generator = self._generate_bond_propagator_mpos(hi_list, tau, 0)
     if self.L % 2 == 0:
         return mp.chain(odd_generator)
     else:
         # Odd number of sites -> need identity at last site!
         return mp.chain(chain(odd_generator, [idL]))
Ejemplo n.º 9
0
def bell_gen(N=2):
    """
    Generates the matrix product state of an N qubit Bell state |psi^+>.
    Nice bit of code. 
    """

    # first generate an N qubit state in |0>^\otimes N
    mps = mp.MPArray.from_kron([np.array([1, 0])] * N)

    # generate entanglement operator in MPO form
    hadamard_mpo = mporep(mpo_dict["h"], mpo_dict["id"], reps=N - 1)
    cx_mpo = mp.chain([mpo_dict["id"]] * N)
    for i in range(0, N - 1):
        # construct selector for iteration
        selector = [0] * (N - 1)
        selector[i] = 1
        # construct CX stage
        cx_mpo_stage = mpojob([mpo_dict["id"], mpo_dict["cx"]], selector)

        # add to cx sequence
        cx_mpo = mp.dot(cx_mpo_stage, cx_mpo)

    # define entangling operation in MPO form
    entangle_op = mp.dot(cx_mpo, hadamard_mpo)
    # compress to minimise memory overhead (TODO: must add overlap check)
    entangle_op.compress("svd", relerr=1e-6)

    # compute Bell state in MPS form
    bell = mp.dot(entangle_op, mps)

    return bell
Ejemplo n.º 10
0
def _local_sum(dims, hi_mpos, compression_relerr=1e-15):
    """
        Embeds the sum of bond local terms (nearest neighbor coupling operators) on a chain as MPArrays
    :param dims: Dimensions of the sites of the chain (axis 0 legs of mpnum shape) as tuple or list
    :param hi_mpos: Two-site operators represented by MPArrays, which are to be embedded on a chain
    :param compression_relerr: If not None, the result of the sum is compressed using svd compression for the
                               specified relative error
    :return: MPArray of hi_mpos embedded on a chain of shape *shape*
    """
    nof_sites = len(dims)
    # Nearest neighbor operators occupy 2 sites each
    op_width = 2
    local_terms = []
    for startpos, hi_mpo in enumerate(hi_mpos):
        left = [mp.factory.eye(startpos, dims[:startpos])
                ] if startpos > 0 else []
        right = [mp.factory.eye(nof_sites - startpos - op_width, dims[startpos+op_width:])] \
            if nof_sites - startpos - op_width > 0 else []
        h_at_startpos = mp.chain(left + [hi_mpo] + right)
        local_terms.append(h_at_startpos)
    H = local_terms[0]
    for local_term in local_terms[1:]:
        H += local_term
    if compression_relerr is not None:
        H.compress(method='svd', relerr=1e-15)
    return H
Ejemplo n.º 11
0
def get_spin_boson_finiteT_chain_initial_state(theta, beta, h_site, h_bond, bath_local_dim, nof_coefficients,
                                               mpa_type='pmps',
                                               nof_steps=None, state_compression_kwargs=None,
                                               op_compression_kwargs=None, second_order_trotter=False,
                                               psi_0_compression_kwargs=None, residual=True,
                                               force_pmps_evolution=True, verbose=True):
    """
        Computes the initial state for the finite temperature spin_boson model in chain geometry.
        The bath state is computed via imaginary time evolution.
    :param theta: Spin parameter for  psi_0 = cos(theta) |1>  + sin(theta) |0>
    :param beta: Inverse temperature of the bath
    :param h_site: Bath local Hamiltonian list
    :param h_bond: Bath nearest neighbor coupling Hamiltonian list
    :param bath_local_dim: Local dimension of the bath
    :param nof_coefficients: Number of bath sites
    :param mpa_type: MPS type of the chain (mps, mpo, pmps)
    :param nof_steps: Number of steps for the imaginary time evolution
    :param state_compression_kwargs: Keyword args for the imaginary time evolution compression
    :param op_compression_kwargs: Keyword args for the imaginary time evolution operator pre-compression
    :param second_order_trotter: Set True for second order trotter based imaginary time evolution
    :param psi_0_compression_kwargs: Keyword args for the imaginary time evolution initial state compression
    :param residual: Set True to compute List of populations in the highest energy state of each bath mode.
    :param force_pmps_evolution: Set True to always use pmps for the imaginary time evolution
    :param verbose: Set true to make imaginary time evolution verbose
    :return: Initial state of system and bath as mps, mpo or pmps, info dict
    """
    assert mpa_type == 'mpo' or mpa_type == 'pmps'
    if nof_steps is None:
        nof_steps = int(beta*100)
    t0_wall = time.clock()
    t0_proc = time.perf_counter()
    if isinstance(bath_local_dim, int):
        dims = [bath_local_dim] * nof_coefficients
    else:
        raise AssertionError('Unsupported data type for fixed_dim')
    psi_0, info = tmps.chain.thermal.from_hamiltonian(beta, mpa_type, h_site, h_bond,
                                                      nof_steps=nof_steps,
                                                      state_compression_kwargs=state_compression_kwargs,
                                                      op_compression_kwargs=op_compression_kwargs,
                                                      second_order_trotter=second_order_trotter,
                                                      psi_0_compression_kwargs=psi_0_compression_kwargs,
                                                      force_pmps_evolution=force_pmps_evolution,
                                                      verbose=verbose)
    tf_proc = time.perf_counter() - t0_proc
    tf_wall = time.clock() - t0_wall
    info['walltime'] = tf_wall
    info['cpu_time'] = tf_proc
    info['bath_dims'] = dims
    if residual:
        res = _compute_finiteT_chain_residual(psi_0, mpa_type, dims)
        max_res = np.max(res)
        info['res'] = res
        info['max_res'] = max_res
    else:
        info['res'] = None
        info['max_res'] = None
    print('Finite T ground state residual ', info['res'])
    print('Finite T ground state max. residual: ', info['max_res'])
    sys_psi_0 = get_spin_initial_state(theta, mpa_type=mpa_type)
    return mp.chain([sys_psi_0, psi_0]), info
Ejemplo n.º 12
0
def test_mppovm_embed_expectation(
        nr_sites, local_dim, rank, startsite, width, rgen):
    if hasattr(local_dim, '__iter__'):
        local_dim2 = local_dim
    else:
        local_dim2 = [local_dim] * nr_sites
    local_dim2 = list(zip(local_dim2, local_dim2))

    # Create a local POVM `red_povm`, embed it onto a larger chain
    # (`full_povm`), and go back to the reduced POVM.
    red_povm = mp.chain(
        mp.povm.MPPovm.from_local_povm(mp.povm.pauli_povm(d), 1)
        for d, _ in local_dim2[startsite:startsite + width]
    )
    full_povm = red_povm.embed(nr_sites, startsite, local_dim)
    axes = [(1, 2) if i < startsite or i >= startsite + width else None
            for i in range(nr_sites)]
    red_povm2 = mp.partialtrace(full_povm, axes, mp.MPArray)
    red_povm2 = mp.prune(red_povm2, singletons=True)
    red_povm2 /= np.prod([d for i, (d, _) in enumerate(local_dim2)
                          if i < startsite or i >= startsite + width])
    assert_almost_equal(mp.normdist(red_povm, red_povm2), 0.0)

    # Test with an arbitrary random MPO instead of an MPDO
    mpo = mp.factory.random_mpa(nr_sites, local_dim2, rank, rgen,
                                dtype=np.complex_, normalized=True)
    mpo_red = next(mp.reductions_mpo(mpo, width, startsites=[startsite]))
    ept = mp.prune(full_povm.pmf(mpo, 'mpdo'), singletons=True).to_array()
    ept_red = red_povm.pmf(mpo_red, 'mpdo').to_array()
    assert_array_almost_equal(ept, ept_red)
Ejemplo n.º 13
0
def test_mppovm_sample(
        method, n_samples, nr_sites, startsite, local_dim, rgen):
    """Check that probability estimates from samples are reasonable accurate"""
    rank = 3
    eps = 1e-10
    mps = factory.random_mps(nr_sites, local_dim, rank, rgen)
    mps.canonicalize()

    local_x = povm.x_povm(local_dim)
    local_y = povm.y_povm(local_dim)
    xx = povm.MPPovm.from_local_povm(local_x, 2)
    y = povm.MPPovm.from_local_povm(local_y, 1)
    mpp = mp.chain([xx, povm.MPPovm.eye([local_dim]), y]) \
            .embed(nr_sites, startsite, local_dim)

    pmf_exact = mpp.pmf_as_array(mps, 'mps', eps)

    if n_samples > 100:
        n_gr = 5
    elif local_dim == 3:
        n_gr = 2
    else:
        n_gr = 3
    samples = mpp.sample(rgen, mps, n_samples, method, n_gr, 'mps', eps=eps)

    pmf_est = mpp.est_pmf(samples)

    assert abs(pmf_est.sum() - 1.0) <= eps
    assert abs(pmf_exact - pmf_est).max() <= 3 / n_samples**0.5
Ejemplo n.º 14
0
def get_spin_boson_0T_chain_initial_state(theta, bath_local_dim, nof_coefficients):
    """
       Returns the full initial state (vacuum state) for 0T chain with nof_coefficients sites and a local dimension of
       bath_local_dim.
    """
    sys_psi_0 = get_spin_initial_state(theta)
    bath_psi_0 = broadcast_number_ground_state(bath_local_dim, nof_coefficients)
    return mp.chain([sys_psi_0, bath_psi_0])
Ejemplo n.º 15
0
def test_mppovm_est_pmf_from(
        method, n_samples, nr_sites, startsite, local_dim, rgen):
    """Check that probability estimates from samples are reasonable accurate"""
    rank = 3
    eps = 1e-10
    mps = factory.random_mps(nr_sites, local_dim, rank, rgen)
    mps.canonicalize()

    lx = povm.x_povm(local_dim)
    ly = povm.y_povm(local_dim)
    lp = povm.pauli_povm(local_dim)
    x = povm.MPPovm.from_local_povm(lx, 1)
    y = povm.MPPovm.from_local_povm(ly, 1)
    pauli = povm.MPPovm.from_local_povm(lp, 1)
    xy = mp.chain((x, y))
    mpp = mp.chain((xy,) * (nr_sites // 2))
    if (nr_sites % 2) == 1:
        mpp = mp.chain((mpp, x))
    small_mpp = mp.chain((pauli, povm.MPPovm.eye([local_dim]), pauli, pauli)) \
                  .embed(nr_sites, startsite, local_dim)

    x_given = np.arange(len(lp)) < len(lx)
    y_given = ((np.arange(len(lp)) >= len(lx))
               & (np.arange(len(lp)) < len(lx) + len(ly)))
    given_sites = [x_given if ((startsite + i) % 2) == 0 else y_given
                   for i in (0, 2, 3)]
    given_expected = np.einsum('i, j, k -> ijk', *given_sites)
    pmf_exact = small_mpp.pmf_as_array(mps, 'mps', eps)

    if n_samples > 100:
        n_gr = 5
    elif local_dim == 3:
        n_gr = 2
    else:
        n_gr = 3

    samples = mpp.sample(rgen, mps, n_samples, method, n_gr, 'mps', eps=eps)
    est_pmf, est_n_samples = small_mpp.est_pmf_from(mpp, samples)
    # In this case, we use all the samples from `mpp`.
    assert est_n_samples == n_samples
    given = ~np.isnan(est_pmf)
    assert (given == given_expected).all()

    assert abs(pmf_exact[given].sum() - est_pmf[given].sum()) <= eps
    assert abs(pmf_exact[given] - est_pmf[given]).max() <= 1 / n_samples**0.5
Ejemplo n.º 16
0
 def _build_even_propagator(self, hi_list, tau, id0, idL):
     """
         Builds propagator for even bonds (even i in hi): e^(-j*H_{even}*tau)
         For an even number of sites we append identities at the start and at the end.
         For an odd number of sites we append an identity at the start.
         Even/odd assumes indices start at 1, so we start with the second hi (python index 1)
     :param hi_list: List/Tuple of all terms in the Hamiltonian H = sum_i hi, where hi is local to one bond
     :param tau: timestep for propagator
     :param id0: identity mpo for the first site
     :param idL: identity mpo for the last site
     :return: e^(-j*H_{even}*\tau) as MPArray (mpo)
     """
     even_generator = self._generate_bond_propagator_mpos(hi_list, tau, 1)
     if self.L % 2 == 0:
         # Even number of sites -> need identity at last site!
         return mp.chain(chain([id0], even_generator, [idL]))
     else:
         return mp.chain(chain([id0], even_generator))
Ejemplo n.º 17
0
def get_spin_boson_finiteT_star_initial_state(theta, beta, system_index, xi, mpa_type='pmps', fixed_dim=None,
                                              high_energy_pop=1e-20, sitewise=False, residual=True):
    """
            Computes the initial state for the finite temperature spin_boson model in star geometry.
        The bath state is computed via imaginary time evolution.
    :param theta: Spin parameter for  psi_0 = cos(theta) |1>  + sin(theta) |0>
    :param beta: Inverse temperature of the bath
    :param system_index: Impurity position in the auxiliary chain
    :param xi: Star geometry bath energies
    :param mpa_type: Type: mps, mpo or pmps of the initial state
    :param fixed_dim: Uses this fixed dimension for the star evolution
    :param high_energy_pop: Chooses local dimension, such that the population in the highest energy of each bath mode
                            stays below this threshold
    :param sitewise: If set False the local dimension is chosen uniformly for all sites to be the
                     highest local dimension from the high_energy_pop calculation.
    :param residual: Computes list of populations in the highest energy state of each mode
    :return: Initial state of system and bath as mps, mpo or pmps, info dict
    """
    assert mpa_type == 'mpo' or mpa_type == 'pmps'
    t0_wall = time.clock()
    t0_proc = time.perf_counter()
    dims = get_star_local_dims(beta, xi, fixed_dim=fixed_dim, high_energy_pop=high_energy_pop, sitewise=sitewise)
    ops = [xi[i] * np.arange(dim) for i, dim in enumerate(dims)]
    if system_index > 0:
        left_state = get_thermal_state(beta, mpa_type, ops[:system_index], to_cform=None)
        right_state = get_thermal_state(beta, mpa_type, ops[system_index:], to_cform=None)
    else:
        left_state = None
        right_state = get_thermal_state(beta, mpa_type, ops, to_cform=None)
    tf_proc = time.perf_counter() - t0_proc
    tf_wall = time.clock() - t0_wall
    info = dict()
    info['walltime'] = tf_wall
    info['cpu_time'] = tf_proc
    info['bath_dims'] = dims
    if residual:
        info['res'] = _compute_finite_T_star_residual(beta, xi, dims)
        info['max_res'] = np.max(info['res'])
    else:
        info['res'] = None
        info['max_res'] = None
    sys_psi_0 = get_spin_initial_state(theta, mpa_type=mpa_type)
    return mp.chain([left_state, sys_psi_0, right_state]) if left_state is not None else \
        mp.chain([sys_psi_0, right_state]), info
    def one_site_mean(self, site, a):
        #making one point observable in the form of mpo
        left = [mp.MPArray.from_kron([idm] *
                                     (site - 1))] if (site - 1) > 0 else []
        right = [mp.MPArray.from_kron([idm] * (self.sites - site))] \
            if (self.sites - site) > 0 else []

        mpo = mp.chain(left + [mp.MPArray.from_array_global(a)] + right)
        #returning matrix element
        return mp.dot(mp.dot(self.psi.conj(), mpo), self.psi).to_array()
Ejemplo n.º 19
0
def test_mppovm_match_elems_bell(eps=1e-10):
    """Test match_elems() for a non-product MPPovm"""
    # Four Bell states (basis: |00>, |01>, |10>, |11>)
    bell = np.array((
        [(1/3)**0.5, 0, 0, (1/3)**0.5],   # (0, 0):  |00> + |11>  (proj. weight 1/3)
        [0, 1, 1, 0],                     # (0, 1):  |01> + |10>  (proj. weight 1)
        [(2/3)**0.5, 0, 0, -(2/3)**0.5],  # (0, 2):  |00> - |11>  (proj. weight 2/3)
        [0, 1, -1, 0],                    # (1, 0):  |01> - |10>  (proj. weight 1)
        [(1/3)**0.5, 0, 0, -(1/3)**0.5],  # (1, 1):  |00> - |11>  (proj. weight 1/3)
        [(2/3)**0.5, 0, 0, (2/3)**0.5],   # (1, 2):  |00> + |11>  (proj. weight 2/3)
    )) / 2**0.5
    bell_proj = np.einsum('ij, ik -> ijk', bell, bell.conj())
    bell_proj = bell_proj.reshape((2, 3) + (2,) * 4)
    # Four Bell states and two product states
    vecs = np.array((
        [0, 1, -1, 0],            # (0, 0):  |01> - |10>  (proj. weight 0.5)
        [0, 2**0.5, 0, 0],        # (0, 1):  |01>         (proj. weight 0.5)
        [0, 0, 2**0.5, 0],        # (1, 0):  |10>         (proj. weight 0.5)
        [2**0.5, 0, 0, -2**0.5],  # (1, 1):  |00> - |11>  (proj. weight 1)
        [0, 1, 1, 0],             # (2, 0):  |01> + |10>  (proj. weight 0.5)
        [2**0.5, 0, 0, 2**0.5],   # (2, 1):  |00> + |11>  (proj. weight 1)
    )) / 2
    proj = np.einsum('ij, ik -> ijk', vecs, vecs.conj())
    proj = proj.reshape((3,) + (2,) * 5)

    # Big POVM: The four Bell states (repeated two times)
    big = povm.MPPovm.from_array_global(bell_proj, ndims=3)
    big = mp.chain([big, big])
    # Small POVM: Two of the Bell states and four product states (on
    # the last two sites)
    small = povm.MPPovm.from_array_global(proj, ndims=3).embed(4, 2, 2)

    # Check that the POVM is normalized: elements must sum to the identity
    for mppovm in big, small:
        element_sum = sum(x.to_array_global().reshape(16, 16)
                          for x in mppovm.elements)
        assert_array_almost_equal(element_sum, np.eye(16))

    match, prefactors = small.match_elems(big, eps=eps)
    # Verify the correspondence which can be read off above
    want = np.zeros((3, 2, 2, 3), dtype=bool)
    want[0, 0, 1, 0] = True  # |01> - |10>
    want[2, 0, 0, 1] = True  # |01> + |10>
    want[1, 1, 0, 2] = True  # |00> - |11>
    want[1, 1, 1, 1] = True  # |00> - |11>
    want[2, 1, 0, 0] = True  # |00> + |11>
    want[2, 1, 1, 2] = True  # |00> + |11>
    assert (match == want).all()
    assert abs(prefactors[0, 0, 1, 0] - 0.5) <= eps
    assert abs(prefactors[2, 0, 0, 1] - 0.5) <= eps
    assert abs(prefactors[1, 1, 0, 2] - 1.5) <= eps
    assert abs(prefactors[1, 1, 1, 1] - 3) <= eps
    assert abs(prefactors[2, 1, 0, 0] - 3) <= eps
    assert abs(prefactors[2, 1, 1, 2] - 1.5) <= eps
    assert np.isnan(prefactors[~match]).all()
Ejemplo n.º 20
0
 def _system_site_mpo(self, h, tau):
     """
     :param h: System site local operator
     :param tau: timestep
     :return: trotterized exponential in mpo form for the system site and the ancilla
     """
     if h is None:
         return mp.chain([
             mp.eye(1, self.shape[self.system_index][0]),
             mp.eye(1, self.shape[self.system_index][1])
         ])
     propagator = expm(-1j * tau * h)
     propagator = propagator.reshape(self.shape[self.system_index][0],
                                     self.shape[self.system_index][0])
     # Add identity to ancilla bond
     mpo = mp.chain([
         mp.MPArray.from_array_global(propagator, ndims=2),
         mp.eye(1, self.shape[self.system_index][1])
     ])
     return self._compress_mpo(mpo)
Ejemplo n.º 21
0
def get_boson_boson_0T_chain_initial_state(alpha, nof_coefficients, cutoff_dim):
    """
        Initial state for the Boson-Boson model in chain geometry (see Sec. 4.4.3 of the thesis)
    :param alpha: accuracy alpha for the impurity coherent state
    :param nof_coefficients: Number of bath sites
    :param cutoff_dim: Local dimension of the system and impurity
    :return: Initial state in MPS form
    """
    pop = lambda x: np.exp(-np.abs(alpha) ** 2 / 2) * alpha ** x / np.sqrt(factorial(x))
    sys_psi_0 = convert.to_mparray(pop(np.arange(cutoff_dim)), 'mps')
    bath_psi_0 = broadcast_number_ground_state(cutoff_dim, nof_coefficients)
    return mp.chain([sys_psi_0, bath_psi_0])
Ejemplo n.º 22
0
def get_spin_boson_0T_star_initial_state(theta, system_index, bath_local_dim, nof_coefficients):
    """
       Returns the full initial state (vacuum state) for 0T star with nof_coefficients sites and a local dimension of
       bath_local_dim. The impurity is located at system_index.
    """
    sys_psi_0 = get_spin_initial_state(theta)
    # Initial states of the bath sites left and right of the system:
    left_bath_psi_0, right_bath_psi_0 = tmps.utils.broadcast_number_ground_state(bath_local_dim, system_index), \
                                        tmps.utils.broadcast_number_ground_state(bath_local_dim,
                                                                                 nof_coefficients - system_index)
    return mp.chain([left_bath_psi_0, sys_psi_0, right_bath_psi_0]
                    if left_bath_psi_0 is not None else [sys_psi_0, right_bath_psi_0])
def ham(sites=3, ta=1., la=0.):

    #two-site zz part
    h_mpo = mp.MPArray.from_kron([z, -z])
    #an empty list will be filled by local terms
    h = []

    #adding zz terms to the hamiltonian
    for startpos in range(sites - 1):
        left = [mp.MPArray.from_kron([idm] * startpos)] if startpos > 0 else []
        right = [mp.MPArray.from_kron([idm] * (sites - 2 - startpos))] \
            if sites - 2 - startpos > 0 else []
        h_at_startpos = mp.chain(left + [h_mpo] + right)
        h.append(h_at_startpos)

    #adding x terms to the hamiltonian
    for startpos in range(sites):
        left = [mp.MPArray.from_kron([idm] * startpos)] if startpos > 0 else []
        right = [mp.MPArray.from_kron([idm] * (sites - 1 - startpos))] \
            if sites - 1 - startpos > 0 else []
        h_at_startpos = mp.chain(left +
                                 [mp.MPArray.from_array_global(-ta * x)] +
                                 right)
        h.append(h_at_startpos)

    #adding z terms to the hamiltonian
    for startpos in range(sites):
        left = [mp.MPArray.from_kron([idm] * startpos)] if startpos > 0 else []
        right = [mp.MPArray.from_kron([idm] * (sites - 1 - startpos))] \
            if sites - 1 - startpos > 0 else []
        h_at_startpos = mp.chain(left +
                                 [mp.MPArray.from_array_global(-la * z)] +
                                 right)
        h.append(h_at_startpos)

    H = h[0]
    for local_term in h[1:]:
        H = H + local_term
    out, _ = H.compression(method='svd', relerr=1e-6)
    return out
Ejemplo n.º 24
0
def mpojob(mpas, selector, relerr=1e-6):
    """
    stacks the set of mpas in the order specified by selector
    """
    prod_chain = []
    for sel in selector:
        prod_chain.append(mpas[sel])

    # compute tensor chain
    chained = mp.chain(iter(prod_chain))
    # compress output rank to within tolerance
    chained.compress("svd", relerr=relerr)

    return chained
Ejemplo n.º 25
0
def mporep(mpa, mpb, reps, relerr=1e-6):
    """
    constructs the MPA [mpa, mpb_1, mpb_2....mpb_reps]
    """
    # construct MPA iterable
    prod_chain = [mpb] * reps
    prod_chain.insert(0, mpa)

    # construct tensor chain
    chained = mp.chain(iter(prod_chain))
    # compress
    chained.compress("svd", relerr=relerr)

    return chained
Ejemplo n.º 26
0
def get_boson_boson_0T_star_initial_state(alpha, system_index, nof_coefficients, cutoff_dim):
    """
        Initial state for the Boson-Boson model in star geometry (see Sec. 4.4.3 of the thesis)
    :param alpha: accuracy alpha for the impurity coherent state
    :param system_index: Index of the impurity in the auxiliary chain
    :param nof_coefficients: Number of bath sites
    :param cutoff_dim: Local dimension of the system and impurity
    :return: Initial state in MPS form
    """
    pop = lambda x: np.exp(-np.abs(alpha) ** 2 / 2) * alpha ** x / np.sqrt(factorial(x, exact=True))
    sys_psi_0 = convert.to_mparray(pop(np.arange(cutoff_dim)), 'mps')
    # Initial states of the bath sites left and right of the system:
    left_bath_psi_0, right_bath_psi_0 = tmps.utils.broadcast_number_ground_state(cutoff_dim, system_index), \
                                        tmps.utils.broadcast_number_ground_state(cutoff_dim,
                                                                                 nof_coefficients - system_index)
    return mp.chain([left_bath_psi_0, sys_psi_0, right_bath_psi_0]
                    if left_bath_psi_0 is not None else [sys_psi_0, right_bath_psi_0])
Ejemplo n.º 27
0
 def _mpo_from_hi(self, hi, tau, bond):
     """
         Convenience function, which allows to split mpo generation for 0T and finite T.
         Generates mpos for e^(-j*\tau*hi) as matrix exponential (uses scipy expm)
         For mps and mpo we just build e^(-j*\tau*hi) for the physical legs and generate the mpo.
         For pmps we first generate
         U^((s1, s2, s3), (s1', s2', s3')) = U^((s1, s3), (s1', s3')) * delta^(s2, s2')
         for each hi with U^((s1, s3), (s1', s3')) = e^(-j*tau*hi)
         with s2/s2' ancilla site. And then append a delta^(s4, s4') at the end for the second ancilla.
     :param hi: Bond operator to generate matrix exponential from
     :param tau: timestep for propagator
     :param bond: Bond index (i) for hi
     :return: e^(-j*tau*hi) in mpo form. For mps/mpo propagation is a two site two legs each operator
              for pmps propagation is a two site four legs (two physical, two ancilla on each site)
              ready for application to a state.
              Pre-compresses the operator if op_compression_kwargs not None
     """
     # Generate e^(-j*tau*hi)
     physical_legs_exp = expm(-1j * tau * hi)
     # Tensorial shape of hi for the two physical sites i and i+1 in global form
     physical_legs_tensor_shape = (self.shape[bond][0], self.shape[bond + 1][0],
                                   self.shape[bond][0], self.shape[bond + 1][0])
     physical_legs_exp = physical_legs_exp.reshape(physical_legs_tensor_shape)
     if not self.ancilla_sites:
         # Need only consider physical legs here
         mpo = mp.MPArray.from_array_global(physical_legs_exp, ndims=2)
     else:
         # Here we need to consider that there is an ancilla between the physical sites for which
         # physical_legs_exp was constructed.
         ldim_first_ancilla = self.shape[bond][1]
         ldim_second_ancilla = self.shape[bond + 1][1]
         # U^((s1, s3), (s1', s3')) * delta^(s2, s2')
         physical_and_first_ancilla = np.tensordot(physical_legs_exp, np.eye(ldim_first_ancilla), axes=0)
         # Slide indices s2 and s2' between s1 and s3/1' and s3' respectively
         physical_and_first_ancilla = np.moveaxis(physical_and_first_ancilla, [-2, -1], [1, 4])
         # Add identity for second ancilla bond
         mpo = mp.chain([mp.MPArray.from_array_global(physical_and_first_ancilla, ndims=2),
                         mp.eye(1, ldim_second_ancilla)]).group_sites(2)
     if self.op_compression_kwargs is not None:
         mpo.compress(**self.op_compression_kwargs)
     return mpo
Ejemplo n.º 28
0
def test_mppovm_pmf_as_array_pmps(
        nr_sites, local_dim, rank, startsite, width, rgen):
    if hasattr(local_dim, '__len__'):
        pdims = [d for d, _ in local_dim]
        mppaulis = mp.chain(
            povm.MPPovm.from_local_povm(povm.pauli_povm(d), 1)
            for d in pdims[startsite:startsite + width]
        )
    else:
        pdims = local_dim
        local_dim = (local_dim, local_dim)
        mppaulis = povm.MPPovm.from_local_povm(povm.pauli_povm(pdims), width)
    mppaulis = mppaulis.embed(nr_sites, startsite, pdims)
    pmps = factory.random_mpa(nr_sites, local_dim, rank,
                              dtype=np.complex_, randstate=rgen, normalized=True)
    rho = mpsmpo.pmps_to_mpo(pmps)
    expect_rho = mppaulis.pmf_as_array(rho, 'mpdo')

    for impl in ['default', 'pmps-ltr', 'pmps-symm']:
        expect_pmps = mppaulis.pmf_as_array(pmps, 'pmps', impl=impl)
        assert_array_almost_equal(expect_rho, expect_pmps, err_msg=impl)
Ejemplo n.º 29
0
    def _mpo_from_hi(self, hi, tau, lbond, rbond):
        """
            Generates
            U^{(s1, s2, s3), (s1', s2', s3')} = U^{(s1, s3), (s1', s3')} * delta^{s2, s2'}
            for each hi with U^{(s1, s3), (s1', s3')} = e^(-1j*\tau*hi)
            with s2/s2' ancilla site. And then appends a delta^{s4, s4'} at the end for the second ancilla.
        :param hi: Bond operator (tuple of (Eigvals, Eigvecs))
        :param tau: timestep for propagator
        :param lbond: Bond index (i) for the left site in hi
        :param rbond: Bond index for the right site in hi
        :return: e^(-1j*\tau*hi) in mpo form. A two site four legs (two physical, two ancilla on each site)
                 ready for application to a state
        """

        # Generate e^(-j*tau*hi)
        physical_legs_exp = expm(-1j * tau * hi)
        # Tensorial shape of hi for the two physical sites i and i+1 in global form
        physical_legs_tensor_shape = (self.shape[lbond][0],
                                      self.shape[rbond][0],
                                      self.shape[lbond][0],
                                      self.shape[rbond][0])
        physical_legs_exp = physical_legs_exp.reshape(
            physical_legs_tensor_shape)
        # Here we need to consider that there is an ancilla between the physical sites for which
        # physical_legs_exp was constructed.
        ldim_first_ancilla = self.shape[lbond][1]
        ldim_second_ancilla = self.shape[rbond][1]
        # U^((s1, s3), (s1', s3')) * delta^(s2, s2')
        physical_and_first_ancilla = np.tensordot(physical_legs_exp,
                                                  np.eye(ldim_first_ancilla),
                                                  axes=0)
        # Slide indices s2 and s2' between s1 and s3/1' and s3' respectively
        physical_and_first_ancilla = np.moveaxis(physical_and_first_ancilla,
                                                 [-2, -1], [1, 4])
        # Add identity for second ancilla bond
        mpo = mp.chain([
            mp.MPArray.from_array_global(physical_and_first_ancilla, ndims=2),
            mp.eye(1, ldim_second_ancilla)
        ])
        return self._compress_mpo(mpo)
Ejemplo n.º 30
0
def test_mppovmlist_est_pmf_from(
        method, n_samples, nr_sites, local_dim, rank, measure_width,
        local_width, nonuniform, splitpauli, rgen, eps=1e-10):
    """Verify that estimated probabilities from MPPovmList.est_pmf_from()
    are reasonable accurate

    """

    mps = factory.random_mps(nr_sites, local_dim, rank, rgen)
    mps.canonicalize()

    x, y = (povm.MPPovm.from_local_povm(p, 1)
            for p in povm.pauli_parts(local_dim)[:2])
    # POVM list with global support
    g_povm = povm.pauli_mpps(measure_width, local_dim).repeat(nr_sites)
    if nonuniform:
        add_povm = mp.chain((nr_sites - 1) * (x,) + (y,))
        g_povm = povm.MPPovmList(g_povm.mpps + (add_povm,))
    # POVM list with local support
    l_povm = povm.pauli_mpps if splitpauli else povm.pauli_mpp
    l_povm = l_povm(local_width, local_dim).block(nr_sites)
    samples = tuple(g_povm.sample(
        rgen, mps, n_samples, method, mode='mps', eps=eps))
    est_prob, n_samples = zip(*l_povm.est_pmf_from(g_povm, samples, eps))
    exact_prob = tuple(l_povm.pmf_as_array(mps, 'mps', eps))
    # Consistency check on n_samples: All entries should be equal
    # unless `nonuniform` is True.
    all_n_sam = np.concatenate(n_samples)
    assert (not (all_n_sam == all_n_sam[0]).all()) == nonuniform
    for n_sam, est, exact, mpp in zip(
            n_samples, est_prob, exact_prob, l_povm.mpps):
        assert est.shape == mpp.nsoutdims
        assert est.shape == exact.shape
        assert n_sam.shape == exact.shape
        # Compare against exact probabilities
        assert (abs(est - exact) / (3 / n_sam**0.5)).max() <= 1
Ejemplo n.º 31
0
def get_thermal_state(beta,
                      h_site,
                      mpa_type='mpo',
                      as_type='mparray',
                      to_cform=None):
    """
        Generates a thermal state e^(-beta*H_i)/Z in pmps form or mpo form site local Hamiltonians (H_i) without
        couplings.
    :param beta: Inverse temperature
    :param h_site: local operator(s) of Hamiltonian as single numpy array or iterable of numpy arrays for each site.
                   If passed as 2d arrays, they are treated as full matrices. If they are passed as
                   vectors, they are treated as diagonal elements of a matrix
    :param mpa_type: Type of mpa to evolve (allowed are 'pmps' and 'mpo')
    :param as_type: 'mparray' means as one combined mparray.
                    'mparray_list' as list of individual mparrays in the same order as the h_site were passed
                    'ndarray' returns the states as a list of ndarrays in the same order as the h_site were passed
    :param to_cform: Desired canonical form of the mparray (if as_type was selected to be 'mparray')
    :return: thermal state in pmps or mpo form (Any ancilla sites have the same dimension as the physical ones),
             info object from the propagation (if beta was set 0 it returns an empty dict)
    """
    assert mpa_type == 'mpo' or mpa_type == 'pmps'
    thermal_states = []
    if isinstance(h_site, np.ndarray):
        if len(h_site.shape) == 2:
            if not isdiag(h_site):
                state = expm(-beta * h_site)
            else:
                state = np.diag(np.exp(-beta * np.diag(h_site)))
        elif len(h_site.shape) == 1:
            state = np.diag(np.exp(-beta * h_site))
        else:
            raise AssertionError(
                'Passed numpy array must either be of matrix or vector shape')
        thermal_states.append(state)
    else:
        try:
            for site in h_site:
                if len(site.shape) == 2:
                    if not isdiag(site):
                        state = expm(-beta * site)
                        thermal_states.append(state / np.trace(state))
                    else:
                        state = np.diag(np.exp(-beta * np.diag(site)))
                        thermal_states.append(state / np.trace(state))
                elif len(site.shape) == 1:
                    state = np.diag(np.exp(-beta * site))
                    thermal_states.append(state / np.trace(state))
                else:
                    raise AssertionError(
                        'Passed numpy array(s) must either be of matrix or vector shape'
                    )
        except TypeError:
            raise AssertionError(
                'h_site must be single numpy array or iterable of numpy arrays'
            )
    if as_type == 'ndarray':
        return thermal_states
    elif as_type == 'mparray':
        if mpa_type == 'pmps':
            state = mp.chain(purify_states(thermal_states, to='mparray'))
        elif mpa_type == 'mpo':
            state = mp.chain([
                mp.MPArray.from_array_global(state, ndims=2)
                for state in thermal_states
            ])
        else:
            raise AssertionError('Invalid mpa_type')
        canonicalize_to(state, to_cform=to_cform)
        return state
    elif as_type == 'mparray_list':
        if mpa_type == 'pmps':
            return purify_states(thermal_states, to='mparray')
        elif mpa_type == 'mpo':
            return [
                mp.MPArray.from_array_global(state, ndims=2)
                for state in thermal_states
            ]
        else:
            raise AssertionError('Invalid mpa_type')
    else:
        raise AssertionError('Unrecognized return type')
Ejemplo n.º 32
0
def mp_from_array_repeat(array, nr_sites):
    """Generate a MPA representation of the `nr_sites`-fold tensor product of
    array.
    """
    mpa = mp.MPArray.from_array(array)
    return mp.chain(it.repeat(mpa, nr_sites))
Ejemplo n.º 33
0
def test_mppovm_est(
        method, n_samples, nr_sites, startsite, local_dim, rgen):
    """Check that estimates from .est_pmf() and .est_lfun() are reasonably
    accurate

    """
    rank = 3
    eps = 1e-10
    mps = factory.random_mps(nr_sites, local_dim, rank, rgen)
    mps.canonicalize()

    local_x = povm.x_povm(local_dim)
    local_y = povm.y_povm(local_dim)
    xx = povm.MPPovm.from_local_povm(local_x, 2)
    y = povm.MPPovm.from_local_povm(local_y, 1)
    mpp = mp.chain([xx, povm.MPPovm.eye([local_dim]), y]) \
            .embed(nr_sites, startsite, local_dim)

    p_exact = mpp.pmf_as_array(mps, 'mps', eps)
    p_exact = project_pmf(p_exact, eps, eps)

    cov_p_exact = np.diag(p_exact.flat) - np.outer(p_exact.flat, p_exact.flat)
    samples = mpp.sample(rgen, mps, n_samples, method, 4, 'mps', eps=eps)

    p_est = mpp.est_pmf(samples)
    ept, cov = mpp.est_lfun(None, None, samples, None, eps)
    ept_ex, single_cov_ex = mpp.lfun(None, None, mps, 'mps', eps)
    # The two exact values must match
    assert abs(ept_ex - p_exact.ravel()).max() <= eps
    # The two exact values must match
    assert abs(cov_p_exact - single_cov_ex).max() <= eps
    # The two estimates must match. This verifies that we have chosen
    # our estimator will be unbiased. (There are many other things we
    # might want to know about our estimator.)
    assert (ept == p_est.ravel()).all()
    # The estimate must be close to the true value
    assert abs(p_exact - p_est).max() <= 3 / n_samples**0.5

    cov_ex = cov_p_exact / n_samples
    # The covariances of the sample means (which we estimate here)
    # decrease by 1/n_samples, so we multiply with n_samples before
    # comparing to the rule-of-thumb for the estimation error.
    assert abs(cov - cov_ex).max() * n_samples <= 1 / n_samples**0.5

    funs = []
    nsoutdims = mpp.nsoutdims
    out = np.unravel_index(range(np.prod(nsoutdims)), nsoutdims)
    out = np.array(out).T[:, None, :].copy()
    for ind in range(np.prod(nsoutdims)):
        funs.append(lambda s, ind=ind: (s == out[ind]).all(1))

    # All probabilities sum to one, and we can estimate that well.
    coeff = np.ones(len(funs), dtype=float)
    # Test with dummy weights
    weights = np.ones(n_samples, dtype=float)
    sum_ept, sum_var = mpp.est_lfun(coeff, funs, samples, weights, eps)
    assert abs(sum_ept - 1.0) <= eps
    assert sum_var <= eps

    # Check a sum of probabilities with varying signs.
    coeff = ((-1)**rgen.choice(2, len(funs))).astype(float)
    sum_ept, sum_var = mpp.est_lfun(coeff, funs, samples, None, eps)
    ex_sum = np.inner(coeff, p_exact.flat)
    ex_var = np.inner(coeff, np.dot(cov_ex, coeff))
    assert abs(sum_ept - ex_sum) <= 5 / n_samples**0.5
    assert abs(sum_var - ex_var) * n_samples <= 5 / n_samples**0.5

    # Convert samples to counts and test again
    counts = mpp.est_pmf(samples, normalize=False, eps=eps)
    assert counts.sum() == n_samples
    count_samples = np.array(np.unravel_index(range(np.prod(mpp.nsoutdims)),
                                              mpp.nsoutdims)).T
    weights = counts.ravel()
    sum_ept2, sum_var2 = mpp.est_lfun(coeff, funs, count_samples, weights, eps)
    assert abs(sum_ept - sum_ept2) <= eps
    assert abs(sum_var - sum_var2) <= eps