def test_commuting_sets_by_zbasis():
    # complicated coefficients, overlap on single qubit
    coeff1 = 0.012870253243021476
    term1 = PauliTerm.from_list([('X', 1), ('Z', 2), ('Y', 3), ('Y', 5),
                                 ('Z', 6), ('X', 7)],
                                coefficient=coeff1)

    coeff2 = 0.13131672212575296
    term2 = PauliTerm.from_list([('Z', 0), ('Z', 6)], coefficient=coeff2)

    d_result = commuting_sets_by_zbasis(term1 + term2)
    d_expected = {
        ((0, 'Z'), (1, 'X'), (2, 'Z'), (3, 'Y'), (5, 'Y'), (6, 'Z'), (7, 'X')):
        [
            coeff1 * sX(1) * sZ(2) * sY(3) * sY(5) * sZ(6) * sX(7),
            coeff2 * sZ(0) * sZ(6)
        ]
    }
    assert d_result == d_expected

    # clumping terms relevant for H2 into same diagonal bases
    x_term = sX(0) * sX(1)
    z1_term = sZ(1)
    z2_term = sZ(0)
    zz_term = sZ(0) * sZ(1)
    z_terms = [z1_term, z2_term, zz_term]
    h2_hamiltonian = sum(z_terms) + x_term
    clumped_terms = commuting_sets_by_zbasis(h2_hamiltonian)
    true_set = {
        ((0, 'X'), (1, 'X')): {x_term},
        ((0, 'Z'), (1, 'Z')): set(z_terms)
    }

    for key, value in clumped_terms.items():
        assert set(value) == true_set[key]

    # clumping 4-qubit terms into same diagonal bases
    zzzz_terms = sZ(1) * sZ(2) + sZ(3) * sZ(4) + \
                 sZ(1) * sZ(3) + sZ(1) * sZ(3) * sZ(4)
    xzxz_terms = sX(1) * sZ(2) + sX(3) * sZ(4) + \
                 sX(1) * sZ(2) * sX(3) * sZ(4) + sX(1) * sX(3) * sZ(4)
    xxxx_terms = sX(1) * sX(2) + sX(2) + sX(3) * sX(4) + sX(4) + \
                 sX(1) * sX(3) * sX(4) + sX(1) * sX(4) + sX(1) * sX(2) * sX(3)
    yyyy_terms = sY(1) * sY(2) + sY(3) * sY(4) + sY(1) * sY(2) * sY(3) * sY(4)

    pauli_sum = zzzz_terms + xzxz_terms + xxxx_terms + yyyy_terms
    clumped_terms = commuting_sets_by_zbasis(pauli_sum)

    true_set = {
        ((1, 'Z'), (2, 'Z'), (3, 'Z'), (4, 'Z')): set(zzzz_terms),
        ((1, 'X'), (2, 'Z'), (3, 'X'), (4, 'Z')): set(xzxz_terms),
        ((1, 'X'), (2, 'X'), (3, 'X'), (4, 'X')): set(xxxx_terms),
        ((1, 'Y'), (2, 'Y'), (3, 'Y'), (4, 'Y')): set(yyyy_terms)
    }
    for key, value in clumped_terms.items():
        assert set(value) == true_set[key]
Exemple #2
0
def test_from_list():
    terms_list = [("X", 0), ("Y", 1), ("Z", 5)]
    term = reduce(lambda x, y: x * y, [PauliTerm(*x) for x in terms_list])

    pterm = PauliTerm.from_list(terms_list)
    assert pterm == term

    with pytest.raises(ValueError):
        # terms are not on disjoint qubits
        pterm = PauliTerm.from_list([("X", 0), ("Y", 0)])
def test_from_list():
    q = QubitPlaceholder.register(8)
    terms_list = [("X", q[0]), ("Y", q[1]), ("Z", q[5])]
    term = reduce(lambda x, y: x * y, [PauliTerm(*x) for x in terms_list])

    pterm = PauliTerm.from_list(terms_list)
    assert pterm == term

    with pytest.raises(ValueError):
        # terms are not on disjoint qubits
        pterm = PauliTerm.from_list([("X", q[0]), ("Y", q[0])])
Exemple #4
0
def test_term_grouping_weird_term():
    term1 = PauliTerm.from_list([('X', 1), ('Z', 2), ('Y', 3), ('Y', 5),
                                 ('Z', 6), ('X', 7)],
                                coefficient=0.012870253243021476)

    term2 = PauliTerm.from_list([('Z', 0), ('Z', 6)],
                                coefficient=0.13131672212575296)

    term_dictionary = commuting_sets_by_zbasis(term1 + term2)
    true_term_key = ((0, 'Z'), (1, 'X'), (2, 'Z'), (3, 'Y'), (5, 'Y'),
                     (6, 'Z'), (7, 'X'))
    assert list(term_dictionary.keys())[0] == true_term_key
