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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 1
Targets 1
Repetitions Repeat with 100 repetitions
Sweep with 2 repetitions
Associations
rx_angles Array(name=scan_values, shape=(2, 1), dtype=float, unit=none)
Layer #0
Layer #0
Duration undefined
qudits 0
RX
ParameterizedGate RX on ('qudits', 0)

Parameters phi_x
Values Array(name=rx_angles, shape=(1,), dtype=float, unit=none)

Matrices
0 1
1 0
Measure on ('qudits', 0)

Parameters
Dim 2

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 ParameterizedLinkers in the calibration set results in the following program:

[3]:
iq_experiment.compiled_program.draw()
keysight-logo-svg
Program
Program
Duration 135 ns
Layers 1
Targets 3
Repetitions Repeat with 100 repetitions
Sweep with 2 repetitions
Associations
rx_angles Array(name=scan_values, shape=(2, 1), dtype=float, unit=none)
Layer #0
Layer #0
Duration 135 ns
xy_pulse 0
RFWaveform on ('xy_pulse', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=_implicit, value=None, dtype=float, unit=none)
Frequency ScalarRef(name=xy_pulse_frequencies, value=5.1 GHz, dtype=float, unit=Hz)
Envelope GaussianEnvelope(2.0)
Instantaneous Phase ScalarRef(name=rx_phase, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=rx_post_phase, value=0 rad, dtype=float, unit=rad)
Delay on ('xy_pulse', 0)

Parameters
Duration Max(Scalar(name=_implicit, value=105 ns, dtype=float, unit=s), Scalar(name=_implicit, value=105 ns, dtype=float, unit=s))
readout_pulse 0
Delay on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Delay on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=readout_pulse_delay, value=0 s, dtype=float, unit=s)
RFWaveform on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=readout_pulse_duration, value=100 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=readout_pulse_amplitudes, value=0.1, dtype=float, unit=none)
Frequency ScalarRef(name=readout_frequencies, value=5.15 GHz, dtype=float, unit=Hz)
Envelope SineEnvelope()
Instantaneous Phase ScalarRef(name=readout_pulse_phases, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=measurement_post_phase, value=0 rad, dtype=float, unit=rad)
Delay on ('readout_pulse', 0)

Parameters
Duration Scalar(name=_implicit, value=5 ns, dtype=float, unit=s)
readout_acquisition 0
Delay on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Delay on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=acquisition_delay, value=5 ns, dtype=float, unit=s)
Acquisition on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=acquisition_duration, value=100 ns, dtype=float, unit=s)
Integration Filter
RFWaveform

Parameters
Duration ScalarRef(name=acquisition_duration, value=100 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=measurement_integrator_amplitude, value=1, dtype=float, unit=none)
Frequency ScalarRef(name=readout_frequencies, value=5.15 GHz, dtype=float, unit=Hz)
Envelope ConstantEnvelope()
Instantaneous Phase ScalarRef(name=measurement_integrator_phase, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=measurement_integrator_post_phase, value=0 rad, dtype=float, unit=rad)
Classifier Classifier(Array(name=references, shape=(1, 2), dtype=complex, unit=none))
Delay on ('readout_acquisition', 0)

Parameters
Duration Scalar(name=_implicit, value=4.96308e-24 s, dtype=float, unit=s)

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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 1
Targets 2
Repetitions Sweep with 2 repetitions
Associations
rx_angles Array(name=scan_values, shape=(2, 1), dtype=float, unit=none)
Repeat with 1 repetitions
Layer #0
Layer #0
Duration undefined
qudits 0
RX
ParameterizedGate RX on ('qudits', 0)

Parameters phi_x
Values Array(name=rx_angles, shape=(1,), dtype=float, unit=none)

Matrices
0 1
1 0
Measure on ('qudits', 0)

Parameters
Dim 2
ancilla 1
Measure on ('ancilla', 1)

