示例#1
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
示例#2
0
 def _get_left_sweep(self, trotter_exponentials, post_swap, pre_swap):
     """
         Builds a list of tuples, which contain all the operators, that are necessary for the complete sweep
         from the system site, to the left edge of the chain and back.
         Sweeping to the left, we have: an evolution operator, followed by a post_swap
         Sweeping back to the right we have: a pre-swap, followed by an evolution operator
         Both combined to a single mpo.
         Each entry in the list contains a tuple, the first element of the tuple is the index of the left one
         of the two sites in the chain, for which the above mentioned operator applies. The second element is
         the operator itself
     :param trotter_exponentials: List of trotterized unitary operators
     :param post_swap: List of swap operators to be applied after the time-evo operators
     :param pre_swap: List of swap operators to be applied before the time-evo operators
     :return: List of tuples with entries as described above
     """
     # System site is back at the start
     left_sweep = []
     # sweep left
     for site in range(self.system_index - 1, 0, -1):
         left_sweep.append(
             (site,
              self._compress_mpo(
                  mp.dot(post_swap[site],
                         trotter_exponentials[site]).group_sites(2))))
     # left edge propagataion
     left_sweep.append((0, trotter_exponentials[0].group_sites(2)))
     # sweep back to the start
     for site in range(1, self.system_index):
         left_sweep.append((site,
                            self._compress_mpo(
                                mp.dot(trotter_exponentials[site],
                                       pre_swap[site]).group_sites(2))))
     return left_sweep
示例#3
0
 def fast_evolve(self, end=True):
     """
         Perform a Trotterized time evolution step by tau. Compress after each dot product. Renormalizes state after
         one full Trotter-step due to compression.
         Without setting the end flag, the evolution does not do a complete trotter step, and the resulting state is
         therefore not immediately useable. Not setting the end flag allows for leaving out some unnecessary
         double steps and can therefore be used for propagating intermediate steps
     :parameter end: Flag, which can be set to false, if the current propagation only needs to produce an
                     intermediate step, which is not used otherwise.
     :return:
     """
     self.last_state_norm = self.curr_state_norm
     if self.start and end:
         trotter_steps = self.propagator.trotter_steps
     elif self.start and not end:
         trotter_steps = self.propagator.start_trotter_steps
         self.start = False
     elif not self.start and end:
         trotter_steps = self.propagator.end_trotter_steps
         self.start = True
     else:
         trotter_steps = self.propagator.step_trotter_steps
     if not self.canonicalize_every_step:
         canonicalize_to(self.psi_t, to_cform=self.to_cform)
     for index, (trotter_ui, trotter_ui_adj) in enumerate(trotter_steps):
         self.psi_t = mp.dot(trotter_ui, self.psi_t, self.axes)
         if self.canonicalize_every_step:
             self.psi_t.compress(**self.state_compression_kwargs)
         else:
             if index < self.len_trotter_steps - 1:
                 self.psi_t.compress(**self.state_compression_kwargs_noc)
             else:
                 if self.canonicalize_last_step:
                     self.psi_t.compress(**self.state_compression_kwargs)
                 else:
                     self.psi_t.compress(
                         **self.state_compression_kwargs_noc)
         self.psi_t = mp.dot(self.psi_t, trotter_ui.adj(), self.axes)
         if self.canonicalize_every_step:
             self.psi_t.compress(**self.state_compression_kwargs)
         else:
             if index < self.len_trotter_steps - 1:
                 self.psi_t.compress(**self.state_compression_kwargs_noc)
             else:
                 if self.canonicalize_last_step:
                     self.psi_t.compress(**self.state_compression_kwargs)
                 else:
                     self.psi_t.compress(
                         **self.state_compression_kwargs_noc)
     self._normalize_state()
     self.curr_state_norm = mp.norm(self.psi_t)
     self.trotter_error += 2 * self.propagator.step_trotter_error
     self.stepno += 1
     if self.start and not end:
         self.start = False
     elif not self.start and end:
         self.start = True
    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()
    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()
示例#6
0
def operator_find(gen, N, relerr=1e-6):
    """
    Computes all unique stabilisers given the list from gen. Assumes Pauli operators.
    """
    # stabiliser storage
    stabilisers = []

    # max depth of stabiliser
    max_depth = len(gen) + 1

    # list of generator identifiers
    indices = range(0, len(gen))

    # iterate over length N stabiliser
    for i in range(1, max_depth):

        # generate every unique generator sequence - assumes Pauli based generators
        gen_combs = set(list(combinations(indices, i)))

        # iterate over index combinations
        for comb in gen_combs:

            # create new
            stab = mpojob([mpo_dict["id"]], [0] * N)
            for k in comb:

                # add generator to stabiliser sequence
                stab = mp.dot(gen[k], stab)

            # add new stabiliser to set
            stabilisers.append(stab)

    return stabilisers