Exemple #5
0
def pauli_terms_for_tpdm_aa(spatial_dim, transform=jordan_wigner):
    """
    Generate a set of pauli operators to measure to evaluate the alpha-alpha
    block of the 2-RDM

    Given a dictionary of expected values of Pauli terms, populate the
    alpha-beta block of the 2-RDM

    :param Int spatial_dim: Dimension of the spatial-orbital basis.
    :param transform: fermion-to-qubit transform from OpenFermion
    :return: List of PauliTerms that corresponds to set of pauli terms to
             measure to construct the 2-RDM.
    """
    # build basis lookup table
    bas_aa = {}
    cnt_aa = 0
    for p, q in product(range(spatial_dim), repeat=2):
        if q < p:
            bas_aa[(p, q)] = cnt_aa
            cnt_aa += 1

    pauli_terms_to_measure = []
    pauli_to_rdm = {}
    for p, q, r, s in product(range(spatial_dim), repeat=4):
        if p < q and s < r:
            # generator 1/sqrt(2) * (a_{p, \alpha}^{\dagger}
            # a_{q, \alpha}^{\dagger} -
            # a_{p, \beta}^{\dagger}a_{q, \beta}^{\dagger})
            spin_adapted_term = FermionOperator(
                ((2 * p, 1), (2 * q, 1), (2 * r, 0), (2 * s, 0))) - \
                                FermionOperator(((2 * p, 1), (2 * q, 1),
                                                 (2 * s, 0), (2 * r, 0))) - \
                                FermionOperator(((2 * q, 1), (2 * p, 1),
                                                 (2 * r, 0), (2 * s, 0))) + \
                                FermionOperator(((2 * q, 1), (2 * p, 1),
                                                 (2 * s, 0), (2 * r, 0)))
            spin_adapted_term *= 0.5

            tpdm_element_as_pauli = remove_imaginary_terms(
                qubitop_to_pyquilpauli(transform(spin_adapted_term)))
            for term in tpdm_element_as_pauli:
                pauli_terms_to_measure.append(term)

    for term in pauli_terms_to_measure:
        # convert term into numerically order pauli tensor term
        pauli_tensor_list = sorted(list(term.operations_as_set()),
                                   key=lambda x: x[0])
        rev_order_pauli_tensor_list = list(
            map(lambda x: (x[1], x[0]), pauli_tensor_list))
        pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                         coefficient=term.coefficient)

        if pauli_term.id() not in pauli_to_rdm.keys():
            pauli_to_rdm[pauli_term.id()] = pauli_term
        else:
            if (abs(pauli_to_rdm[pauli_term.id()].coefficient) < abs(
                    pauli_term.coefficient)):
                pauli_to_rdm[pauli_term.id()] = pauli_term

    return list(pauli_to_rdm.values())
Exemple #6
0
    def build_setting_memory_map(self, setting: ExperimentSetting) -> Dict[str, List[float]]:
        """
        Build the memory map corresponding to the state preparation and measurement specifications
        encoded in the provided ``ExperimentSetting``, taking into account the full set of qubits
        that are present in the ``Experiment`` object.

        :return: Memory map for state prep and measurement.
        """
        meas_qubits = self.get_meas_qubits()

        in_pt = PauliTerm.from_list([(op, meas_qubits.index(cast(int, q))) for q, op in setting._in_operator()])
        out_pt = PauliTerm.from_list([(op, meas_qubits.index(cast(int, q))) for q, op in setting.out_operator])

        preparation_map = pauli_term_to_preparation_memory_map(in_pt)
        measurement_map = pauli_term_to_measurement_memory_map(out_pt)

        return {**preparation_map, **measurement_map}
Exemple #7
0
def pauli_to_tpdm_bb(spatial_dim, pauli_to_coeff, transform=jordan_wigner):
    """
    Populate the beta-beta block of the 2-RDM

    :param Int spatial_dim: spatial basis set rank
    :param pauli_to_coeff: a map between the Pauli term label to the expected
                           value.
    :param transform: Openfermion fermion-to-qubit transform
    :return: the 2-RDM alpha-beta block of the 2-RDM
    """
    aa_dim = int(spatial_dim * (spatial_dim - 1) / 2)
    d2_aa = np.zeros((aa_dim, aa_dim), dtype=complex)

    # build basis lookup table
    bas_aa = {}
    cnt_aa = 0
    for p, q in product(range(spatial_dim), repeat=2):
        if p < q:
            bas_aa[(p, q)] = cnt_aa
            cnt_aa += 1

    for p, q, r, s in product(range(spatial_dim), repeat=4):
        if p < q and s < r:
            # generator 1/sqrt(2) * (a_{p, \alpha}^{\dagger}
            # a_{q, \alpha}^{\dagger} -
            # a_{p, \beta}^{\dagger}a_{q, \beta}^{\dagger})
            spin_adapted_term = FermionOperator(((2 * p + 1, 1), (2 * q + 1, 1),
                                                 (2 * r + 1, 0),
                                                 (2 * s + 1, 0))) - \
                                FermionOperator(((2 * p + 1, 1), (2 * q + 1, 1),
                                                 (2 * s + 1, 0),
                                                 (2 * r + 1, 0))) - \
                                FermionOperator(((2 * q + 1, 1), (2 * p + 1, 1),
                                                 (2 * r + 1, 0),
                                                 (2 * s + 1, 0))) + \
                                FermionOperator(((2 * q + 1, 1), (2 * p + 1, 1),
                                                 (2 * s + 1, 0),
                                                 (2 * r + 1, 0)))
            spin_adapted_term *= 0.5

            tpdm_element_as_pauli = remove_imaginary_terms(
                qubitop_to_pyquilpauli(transform(spin_adapted_term)))

            for term in tpdm_element_as_pauli:
                pauli_tensor_list = sorted(list(term.operations_as_set()),
                                           key=lambda x: x[0])
                rev_order_pauli_tensor_list = list(
                    map(lambda x: (x[1], x[0]), pauli_tensor_list))
                pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                                 coefficient=term.coefficient)
                try:
                    d2_aa[bas_aa[(p, q)], bas_aa[(s, r)]] += pauli_to_coeff[
                                                        pauli_term.id()] * \
                                                        pauli_term.coefficient
                except KeyError:
                    raise Warning("key was not in the coeff matrix.")
    return d2_aa
def pauli_term_from_string(pauli_string):
    """
    Convert a string to a Pauli term

    Strings look like `X0Z1Y3I4'

    :param pauli_string: String to be translated to a PauliTerm
    :return: PauliTerm
    """
    pauli_elements = pauli_string[::2]
    qubit_elements = list(map(int, pauli_string[1::2]))
    return PauliTerm.from_list(list(zip(pauli_elements, qubit_elements)))
