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]
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]
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)
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)
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
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)
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)
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