def swap_rzz(theta: float, q0: ops.Qid, q1: ops.Qid) -> ops.OP_TREE: """ An implementation of SWAP * EXP(1j theta ZZ) using three sycamore gates. This builds off of the zztheta method. A sycamore gate following the zz-gate is a SWAP EXP(1j (THETA - pi/24) ZZ). Args: theta: exp(1j * theta ) q0: First qubit id to operate on q1: Second qubit id to operate on Returns: The circuit that implements ZZ followed by a swap """ # Set interaction part. circuit = circuits.Circuit() angle_offset = np.pi / 24 - np.pi / 4 circuit.append(google.SYC(q0, q1)) circuit.append(rzz(theta - angle_offset, q0, q1)) # Get the intended circuit. intended_circuit = circuits.Circuit( ops.SWAP(q0, q1), ops.ZZPowGate(exponent=2 * theta / np.pi, global_shift=-0.5).on(q0, q1)) yield create_corrected_circuit(intended_circuit, circuit, q0, q1)
def operation_decomposer( self, op: ops.Operation) -> Optional[list[ops.Operation]]: # Decomposes CNOT and the CZPowGate family to Valkmusa native gates. # All the decompositions below keep track of global phase (required for decomposing controlled gates). if isinstance(op.gate, ops.CXPowGate) and op.gate.exponent == 1.0: # CNOT is a special case, we decompose it using iSWAPs to be able to commute Z rotations through control_qubit = op.qubits[0] target_qubit = op.qubits[1] s = op.gate.global_shift iSWAP = ops.ISwapPowGate(exponent=1, global_shift=(s + 0.25) / 2) return [ Lx.on(target_qubit), Lzi.on(control_qubit), Lz.on(target_qubit), iSWAP.on(*op.qubits), Lx.on(control_qubit), iSWAP.on(*op.qubits), Lz.on(target_qubit), ] if isinstance(op.gate, ops.CZPowGate): # decompose CZPowGate using ZZPowGate t = op.gate.exponent s = op.gate.global_shift L = ops.rz(t / 2 * PI) return [ ops.ZZPowGate(exponent=-0.5 * t, global_shift=-2 * s - 1).on(*op.qubits), L.on(op.qubits[0]), L.on(op.qubits[1]), ] if isinstance(op.gate, ops.ZZPowGate): # ZZPowGate is decomposed using two applications of the XY interaction t = op.gate.exponent s = op.gate.global_shift XY = ops.ISwapPowGate(exponent=-t, global_shift=-(s + 0.5) / 2) return [ Lyi.on(op.qubits[0]), Lyi.on(op.qubits[1]), XY.on(*op.qubits), ops.XPowGate(exponent=-1, global_shift=-0.5).on(op.qubits[0]), XY.on(*op.qubits), ops.XPowGate(exponent=1, global_shift=-0.5).on(op.qubits[0]), Ly.on(op.qubits[0]), Ly.on(op.qubits[1]), ] if isinstance(op.gate, ops.ZPowGate): # Rz using Rx, Ry q = op.qubits[0] return [ ops.XPowGate(exponent=-0.5).on(q), ops.YPowGate(exponent=op.gate.exponent).on(q), ops.XPowGate(exponent=0.5).on(q), ] return None
def _translate_two_qubit_braket_instruction_to_cirq_operation( instr: Instruction, ) -> List["cirq.Operation"]: """Converts the two-qubit braket instruction to Cirq. Args: instr: Two-qubit Braket instruction to convert. Raises: ValueError: If the instruction cannot be converted to Cirq. """ qubits = [LineQubit(int(qubit)) for qubit in instr.target] gate = instr.operator # Two-qubit non-parameterized gates. if isinstance(gate, braket_gates.CNot): return [cirq_ops.CNOT.on(*qubits)] elif isinstance(gate, braket_gates.Swap): return [cirq_ops.SWAP.on(*qubits)] elif isinstance(gate, braket_gates.ISwap): return [cirq_ops.ISWAP.on(*qubits)] elif isinstance(gate, braket_gates.CZ): return [cirq_ops.CZ.on(*qubits)] elif isinstance(gate, braket_gates.CY): return [ cirq_ops.S.on(qubits[1])**-1, cirq_ops.CNOT.on(*qubits), cirq_ops.S.on(qubits[1]), ] # Two-qubit parameterized gates. elif isinstance(gate, braket_gates.PSwap): raise ValueError # TODO. elif isinstance(gate, braket_gates.CPhaseShift): return [cirq_ops.CZ.on(*qubits)**(gate.angle / np.pi)] elif isinstance(gate, braket_gates.CPhaseShift00): raise ValueError # TODO. elif isinstance(gate, braket_gates.CPhaseShift01): raise ValueError # TODO. elif isinstance(gate, braket_gates.CPhaseShift10): raise ValueError # TODO. elif isinstance(gate, braket_gates.XX): return [ cirq_ops.XXPowGate(exponent=gate.angle / np.pi, global_shift=-0.5).on(*qubits) ] elif isinstance(gate, braket_gates.YY): return [ cirq_ops.YYPowGate(exponent=gate.angle / np.pi, global_shift=-0.5).on(*qubits) ] elif isinstance(gate, braket_gates.ZZ): return [ cirq_ops.ZZPowGate(exponent=gate.angle / np.pi, global_shift=-0.5).on(*qubits) ] elif isinstance(gate, braket_gates.XY): return [cirq_ops.ISwapPowGate(exponent=gate.angle / np.pi).on(*qubits)] else: raise ValueError( f"Unable to convert the instruction {instr} to Cirq. If you think " "this is a bug, you can open an issue on the Mitiq GitHub at " "https://github.com/unitaryfund/mitiq.")
testing.assert_allclose_up_to_global_phase( protocols.unitary(test_circuit), protocols.unitary(cirq_circuit), atol=1e-7, ) @pytest.mark.parametrize( "common_gate", [ ops.CNOT, ops.CZ, ops.ISWAP, ops.XXPowGate(exponent=-0.2), ops.YYPowGate(exponent=0.3), ops.ZZPowGate(exponent=-0.1), ], ) def test_to_from_braket_common_two_qubit_gates(common_gate): """These gates should stay the same (i.e., not get decomposed) when converting Cirq -> Braket -> Cirq. """ cirq_circuit = Circuit(common_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, ) # Cirq AAPowGate has a different global phase than braket AA which gets
def _translate_two_qubit_braket_instruction_to_cirq_operation( instr: Instruction, ) -> List["cirq.Operation"]: """Converts the two-qubit braket instruction to Cirq. Args: instr: Two-qubit Braket instruction to convert. Raises: ValueError: If the instruction cannot be converted to Cirq. """ qubits = [LineQubit(int(qubit)) for qubit in instr.target] gate = instr.operator # Two-qubit non-parameterized gates. if isinstance(gate, braket_gates.CNot): return [cirq_ops.CNOT.on(*qubits)] elif isinstance(gate, braket_gates.Swap): return [cirq_ops.SWAP.on(*qubits)] elif isinstance(gate, braket_gates.ISwap): return [cirq_ops.ISWAP.on(*qubits)] elif isinstance(gate, braket_gates.CZ): return [cirq_ops.CZ.on(*qubits)] elif isinstance(gate, braket_gates.CY): return [ cirq_ops.S.on(qubits[1]) ** -1, cirq_ops.CNOT.on(*qubits), cirq_ops.S.on(qubits[1]), ] # Two-qubit parameterized gates. elif isinstance(gate, braket_gates.CPhaseShift): return [cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi)] elif isinstance(gate, braket_gates.CPhaseShift00): return [ cirq_ops.XX(*qubits), cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi), cirq_ops.XX(*qubits), ] elif isinstance(gate, braket_gates.CPhaseShift01): return [ cirq_ops.X(qubits[0]), cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi), cirq_ops.X(qubits[0]), ] elif isinstance(gate, braket_gates.CPhaseShift10): return [ cirq_ops.X(qubits[1]), cirq_ops.CZ.on(*qubits) ** (gate.angle / np.pi), cirq_ops.X(qubits[1]), ] elif isinstance(gate, braket_gates.PSwap): return [ cirq_ops.SWAP.on(*qubits), cirq_ops.CNOT.on(*qubits), cirq_ops.Z.on(qubits[1]) ** (gate.angle / np.pi), cirq_ops.CNOT.on(*qubits), ] elif isinstance(gate, braket_gates.XX): return [ cirq_ops.XXPowGate( exponent=gate.angle / np.pi, global_shift=-0.5 ).on(*qubits) ] elif isinstance(gate, braket_gates.YY): return [ cirq_ops.YYPowGate( exponent=gate.angle / np.pi, global_shift=-0.5 ).on(*qubits) ] elif isinstance(gate, braket_gates.ZZ): return [ cirq_ops.ZZPowGate( exponent=gate.angle / np.pi, global_shift=-0.5 ).on(*qubits) ] elif isinstance(gate, braket_gates.XY): return [cirq_ops.ISwapPowGate(exponent=gate.angle / np.pi).on(*qubits)] else: _raise_braket_to_cirq_error(instr)