def test_extended_get_constraints():
    weights = [0.1, 0.3, 0.5, -0.7]
    term1 = PauliTerm.from_list([("Z", 0), ("Z", 1)], 0.1)
    term2 = PauliTerm.from_list([("Z", 1), ("Z", 2)], 0.3)
    term3 = PauliTerm.from_list([("Z", 2), ("Z", 3)], 0.5)
    term4 = PauliTerm.from_list([("Z", 3), ("Z", 0)], -0.7)
    ham = PauliSum([term1, term2, term3, term4])
    p = 2

    params = ExtendedParams.linear_ramp_from_hamiltonian(ham, p)

    actual_constraints = params.get_constraints()

    expected_constraints = [(0, 2 * np.pi), (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi), (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi / weights[0]),
                            (0, 2 * np.pi / weights[1]),
                            (0, 2 * np.pi / weights[2]),
                            (0, 2 * np.pi / weights[3]),
                            (0, 2 * np.pi / weights[0]),
                            (0, 2 * np.pi / weights[1]),
                            (0, 2 * np.pi / weights[2]),
                            (0, 2 * np.pi / weights[3])]

    assert (np.allclose(expected_constraints, actual_constraints))

    cost_function = QAOACostFunctionOnWFSim(ham, params)
    np.random.seed(0)
    random_angles = np.random.uniform(-100, 100, size=len(params.raw()))
    value = cost_function(random_angles)

    normalised_angles = [
        random_angles[i] % actual_constraints[i][1]
        for i in range(len(params.raw()))
    ]
    normalised_value = cost_function(normalised_angles)

    assert (np.allclose(value, normalised_value))
Exemple #10
0
def get_diagonalizing_basis(list_of_pauli_terms):
    """
    Find the Pauli Term with the most non-identity terms

    :param list_of_pauli_terms: List of Pauli terms to check
    :return: The highest weight Pauli Term
    :rtype: PauliTerm
    """
    qubit_ops = set(reduce(lambda x, y: x + y,
                       [list(term._ops.items()) for term in list_of_pauli_terms]))
    qubit_ops = sorted(list(qubit_ops), key=lambda x: x[0])

    return PauliTerm.from_list(list(map(lambda x: tuple(reversed(x)), qubit_ops)))
Exemple #11
0
def pauli_terms_for_tpdm_ab(spatial_dim, transform=jordan_wigner):
    """
    Build the alpha-beta block of the 2-RDM

    Note: OpenFermion ordering is used.  The 2-RDM(alpha-beta) block
          is spatial_dim**2 linear size. (DOI: 10.1021/acs.jctc.6b00190)

    :param spatial_dim: rank of spatial orbitals in the basis set.
    :param transform: type of fermion-to-qubit transformation.
    :return: list of Pauli terms to measure required to construct a the alpha-
             beta block.
    """
    # build basis look up table
    bas_ab = {}
    cnt_ab = 0
    # iterate over spatial orbital indices
    for p, q in product(range(spatial_dim), repeat=2):
        bas_ab[(p, q)] = cnt_ab
        cnt_ab += 1

    pauli_terms_to_measure = []
    pauli_to_rdm = {}
    for p, q, r, s in product(range(spatial_dim), repeat=4):
        spin_adapted_term = FermionOperator(
            ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)))

        tpdm_element_as_pauli = remove_imaginary_terms(
            qubitop_to_pyquilpauli(transform(spin_adapted_term)))
        for term in tpdm_element_as_pauli:
            pauli_terms_to_measure.append(term)

    for term in pauli_terms_to_measure:
        # convert term into numerically order pauli tensor term
        pauli_tensor_list = sorted(list(term.operations_as_set()),
                                   key=lambda x: x[0])
        rev_order_pauli_tensor_list = list(
            map(lambda x: (x[1], x[0]), pauli_tensor_list))
        pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                         coefficient=term.coefficient)

        if pauli_term.id() not in pauli_to_rdm.keys():
            pauli_to_rdm[pauli_term.id()] = pauli_term
        else:
            if (abs(pauli_to_rdm[pauli_term.id()].coefficient) < abs(
                    pauli_term.coefficient)):
                pauli_to_rdm[pauli_term.id()] = pauli_term

    return list(pauli_to_rdm.values())
def str_to_pauli_term(pauli_str: str, qubit_labels=None):
    """
    Convert a string into a :class:`~pyquil.paulis.PauliTerm`.

    >>> str_to_pauli_term('XY', [])

    :param str pauli_str: The input string, made of of 'I', 'X', 'Y' or 'Z'
    :param qubit_labels: The integer labels for the qubits in the string
        If None, default to the range of the length of pauli_str.
    :return: the corresponding PauliTerm
    :rtype: pyquil.paulis.PauliTerm
    """
    if qubit_labels is None:
        qubit_labels = [qubit for qubit in range(len(pauli_str))]

    pauli_term = PauliTerm.from_list(list(zip(pauli_str, qubit_labels)))
    return pauli_term
def str_to_pauli_term(pauli_str: str, qubit_labels=None):
    """
    Convert a string into a pyquil.paulis.PauliTerm.

    >>> str_to_pauli_term('XY', [])

    :param str pauli_str: The input string, made of of 'I', 'X', 'Y' or 'Z'
    :param set qubit_labels: The integer labels for the qubits in the string, given in reverse
    order. If None, default to the range of the length of pauli_str.
    :return: the corresponding PauliTerm
    :rtype: pyquil.paulis.PauliTerm
    """
    if qubit_labels is None:
        labels_list = [qubit for qubit in reversed(range(len(pauli_str)))]
    else:
        labels_list = sorted(qubit_labels)[::-1]
    pauli_term = PauliTerm.from_list(list(zip(pauli_str, labels_list)))
    return pauli_term