示例#7
0
 def evolve(self):
     """
         Perform a Trotterized time evolution step by tau. Renormalizes state after
         one full Trotter-step due to compression.
     :return:
     """
     overlap = self.base_overlap
     if not self.canonicalize_every_step:
         canonicalize_to(self.psi_t, to_cform=self.to_cform)
     for index, trotter_ui in enumerate(self.propagator.trotter_steps):
         self.psi_t = mp.dot(trotter_ui, self.psi_t, self.axes)
         if self.canonicalize_every_step:
             overlap *= self.psi_t.compress(**self.state_compression_kwargs)
         else:
             if index < self.len_trotter_steps-1:
                 overlap *= self.psi_t.compress(**self.state_compression_kwargs_noc)
             else:
                 if self.canonicalize_last_step:
                     overlap *= self.psi_t.compress(**self.state_compression_kwargs)
                 else:
                     overlap *= self.psi_t.compress(**self.state_compression_kwargs_noc)
         self.pmps_compression_step += 1
         if self.pmps_compression_step == self.compress_sites_step:
             compress_pmps_sites(self.psi_t, relerr=self.compress_sites_relerr, rank=self.compress_sites_rank,
                                 stable=self.compress_sites_stable, to_cform=self.to_cform)
             self.pmps_compression_step = 0
     self._normalize_state()
     self.cumulative_overlap *= overlap
     self.last_overlap = overlap
     self.trotter_error += self.propagator.step_trotter_error
     self.stepno += 1
示例#8
0
def bures_mps(rho, sigma):
    """
    Computes the Bures angle for two matrix product states. Requires
    rho and sigma to be pure and input as MPOs
    """
    fid = mp.trace(mp.dot(rho, sigma))

    return np.arccos(np.clip(np.sqrt(fid), 0.0, 1.0))
示例#9
0
def test_povm_ic_mpa(nr_sites, local_dim, rank, rgen):
    # Check that the tensor product of the PauliGen POVM is IC.
    paulis = povm.pauli_povm(local_dim)
    inv_map = mp_from_array_repeat(paulis.linear_inversion_map, nr_sites)
    probab_map = mp_from_array_repeat(paulis.probability_map, nr_sites)
    reconstruction_map = mp.dot(inv_map, probab_map)

    eye = factory.eye(nr_sites, local_dim**2)
    assert mp.norm(reconstruction_map - eye) < 1e-5

    # Check linear inversion for a particular example MPA.
    # Linear inversion works for arbitrary matrices, not only for states,
    # so we test it for an arbitrary MPA.
    # Normalize, otherwise the absolute error check below will not work.
    mpa = factory.random_mpa(nr_sites, local_dim**2, rank,
                             dtype=np.complex_, randstate=rgen, normalized=True)
    probabs = mp.dot(probab_map, mpa)
    recons = mp.dot(inv_map, probabs)
    assert mp.norm(recons - mpa) < 1e-6
示例#10
0
文件: iht.py 项目: dsuess/pycsalgs
    def stepsize(A, g, X):
        X.normalize(left=len(X) - 1)

        # get the left-eigenvectors
        Us, _ = X.split(len(X) - 2)
        Us = Us.reshape([s + (1, ) for s in Us.pdims[:-1]] + [Us.pdims[-1]])
        proj = mp.dot(Us.conj(), Us, axes=(1, 1))
        g = mp.partialdot(proj, g, start_at=0)
        g.compress(**compargs)

        return stepfun(A, g, _)
示例#11
0
def sandwich(op, propagator, startsite=0):
    """
        Calculates the expectation value of an operator in mpo form with a reduced state: <psi_t_red| mpo |psi_t_red>.
        nof_sites can be inferred from len(mpo) here
    :param op: Operator for which to take expectation value as mpo
    :param propagator: TMPSPropagator object. The reduced state |psi_red> is generated from propagator.psi_t
    :param startsite: first site of the reduced state (may be negative, indexing works like for python lists)
    :return: expectation value <psi_t_red| mpo |psi_t_red>
    """
    reduced = reduction(propagator, startsite=startsite, nof_sites=len(op))
    # TODO: maybe compress first after dot?
    return mp.trace(mp.dot(op, reduced))
