Hello Pulse - Amazon Braket

Hello Pulse

Here, you will learn how to construct a simple Bell pair directly with pulses, and execute this pulse program on the Rigetti device. A Bell pair is a two-qubit circuit consisting of a Hadamard gate on the first qubit followed by a cnot gate between the first and second qubits. Creating entangled states with pulses requires specific mechanisms that are dependent on the hardware type and device architecture. We will not use a native mechanism to create the cnot gate. Instead, we’ll use specific waveforms and frames that enable the cz gate natively. In this example, we will create a Hadamard gate using the single-qubit native gates rx and rz and express the cz gate using pulses.

First, let’s import the necessary libraries. In addition to the Circuit class, you will now also need to import the PulseSequence class.

from braket.aws import AwsDevice from braket.pulse import PulseSequence, ArbitraryWaveform, GaussianWaveform from braket.circuits import Circuit import braket.circuits.circuit as circuit

Next, instantiate a new Braket device using the Amazon Resource Name (ARN) of the Rigetti Aspen-M-3 device. Refer to the Devices page on the Amazon Braket console to view the layout of the Rigetti Aspen-M-3 device.

a=10 #specifies the control qubit b=113 #specifies the target qubit device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3")

As the Hadamard gate is not a native gate of the Rigetti device, it cannot be used in combination with pulses. You therefore need to decompose it into a sequence of the rx and rz native gates.

import numpy as np import matplotlib.pyplot as plt @circuit.subroutine(register=True) def rigetti_native_h(q0): return ( Circuit() .rz(q0, np.pi) .rx(q0, np.pi/2) .rz(q0, np.pi/2) .rx(q0, -np.pi/2) )

For the cz gate, we will use an arbitrary waveform with parameters (amplitude, rise/fall time, and duration) that have been predetermined by the hardware provider during a calibration stage. This waveform will be applied on the q10_q113_cz_frame. For a more recent version of the arbitrary waveform used here, see QCS, on the Rigetti website. You may be required to create a QCS account.

a_b_cz_wfm = ArbitraryWaveform([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00017888439538396808, 0.00046751103636033026, 0.0011372942989106456, 0.002577059611929697, 0.005443941944632366, 0.010731922770068104, 0.01976701723583167, 0.03406712171899736, 0.05503285980691202, 0.08350670755829034, 0.11932853352131022, 0.16107456696238298, 0.20614055551722368, 0.2512065440720643, 0.292952577513137, 0.328774403476157, 0.3572482512275353, 0.3782139893154499, 0.3925140937986156, 0.40154918826437913, 0.4068371690898149, 0.4097040514225177, 0.41114381673553674, 0.411813599998087, 0.4121022266390633, 0.4122174383870584, 0.41226003881132406, 0.4122746298554775, 0.4122792591252675, 0.4122806196003006, 0.41228098995582513, 0.41228108334474756, 0.4122811051578895, 0.4122811098772742, 0.4122811108230642, 0.4122811109986316, 0.41228111102881937, 0.41228111103362725, 0.4122811110343365, 0.41228111103443343, 0.4122811110344457, 0.4122811110344471, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.41228111103444737, 0.4122811110344471, 0.4122811110344457, 0.41228111103443343, 0.4122811110343365, 0.41228111103362725, 0.41228111102881937, 0.4122811109986316, 0.4122811108230642, 0.4122811098772742, 0.4122811051578895, 0.41228108334474756, 0.41228098995582513, 0.4122806196003006, 0.4122792591252675, 0.4122746298554775, 0.41226003881132406, 0.4122174383870584, 0.4121022266390633, 0.411813599998087, 0.41114381673553674, 0.4097040514225176, 0.4068371690898149, 0.40154918826437913, 0.3925140937986155, 0.37821398931544986, 0.3572482512275351, 0.32877440347615655, 0.2929525775131368, 0.2512065440720641, 0.20614055551722307, 0.16107456696238268, 0.11932853352131002, 0.08350670755829034, 0.05503285980691184, 0.03406712171899729, 0.01976701723583167, 0.010731922770068058, 0.005443941944632366, 0.002577059611929697, 0.0011372942989106229, 0.00046751103636033026, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) a_b_cz_frame = device.frames[f'q{a}_q{b}_cz_frame'] dt = a_b_cz_frame.port.dt a_b_cz_wfm_duration = len(a_b_cz_wfm.amplitudes)*dt print('CZ pulse duration:', a_b_cz_wfm_duration*1e9, 'ns')

This should return:

CZ pulse duration: 124 ns

Now we can construct the cz gate using the waveform we just defined. Recall that the cz gate consists of a phase flip of the target qubit if the control qubit is in the |1> state.

phase_shift_a=1.1733407221086924 phase_shift_b=6.269846678712192 a_rf_frame = device.frames[f'q{a}_rf_frame'] b_rf_frame = device.frames[f'q{b}_rf_frame'] frames = [a_rf_frame, b_rf_frame, a_b_cz_frame] cz_pulse_sequence = ( PulseSequence() .barrier(frames) .play(a_b_cz_frame, a_b_cz_wfm) .delay(a_rf_frame, a_b_cz_wfm_duration) .shift_phase(a_rf_frame, phase_shift_a) .delay(b_rf_frame, a_b_cz_wfm_duration) .shift_phase(b_rf_frame, phase_shift_b) .barrier(frames) )

The a_b_cz_wfm waveform is played on a frame that is associated to a fast-flux port. Its role is to shift the qubit frequency to activate a qubit-qubit interaction. For more information, see Roles of frames and ports. As the frequency varies, the qubit frames rotate at different rates than the single-qubit rf frames that are kept untouched: the latter ones are getting dephased. These phase shifts were calibrated through Ramsey sequences beforehand and are provided here as hardcoded information through phase_shift_a and phase_shift_b (Full period). We correct this dephasing by using shift_phase instructions on rf frames. Note that this sequence will only work in programs where no XY frame related to qubit a and b is used as we do not compensate for the phase shift that occurs on these frames. This is the case for this single Bell pair program, which uses only rf and cz frames. For more information, see Caldwell et al..

Now we’re ready to create a Bell pair with pulses.

bell_circuit_pulse = ( Circuit() .rigetti_native_h(a) .rigetti_native_h(b) .pulse_gate([a, b], cz_pulse_sequence) .rigetti_native_h(b) ) print(bell_circuit_pulse)
T : | 0 | 1 | 2 | 3 |4 | 5 | 6 | 7 | 8 | q5 : -Rz(3.14)-Rx(1.57)-Rz(1.57)-Rx(-1.57)-PG-------------------------------------- | q6 : -Rz(3.14)-Rx(1.57)-Rz(1.57)-Rx(-1.57)-PG-Rz(3.14)-Rx(1.57)-Rz(1.57)-Rx(-1.57)- T : | 0 | 1 | 2 | 3 |4 | 5 | 6 | 7 | 8 |

Let’s run this Bell pair on the Rigetti device. Note that running this code block will incur a charge. For more information about these costs, see the Amazon Braket Pricing page. We recommend that you test your circuits using a small amount of shots to ensure that it can run on the device before increasing the shot count.

task = device.run(bell_pair_pulses, shots=100) counts = task.result().measurement_counts plt.bar(sorted(counts), [counts[k] for k in sorted(counts)])
Bar chart showing population rates for 4 different binary states: 00, 01, 10, and 11, with states 00 and 11 having the highest proportion around 0.4 and states 01 and 10 having the lowest proportions.