Exemple #14
0
def pauli_to_tpdm_ab(spatial_dim, pauli_to_coeff, transform=jordan_wigner):
    """
    Populate the alpha-beta block of the 2-RDM

    Given a dictionary of expected values of Pauli terms, populate the
    alpha-beta block of the 2-RDM

    :param Int spatial_dim: spatial basis set rank
    :param pauli_to_coeff: a map between the Pauli term label to the expected
                           value.
    :param transform: Openfermion fermion-to-qubit transform
    :return: the 2-RDM alpha-beta block of the 2-RDM
    """
    d2_ab = np.zeros((spatial_dim**2, spatial_dim**2), dtype=complex)
    # build basis look up table
    bas_ab = {}
    cnt_ab = 0
    # iterate over spatial orbital indices
    for p, q in product(range(spatial_dim), repeat=2):
        bas_ab[(p, q)] = cnt_ab
        cnt_ab += 1

    for p, q, r, s in product(range(spatial_dim), repeat=4):
        spin_adapted_term = FermionOperator(
            ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)))

        tpdm_element_as_pauli = remove_imaginary_terms(
            qubitop_to_pyquilpauli(transform(spin_adapted_term)))

        for term in tpdm_element_as_pauli:
            pauli_tensor_list = sorted(list(term.operations_as_set()),
                                       key=lambda x: x[0])
            rev_order_pauli_tensor_list = list(
                map(lambda x: (x[1], x[0]), pauli_tensor_list))
            pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                             coefficient=term.coefficient)
            try:
                d2_ab[bas_ab[(p, q)], bas_ab[(s, r)]] += pauli_to_coeff[
                                                         pauli_term.id()] * \
                                                         pauli_term.coefficient
            except KeyError:
                raise Warning("key was not in the coeff matrix.")
    return d2_ab
Exemple #15
0
def pauli_to_tpdm(dim, pauli_to_coeff, transform=jordan_wigner):
    """
    Construct the 2-RDM from expected values of Pauli operators

    Construct the 2-RDM by looping over the 2-RDM and loading up the
    coefficients for the expected values of each transformed Pauli operator.
    We assume the fermionic ladder operators are transformed via Jordan-Wigner.
    This constraint can be relaxed later.

    We don't check that the `pauli_expected` dictionary contains an
    informationally complete set of expected values.  This is useful for
    testing if under sampling the 2-RDM is okay if a projection technique is
    included in the calculation.

    :param Int dim: spin-orbital basis dimension
    :param Dict pauli_to_coeff: a map from pauli term ID's to
    :param func transform: optional argument defining how to transform
                           fermionic operators into Pauli operators
    """
    tpdm = np.zeros((dim, dim, dim, dim), dtype=complex)
    for p, q, r, s in product(range(dim), repeat=4):
        if p != q and r != s:
            tpdm_element = FermionOperator(((p, 1), (q, 1), (r, 0), (s, 0)))
            tpdm_element_as_pauli = remove_imaginary_terms(
                qubitop_to_pyquilpauli(transform(tpdm_element)))

            for term in tpdm_element_as_pauli:
                pauli_tensor_list = sorted(list(term.operations_as_set()),
                                           key=lambda x: x[0])
                rev_order_pauli_tensor_list = list(
                    map(lambda x: (x[1], x[0]), pauli_tensor_list))
                pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                                 coefficient=term.coefficient)
                try:
                    tpdm[p, q, r, s] += pauli_to_coeff[pauli_term.id()] * \
                                        pauli_term.coefficient
                except KeyError:
                    raise Warning("key was not in the coeff matrix.")
    return tpdm
def address_qubits_hamiltonian(
        hamiltonian: PauliSum,
        qubit_mapping: Dict[QubitPlaceholder, Union[Qubit, int]]
        ) -> PauliSum:
    """Map Qubit Placeholders to ints in a PauliSum.

    Parameters
    ----------
    hamiltonian:
        The PauliSum.
    qubit_mapping:
        A qubit_mapping. e.g. provided by pyquil.quil.get_default_qubit_mapping.

    Returns
    -------
    PauliSum
        A PauliSum with remapped Qubits.

    Note
    ----
    This code relies completely on going all the way down the rabbits hole
    with for loops. It would be preferable to have this functionality in
    pyquil.paulis.PauliSum directly
    """
    out = PauliTerm("I", 0, 0)
    # Make sure we map to integers and not to Qubits(), these are not
    # supported by pyquil.paulis.PauliSum().
    if set([Qubit]) == set(map(type, qubit_mapping.values())):
        qubit_mapping = dict(zip(qubit_mapping.keys(),
                                 [q.index for q in qubit_mapping.values()]))
    # And change all of them
    for term in hamiltonian:
        coeff = term.coefficient
        ops = []
        for factor in term:
            ops.append((factor[1], qubit_mapping[factor[0]]))
        out += PauliTerm.from_list(ops, coeff)
    return out
