def test_merge_single_qubit_gates_into_phxz_deep(): a = cirq.NamedQubit("a") c_nested = cirq.FrozenCircuit(cirq.H(a), cirq.Z(a), cirq.H(a).with_tags("ignore")) c_nested_merged = cirq.FrozenCircuit( _phxz(-0.5, 0.5, 0).on(a), cirq.H(a).with_tags("ignore")) c_orig = cirq.Circuit( c_nested, cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"), c_nested, cirq.CircuitOperation(c_nested).repeat(5).with_tags("preserve_tags"), c_nested, cirq.CircuitOperation(c_nested).repeat(6), ) c_expected = cirq.Circuit( c_nested_merged, cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"), c_nested_merged, cirq.CircuitOperation(c_nested_merged).repeat(5).with_tags( "preserve_tags"), c_nested_merged, cirq.CircuitOperation(c_nested_merged).repeat(6), ) context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True) c_new = cirq.merge_single_qubit_gates_to_phxz(c_orig, context=context) cirq.testing.assert_same_circuits(c_new, c_expected)
def test_merge_single_qubit_gates_into_phxz(): def phxz(a, x, z): return cirq.PhasedXZGate( axis_phase_exponent=a, x_exponent=x, z_exponent=z, ) a, b = cirq.LineQubit.range(2) c = cirq.Circuit( cirq.X(a), cirq.Y(b) ** 0.5, cirq.CZ(a, b), cirq.H(a), cirq.Z(a), cirq.measure(b, key="m"), cirq.H(a).with_classical_controls("m"), ) assert_optimizes( optimized=cirq.merge_single_qubit_gates_to_phxz(c), expected=cirq.Circuit( phxz(-1, 1, 0).on(a), phxz(0.5, 0.5, 0).on(b), cirq.CZ(a, b), phxz(-0.5, 0.5, 0).on(a), cirq.measure(b, key="m"), cirq.H(a).with_classical_controls("m"), ), )
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 two_qubit_matrix_to_sycamore_operations( q0: cirq.Qid, q1: cirq.Qid, mat: np.ndarray, *, atol: float = 1e-8, clean_operations: bool = True, ) -> cirq.OP_TREE: """Decomposes a two-qubit unitary matrix into `cirq_google.SYC` + single qubit rotations. The analytical decomposition first Synthesizes the given operation using `cirq.CZPowGate` + single qubit rotations and then decomposes each `cirq.CZPowGate` into `cirq_google.SYC` + single qubit rotations using `cirq_google.known_2q_op_to_sycamore_operations`. Note that the resulting decomposition may not be optimal, and users should first try to decompose a given operation using `cirq_google.known_2q_op_to_sycamore_operations`. Args: q0: The first qubit being operated on. q1: The other qubit being operated on. mat: Defines the operation to apply to the pair of qubits. atol: A limit on the amount of absolute error introduced by the construction. clean_operations: Merges runs of single qubit gates to a single `cirq.PhasedXZGate` in the resulting operations list. Returns: A `cirq.OP_TREE` that implements the given unitary operation using only `cirq_google.SYC` + single qubit rotations. """ decomposed_ops: List[cirq.OP_TREE] = [] for op in cirq.two_qubit_matrix_to_cz_operations( q0, q1, mat, allow_partial_czs=True, atol=atol, clean_operations=clean_operations): if cirq.num_qubits(op) == 2: decomposed_cphase = known_2q_op_to_sycamore_operations(op) assert decomposed_cphase is not None decomposed_ops.append(decomposed_cphase) else: decomposed_ops.append(op) return ([ *cirq.merge_single_qubit_gates_to_phxz( cirq.Circuit(decomposed_ops)).all_operations() ] if clean_operations else decomposed_ops)
def _convert_one(self, op: cirq.Operation) -> cirq.OP_TREE: """The main conversion step for the PointOptimizer.""" if not (cirq.has_unitary(op) and 1 <= cirq.num_qubits(op) <= 2): return NotImplemented if cirq.num_qubits(op) == 1: return [ *cirq.merge_single_qubit_gates_to_phxz( cirq.Circuit(op)).all_operations() ] known_decomp = two_qubit_to_sycamore.known_2q_op_to_sycamore_operations( op) if known_decomp is not None: return known_decomp if self.tabulation is not None: return two_qubit_to_sycamore._decompose_arbitrary_into_syc_tabulation( op, self.tabulation) return two_qubit_to_sycamore.two_qubit_matrix_to_sycamore_operations( op.qubits[0], op.qubits[1], cirq.unitary(op))
def test_merge_single_qubit_gates_into_phxz(): a, b = cirq.LineQubit.range(2) c = cirq.Circuit( cirq.X(a), cirq.Y(b)**0.5, cirq.CZ(a, b), cirq.H(a), cirq.Z(a), cirq.measure(b, key="m"), cirq.H(a).with_classical_controls("m"), ) assert_optimizes( optimized=cirq.merge_single_qubit_gates_to_phxz(c), expected=cirq.Circuit( _phxz(-1, 1, 0).on(a), _phxz(0.5, 0.5, 0).on(b), cirq.CZ(a, b), _phxz(-0.5, 0.5, 0).on(a), cirq.measure(b, key="m"), cirq.H(a).with_classical_controls("m"), ), )