Exemple #1
0
def test_get_qubits():
    term = PauliTerm("Z", 0) * PauliTerm("X", 1)
    assert term.get_qubits() == [0, 1]

    sum_term = PauliTerm(
        "X", 0, 0.5) + 0.5j * PauliTerm("Y", 10) * PauliTerm("Y", 0, 0.5j)
    assert sum_term.get_qubits() == [0, 10]
Exemple #2
0
def test_get_qubits():
    term = PauliTerm('Z', 0) * PauliTerm('X', 1)
    assert term.get_qubits() == [0, 1]

    sum_term = PauliTerm(
        'X', 0, 0.5) + 0.5j * PauliTerm('Y', 10) * PauliTerm('Y', 0, 0.5j)
    assert sum_term.get_qubits() == [0, 10]
Exemple #3
0
def _all_qubits_diagonal_in_tpb(op1: PauliTerm, op2: PauliTerm):
    """
    Compare all qubits between two PauliTerms to see if they are all diagonal in an
    overall shared tensor product basis.
    """
    all_qubits = set(op1.get_qubits()) | set(op2.get_qubits())
    return all(_ops_diagonal_in_tpb(op1[q], op2[q]) for q in all_qubits)
Exemple #4
0
def _all_qubits_diagonal_in_tpb(op1: PauliTerm, op2: PauliTerm):
    """
    Compare all qubits between two PauliTerms to see if they are all diagonal in an
    overall shared tensor product basis. More concretely, test if ``op1`` and
    ``op2`` are diagonal in each others' "natural" tensor product basis.

    Given some PauliTerm, the 'natural' tensor product basis (tpb) to
    diagonalize this term is the one which diagonalizes each Pauli operator in the
    product term-by-term.

    For example, X(1) * Z(0) would be diagonal in the 'natural' tensor product basis
    {(|0> +/- |1>)/Sqrt[2]} * {|0>, |1>}, whereas Z(1) * X(0) would be diagonal
    in the 'natural' tpb {|0>, |1>} * {(|0> +/- |1>)/Sqrt[2]}. The two operators
    commute but are not diagonal in each others 'natural' tpb (in fact, they are
    anti-diagonal in each others 'natural' tpb). This function tests whether two
    operators given as PauliTerms are both diagonal in each others 'natural' tpb.

    Note that for the given example of X(1) * Z(0) and Z(1) * X(0), we can construct
    the following basis which simultaneously diagonalizes both operators:

      -- |0>' = |0> (|+>) + |1> (|->)
      -- |1>' = |0> (|+>) - |1> (|->)
      -- |2>' = |0> (|->) + |1> (|+>)
      -- |3>' = |0> (-|->) + |1> (|+>)

    In this basis, X Z looks like diag(1, -1, 1, -1), and Z X looks like diag(1, 1, -1, -1).
    Notice however that this basis cannot be constructed with single-qubit operations, as each
    of the basis vectors are entangled states.

    :param op1: PauliTerm to check diagonality of in the natural tpb of ``op2``
    :param op2: PauliTerm to check diagonality of in the natural tpb of ``op1``
    :return: Boolean of diagonality in each others natural tpb
    """
    all_qubits = set(op1.get_qubits()) & set(op2.get_qubits())
    return all(_ops_commute(op1[q], op2[q]) for q in all_qubits)
Exemple #5
0
    def _evaluate_single_term(self, ansatz_circuit: Program,
                              meas_term: PauliTerm) -> np.ndarray:
        """Compute the expectation value of a single PauliTerm , given a quantum circuit to evaluate on.
        
        Args:
            ansatz_circuit: A Program representing the quantum state to evaluate the PauliTerm on.
            
            meas_term: a PauliTerm object to be evaluated.
        
        """
        # First, create the quantum circuit needed for evaluation.

        # concatenate operator (converted to a Program) and ansatz circuit
        expectation_circuit = ansatz_circuit.copy()

        expectation_circuit += meas_term.program

        ro = expectation_circuit.declare('ro', 'BIT',
                                         len(meas_term.get_qubits()))

        # add necessary post-rotations and measurement
        for i, qubit in enumerate(sorted(list(meas_term.get_qubits()))):

            if meas_term.pauli_string([qubit]) == 'X':
                expectation_circuit += H(qubit)
            elif meas_term.pauli_string([qubit]) == 'Y':
                expectation_circuit += H(qubit)
                expectation_circuit += S(qubit)

            expectation_circuit += Program().measure(qubit, ro[i])

        result = self._run(expectation_circuit,
                           num_shots=self._num_shots_evaluation)

        return result
Exemple #6
0
def test_get_qubits():
    q = QubitPlaceholder.register(2)
    term = PauliTerm("Z", q[0]) * PauliTerm("X", q[1])
    assert term.get_qubits() == q

    q10 = QubitPlaceholder()
    sum_term = PauliTerm("X", q[0], 0.5) + 0.5j * PauliTerm("Y", q10) * PauliTerm("Y", q[0], 0.5j)
    assert sum_term.get_qubits() == [q[0], q10]
def test_ExtendedParams_plot():
    ham_no_bias = PauliTerm("Z", 0)
    ham_no_bias *= PauliTerm("Z", 1)
    next_term = PauliTerm("Z", 0, -2.0)
    next_term *= PauliTerm("Z", 2)
    ham_no_bias += next_term

    p = 5
    params = ExtendedParams.linear_ramp_from_hamiltonian(ham_no_bias, p)
    fig, ax = plt.subplots()
    params.plot(ax=ax)
    # plt.show()

    p = 8
    params = ExtendedParams((ham_no_bias, p),
                            ([0.1] * p * len(ham_no_bias.get_qubits()), [],
                             [0.2] * p * len(ham_no_bias)))
    fig, ax = plt.subplots()
    params.plot(ax=ax)