示例#12
0
 def evolve(self):
     """
         Perform a Trotterized time evolution step by tau. Compress after each dot product. Renormalizes state after
         one full timestep due to compression.
     :return:
     """
     self.last_state_norm = self.curr_state_norm
     # Can use the symmetry of the Suzuki-Trotter decomposition here to do it in one loop and
     # the fact, that for hermitian hi we have U = e^(-i*tau*hi), U^dag = U*
     for index, (trotter_ui, trotter_ui_adj) in enumerate(
             self.propagator.trotter_steps):
         self.psi_t = mp.dot(trotter_ui, self.psi_t, self.axes)
         if self.canonicalize_every_step:
             self.psi_t.compress(**self.state_compression_kwargs)
         else:
             if index < self.len_trotter_steps - 1:
                 self.psi_t.compress(**self.state_compression_kwargs_noc)
             else:
                 if self.canonicalize_last_step:
                     self.psi_t.compress(**self.state_compression_kwargs)
                 else:
                     self.psi_t.compress(
                         **self.state_compression_kwargs_noc)
         self.psi_t = mp.dot(self.psi_t, trotter_ui_adj, self.axes)
         if self.canonicalize_every_step:
             self.psi_t.compress(**self.state_compression_kwargs)
         else:
             if index < self.len_trotter_steps - 1:
                 self.psi_t.compress(**self.state_compression_kwargs_noc)
             else:
                 if self.canonicalize_last_step:
                     self.psi_t.compress(**self.state_compression_kwargs)
                 else:
                     self.psi_t.compress(
                         **self.state_compression_kwargs_noc)
     self._normalize_state()
     self.curr_state_norm = mp.norm(self.psi_t)
     self.trotter_error += 2 * self.propagator.step_trotter_error
     self.stepno += 1
示例#13
0
def sandwich_mpa(op, psi, mpa_type):
    """
        Calculates <op>_{psi} = tr(op*psi) for a state of specified mpa_type
    """
    if mpa_type == 'mps':
        psi = mp.mps_to_mpo(psi)
    elif mpa_type == 'pmps':
        psi = mp.pmps_to_mpo(psi)
    elif mpa_type == 'mpo':
        pass
    else:
        raise AssertionError('Unknown mpa_type')
    return mp.trace(mp.dot(op, psi))
示例#14
0
def sandwich_state(op, psi, mpa_type, startsite=0):
    """
        Calculates the expectation value of an operator in mpo form with a reduced state: <psi_red| mpo |psi_red>.
        nof_sites can be inferred from len(mpo) here
    :param op: Operator for which to take expectation value as mpo
    :param psi: State from which to generate reduced state |psi_red>
    :param startsite: first site of the reduced state
    :param mpa_type: mpa type of psi (mps, pmo or pmps)
    :return: expectation value <psi_red| mpo |psi_red>
    """
    reduced = state_reduction(psi,
                              mpa_type,
                              startsite=startsite,
                              nof_sites=len(op))
    # TODO: maybe compress first after dot?
    return mp.trace(mp.dot(op, reduced))
示例#15
0
def angle_estimate(stabilisers, state, N, shots=500):
    """
    Compute Bures angle given the perturbed state and the stabilisers for the True state
    """

    # initialise total
    stab_sum = 0.0
    # iterate over stabilisers
    for stab in stabilisers:
        # compute out probability given perturbed state
        prob = (1 + mp.trace(mp.dot(stab, state))) / 2
        stab_sum += mle(rand_res([1 - prob, prob], num=int(shots)))

    theta = theta_compute(stab_sum, N=N)
    # TODO: Fix this uncertainty calculation issue
    uncert = 0  # np.sqrt(var(theta, stab_sum, int(N)))
    return theta, uncert
示例#16
0
def calculate_expectation_values(states, observable):
    """
    Calculates the expectation values :math:`\\langle M \\rangle_i` of the the
    ``observable`` :math:`M` with respect to the ``states``
    :math:`\\{\\rho\\}`

    .. math::
        \\langle M \\rangle_i = \\text{tr}(\\rho_i M)

    For this function to work, the observable has to have the same dimension
    as the density matrix which represents the state, i.e. all states must
    have the same dimensions. This function is meant to work with a list of
    evolved states of the same system at different times.

    Args:
        states (list[mpnum.MPArray]): List of states :math:`\\{\\rho\\}`.
            They are assumed to be MPOs and already normalized
        observable (numpy.ndarray): The matrix representing the observable
            :math:`M` in global form (as opposed to local form. Global form
            is just the usual form to write matrices in QM. For more
            information, see the ``mpnum`` documentation.)

    Returns:
        list[mpnum.MPArray.dtype]: List of expectation values for the states
    """
    if not all(state.shape == states[0].shape for state in states):
        raise ValueError("The states in the provided list are not all of the "
                         "same shape.")
    if len(observable) != np.prod(np.array([_[0] for _ in states[0].shape])):
        raise ValueError("Observable dimensions and state dimensions do not "
                         "fit")
    expct_values = [
        mp.trace(
            mp.dot(
                state,
                tmps.matrix_to_mpo(observable, [[_[0]] * 2
                                                for _ in state.shape])))
        for state in states
    ]
    return expct_values
