def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit, compare_unitaries: bool = True): opt = cg.EjectFullW() circuit = before.copy() opt.optimize_circuit(circuit) # They should have equivalent effects. if compare_unitaries: cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( circuit, expected, 1e-8) # And match the expected circuit. if circuit != expected: # coverage: ignore print("BEFORE") print(before) print("EXPECTED") print(expected) print("AFTER") print(circuit) print(repr(circuit)) assert circuit == expected # And it should be idempotent. opt.optimize_circuit(circuit) assert circuit == expected
def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit, pre_opts: Iterable[cirq.OptimizationPass] = ( cg.ConvertToXmonGates(ignore_failures=True),), post_opts: Iterable[cirq.OptimizationPass] = ( cg.ConvertToXmonGates(ignore_failures=True), cirq.DropEmptyMoments())): opt = cg.EjectZ() circuit = before.copy() for pre in pre_opts: pre.optimize_circuit(circuit) opt.optimize_circuit(circuit) for post in post_opts: post.optimize_circuit(circuit) post.optimize_circuit(expected) if circuit != expected: # coverage: ignore print("BEFORE") print(before) print("AFTER") print(circuit) print("EXPECTED") print(expected) assert circuit == expected # And it should be idempotent. opt.optimize_circuit(circuit) assert circuit == expected
def test_represent_operations_in_circuit_local(circuit_type: str): """Tests all operation representations are created.""" qreg = LineQubit.range(2) circ_mitiq = Circuit([CNOT(*qreg), H(qreg[0]), Y(qreg[1]), CNOT(*qreg)]) circ = convert_from_mitiq(circ_mitiq, circuit_type) reps = represent_operations_in_circuit_with_local_depolarizing_noise( ideal_circuit=circ, noise_level=0.1, ) for op in convert_to_mitiq(circ)[0].all_operations(): found = False for rep in reps: if _equal(rep.ideal, Circuit(op), require_qubit_equality=True): found = True assert found # The number of reps. should match the number of unique operations assert len(reps) == 3
def assert_removes_all_z_gates(circuit: cirq.Circuit): opt = cirq.EjectZ() optimized = circuit.copy() opt.optimize_circuit(optimized) has_z = any( _try_get_known_z_half_turns(op) is not None for moment in optimized for op in moment.operations) assert not has_z cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( circuit, optimized, atol=1e-8)
def test_extensions(): # We test that an extension is being applied, by created an incorrect # gate with an extension. class WrongH(CompositeGate): def default_decompose(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE: return X(Q1) extensions = Extensions( desired_to_actual_to_wrapper={CompositeGate: { H: lambda e: WrongH() }}) circuit = Circuit() circuit.append([WrongH()(Q1)]) simulator = xmon_simulator.XmonSimulator() results = simulator.simulate(circuit, extensions=extensions) np.testing.assert_almost_equal(results.final_state, np.array([0, -1j]))
def test_format_header_with_quelibinc_circuit(): qasm = """OPENQASM 2.0; include "qelib1.inc"; """ parser = QasmParser() parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, Circuit())
def _compute_weight(circuit: Circuit, weights: Dict[str, float]) -> float: """Returns the weight of the circuit as the sum of weights of individual gates. Gates not defined have a default weight of one. Args: circuit: Circuit to compute the weight of. weights: Dictionary mapping string keys of gates to weights. """ return sum( _get_weight_for_gate(weights, op) for op in circuit.all_operations() )
def compile_swap_network_to_zzswap(circuit: cirq.Circuit, *, mutate=False) -> cirq.Circuit: """Compile a circuit containing SwapNetworkProblemUnitary's to one using ZZSwap interactions.""" if mutate: c2 = circuit else: c2 = circuit.copy() _SwapNetworkToZZSWAP().optimize_circuit(c2) return c2
def test_find_optimal_representation_single_qubit_depolarizing(circ_type): """Test optimal representation agrees with a known analytic result.""" for ideal_gate, noise_level in product([X, Y, H], [0.1, 0.3]): q = LineQubit(0) ideal_op = Circuit(ideal_gate(q)) implementable_circuits = [Circuit(ideal_op)] # Add ideal gate followed by Paulis for gate in [X, Y, Z]: implementable_circuits.append(Circuit([ideal_op, gate(q)])) noisy_circuits = [ circ + Circuit(DepolarizingChannel(noise_level).on_each(q)) for circ in implementable_circuits ] super_operators = [ choi_to_super(_circuit_to_choi(circ)) for circ in noisy_circuits ] # Define circuits with native types implementable_native = [ convert_from_mitiq(c, circ_type) for c in implementable_circuits ] ideal_op_native = convert_from_mitiq(ideal_op, circ_type) noisy_operations = [ NoisyOperation(ideal, real) for ideal, real in zip(implementable_native, super_operators) ] # Find optimal representation noisy_basis = NoisyBasis(*noisy_operations) rep = find_optimal_representation( ideal_op_native, noisy_basis, tol=1.0e-8 ) # Expected analytical result expected_rep = represent_operation_with_local_depolarizing_noise( ideal_op_native, noise_level, ) assert np.allclose(np.sort(rep.coeffs), np.sort(expected_rep.coeffs)) assert rep == expected_rep
def tensor_state_vector( circuit: cirq.Circuit, qubits: Optional[Sequence[cirq.Qid]] = None ) -> np.ndarray: """Given a circuit contract a tensor network into a final state vector.""" if qubits is None: qubits = sorted(circuit.all_qubits()) tensors, qubit_frontier, _ = circuit_to_tensors(circuit=circuit, qubits=qubits) tn = qtn.TensorNetwork(tensors) f_inds = tuple(f'i{qubit_frontier[q]}_q{q}' for q in qubits) tn.contract(inplace=True) return tn.to_dense(f_inds)
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 simplify_circuit( circuit: cirq.Circuit, *, max_iterations: int = 20, drop_final_rz: bool = False, ) -> cirq.Circuit: """Simplifies and optimizes the given circuit. Currently it * merges any neighboring gates belonging to the same one-parameter family * merges all one-qubit rotations into phased X rotations followed by Z rotations * pushes the Z rotations towards the end of the circuit as far as possible * drops any empty Moments This sequence of optimization passes is repeated until the circuit hits a fixed point, or ``max_iterations`` is exceeded. Finally, it removes Z rotations that are immediately followed by a Z-basis measurement. Args: circuit: circuit to simplify max_iterations: maximum number of simplification rounds drop_final_rz: iff True, drop z rotations that have no successor operations Returns: simplified circuit """ c = circuit.copy() # the optimizers cause the immediate decomposition of any gates they insert into the Circuit for _ in range(max_iterations): # It seems that Cirq optimizers have no way of communicating # if they actually made any changes to the Circuit. # See https://github.com/quantumlib/Cirq/issues/3761 before = c.copy() # all mergeable 2-qubit gates are merged MergeOneParameterGroupGates().optimize_circuit(c) c = cirq.merge_single_qubit_gates_to_phased_x_and_z(c) # all z rotations are pushed past the first two-qubit gate following them c = cirq.eject_z(c, eject_parameterized=True) c = cirq.drop_empty_moments(c) if c == before: # the optimization hit a fixed point break DropRZBeforeMeasurement(drop_final=drop_final_rz).optimize_circuit(c) c = cirq.drop_empty_moments(c) return c
def test_sample_circuit_with_seed(): decomp = _simple_pauli_deco_dict(0.7, simplify_paulis=True) circ = Circuit([X.on(LineQubit(0)) for _ in range(10)]) expected = sample_circuit(circ, decomp, random_state=4)[0] # Check we're not sampling the same operation every call to sample_sequence assert len(set(expected.all_operations())) > 1 for _ in range(10): sampled = sample_circuit(circ, decomp, random_state=4)[0] assert _equal(sampled, expected)
def serialize_circuit(circuit: cirq.Circuit) -> iqm_client.Circuit: """Serializes a quantum circuit into the IQM data transfer format. Args: circuit: quantum circuit to serialize Returns: data transfer object representing the circuit """ instructions = list(map(map_operation, circuit.all_operations())) return iqm_client.Circuit(name='Serialized from Cirq', instructions=instructions)
def print_stats(time_sec: float, circuit: cirq.Circuit) -> None: """Prints some statistics about the circuit size. Args: time_sec: the time it took to produce the circuit circuit: print stats about this circuit """ n_ops = len(list(circuit.all_operations())) print(f' qubits={len(circuit.all_qubits())}') print(f' ops={n_ops}') print(f'moments={len(circuit.moments)}', flush=True) print(f' time={time_sec:0.3f}s ({time_sec * 1e6 / n_ops :0.1f}us/op)')
def sample_noisy_bitstrings( circuit: cirq.Circuit, qubit_order: Sequence[cirq.Qid], depolarization: float, repetitions: int ) -> np.ndarray: assert 0 <= depolarization <= 1 dim = np.prod(circuit.qid_shape(), dtype=np.int64) n_incoherent = int(depolarization * repetitions) n_coherent = repetitions - n_incoherent incoherent_samples = np.random.randint(dim, size=n_incoherent) circuit_with_measurements = cirq.Circuit(circuit, cirq.measure(*qubit_order, key='m')) r = cirq.sample(circuit_with_measurements, repetitions=n_coherent) coherent_samples = r.data['m'].to_numpy() return np.concatenate((coherent_samples, incoherent_samples))
def to_qasm(circuit: cirq.Circuit) -> QASMType: """Returns a QASM string representing the input Mitiq circuit. Args: circuit: Mitiq circuit to convert to a QASM string. Returns: QASMType: QASM string equivalent to the input Mitiq circuit. """ # Simplify exponents of gates. For example, H**-1 is simplified to H. _simplify_circuit_exponents(circuit) return circuit.to_qasm()
def test_r_gate(): qasm = """ OPENQASM 2.0; include "qelib1.inc"; qreg q[1]; r(pi, pi / 2.0) q[0]; """ parser = QasmParser() q0 = cirq.NamedQubit('q_0') expected_circuit = Circuit() expected_circuit.append(QasmUGate(1.0, 0.0, 0.0)(q0)) parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) assert parsed_qasm.qregs == {'q': 1}
def from_braket(circuit: BKCircuit) -> "cirq.Circuit": """Returns a Cirq circuit equivalent to the input Braket circuit. Note: The returned Cirq circuit acts on cirq.LineQubit's with indices equal to the qubit indices of the Braket circuit. Args: circuit: Braket circuit to convert to a Cirq circuit. """ return Circuit( _translate_braket_instruction_to_cirq_operation(instr) for instr in circuit.instructions)
def _fold_gate_at_index_in_moment(circuit: Circuit, moment_index: int, gate_index: int) -> None: """Replaces the gate G at (moment, index) with G G^dagger G, modifying the circuit in place. Inserts two new moments into the circuit for G^dagger and G. Args: circuit: Circuit with gates to fold. moment_index: Moment in which the gate to be folded sits in the circuit. gate_index: Index of the gate to be folded within the specified moment. """ moment = circuit[moment_index] op = moment.operations[gate_index] try: inverse_op = inverse(op) except TypeError: raise UnfoldableGateError(f"Operation {op} does not have a defined " f"inverse and cannot be folded.") circuit.insert(moment_index, [op, inverse_op], strategy=InsertStrategy.NEW)
def get_all_line_qubit_ids(circuit: cirq.Circuit) -> typing.Tuple[int]: """ Return IDs of all LineQubits in `circuit` in order. """ all_line_qubit_ids = set() for qubit in circuit.all_qubits(): if isinstance(qubit, cirq.LineQubit): all_line_qubit_ids.add(qubit.x) return tuple(sorted(all_line_qubit_ids))
def transform(self, circuit: cirq.Circuit) -> cirq.Circuit: """Creates a new qubit mapping for a circuit and transforms it. This uses `qubit_mapping` to create a mapping from the qubits in the circuit to the qubits on the device, then substitutes in those qubits in the circuit and returns the new circuit. Returns: The circuit with the mapped qubits. """ self.qubit_mapping(circuit) return circuit.transform_qubits(lambda q: self.mapping[q])
def test_to_from_braket_uncommon_two_qubit_gates(uncommon_gate): """These gates get decomposed when converting Cirq -> Braket -> Cirq, but the unitaries should be equal up to global phase. """ cirq_circuit = Circuit(uncommon_gate.on(*LineQubit.range(2))) test_circuit = from_braket(to_braket(cirq_circuit)) testing.assert_allclose_up_to_global_phase( protocols.unitary(test_circuit), protocols.unitary(cirq_circuit), atol=1e-7, )
def assert_optimization_not_broken(circuit: cirq.Circuit, **kwargs): """Check that the unitary matrix for the input circuit is the same (up to global phase and rounding error) as the unitary matrix of the optimized circuit.""" u_before = circuit.unitary(sorted(circuit.all_qubits())) c_sqrt_iswap = circuit.copy() with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2): cirq.MergeInteractionsToSqrtIswap( **kwargs).optimize_circuit(c_sqrt_iswap) u_after = c_sqrt_iswap.unitary(sorted(circuit.all_qubits())) # Not 1e-8 because of some unaccounted accumulated error in some of Cirq's linalg functions cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after, atol=1e-6) # Also test optimization with SQRT_ISWAP_INV c_sqrt_iswap_inv = circuit.copy() with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2): cirq.MergeInteractionsToSqrtIswap( use_sqrt_iswap_inv=True).optimize_circuit(c_sqrt_iswap_inv) u_after2 = c_sqrt_iswap_inv.unitary(sorted(circuit.all_qubits())) cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after2, atol=1e-6)
def test_u3_gate(): qasm = """ OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; u3(pi, 2.3, 3) q[0]; u3(+3.14, -pi, (8)) q; """ parser = QasmParser() q0 = cirq.NamedQubit('q_0') q1 = cirq.NamedQubit('q_1') expected_circuit = Circuit() expected_circuit.append( cirq.Moment([ QasmUGate(1.0, 2.3 / np.pi, 3 / np.pi)(q0), QasmUGate(3.14 / np.pi, -1.0, 8 / np.pi)(q1), ])) expected_circuit.append( cirq.Moment([QasmUGate(3.14 / np.pi, -1.0, 8 / np.pi)(q0)])) parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) assert parsed_qasm.qregs == {'q': 2}
def test_measure_individual_bits(): qasm = """ OPENQASM 2.0; include "qelib1.inc"; qreg q1[2]; creg c1[2]; measure q1[0] -> c1[0]; measure q1[1] -> c1[1]; """ parser = QasmParser() q1_0 = cirq.NamedQubit('q1_0') q1_1 = cirq.NamedQubit('q1_1') expected_circuit = Circuit() expected_circuit.append( cirq.MeasurementGate(num_qubits=1, key='c1_0').on(q1_0)) expected_circuit.append( cirq.MeasurementGate(num_qubits=1, key='c1_1').on(q1_1)) parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) assert parsed_qasm.qregs == {'q1': 2} assert parsed_qasm.cregs == {'c1': 2}
def test_rotation_gates(qasm_gate: str, cirq_gate: cirq.SingleQubitGate): qasm = """OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; {0}(pi/2) q[0]; {0}(pi) q; """.format(qasm_gate) parser = QasmParser() q0 = cirq.NamedQubit('q_0') q1 = cirq.NamedQubit('q_1') expected_circuit = Circuit() expected_circuit.append( cirq.Moment([cirq_gate(np.pi / 2).on(q0), cirq_gate(np.pi).on(q1)])) expected_circuit.append(cirq.Moment([ cirq_gate(np.pi).on(q0), ])) parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) assert parsed_qasm.qregs == {'q': 2}
def test_simulate(self): compressed_status = [True, False] rad = 0.75 pauli_word = PauliWord("XZYX") for initial_state in range(2**len(pauli_word)): results = [] for is_compressed in compressed_status: simulator = Simulator() circuit = Circuit() qubits = [GridQubit(i, j) for i in range(3) for j in range(3)] gate = PauliWordExpGate(rad, pauli_word) operation = gate.on(*qubits[0:len(gate.pauli_word)]) if is_compressed: circuit.append(operation) else: circuit.append(cirq.decompose(operation)) result = simulator.simulate( circuit, initial_state=initial_state ) # type: cirq.SimulationTrialResult # print(circuit) print(result.final_state) # print(cirq.unitary(circuit_compressed)) results.append(result) self.assertTrue( np.allclose(results[0].final_state, results[1].final_state, rtol=1e-4, atol=1e-5))
def state_prep_circuit(alpha, exact=False): """Returns state preparation circuit. Args: ===== alpha : numeric Parameter for state preparation circuit exact : bool If True, works with wavefunction Returns: ======== sp_circuit : cirq.Circuit State preparation circuit """ sp_circuit = Circuit() sp_circuit.append(_input_prep_gates(alpha)) if exact is False: sp_circuit.append([ MeasurementGate('r00').on(q00), MeasurementGate('r01').on(q01), MeasurementGate('r10').on(q10), MeasurementGate('r11').on(q11) ]) return sp_circuit
def generate_state_preparation_circuit(self, state_preparation_angles): state_preparation_circuit = Circuit() for angles in state_preparation_angles: qubit_index = state_preparation_angles.index(angles) if (qubit_index == 0): qubit_angle = angles[qubit_index] RY = Ry(qubit_angle) state_preparation_circuit.append( [RY(self.qubits[qubit_index])], strategy=InsertStrategy.EARLIEST) else: gray_code_list = generate_gray_code(qubit_index) for qubit_angle in angles: RY = Ry(qubit_angle) state_preparation_circuit.append( [RY(self.qubits[qubit_index])], strategy=InsertStrategy.EARLIEST) l = angles.index(qubit_angle) cnot_position = self.find_cnot_position( gray_code_list[(l + 1) % len(angles)], gray_code_list[l % len(angles)]) state_preparation_circuit += self.get_cnot_circuit( self.qubits[cnot_position[0]], self.qubits[qubit_index]) self.state_preparation_circuit = state_preparation_circuit return self.state_preparation_circuit
def assert_removes_all_z_gates(circuit: cirq.Circuit): opt = cg.EjectZ() optimized = circuit.copy() opt.optimize_circuit(optimized) has_z = any(_try_get_known_z_half_turns(op) is not None for moment in optimized for op in moment.operations) assert not has_z cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( circuit, optimized, atol=1e-8)
def optimize_circuit(self, circuit: cirq.Circuit): number_patterns = 0 for mi, moment in enumerate(circuit): for op in moment: if op.gate == cirq.ops.CNOT: control = op.qubits[0] target = op.qubits[1] m2 = circuit.next_moment_operating_on([target], mi + 1) if m2 is None: continue m2_op = circuit.operation_at(target, m2) if m2_op.gate == cirq.ops.CNOT: m2_control = m2_op.qubits[0] m2_target = m2_op.qubits[1] if m2_control != target: continue m3 = circuit.next_moment_operating_on([m2_target], m2 + 1) if m3 is None: continue m3_op = circuit.operation_at(m2_target, m3) if m3_op.gate == cirq.ops.CNOT: m3_control = m3_op.qubits[0] m3_target = m3_op.qubits[1] if m3_control != control: continue if m3_target != m2_target: continue print("found a pattern", mi, op, m2_op, m3_op) number_patterns += 1
def execute(circ: cirq.Circuit, obs: np.ndarray) -> float: """Simulates noiseless wavefunction evolution and returns the expectation value of some observable. Args: circ: The input Cirq circuit. obs: The observable to measure as a NumPy array. Returns: The expectation value of obs as a float. """ final_wvf = circ.final_state_vector() return np.real(final_wvf.conj().T @ obs @ final_wvf)
def _assert_no_multi_qubit_pauli_strings(circuit: cirq.Circuit) -> None: for op in circuit.all_operations(): if isinstance(op, PauliStringGateOperation): assert len(op.pauli_string) == 1