Parameters
Dim 2

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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 1
Targets 2
Repetitions Sweep with 2 repetitions
Associations
rx_angles Array(name=scan_values, shape=(2, 1), dtype=float, unit=none)
Repeat with 1 repetitions
Layer #0
Layer #0
Duration undefined
qudits 0
RX
ParameterizedGate RX on ('qudits', 0)

Parameters phi_x
Values Array(name=rx_angles, shape=(1,), dtype=float, unit=none)

Matrices
0 1
1 0
Measure on ('qudits', 0)

Parameters
Dim 2
ancilla 1
Measure on ('ancilla', 1)

Parameters
Dim 2
[8]:
iq_experiment.compiled_program.draw()
keysight-logo-svg
Program
Program
Duration 135 ns
Layers 1
Targets 4
Repetitions Sweep with 2 repetitions
Associations
rx_angles Array(name=scan_values, shape=(2, 1), dtype=float, unit=none)
Repeat with 1 repetitions
Layer #0
Layer #0
Duration 135 ns
xy_pulse 0
RFWaveform on ('xy_pulse', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=_implicit, value=None, dtype=float, unit=none)
Frequency ScalarRef(name=xy_pulse_frequencies, value=5.1 GHz, dtype=float, unit=Hz)
Envelope GaussianEnvelope(2.0)
Instantaneous Phase ScalarRef(name=rx_phase, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=rx_post_phase, value=0 rad, dtype=float, unit=rad)
Delay on ('xy_pulse', 0)

Parameters
Duration Max(Scalar(name=_implicit, value=105 ns, dtype=float, unit=s), Scalar(name=_implicit, value=105 ns, dtype=float, unit=s))
readout_pulse 0
Delay on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Delay on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=readout_pulse_delay, value=0 s, dtype=float, unit=s)
RFWaveform on ('readout_pulse', 0)

Parameters
Duration ScalarRef(name=readout_pulse_duration, value=100 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=readout_pulse_amplitudes, value=0.1, dtype=float, unit=none)
Frequency ScalarRef(name=readout_frequencies, value=5.15 GHz, dtype=float, unit=Hz)
Envelope SineEnvelope()
Instantaneous Phase ScalarRef(name=readout_pulse_phases, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=measurement_post_phase, value=0 rad, dtype=float, unit=rad)
Delay on ('readout_pulse', 0)

Parameters
Duration Scalar(name=_implicit, value=5 ns, dtype=float, unit=s)
readout_acquisition 0
Delay on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=xy_pulse_durations, value=30 ns, dtype=float, unit=s)
Delay on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=acquisition_delay, value=5 ns, dtype=float, unit=s)
Acquisition on ('readout_acquisition', 0)

Parameters
Duration ScalarRef(name=acquisition_duration, value=100 ns, dtype=float, unit=s)
Integration Filter
RFWaveform

Parameters
Duration ScalarRef(name=acquisition_duration, value=100 ns, dtype=float, unit=s)
Amplitude ScalarRef(name=measurement_integrator_amplitude, value=1, dtype=float, unit=none)
Frequency ScalarRef(name=readout_frequencies, value=5.15 GHz, dtype=float, unit=Hz)
Envelope ConstantEnvelope()
Instantaneous Phase ScalarRef(name=measurement_integrator_phase, value=0 rad, dtype=float, unit=rad)
Post-phase ScalarRef(name=measurement_integrator_post_phase, value=0 rad, dtype=float, unit=rad)
Classifier Classifier(Array(name=references, shape=(1, 2), dtype=complex, unit=none))
Delay on ('readout_acquisition', 0)

Parameters
Duration Scalar(name=_implicit, value=4.96308e-24 s, dtype=float, unit=s)
1
Acquisition on ('readout_acquisition', 1)

Parameters
Duration Scalar(name=_implicit, value=40 ns, dtype=float, unit=s)
Integration Filter
DCWaveform

Parameters
Duration Scalar(name=_implicit, value=40 ns, dtype=float, unit=s)
Amplitude 1
Frequency 0 Hz
Envelope ConstantEnvelope()
Instantaneous Phase 0
Post-phase 0

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.

On this page