Download
Download this file as Jupyter notebook: qubit_iq_distribution.ipynb.
IQ Distribution experiment
This guide shows how to measure the I/Q distribution of both the ground state and excited state of a qubit using a variable amplitude X pulse followed by a measurement.
We can use the pre-defined
IQDistributionExperiment
and our example
calibration set from the experiments library. To start out, we define an
empty ChannelMapper
to instantiate our experiment
for a single qubit.
[2]:
import keysight.qcs as qcs
from keysight.qcs.experiments import IQDistributionExperiment, make_calibration_set
run_on_hw = False
n_qubits = 1
cal_set = make_calibration_set(n_qubits)
qubits = qcs.Qudits(range(n_qubits))
# generate an empty channel mapper
mapper = qcs.ChannelMapper("ip_addr")
iq_experiment = IQDistributionExperiment(mapper, cal_set, qubits)
iq_experiment.draw()
Program
Program
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||
|
|
RX
ParameterizedGate RX on ('qudits', 0)
Matrices
|
Measure on ('qudits', 0)
Parameters
|
The program consists of a simple RX gate with variable amplitude followed by a measurement. During execution, we set the amplitude of this gate to both zero and one to prepare the ground and excited states.
Compiling this program to the waveform level using the
ParameterizedLinker
s in the calibration set
results in the following program:
[3]:
iq_experiment.compiled_program.draw()
Program
Program
|
|||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||||||||||||||||||
|
|
RFWaveform on ('xy_pulse', 0)
Parameters
|
Delay on ('xy_pulse', 0)
Parameters
|
||||||||||||||||||||||||||
|
|
Delay on ('readout_pulse', 0)
Parameters
|
Delay on ('readout_pulse', 0)
Parameters
|
RFWaveform on ('readout_pulse', 0)
Parameters
|
Delay on ('readout_pulse', 0)
Parameters
|
||||||||||||||||||||||||
|
|
Delay on ('readout_acquisition', 0)
Parameters
|
Delay on ('readout_acquisition', 0)
Parameters
|
Acquisition on ('readout_acquisition', 0)
Parameters
|
Delay on ('readout_acquisition', 0)
Parameters
|
The RX pulse has been replaced by an appropriately modulated Gaussian pulse and the measurement waveform and simultaneous acquisition have been added on a separate virtual readout channel.
We can also use the render method to visualize the waveform sequence within this program.
[4]:
iq_experiment.compiled_program.render(
channel_subplots=False,
lo_frequency=5e9,
sweep_index=1,
sample_rate=5e9,
)
To execute this experiment, we can simply run
[5]:
if run_on_hw:
iq_experiment.execute()
else:
# load in a previously executed version of this experiment
iq_experiment = qcs.load("IQDistribution.qcs")
[6]:
iq_experiment = qcs.load("IQDistribution.qcs")
iq_experiment.draw()
Program
Program
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||
|
|
RX
ParameterizedGate RX on ('qudits', 0)
Matrices
|
Measure on ('qudits', 0)
Parameters
|
||||||||||
|
|
Measure on ('ancilla', 1)
Parameters
|
For the purposes of this demonstration, we added a second “ancilla” qubit to the IQ distribution program and connected the physical output channels for our qubit to the digizer associated with the ancilla to allow us to capture the full pulse sequence.
[7]:
iq_experiment.draw()
Program
Program
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||
|
|
RX
ParameterizedGate RX on ('qudits', 0)
Matrices
|
Measure on ('qudits', 0)
Parameters
|
||||||||||
|
|
Measure on ('ancilla', 1)
Parameters
|
[8]:
iq_experiment.compiled_program.draw()
Program
Program
|
|||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||||||||||||||||||
|
|
RFWaveform on ('xy_pulse', 0)
Parameters
|
Delay on ('xy_pulse', 0)
Parameters
|
||||||||||||||||||||||||||
|
|
Delay on ('readout_pulse', 0)
Parameters
|
Delay on ('readout_pulse', 0)
Parameters
|
RFWaveform on ('readout_pulse', 0)
Parameters
|
Delay on ('readout_pulse', 0)
Parameters
|
||||||||||||||||||||||||
|
|
Delay on ('readout_acquisition', 0)
Parameters
|
Delay on ('readout_acquisition', 0)
Parameters
|
Acquisition on ('readout_acquisition', 0)
Parameters
|
Delay on ('readout_acquisition', 0)
Parameters
|
||||||||||||||||||||||||
|
Acquisition on ('readout_acquisition', 1)
Parameters
|
The ancilla qubit is mapped to the digitizer channel 1
and has a single
acquisition that spans the duration of the control pulse.
[9]:
iq_experiment.plot_trace()
Here we can see the control pulse with amplitudes zero or one, followed by the readout pulse. Note that our local oscillator (LO) frequency was set to 5 GHz in this example.
And to look at the IQ distribution, we can plot the IQ data as a scatter plot. This data was taken with a simple constant integration filter.
[10]:
iq_experiment.plot_iq(channels=qcs.Qudits(1, "ancilla"), plot_type="scatter")
IQ Kernel Optimization
To improve blob separation and therefore readout fidelity, we provide the possibility
of optimizing the shape of the integration filters of the measurment linker using
kernel_optimization()
.
For any subset of the target qubits for which the IQ distribution experiment has been run, we demodulate the time-domain data trace from its respective IQ blob experiment using the integration envelope. We compute the average distance between the ground state and the excitated blobs, which will be maximized to enhance qubit readout fidelity.
The filter envelope consists of two parts: a rising gaussian and a decaying exponential. The gaussian rise accounts for the ring-up time of the readout resonator placing small weight to the first samples. The decaying term confers less weight to the last samples acquired as the qubit decoheres during readout.
[11]:
iq_experiment.kernel_optimization(
target_qubits=[0],
acquisition_duration_name="acquisition_duration",
readout_frequencies_name="readout_frequencies",
update_measurement=False,
)
/Users/kpclocal/jenkins/workspace/qcs_main/keysight/qcs/experiments/iq_distribution_experiment.py:323: OptimizeWarning:
Unknown solver options: ftol
/Users/kpclocal/jenkins/workspace/qcs_main/keysight/qcs/experiments/iq_distribution_experiment.py:323: OptimizeWarning:
Initial guess is not within the specified bounds
The method will create a new optimized measurement linker if update_measurement
is set to True.
The method is designed to function with the
make_calibration_set()
method. If the user
has their own custom calibration set whose readout frequency variable is not named
readout_frequencies or the duration of the acquisition operation is not named
acquisition_duration, they should indicate it when calling the function.
We can inspect the optimized filter parameters after running the kernel optimization operation with the get_optimized_parameters command, and then decide whether to update the measurement linker or not.
The three parameters that characterize the filter’s envelope are:
tau: The time value at which the filter starts decaying.
std: The standard deviation of the gaussian component of the envelope.
decay: The decay rate of the exponential part of the envelope.
The format input argument ‘format’ refers to the format of the array returned. flat is a one-dimensional array consisting of a , whereas reshape is (number of qubits, 3)-dimensional.
[12]:
iq_experiment.get_optimized_parameters(format="flat")
[12]:
array([5.00e-08, 4.75e-08, 1.00e-06])
After inspecting the envelope parameters, we can reates a new measurement linker with the updated integration filter for the target qubits with the optimized values.
[13]:
iq_experiment.update_measurement_operation(
operation_name="measurement",
new_operation_name="optimized_measurement",
)
Note that this does not modify the current measurement linker, but rather, creates a new one.
Download
Download this file as Jupyter notebook: qubit_iq_distribution.ipynb.