def test_kak_decomposition_depth_partial_cz(): a, b = cirq.LineQubit.range(2) # Random. u = cirq.testing.random_unitary(4) operations_with_full = cirq.two_qubit_matrix_to_cz_operations( a, b, u, True) c = cirq.Circuit(operations_with_full) # 3 CP, 3+1 PhasedX, 1 Z assert len(c) <= 8 # Double-axis interaction. u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, True) c = cirq.Circuit(operations_with_part) # 2 CP, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Partial single-axis interaction. u = cirq.unitary(cirq.CNOT**0.1) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, True) c = cirq.Circuit(operations_with_part) # 1 CP, 1+1 PhasedX, 1 Z assert len(c) <= 4 # Full single-axis interaction. u = cirq.unitary(cirq.ControlledGate(cirq.Y)) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, True) c = cirq.Circuit(operations_with_part) # 1 CP, 1+1 PhasedX, 1 Z assert len(c) <= 4
def test_two_to_ops_equivalent_and_bounded_for_known_and_random( max_partial_cz_depth, max_full_cz_depth, effect ): q0 = cirq.NamedQubit('q0') q1 = cirq.NamedQubit('q1') operations_with_partial = cirq.two_qubit_matrix_to_cz_operations(q0, q1, effect, True) operations_with_full = cirq.two_qubit_matrix_to_cz_operations(q0, q1, effect, False) assert_ops_implement_unitary(q0, q1, operations_with_partial, effect) assert_ops_implement_unitary(q0, q1, operations_with_full, effect) assert_cz_depth_below(operations_with_partial, max_partial_cz_depth, False) assert_cz_depth_below(operations_with_full, max_full_cz_depth, True)
def decompose_to_target_gateset(self, op: 'cirq.Operation', moment_idx: int) -> DecomposeResult: """Method to rewrite the given operation using gates from this gateset. Args: op: `cirq.Operation` to be rewritten using gates from this gateset. moment_idx: Moment index where the given operation `op` occurs in a circuit. Returns: - An equivalent `cirq.OP_TREE` implementing `op` using gates from this gateset. - `None` or `NotImplemented` if does not know how to decompose `op`. """ # Known matrix? mat = cirq.unitary(op, None) if len(op.qubits) <= 2 else None if mat is not None and len(op.qubits) == 1: gates = cirq.single_qubit_matrix_to_phased_x_z(mat) return [g.on(op.qubits[0]) for g in gates] if mat is not None and len(op.qubits) == 2: return cirq.two_qubit_matrix_to_cz_operations( op.qubits[0], op.qubits[1], mat, allow_partial_czs=False, clean_operations=True) return NotImplemented
def test_kak_decomposition_depth_full_cz(): a, b = cirq.LineQubit.range(2) # Random. u = cirq.testing.random_unitary(4) operations_with_full = cirq.two_qubit_matrix_to_cz_operations( a, b, u, False) c = cirq.Circuit(operations_with_full) # 3 CZ, 3+1 PhasedX, 1 Z assert len(c) <= 8 # Double-axis interaction. u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, False) c = cirq.Circuit(operations_with_part) # 2 CZ, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Test unoptimized/un-cleaned length of Double-axis interaction. u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a))) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, False, 1e-8, False) c = cirq.Circuit(operations_with_part) assert len(c) > 6 # Length should be 13 with extra Pauli gates # Partial single-axis interaction. u = cirq.unitary(cirq.CNOT**0.1) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, False) c = cirq.Circuit(operations_with_part) # 2 CZ, 2+1 PhasedX, 1 Z assert len(c) <= 6 # Full single-axis interaction. u = cirq.unitary(cirq.ControlledGate(cirq.Y)) operations_with_part = cirq.two_qubit_matrix_to_cz_operations( a, b, u, False) c = cirq.Circuit(operations_with_part) # 1 CZ, 1+1 PhasedX, 1 Z assert len(c) <= 4
def _convert_one(self, op: cirq.Operation) -> cirq.OP_TREE: # Known matrix? mat = cirq.unitary(op, None) if len(op.qubits) <= 2 else None if mat is not None and len(op.qubits) == 1: gates = cirq.single_qubit_matrix_to_phased_x_z(mat) return [g.on(op.qubits[0]) for g in gates] if mat is not None and len(op.qubits) == 2: return cirq.two_qubit_matrix_to_cz_operations( op.qubits[0], op.qubits[1], mat, allow_partial_czs=True, clean_operations=False ) return NotImplemented
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 _decompose_two_qubit(operation: cirq.Operation) -> cirq.OP_TREE: """Decomposes a two qubit unitary operation into ZPOW, XPOW, and CNOT.""" mat = cirq.unitary(operation) q0, q1 = operation.qubits naive = cirq.two_qubit_matrix_to_cz_operations(q0, q1, mat, allow_partial_czs=False) temp = cirq.map_operations_and_unroll( cirq.Circuit(naive), lambda op, _: [cirq.H(op.qubits[1]), cirq.CNOT(*op.qubits), cirq.H(op.qubits[1])] if type(op.gate) == cirq.CZPowGate else op, ) temp = cirq.merge_single_qubit_gates_to_phased_x_and_z(temp) # A final pass breaks up PhasedXPow into Rz, Rx. yield cirq.map_operations_and_unroll( temp, lambda op, _: cirq.decompose_once(op) if type(op.gate) == cirq.PhasedXPowGate else op).all_operations()
def _decompose_two_qubit_operation(self, op: cirq.Operation, _) -> cirq.OP_TREE: if not cirq.has_unitary(op): return NotImplemented mat = cirq.unitary(op) q0, q1 = op.qubits naive = cirq.two_qubit_matrix_to_cz_operations(q0, q1, mat, allow_partial_czs=False) temp = cirq.map_operations_and_unroll( cirq.Circuit(naive), lambda op, _: [ cirq.H(op.qubits[1]), cirq.CNOT(*op.qubits), cirq.H(op.qubits[1]) ] if op.gate == cirq.CZ else op, ) return cirq.merge_k_qubit_unitaries( temp, k=1, rewriter=lambda op: self._decompose_single_qubit_operation( op, -1)).all_operations()