Exemple #8
0
    def apply_clifford_to_pauli(self, clifford: Program,
                                pauli_in: PauliTerm) -> PauliTerm:
        r"""
        Given a circuit that consists only of elements of the Clifford group,
        return its action on a PauliTerm.

        In particular, for Clifford C, and Pauli P, this returns the PauliTerm
        representing CPC^{\dagger}.

        :param clifford: A Program that consists only of Clifford operations.
        :param pauli_in: A PauliTerm to be acted on by clifford via conjugation.
        :return: A PauliTerm corresponding to clifford * pauli_in * clifford^{\dagger}
        """
        # do nothing if `pauli_in` is the identity
        if is_identity(pauli_in):
            return pauli_in

        indices_and_terms = list(zip(*list(pauli_in.operations_as_set())))

        payload = ConjugateByCliffordRequest(
            clifford=clifford.out(),
            pauli=rpcq.messages.PauliTerm(indices=list(indices_and_terms[0]),
                                          symbols=list(indices_and_terms[1])),
        )
        response: ConjugateByCliffordResponse = self.client.call(
            "conjugate_pauli_by_clifford", payload)
        phase_factor, paulis = response.phase, response.pauli

        pauli_out = PauliTerm("I", 0, 1.0j**phase_factor)
        clifford_qubits = clifford.get_qubits()
        pauli_qubits = pauli_in.get_qubits()
        all_qubits = sorted(
            set(cast(List[int],
                     pauli_qubits)).union(set(cast(List[int],
                                                   clifford_qubits))))
        # The returned pauli will have specified its value on all_qubits, sorted by index.
        #  This is maximal set of qubits that can be affected by this conjugation.
        for i, pauli in enumerate(paulis):
            pauli_out = cast(PauliTerm,
                             pauli_out * PauliTerm(pauli, all_qubits[i]))
        return cast(PauliTerm, pauli_out * pauli_in.coefficient)
Exemple #9
0
def pauli_term_to_euler_memory_map(
    term: PauliTerm,
    *,
    prefix: str,
    tuple_x: Tuple[float, float, float],
    tuple_y: Tuple[float, float, float],
    tuple_z: Tuple[float, float, float],
    suffix_alpha: str = "alpha",
    suffix_beta: str = "beta",
    suffix_gamma: str = "gamma",
) -> Dict[str, List[float]]:
    """
    Given a ``PauliTerm``, create a memory map corresponding to a collection of ZXZXZ-decomposed
    single-qubit gates. The intent is that these gate are used to prepare an eigenstate of the
    ``PauliTerm`` or measure in the eigenbasis of the ``PauliTerm``, which is more clearly
    discernible from the calling functions ``pauli_term_to_preparation_memory_map`` (for state
    preparation) and ``pauli_term_to_measurement_memory_map`` (for measuring in different bases).
    This function is not really meant to be used by itself, but rather by the aforementioned
    calling functions.

    :param term: The ``PauliTerm`` in question.
    :param prefix: The prefix for the declared memory region labels. For example, if the prefix
        is "preparation" and the alpha, beta, and gamma suffixes are left as default, the labels
        would be "preparation_alpha", "preparation_beta", and "preparation_gamma".
    :param tuple_x: A tuple of Euler angles as (alpha, beta, gamma) to be used for the ``X``
        operators in the ``PauliTerm``.
    :param tuple_y: A tuple of Euler angles as (alpha, beta, gamma) to be used for the ``Y``
        operators in the ``PauliTerm``.
    :param tuple_z: A tuple of Euler angles as (alpha, beta, gamma) to be used for the ``Z``
        and ``I`` operators in the ``PauliTerm``.
    :param suffix_alpha: The suffix for the "alpha" memory region label, which corresponds to the
        first (rightmost) ``Z`` in the ZXZXZ decomposition. Defaults to "alpha".
    :param suffix_beta: The suffix for the "beta" memory region label, which corresponds to the
        second (middle) ``Z`` in the ZXZXZ decomposition. Defaults to "beta".
    :param suffix_gamma: The suffix for the "gamma" memory region label, which corresponds to the
        last (leftmost) ``Z`` in the ZXZXZ decomposition. Defaults to "gamma".
    :return: Memory map dictionary containing three entries (three labels as keys and three lists
        of angles as values).
    """
    # no need to provide a memory map when no rotations are necessary
    if ("X" not in term.pauli_string()) and ("Y" not in term.pauli_string()):
        return {}

    alpha_label = f"{prefix}_{suffix_alpha}"
    beta_label = f"{prefix}_{suffix_beta}"
    gamma_label = f"{prefix}_{suffix_gamma}"

    # assume the pauli indices are equivalent to the memory region
    memory_size = max(term.get_qubits()) + 1

    memory_map = {
        alpha_label: [0.0] * memory_size,
        beta_label: [0.0] * memory_size,
        gamma_label: [0.0] * memory_size,
    }

    tuples = {"X": tuple_x, "Y": tuple_y, "Z": tuple_z, "I": tuple_z}

    for qubit, operator in term:
        if operator not in tuples:
            raise ValueError(f"Unknown operator {operator}")
        memory_map[alpha_label][qubit] = tuples[operator][0]
        memory_map[beta_label][qubit] = tuples[operator][1]
        memory_map[gamma_label][qubit] = tuples[operator][2]

    return memory_map