Exemple #17
0
def pauli_terms_for_tpdm(dim, transform=jordan_wigner):
    """
    Generate a set of pauli operators to measure to evaluate the 2-RDM

    :param Int dim: Dimension of the spin-orbital basis.
    :param transform: fermion-to-qubit transform from OpenFermion
    :return: List of PauliTerms that corresponds to set of pauli terms to
             measure to construct the 2-RDM.
    """

    # first make a map between pauli terms and elements of the 2-RDM
    pauli_to_rdm = {}
    pauli_terms_to_measure = []
    for p, q, r, s in product(range(dim), repeat=4):
        if p != q and r != s:
            tpdm_element = FermionOperator(((p, 1), (q, 1), (r, 0), (s, 0)))
            tpdm_element_as_pauli = remove_imaginary_terms(
                qubitop_to_pyquilpauli(transform(tpdm_element)))
            for term in tpdm_element_as_pauli:
                pauli_terms_to_measure.append(term)

    for term in pauli_terms_to_measure:
        # convert term into numerically order pauli tensor term
        pauli_tensor_list = sorted(list(term.operations_as_set()),
                                   key=lambda x: x[0])
        rev_order_pauli_tensor_list = list(
            map(lambda x: (x[1], x[0]), pauli_tensor_list))
        pauli_term = PauliTerm.from_list(rev_order_pauli_tensor_list,
                                         coefficient=term.coefficient)

        if pauli_term.id() not in pauli_to_rdm.keys():
            pauli_to_rdm[pauli_term.id()] = pauli_term
        else:
            if (abs(pauli_to_rdm[pauli_term.id()].coefficient) < abs(
                    pauli_term.coefficient)):
                pauli_to_rdm[pauli_term.id()] = pauli_term

    return list(pauli_to_rdm.values())
Exemple #18
0
def _max_key_overlap(pauli_term, diagonal_sets):
    """
    Calculate the max overlap of a pauli term ID with keys of diagonal_sets

    Returns a different key if we find any collisions.  If no collisions is
    found then the pauli term is added and the key is updated so it has the
    largest weight.

    :param pauli_term:
    :param diagonal_sets:
    :return: dictionary where key value pair is tuple indicating diagonal basis
             and list of PauliTerms that share that basis
    :rtype: dict
    """
    # a lot of the ugliness comes from the fact that
    # list(PauliTerm._ops.items()) is not the appropriate input for
    # Pauliterm.from_list()
    for key in list(diagonal_sets.keys()):
        pauli_from_key = PauliTerm.from_list(
            list(map(lambda x: tuple(reversed(x)), key)))
        if diagonal_basis_commutes(pauli_term, pauli_from_key):
            updated_pauli_set = diagonal_sets[key] + [pauli_term]
            diagonalizing_term = get_diagonalizing_basis(updated_pauli_set)
            if len(diagonalizing_term) > len(key):
                del diagonal_sets[key]
                new_key = tuple(sorted(diagonalizing_term._ops.items(),
                                       key=lambda x: x[0]))
                diagonal_sets[new_key] = updated_pauli_set
            else:
                diagonal_sets[key] = updated_pauli_set
            return diagonal_sets
    # made it through all keys and sets so need to make a new set
    else:
        # always need to sort because new pauli term functionality
        new_key = tuple(sorted(pauli_term._ops.items(), key=lambda x: x[0]))
        diagonal_sets[new_key] = [pauli_term]
        return diagonal_sets
def pauli_term_relabel(pauli_sum, label_map):
    """
    Relabel the elements of a pauli_sum via the `label_map`

    :param pauli_sum: pauli sum to relabel.  this can be a PauliTerm, PauliSum,
                      or even better a LIST!
    :param label_map: a dictionary mapping old label to new label
    :return: a list of pauli_terms relabeled
    """
    if isinstance(pauli_sum, PauliTerm):
        pauli_sum = PauliSum([pauli_sum])

    if isinstance(pauli_sum, PauliSum):
        pauli_sum = pauli_sum.terms

    relabeled_terms = []
    for term in pauli_sum:
        new_term_as_list = []
        for qlabel, pauli_element in term._ops.items():
            new_term_as_list.append((pauli_element, label_map[qlabel]))
        relabeled_terms.append(
            PauliTerm.from_list(new_term_as_list,
                                coefficient=term.coefficient))
    return relabeled_terms
# numpy and scipy libraries
import numpy as np
from scipy.optimize import minimize
# pyQuil Library
from pyquil.quil import Program
from pyquil.api import QVMConnection
from pyquil.gates import RY, CNOT
from pyquil.paulis import PauliSum, PauliTerm
# Grove Library
from grove.pyvqe.vqe import VQE

# Defining Hamiltonian
hamiltonian = PauliSum([
              PauliTerm.from_list([("I", 0),("I", 1)], coefficient=-3.8505),
              PauliTerm.from_list([("I", 0),("X", 1)], coefficient=-0.2288),
              PauliTerm.from_list([("I", 0),("Z", 1)], coefficient=-1.0466),
              PauliTerm.from_list([("X", 0),("I", 1)], coefficient=-0.2288),
              PauliTerm.from_list([("X", 0),("X", 1)], coefficient=0.2613),
              PauliTerm.from_list([("X", 0),("Z", 1)], coefficient=0.2288),
              PauliTerm.from_list([("Z", 0),("I", 1)], coefficient=-1.0466),
              PauliTerm.from_list([("Z", 0),("X", 1)], coefficient=0.2288),
              PauliTerm.from_list([("Z", 0),("Z", 1)], coefficient=0.2356)])
print(hamiltonian)

# Define ansatz
n_qubits = 2
depth = 3

def ansatz(params): 
    qp = Program()
    for i in range(depth):   