示例#17
0
def _time_evolution(state, us, step_numbers, subsystems, tau, method,
                    trotter_compr, v):
    """
    Implements time-evolution via Trotter-Suzuki decomposition

    Args:
        state (mpnum.MPArray):
            The state to be evolved in time
        us (list[mpnum.MPArray]):
            List of ordered operator exponentials for a single Trotter slice
        step_numbers (list[int]):
            List of time steps as generated by :func:`_times_to_steps`
        subsystems (list[list[int]]):
            Sites for which the subsystem states should be returned at the
            respective times
        tau (float):
            Duration of one Trotter slice. As defined in :func:`_times_to_steps`
        method (str):
            Which method to use. Either 'mps', 'mpo' or 'pmps'.
        trotter_compr (dict):
            Compression parameters used in the iterations of Trotter-Suzuki
            decomposition.
        v (int):
            Level of verbose output. 0 means no output, 1 means that some
            basic output showing the progress of calculations is produced. 2
            will in addition show the bond dimensions of the state after every
            couple of iterations, 3 will show bond dimensions after every
            Trotter iteration.

    Returns:
        list[list[float], list[list[int]], list[mpnum.MPArray]:
            A list with five items: (i) The list of times for which the density
            matrices have been computed (ii) The list indicating which
            subsystems of the system are returned at the respective time of the
            first list (iii) The list of density matrices as MPO or PMPS as
            mpnum.MPArray, depending on the input "method". If that was MPS, the
            full states will still be MPSs, the reduced ones will be MPOs.

    """
    c = Counter(step_numbers)

    times = []
    states = []
    compr_errors = []
    trot_errors = []

    var_compression = False
    if trotter_compr['method'] == 'var':
        var_compression = True
    accumulated_overlap = 1
    accumulated_trotter_error = 0

    for i in range(max(step_numbers) + 1):
        for j in range(c[i]):
            _append(times, states, compr_errors, trot_errors, tau, i, j,
                    step_numbers, subsystems, state, accumulated_overlap,
                    accumulated_trotter_error, method)
        for u in us:
            if var_compression:
                trotter_compr['startmpa'] = mp.MPArray.copy(state)
            state = mp.dot(u, state)
            accumulated_overlap *= state.compress(**trotter_compr)
        if method == 'mpo':
            for u in us:
                if var_compression:
                    trotter_compr['startmpa'] = mp.MPArray.copy(state)
                state = mp.dot(state, u.T.conj())
                accumulated_overlap *= state.compress(**trotter_compr)
        state = normalize(state, method)
        accumulated_trotter_error += tau**3
        if (v == 1 or v == 2) and np.sqrt(i + 1) % 1 == 0 and i < \
                step_numbers[-1]:
            print(str(i + 1) + " Trotter iterations finished...")
            if v == 2:
                print("Ranks: " + str(state.ranks))
        if v == 3 and i < step_numbers[-1]:
            print(str(i + 1) + " Trotter iterations finished...")
            print("Ranks: " + str(state.ranks))

    if v != 0:
        print("Done with time evolution")
    return times, subsystems, states  # , compr_errors, trot_errors
