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
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
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.](/images/braket/latest/developerguide/images/PulseControlCounts.png)