Exemple #21
0
 def ZI(params):
     zi = PauliSum([
         PauliTerm.from_list([('Z', 0), ('I', 1)],
                             coefficient=molecule_coefficients[2])
     ])
     return VQE.expectation(ansatz(params), zi, None, qvm)
    def experiment(
        self,
        experiment: Experiment,
        memory_map: Optional[Mapping[str, Sequence[Union[int, float]]]] = None,
    ) -> List[ExperimentResult]:
        """
        Run an ``Experiment`` on a QVM or QPU backend. An ``Experiment`` is composed of:

            - A main ``Program`` body (or ansatz).
            - A collection of ``ExperimentSetting`` objects, each of which encodes a particular
              state preparation and measurement.
            - A ``SymmetrizationLevel`` for enacting different readout symmetrization strategies.
            - A number of shots to collect for each (unsymmetrized) ``ExperimentSetting``.

        Because the main ``Program`` is static from run to run of an ``Experiment``, we can leverage
        our platform's Parametric Compilation feature. This means that the ``Program`` can be
        compiled only once, and the various alterations due to state preparation, measurement,
        and symmetrization can all be realized at runtime by providing a ``memory_map``. Thus, the
        steps in the ``experiment`` method are as follows:

            1. Generate a parameterized program corresponding to the ``Experiment``
                (see the ``Experiment.generate_experiment_program()`` method for more
                details on how it changes the main body program to support state preparation,
                measurement, and symmetrization).
            2. Compile the parameterized program into a parametric (binary) executable, which
                   contains declared variables that can be assigned at runtime.

            3. For each ``ExperimentSetting`` in the ``Experiment``, we repeat the following:

                a. Build a collection of memory maps that correspond to the various state
                   preparation, measurement, and symmetrization specifications.
                b. Run the parametric executable on the QVM or QPU backend, providing the memory map
                   to assign variables at runtime.
                c. Extract the desired statistics from the classified bitstrings that are produced
                   by the QVM or QPU backend, and package them in an ``ExperimentResult`` object.

            4. Return the list of ``ExperimentResult`` objects.

        This method is extremely useful shorthand for running near-term applications and algorithms,
        which often have this ansatz + settings structure.

        :param experiment: The ``Experiment`` to run.
        :param memory_map: A dictionary mapping declared variables / parameters to their values.
            The values are a list of floats or integers. Each float or integer corresponds to
            a particular classical memory register. The memory map provided to the ``experiment``
            method corresponds to variables in the main body program that we would like to change
            at runtime (e.g. the variational parameters provided to the ansatz of the variational
            quantum eigensolver).
        :return: A list of ``ExperimentResult`` objects containing the statistics gathered
            according to the specifications of the ``Experiment``.
        """
        experiment_program = experiment.generate_experiment_program()
        executable = self.compile(experiment_program)

        if memory_map is None:
            memory_map = {}

        results = []
        for settings in experiment:
            if len(settings) > 1:
                raise ValueError("settings must be of length 1")
            setting = settings[0]

            qubits = cast(List[int], setting.out_operator.get_qubits())
            experiment_setting_memory_map = experiment.build_setting_memory_map(
                setting)
            symmetrization_memory_maps = experiment.build_symmetrization_memory_maps(
                qubits)
            merged_memory_maps = merge_memory_map_lists(
                [experiment_setting_memory_map], symmetrization_memory_maps)

            all_bitstrings = []
            for merged_memory_map in merged_memory_maps:
                final_memory_map = {**memory_map, **merged_memory_map}
                self.qam.reset()
                bitstrings = self.run(executable, memory_map=final_memory_map)

                if "symmetrization" in final_memory_map:
                    bitmask = np.array(
                        np.array(final_memory_map["symmetrization"]) / np.pi,
                        dtype=int)
                    bitstrings = np.bitwise_xor(bitstrings, bitmask)
                all_bitstrings.append(bitstrings)
            symmetrized_bitstrings = np.concatenate(all_bitstrings)

            joint_expectations = [experiment.get_meas_registers(qubits)]
            if setting.additional_expectations:
                joint_expectations += setting.additional_expectations
            expectations = bitstrings_to_expectations(
                symmetrized_bitstrings, joint_expectations=joint_expectations)

            means = cast(np.ndarray, np.mean(expectations, axis=0))
            std_errs = np.std(expectations, axis=0, ddof=1) / np.sqrt(
                len(expectations))

            joint_results = []
            for qubit_subset, mean, std_err in zip(joint_expectations, means,
                                                   std_errs):
                out_operator = PauliTerm.from_list([
                    (setting.out_operator[i], i) for i in qubit_subset
                ])
                s = ExperimentSetting(
                    in_state=setting.in_state,
                    out_operator=out_operator,
                    additional_expectations=None,
                )
                r = ExperimentResult(setting=s,
                                     expectation=mean,
                                     std_err=std_err,
                                     total_counts=len(expectations))
                joint_results.append(r)

            result = ExperimentResult(
                setting=setting,
                expectation=joint_results[0].expectation,
                std_err=joint_results[0].std_err,
                total_counts=joint_results[0].total_counts,
                additional_results=joint_results[1:],
            )
            results.append(result)

        return results
Exemple #23
0
def test_relabel_terms():
    """
    Check if Pauli term relabeling works
    """
    n_qubits = 4
    test_operator = PauliTerm.from_list([('Y', 0), ('X', 1), ('Y', 2),
                                         ('X', 3)])
    test_operator += PauliTerm.from_list([('Y', 0), ('X', 1), ('X', 2),
                                          ('Y', 3)])
    test_operator += PauliTerm.from_list([('Y', 0), ('Y', 1), ('Y', 2),
                                          ('Y', 3)])
    test_operator += PauliTerm.from_list([('Y', 0), ('Y', 1), ('X', 2),
                                          ('X', 3)])
    test_operator += PauliTerm.from_list([('X', 0), ('X', 1), ('Y', 2),
                                          ('Y', 3)])
    test_operator += PauliTerm.from_list([('X', 0), ('X', 1), ('X', 2),
                                          ('X', 3)])
    test_operator += PauliTerm.from_list([('X', 0), ('Y', 1), ('Y', 2),
                                          ('X', 3)])
    test_operator += PauliTerm.from_list([('X', 0), ('Y', 1), ('X', 2),
                                          ('Y', 3)])

    true_operator = PauliTerm.from_list([('Y', 4), ('X', 5), ('Y', 6),
                                         ('X', 7)])
    true_operator += PauliTerm.from_list([('Y', 4), ('X', 5), ('X', 6),
                                          ('Y', 7)])
    true_operator += PauliTerm.from_list([('Y', 4), ('Y', 5), ('Y', 6),
                                          ('Y', 7)])
    true_operator += PauliTerm.from_list([('Y', 4), ('Y', 5), ('X', 6),
                                          ('X', 7)])
    true_operator += PauliTerm.from_list([('X', 4), ('X', 5), ('Y', 6),
                                          ('Y', 7)])
    true_operator += PauliTerm.from_list([('X', 4), ('X', 5), ('X', 6),
                                          ('X', 7)])
    true_operator += PauliTerm.from_list([('X', 4), ('Y', 5), ('Y', 6),
                                          ('X', 7)])
    true_operator += PauliTerm.from_list([('X', 4), ('Y', 5), ('X', 6),
                                          ('Y', 7)])

    label_map = dict(zip(range(n_qubits), range(n_qubits, 2 * n_qubits + 1)))
    relabeled_terms = pauli_term_relabel(test_operator.terms, label_map)
    assert sum(relabeled_terms) == true_operator
