def assert_optimizes( before: cirq.Circuit, expected: cirq.Circuit, eject_parameterized: bool = False, *, with_context: bool = False, ): if cirq.has_unitary(before): cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( before, expected, atol=1e-8) context = cirq.TransformerContext( tags_to_ignore=("nocompile", )) if with_context else None circuit = cirq.eject_z(before, eject_parameterized=eject_parameterized, context=context) expected = cirq.eject_z(expected, eject_parameterized=eject_parameterized, context=context) cirq.testing.assert_same_circuits(circuit, expected) # And it should be idempotent. circuit = cirq.eject_z(before, eject_parameterized=eject_parameterized, context=context) cirq.testing.assert_same_circuits(circuit, expected)
def test_eject_phased_xz(): a = cirq.NamedQubit('a') b = cirq.NamedQubit('b') c = cirq.Circuit( cirq.PhasedXZGate(x_exponent=1, z_exponent=0.5, axis_phase_exponent=0.5).on(a), cirq.CZ(a, b) ** 0.25, ) c_expected = cirq.Circuit( cirq.CZ(a, b) ** -0.25, cirq.PhasedXPowGate(phase_exponent=0.75).on(a), cirq.T(b) ) cirq.testing.assert_same_circuits( cirq.eject_z(cirq.eject_phased_paulis(cirq.eject_z(c))), c_expected ) cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(c, c_expected, 1e-8)
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 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_swap_iswap(exponent): a, b = cirq.LineQubit.range(2) original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.ISWAP(a, b)**exponent]) optimized = original.copy() optimized = cirq.eject_z(optimized) optimized = cirq.drop_empty_moments(optimized) assert optimized[0].operations == (cirq.ISWAP(a, b)**exponent, ) # Note: EjectZ drops `global_phase` from Rz turning it into a Z assert optimized[1].operations == (cirq.Z(b)**(0.123 / np.pi), ) cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(original), cirq.unitary(optimized), atol=1e-8)
def test_eject_z(self, family, ex, qubits): """Commuting z rotations towards the end of the circuit.""" q0, q1 = qubits[:2] c = cirq.Circuit() c.append([ cirq.ZPowGate(exponent=0.3)(q0), cirq.ZPowGate(exponent=0.8)(q1), family(exponent=ex)(q0, q1), cirq.MeasurementGate(1, key='q0')(q0), cirq.MeasurementGate(1, key='q1')(q1), ]) c = cirq.eject_z(c) c = cirq.drop_empty_moments(c) # the ZPowGates have been commuted and canceled assert len(c) == 2
def assert_removes_all_z_gates(circuit: cirq.Circuit, eject_parameterized: bool = True): optimized = cirq.eject_z(circuit, eject_parameterized=eject_parameterized) for op in optimized.all_operations(): # assert _try_get_known_z_half_turns(op, eject_parameterized) is None if isinstance(op.gate, cirq.PhasedXZGate) and ( eject_parameterized or not cirq.is_parameterized(op.gate.z_exponent)): assert op.gate.z_exponent == 0 if cirq.is_parameterized(circuit): for a in (0, 0.1, 0.5, 1.0, -1.0, 3.0): (cirq.testing. assert_circuits_with_terminal_measurements_are_equivalent( cirq.resolve_parameters(circuit, {'a': a}), cirq.resolve_parameters(optimized, {'a': a}), atol=1e-8, )) else: cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( circuit, optimized, atol=1e-8)
def assert_optimizes( before: cirq.Circuit, expected: cirq.Circuit, eject_parameterized: bool = False, *, with_context: bool = False, ): if cirq.has_unitary(before): cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( before, expected, atol=1e-8) context = cirq.TransformerContext( tags_to_ignore=("nocompile", )) if with_context else None circuit = cirq.eject_z(before, eject_parameterized=eject_parameterized, context=context) expected = cirq.eject_z(expected, eject_parameterized=eject_parameterized, context=context) cirq.testing.assert_same_circuits(circuit, expected) # And it should be idempotent. circuit = cirq.eject_z(before, eject_parameterized=eject_parameterized, context=context) cirq.testing.assert_same_circuits(circuit, expected) # Nested sub-circuits should also get optimized. q = before.all_qubits() c_nested = cirq.Circuit( [(cirq.Z**0.5).on_each(*q), (cirq.Y**0.25).on_each(*q)], cirq.Moment( cirq.CircuitOperation( before.freeze()).repeat(2).with_tags("ignore")), [(cirq.Z**0.5).on_each(*q), (cirq.Y**0.25).on_each(*q)], cirq.Moment( cirq.CircuitOperation( before.freeze()).repeat(3).with_tags("preserve_tag")), ) c_expected = cirq.Circuit( cirq.PhasedXPowGate(phase_exponent=0, exponent=0.25).on_each(*q), (cirq.Z**0.5).on_each(*q), cirq.Moment( cirq.CircuitOperation( before.freeze()).repeat(2).with_tags("ignore")), cirq.PhasedXPowGate(phase_exponent=0, exponent=0.25).on_each(*q), (cirq.Z**0.5).on_each(*q), cirq.Moment( cirq.CircuitOperation( expected.freeze()).repeat(3).with_tags("preserve_tag")), ) if context is None: context = cirq.TransformerContext(tags_to_ignore=("ignore", ), deep=True) else: context = dataclasses.replace(context, tags_to_ignore=context.tags_to_ignore + ("ignore", ), deep=True) c_nested = cirq.eject_z(c_nested, context=context, eject_parameterized=eject_parameterized) cirq.testing.assert_same_circuits(c_nested, c_expected) c_nested = cirq.eject_z(c_nested, context=context, eject_parameterized=eject_parameterized) cirq.testing.assert_same_circuits(c_nested, c_expected)