示例#18
0
def _time_evolution(state, us, ts, subsystems, tau, method, trotter_compr, v):
    """
    Implements time-evolution via Trotter-Suzuki decomposition



    Args:
        state (mpnum.MPArray):
            The state to be evolved in time
        us (list[mpnum.MPArray]):
            List of ordered operator exponentials for a single Trotter slice
        ts (list[int]):
            List of time steps as generated by _times_to_steps()
        subsystems (list[list[int]]):
            Sites for which the subsystem states should be returned at the
            respective times
        tau (float):
            Duration of one Trotter slice. As defined in _times_to_steps()
        method (str):
            Which method to use. Either 'mps', 'mpo' or 'pmps'.
        trotter_compr (dict):
            Compression parameters used in the iterations of Trotter-Suzuki
            decomposition.
        v (bool):
            Verbose (Yes) or not verbose (No). Will print what is going on vs.
            will not print anything.

    Returns:
        list[list[float], list[list[int]], list[mpnum.MPArray], list[float], list[float]]:
            A list with five items: (i)The list of times for which the density
            matrices have been computed (ii) The list indicating which
            subsystems of the system are returned at the respective
            time of the first list (iii) The list of density matrices as MPO
            or PMPS as mpnum.MPArray, depending on the input "method". If
            that was MPS, the full states will still be MPSs, the reduced
            ones will be MPOs. (iv) The errors due to compression during the
            procedure (v) The order of errors due to application of Trotter
            during the procedure
    """

    # NOTE: is this for debugging purposes?
    c = Counter(ts)

    times = []
    states = []
    compr_errors = []
    trot_errors = []

    var_compression = False
    if trotter_compr['method'] == 'var':
        var_compression = True
    accumulated_overlap = 1
    accumulated_trotter_error = 0

    for i in range(ts[-1] + 1):
        for j in range(c[i]):
            _append(times, states, compr_errors, trot_errors, tau, i, j, ts,
                    subsystems, state, accumulated_overlap,
                    accumulated_trotter_error, method)

        for u in us:
            if var_compression:
                trotter_compr['startmpa'] = mp.MPArray.copy(state)
            state = mp.dot(u, state)
            accumulated_overlap *= state.compress(**trotter_compr)

        if method == 'mpo':
            for u in us:
                if var_compression:
                    trotter_compr['startmpa'] = mp.MPArray.copy(state)
                state = mp.dot(state, u.T.conj())
                accumulated_overlap *= state.compress(**trotter_compr)

        state = normalize(state, method)
        accumulated_trotter_error += tau**3

        if v and np.sqrt(i) % 1 == 0 and i != 0:
            print(str(i) + " Trotter iterations finished...")
    if v:
        print("Done with time evolution")

    return times, subsystems, states, compr_errors, trot_errors
示例#19
0
def UniU_error(U, N, perturbations=100, exact=True, shots=8000):
    """
    Check estimation error for an input U, number of perturbations and wether to use exact or finite sampling.
    """

    # generate entangled states
    rho = bell_gen(N=N)

    # generate Bell state stabilisers
    bell_gstab = bell_stab_gen(N=N)

    # convert to MPO if required
    if type(U) != mp.mparray.MPArray:
        try:
            U = mp.MPArray.from_array_global(U.reshape([2] * N * 2), ndims=2)
        except ValueError:
            # catch operator dimension mismatch
            raise ValueError(
                "Cannot reshape unitary into MPO, check dimensions")

    # apply to entangled state
    rho = mp.dot(U, rho)

    # evolve generators under unitary
    gstab = [mp.dot(mp.dot(U, stb), U.adj()) for stb in bell_gstab]

    # generate stabiliser set
    stabilisers = operator_find(gstab, N=N)

    # apply to entangled state and convert to MPO for measurement phase
    rho = mp.mpsmpo.mps_to_mpo(rho)

    # calculate the estimation error for requested number of perturbations
    error = []

    # initialise random unitary generator
    U_perturb = random_MPUgen(N)

    for i in range(0, perturbations):
        print("Now computing unitary perturbation {}\r".format(i),
              end="",
              flush=True)

        # make a copy
        rho_c = rho.copy()

        # compute a local perturbation using generator
        U_p = next(U_perturb)

        # apply to Choi state
        rho_c = mp.dot(mp.dot(U_p, rho_c), U_p.adj())

        # compute expectation values exactly or with finite samples
        if exact:
            Q = 0.0

            # iterate over stabiliser measurements
            for stab_proj in stabilisers:
                # add to Q sum
                Q += (1 + mp.trace(mp.dot(stab_proj, rho_c))) / 2
                print(Q)

            # estimate angle
            a_est = theta_compute(Q, N=N)

        else:
            # estimate expectation values from finite number of outcomes
            a_est, a_uncert = angle_estimate(stabilisers,
                                             rho_c,
                                             N=N,
                                             shots=shots)

            if np.abs(np.real(bures_mps(rho, rho_c) - a_est)) > 0.5:
                print(
                    "High estimation error: {:.3f}, something has gone wrong".
                    format(a_est))
                continue

        # compute angle estimate error
        error.append(np.real(bures_mps(rho, rho_c) - a_est))

    if exact:
        # output average estimation error - should always be small (<1e-4 depending on MPO compression)
        print("Average estimation error for {} perturbations: {:.3f}".format(
            perturbations, np.real(np.mean(error))))
    else:
        # plot errors as histogram
        n, bins, patches = plt.hist(x=error,
                                    bins=len(error) // 10,
                                    alpha=0.65,
                                    color='red',
                                    histtype='step')
        plt.xlabel("Error")
        plt.ylabel("Counts")
        plt.title("Error distribution for {} qubit Clifford+T unitary".format(
            N // 2))
        plt.show()