Exemple #24
0
def test_term_from_string():
    pauli_string = 'X0Z1Y3I4'
    true_pauli_term = PauliTerm.from_list([('X', 0), ('Z', 1), ('Y', 3)])
    test_pauli_term = pauli_term_from_string(pauli_string)
    assert test_pauli_term == true_pauli_term
    # input molecule and basis set (this is the only user input necessary to perform VQE
    # on the Rigetti quantum computer with a UCC ansatz)
    name = 'helium'
    basis = 'sto-3g'

    # # this input would be then converted to the correct Hamiltonian using the q_chem library
    # # developed here at River Lane
    # import q_chem
    # _, _, hamiltonian, _, _ = q_chem.run_chem(name, basis)

    # in this trial, we instead explicitly supply the hamiltonian for "helium, sto-3g"
    hamiltonian = \
        -1.6678202144537553*PauliTerm('I',0) + \
        0.7019459893849936*PauliTerm('Z',0) + \
        0.263928235683768058*PauliTerm.from_list([("Z", 0), ("Z", 1)]) + \
        0.7019459893849936*PauliTerm('Z',1)

    # Transforming the Rigetti-style hamiltonian into numpy-friendly dense form
    # to compute the energy classically:
    #
    #qubitOp                 = pyquilpauli_to_qubitop(hamiltonian)
    #sparse_hamiltonian_jw   = get_sparse_operator(qubitOp)
    #dense_hamiltonian_jw    = sparse_hamiltonian_jw.todense()
    #classical_energy        = np.amin(la.eigh(dense_hamiltonian_jw)[0])

    # Due to difficulty in reliably installing forestopenfermion + openfermion,
    # the code above is temporarily commented out.
    # The following result has been obtained using the code above:
    #
    classical_energy = -2.8077839575399746
        qvm.expectation(ansatz(optimizer_output['x']),
                        operator_programs=progs))
    optimizer_output['fun_exact'] = np.real_if_close(
        np.dot(coefs, expect_coeffs))

    print('Total Q seconds = %f' % report['total_q_seconds'])
    print('Total Q shots = %d' % report['total_q_shots'])
    print('Total seconds = %f' % report['total_seconds'])

    return (optimizer_output, report)


helium_sto_3g_hamiltonian = \
        -1.6678202144537553*PauliTerm('I',0) + \
        0.7019459893849936*PauliTerm('Z',0) + \
        0.263928235683768058*PauliTerm.from_list([("Z", 0), ("Z", 1)]) + \
        0.7019459893849936*PauliTerm('Z',1)


def helium_tiny_ansatz(ab):
    "in this trial, we also explicitly supply the UCC ansatz"

    return Program(X(0), X(1), RX(np.pi / 2, 0), H(1), CNOT(0, 1),
                   RZ(ab[0])(1), CNOT(0, 1),
                   RX(-np.pi / 2)(0), H(1), H(0),
                   RX(np.pi / 2)(1), CNOT(0, 1),
                   RZ(ab[1])(1), CNOT(0, 1), H(0), RX(-np.pi / 2, 1))


hydrogen_sto_3g_hamiltonian = \
        0.18128880839426165 * PauliTerm.from_list([("X", 0), ("X", 1)]) + \
