def optimized_for_sycamore( circuit: cirq.Circuit, *, qubit_map: Callable[[cirq.Qid], cirq.GridQubit] = lambda e: cast(cirq.GridQubit, e), optimizer_type: str = 'sqrt_iswap', tolerance: float = 1e-5, tabulation_resolution: Optional[float] = None, ) -> cirq.Circuit: """Optimizes a circuit for Google devices. Uses a set of optimizers that will compile to the proper gateset for the device (xmon, sqrt_iswap, or sycamore gates) and then use optimizers to compress the gate depth down as much as is easily algorithmically possible by merging rotations, ejecting Z gates, etc. Args: circuit: The circuit to optimize. qubit_map: Transforms the qubits (e.g. so that they are GridQubits). optimizer_type: A string defining the optimizations to apply. Possible values are 'xmon', 'xmon_partial_cz', 'sqrt_iswap', 'sycamore' tolerance: The tolerance passed to the various circuit optimization passes. tabulation_resolution: If provided, compute a gateset tabulation with the specified resolution and use it to approximately compile arbitrary two-qubit gates for which an analytic compilation is not known. Returns: The optimized circuit. Raises: ValueError: If the `optimizer_type` is not a supported type. """ copy = circuit.copy() if optimizer_type not in _TARGET_GATESETS: raise ValueError( f'{optimizer_type} is not an allowed type. Allowed ' f'types are: {_TARGET_GATESETS.keys()}' ) tabulation: Optional[cirq.TwoQubitGateTabulation] = None if tabulation_resolution is not None: tabulation = _gate_product_tabulation_cached(optimizer_type, tabulation_resolution) if optimizer_type in _TARGET_GATESETS: copy = cirq.optimize_for_target_gateset( circuit, gateset=_TARGET_GATESETS[optimizer_type](tolerance, tabulation), context=cirq.TransformerContext(deep=True), ) copy = cirq.merge_single_qubit_gates_to_phxz(copy, atol=tolerance) copy = cirq.eject_phased_paulis(copy, atol=tolerance) copy = cirq.eject_z(copy, atol=tolerance) copy = cirq.drop_negligible_operations(copy, atol=tolerance) ret = cirq.Circuit( (op.transform_qubits(qubit_map) for op in copy.all_operations()), strategy=cirq.InsertStrategy.EARLIEST, ) return ret
def test_clears_known_empties_even_at_zero_tolerance(): a, b = cirq.LineQubit.range(2) circuit = cirq.Circuit( cirq.Z(a)**0, cirq.Y(a)**0.0000001, cirq.X(a)**-0.0000001, cirq.CZ(a, b)**0) cirq.testing.assert_same_circuits( cirq.drop_negligible_operations(circuit, atol=0.001), cirq.Circuit([cirq.Moment()] * 4)) cirq.testing.assert_same_circuits( cirq.drop_negligible_operations(circuit, atol=0), cirq.Circuit( cirq.Moment(), cirq.Moment(cirq.Y(a)**0.0000001), cirq.Moment(cirq.X(a)**-0.0000001), cirq.Moment(), ), )
def test_does_not_clear_small_no_compile(): a = cirq.NamedQubit('a') circuit = cirq.Circuit( cirq.Moment((cirq.Z(a)**0.000001).with_tags(NO_COMPILE_TAG))) cirq.testing.assert_same_circuits( cirq.drop_negligible_operations( circuit, context=cirq.TransformerContext(tags_to_ignore=(NO_COMPILE_TAG, )), atol=0.001), circuit, )
def test_drop_negligible(): (q0, ) = _make_qubits(1) sym = sympy.Symbol('a') circuit = cirq.Circuit( cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**0.25, cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**1e-10, cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**sym, ) expected = cirq.Circuit( cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**0.25, cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**sym, ) circuit = cirq.drop_negligible_operations(circuit) circuit = cirq.drop_empty_moments(circuit) assert circuit == expected
def test_recursively_runs_inside_circuit_ops_deep(): a = cirq.NamedQubit('a') small_op = cirq.Z(a)**0.000001 nested_circuit = cirq.FrozenCircuit(cirq.X(a), small_op, small_op.with_tags(NO_COMPILE_TAG), small_op, cirq.Y(a)) nested_circuit_dropped = cirq.FrozenCircuit( cirq.Moment(cirq.X(a)), cirq.Moment(), cirq.Moment(small_op.with_tags(NO_COMPILE_TAG)), cirq.Moment(), cirq.Moment(cirq.Y(a)), ) c_orig = cirq.Circuit( small_op, cirq.CircuitOperation(nested_circuit).repeat(6).with_tags( NO_COMPILE_TAG), small_op, cirq.CircuitOperation(nested_circuit).repeat(5).with_tags( "preserve_tag"), small_op, ) c_expected = cirq.Circuit( cirq.Moment(), cirq.Moment( cirq.CircuitOperation(nested_circuit).repeat(6).with_tags( NO_COMPILE_TAG)), cirq.Moment(), cirq.Moment( cirq.CircuitOperation(nested_circuit_dropped).repeat(5).with_tags( "preserve_tag")), cirq.Moment(), ) context = cirq.TransformerContext(tags_to_ignore=[NO_COMPILE_TAG], deep=True) cirq.testing.assert_same_circuits( cirq.drop_negligible_operations(c_orig, context=context, atol=0.001), c_expected)
def simplify_expectation_value_circuit(circuit_sand: cirq.Circuit): """For low weight operators on low-degree circuits, we can simplify the circuit representation of an expectation value. In particular, this should be used on `circuit_for_expectation_value` circuits. It will merge single- and two-qubit gates from the "forwards" and "backwards" parts of the circuit outside of the operator's lightcone. This might be too slow in practice and you can just use quimb to simplify things for you. """ n_op = sum(1 for _ in circuit_sand.all_operations()) while True: MergeNQubitGates(n_qubits=1).optimize_circuit(circuit_sand) circuit_sand = cirq.drop_negligible_operations(circuit_sand, atol=1e-6) MergeNQubitGates(n_qubits=2).optimize_circuit(circuit_sand) circuit_sand = cirq.drop_empty_moments(circuit_sand) new_n_op = sum(1 for _ in circuit_sand.all_operations()) if new_n_op < n_op: n_op = new_n_op else: return
def test_clears_small(): a = cirq.NamedQubit('a') circuit = cirq.Circuit(cirq.Moment(cirq.Z(a)**0.000001)) cirq.testing.assert_same_circuits( cirq.drop_negligible_operations(circuit, atol=0.001), cirq.Circuit(cirq.Moment()))