def test_qubitop_to_paulisum_more_terms(self): # Given qubit_operator = ( QubitOperator("Z0 Z1 Z2", -1.5) + QubitOperator("X0", 2.5) + QubitOperator("Y1", 3.5) ) expected_qubits = (LineQubit(0), LineQubit(5), LineQubit(8)) expected_paulisum = ( PauliSum() + ( PauliString(Z.on(expected_qubits[0])) * PauliString(Z.on(expected_qubits[1])) * PauliString(Z.on(expected_qubits[2])) * -1.5 ) + (PauliString(X.on(expected_qubits[0]) * 2.5)) + (PauliString(Y.on(expected_qubits[1]) * 3.5)) ) # When paulisum = qubitop_to_paulisum(qubit_operator, qubits=expected_qubits) # Then self.assertEqual(paulisum.qubits, expected_qubits) self.assertEqual(paulisum, expected_paulisum)
def test_qubitop_to_paulisum_setting_qubits(self): # Given qubit_operator = QubitOperator("Z0 Z1", -1.5) expected_qubits = (LineQubit(0), LineQubit(5)) expected_paulisum = (PauliSum() + PauliString(Z.on(expected_qubits[0])) * PauliString(Z.on(expected_qubits[1])) * -1.5) # When paulisum = qubitop_to_paulisum(qubit_operator, qubits=expected_qubits) # Then self.assertEqual(paulisum.qubits, expected_qubits) self.assertEqual(paulisum, expected_paulisum)
def test_qubitop_to_paulisum_z0z1_operator(self): # Given qubit_operator = QubitOperator("Z0 Z1", -1.5) expected_qubits = (GridQubit(0, 0), GridQubit(1, 0)) expected_paulisum = (PauliSum() + PauliString(Z.on(expected_qubits[0])) * PauliString(Z.on(expected_qubits[1])) * -1.5) # When paulisum = qubitop_to_paulisum(qubit_operator) # Then self.assertEqual(paulisum.qubits, expected_qubits) self.assertEqual(paulisum, expected_paulisum)
def simplify_cirq_pauli_sum(cirq_pauli_sum): """ Simplifies the input CirqPauliSum object according to Pauli algebra rules Parameters ---------- cirq_pauli_sum : (CirqPauliSum object) represents the objects that needs to be simplified according to Pauli algebra rules Returns ------- CirqPauliSum object simplified according to Pauli algebra rules """ pauli_strings = [] identity_strings = [] for pauli_string in cirq_pauli_sum.pauli_strings: if not pauli_string._qubit_pauli_map == {} and not np.isclose(pauli_string.coefficient, 0.0): pauli_strings.append(pauli_string) else: identity_strings.append(pauli_string) coeff = sum(i.coefficient for i in identity_strings) if not np.isclose(coeff, 0.0): total_identity_string = PauliString( qubit_pauli_map={}, coefficient=coeff) pauli_strings.append(total_identity_string) return CirqPauliSum(pauli_strings)
def create_cost_operators(self): """ Creates family of phase separation operators that depend on the objective function to be optimized Returns ------- cost_operators : (list) cost clauses for the graph on which Maxcut needs to be solved """ cost_operators = [] for i, j in self.graph.edges(): qubit_map_i = {i: Pauli.by_index(2)} qubit_map_j = {j: Pauli.by_index(2)} pauli_z_term = PauliString( qubit_map_i, coefficient=0.5) * PauliString(qubit_map_j) pauli_identity_term = PauliString(coefficient=-0.5) cost_pauli_sum = add_pauli_strings( [pauli_z_term, pauli_identity_term]) cost_operators.append(cost_pauli_sum) return cost_operators
def create_driver_operators(self): """ Creates family of mixing operators that depend on the domain of the problem and its structure Returns ------- driver_operators : (list) mixing clauses for the graph on which Maxcut needs to be solved """ driver_operators = [] for i in self.graph.nodes(): qubit_map_i = {i: Pauli.by_index(0)} driver_operators.append( CirqPauliSum([PauliString(qubit_map_i, coefficient=-1.0)])) return driver_operators
def compute_characteristic_function( pauli_string: cirq.PauliString, qubits: List[cirq.Qid], density_matrix: np.ndarray ): n_qubits = len(qubits) d = 2**n_qubits qubit_map = dict(zip(qubits, range(n_qubits))) # rho_i or sigma_i in https://arxiv.org/abs/1104.3835 trace = pauli_string.expectation_from_density_matrix(density_matrix, qubit_map) assert np.isclose(trace.imag, 0.0, atol=1e-6) trace = trace.real prob = trace * trace / d # Pr(i) in https://arxiv.org/abs/1104.3835 return trace, prob
def circuit_for_expectation_value( circuit: cirq.Circuit, pauli_string: cirq.PauliString) -> cirq.Circuit: """Sandwich a PauliString operator between a forwards and backwards copy of a circuit. This is a circuit representation of the expectation value of an operator <A> = <psi|A|psi> = <0|U^dag A U|0>. You can either extract the 0..0 amplitude of the final state vector (assuming starting from the |0..0> state or extract the [0, 0] entry of the unitary matrix of this combined circuit. """ assert pauli_string.coefficient == 1 return cirq.Circuit([ circuit, cirq.Moment(gate.on(q) for q, gate in pauli_string.items()), cirq.inverse(circuit) ])
def _measure_in(circuit: cirq.Circuit, pauli: cirq.PauliString): # Transform circuit to canonical qubit layout. qubit_map = dict( zip( sorted(circuit.all_qubits()), cirq.LineQubit.range(len(circuit.all_qubits())), )) circuit = circuit.transform_qubits(lambda q: qubit_map[q]) # Measure the Paulis. if not set(pauli).issubset(set(circuit.all_qubits())): raise ValueError( f"Qubit mismatch. The PauliString {self} acts on qubits " f"{[q.x for q in pauli.qubits]} but the circuit has qubit " f"indices {sorted([q.x for q in circuit.all_qubits()])}.") measured = (circuit + pauli.to_z_basis_ops() + cirq.measure(*pauli.qubits)) # Transform circuit back to original qubits. reverse_qubit_map = dict(zip(qubit_map.values(), qubit_map.keys())) return measured.transform_qubits(lambda q: reverse_qubit_map[q])
def _assert_pass_over(ops: List[cirq.Operation], before: cirq.PauliString, after: cirq.PauliString): assert before.pass_operations_over(ops[::-1]) == after assert after.pass_operations_over(ops, after_to_before=True) == before