Exemple #27
0
def estimate_pauli_sum(pauli_terms, basis_transform_dict, program,
                       variance_bound, quantum_resource,
                       commutation_check=True):
    """
    Estimate the mean of a sum of pauli terms to set variance

    The sample variance is calculated by

    .. math::
        \begin{align}
        \mathrm{Var}[\hat{\langle H \rangle}] = \sum_{i, j}h_{i}h_{j}
        \mathrm{Cov}(\hat{\langle P_{i} \rangle}, \hat{\langle P_{j} \rangle})
        \end{align}

    The expectation value of each Pauli operator (term and coefficient) is
    also returned.  It can be accessed through the named-tuple field
    `pauli_expectations'.

    :param pauli_terms: list of pauli terms to measure simultaneously or a
                        PauliSum object
    :param basis_transform_dict: basis transform dictionary where the key is
                                 the qubit index and the value is the basis to
                                 rotate into. Valid basis is [I, X, Y, Z].
    :param program: program generating a state to sample from
    :param variance_bound:  Bound on the variance of the estimator for the
                            PauliSum. Remember this is the SQUARE of the
                            standard error!
    :param quantum_resource: quantum abstract machine object
    :param Bool commutation_check: Optional flag toggling a safety check
                                   ensuring all terms in `pauli_terms`
                                   commute with each other
    :return: estimated expected value, expected value of each Pauli term in
             the sum, covariance matrix, variance of the estimator, and the
             number of shots taken.  The objected returned is a named tuple with
             field names as follows: expected_value, pauli_expectations,
             covariance, variance, n_shots.
             `expected_value' == coef_vec.dot(pauli_expectations)
    :rtype: EstimationResult
    """
    if not isinstance(pauli_terms, (list, PauliSum)):
        raise TypeError("pauli_terms needs to be a list or a PauliSum")

    if isinstance(pauli_terms, PauliSum):
        pauli_terms = pauli_terms.terms

    # check if each term commutes with everything
    if commutation_check:
        if len(commuting_sets(sum(pauli_terms))) != 1:
            raise CommutationError("Not all terms commute in the expected way")

    pauli_for_rotations = PauliTerm.from_list(
        [(value, key) for key, value in basis_transform_dict.items()])

    program += get_rotation_program(pauli_for_rotations)

    qubits = sorted(list(basis_transform_dict.keys()))
    for qubit in qubits:
        program.inst(MEASURE(qubit, qubit))

    coeff_vec = np.array(
        list(map(lambda x: x.coefficient, pauli_terms))).reshape((-1, 1))

    # upper bound on samples given by IV of arXiv:1801.03524
    num_sample_ubound = int(np.ceil(np.sum(np.abs(coeff_vec))**2 / variance_bound))
    results = None
    sample_variance = np.infty
    number_of_samples = 0
    while (sample_variance > variance_bound and
           number_of_samples < num_sample_ubound):
        tresults = quantum_resource.run(program, qubits, trials=min(10000, num_sample_ubound))
        number_of_samples += len(tresults)

        parity_results = get_parity(pauli_terms, tresults)

        # Note: easy improvement would be to update mean and variance on the fly
        # instead of storing all these results.
        if results is None:
            results = parity_results
        else:
            results = np.hstack((results, parity_results))

        # calculate the expected values....
        covariance_mat = np.cov(results)
        sample_variance = coeff_vec.T.dot(covariance_mat).dot(coeff_vec) / results.shape[1]

    return EstimationResult(expected_value=coeff_vec.T.dot(np.mean(results, axis=1)),
                            pauli_expectations=np.multiply(coeff_vec.flatten(), np.mean(results, axis=1).flatten()),
                            covariance=covariance_mat,
                            variance=sample_variance,
                            n_shots=results.shape[1])
Exemple #28
0
 def XX(params):
     xx = PauliSum([
         PauliTerm.from_list([('X', 0), ('X', 1)],
                             coefficient=molecule_coefficients[4])
     ])
     return VQE.expectation(ansatz(params), xx, None, qvm)
Exemple #29
0
    def pauli_term(self, name, expression, qubits):
        from pyquil.paulis import PauliTerm

        return PauliTerm.from_list(list(zip(name, qubits)), expression)
Exemple #30
0
def estimate_pauli_sum(pauli_terms,
                       basis_transform_dict,
                       program,
                       variance_bound,
                       quantum_resource,
                       commutation_check=True):
    """
    Estimate the mean of a sum of pauli terms to set variance

    The sample variance is calculated by

    .. math::
        \begin{align}
        \mathrm{Var}[\hat{\langle H \rangle}] = \sum_{i, j}h_{i}h_{j}
        \mathrm{Cov}(\hat{\langle P_{i} \rangle}, \hat{\langle P_{j} \rangle})
        \end{align}

    :param pauli_terms: list of pauli terms to measure simultaneously or a
                        PauliSum object
    :param basis_transform_dict: basis transform dictionary where the key is
                                 the qubit index and the value is the basis to
                                 rotate into. Valid basis is [I, X, Y, Z].
    :param program: program generating a state to sample from
    :param variance_bound:  Bound on the variance of the estimator for the
                            PauliSum. Remember this is the SQUARE of the
                            standard error!
    :param quantum_resource: quantum abstract machine object
    :param Bool commutation_check: Optional flag toggling a safety check
                                   ensuring all terms in `pauli_terms`
                                   commute with each other
    :return: estimated expected value, covariance matrix, variance of the
             estimator, and the number of shots taken
    """
    if not isinstance(pauli_terms, (list, PauliSum)):
        raise TypeError("pauli_terms needs to be a list or a PauliSum")

    if isinstance(pauli_terms, PauliSum):
        pauli_terms = PauliSum.terms

    # check if each term commutes with everything
    if commutation_check:
        if len(commuting_sets(sum(pauli_terms))) != 1:
            raise CommutationError("Not all terms commute in the expected way")

    pauli_for_rotations = PauliTerm.from_list([
        (value, key) for key, value in basis_transform_dict.items()
    ])

    post_rotations = get_rotation_program(pauli_for_rotations)

    coeff_vec = np.array(list(map(lambda x: x.coefficient,
                                  pauli_terms))).reshape((-1, 1))

    # upper bound on samples given by IV of arXiv:1801.03524
    num_sample_ubound = int(
        np.ceil(np.sum(np.abs(coeff_vec))**2 / variance_bound))
    results = None
    sample_variance = np.infty
    number_of_samples = 0
    while (sample_variance > variance_bound
           and number_of_samples < num_sample_ubound):
        # note: bit string values are returned according to their listed order
        # in run_and_measure.  Therefore, sort beforehand to keep alpha numeric
        tresults = quantum_resource.run_and_measure(
            program + post_rotations,
            sorted(list(basis_transform_dict.keys())),
            trials=min(10000, num_sample_ubound))
        number_of_samples += len(tresults)

        parity_results = get_parity(pauli_terms, tresults)

        # Note: easy improvement would be to update mean and variance on the fly
        # instead of storing all these results.
        if results is None:
            results = parity_results
        else:
            results = np.hstack((results, parity_results))

        # calculate the expected values....
        covariance_mat = np.cov(results)
        sample_variance = coeff_vec.T.dot(covariance_mat).dot(coeff_vec) / \
                          results.shape[1]

    return coeff_vec.T.dot(np.mean(results, axis=1)), covariance_mat, \
           sample_variance, results.shape[1]