Mock Data (Loading Simulated Experiment data)#
This section explains how to load simulated experiment data using the mock_data parameter. The mock_data parameter allows you to test and analyze experiments without requiring actual hardware execution.
The mock_data parameter should be structured as follows:
Keys: Instances of Channels representing the channels used in the experiment.
Values: Iterables containing the simulated data for each channel.
Example usage:
mock_data = {
channel_1: [trace_data_1, trace_data_2, ...],
channel_2: [trace_data_1, trace_data_2, ...],
qubit_1: [trace_data_1, trace_data_2, ...],
...
}
[2]:
import keysight.qcs as qcs
from keysight.qcs.experiments import Experiment, make_calibration_set
import numpy as np
Before creating the Experiment
with mock_data, we need a
ChannelMapper
and Program
object to tell the
Experiment
class for what data structure to expect for
mock_data.
Creating a dummy ChannelMapper
#
ChannelMapper
is required to configure the number of channels
available for acquisition, and how many number of channel-data to expect in mock_data.
The dimension of the accepted program is [n_shots, sweep_a, sweep_b, …, n_points]
n_shots: Number of program repetitions
sweep_a, sweep_b, …: Sweep variables
n_points: number of accepted points by the
IntegrationFilter
to covert trace data into IQ data.
[3]:
n_channels = 2
n_qubits = 2
n_points = 240 # points accepted by the integration filter
qubits = qcs.Qudits(range(n_qubits))
channels = qcs.Channels(range(n_channels), "xy_channels", absolute_phase=False)
mapper = qcs.ChannelMapper("dummy")
# assigning dummy physical addresses to the channels
for i in range(n_channels):
mapper.add_channel_mapping(
channels=channels[i],
addresses=qcs.Address(1, 1, i + 1),
instrument_types=qcs.InstrumentEnum.M5300AWG,
)
Loading a simple Sweep Program and Data#
The Program
class gives information what dimension of data to expect
from mock_data. The program can be a simple non-iterating program, a normal sweep,
a nested sweep, or a zipped sweep. The data shape is determined by the structure of
program.repetitions.items.
Here, we will load data for a simple amplitude sweep program.
[4]:
n_shots = 7
num_amps = 13 # number of amplitude points to sweep on
program = qcs.Program()
amplitude = qcs.Scalar("amplitude", dtype=float)
phase = qcs.Scalar("phase", dtype=float)
amps = qcs.Array("amps", value=np.linspace(0, 0.8, num_amps), dtype=float)
waveform = qcs.RFWaveform(
duration=20e-9,
envelope=qcs.GaussianEnvelope(),
amplitude=amplitude,
rf_frequency=5.1e9,
)
program.add_waveform(waveform, channels)
int_filter = qcs.RFWaveform(10e-8, qcs.ConstantEnvelope(), 1, 5.15e9)
program.add_acquisition(int_filter, channels)
program.sweep(amps, amplitude).n_shots(n_shots)
# The shape of the accepted data must account for these dimensions
for i in program.repetitions.items:
print(i)
Repeat(7)
Sweep(amplitude=Array(name=amps, shape=(13,), dtype=float, unit=none))
Defining mock data and loading into the Experiment:
[5]:
fpga_postprocessing = False
# acceptable data shape
shape = [n_shots, num_amps, n_points]
# generate mock trace data
mock_data = dict()
for chan in channels:
mock_data[chan] = np.random.random(shape)
# load the mock_data using the mock_data parameter in the Experiment class
backend = qcs.HclBackend(mapper, fpga_postprocessing=fpga_postprocessing)
exp = Experiment(backend, make_calibration_set(2), qubits, program, mock_data=mock_data)
exp.get_iq() # or exp.get_trace()
/home/kpclocal/jenkins/workspace/qcs_release_2025B/.venv3.10/lib/python3.10/site-packages/keysight/qcs/programs/program.py:891: UserWarning: get_iq() is deprecated and will be removed in a future version. Use get_iq_array() instead.
warn(
[5]:
(((xy_channels_0))) | ... | (((xy_channels_1))) | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ... | 6 | |||||||||||||||||||
(amplitude, 0) | (amplitude, 0.0667) | (amplitude, 0.1333) | (amplitude, 0.2) | (amplitude, 0.2667) | (amplitude, 0.3333) | (amplitude, 0.4) | (amplitude, 0.4667) | (amplitude, 0.5333) | (amplitude, 0.6) | ... | (amplitude, 0.2) | (amplitude, 0.2667) | (amplitude, 0.3333) | (amplitude, 0.4) | (amplitude, 0.4667) | (amplitude, 0.5333) | (amplitude, 0.6) | (amplitude, 0.6667) | (amplitude, 0.7333) | (amplitude, 0.8) | |
0 | 0.013288-0.040697j | -0.036325-0.018743j | -0.016354-0.009013j | -0.010501-0.052119j | 0.047378-0.021561j | 0.041027-0.022543j | 0.015198+0.020692j | 0.013215-0.016203j | 0.009075-0.007470j | -0.000150-0.014468j | ... | 0.035916+0.012897j | -0.01104+0.01811j | 0.000592+0.024496j | -0.038767+0.017171j | 0.002840+0.032478j | -0.016351+0.004400j | -0.048978-0.001063j | 0.049238-0.007242j | -0.014629+0.002769j | -0.002650+0.046695j |
1 rows × 182 columns
Nested Sweeps with mock_data#
Moving on to more complicated sweeps, we just have to that the shape of program.repetitions.items and our data matches.
[6]:
n_shots = 7
num_a = 13 # number of points to sweep on
num_b = 17 # number of points to sweep on
qubits = qcs.Qudits(range(n_qubits))
channels = qcs.Channels(range(n_channels), "xy_channels", absolute_phase=False)
program = qcs.Program()
amplitude = qcs.Scalar("amplitude", dtype=float)
phase = qcs.Scalar("phase", dtype=float)
amps = qcs.Array("amps", value=np.linspace(0, 0.8, num_a), dtype=float)
phases = qcs.Array("phases", value=np.linspace(0, np.pi, num_b), dtype=float)
waveform = qcs.RFWaveform(
duration=20e-9,
envelope=qcs.GaussianEnvelope(),
amplitude=amplitude,
rf_frequency=5.1e9,
instantaneous_phase=phase,
)
program.add_waveform(waveform, channels)
int_filter = qcs.RFWaveform(10e-8, qcs.ConstantEnvelope(), 1, 5.15e9)
program.add_acquisition(int_filter, channels)
program.sweep(amps, amplitude).sweep(phases, phase).n_shots(n_shots)
for i in program.repetitions.items:
print(i)
Repeat(7)
Sweep(phase=Array(name=phases, shape=(17,), dtype=float, unit=none))
Sweep(amplitude=Array(name=amps, shape=(13,), dtype=float, unit=none))
Defining and loading mock_data
[7]:
fpga_postprocessing = False
shape = [n_shots, num_a, num_b, n_points]
mock_data = dict()
for chan in channels:
mock_data[chan] = np.random.random(shape)
backend = qcs.HclBackend(mapper, fpga_postprocessing=fpga_postprocessing)
exp = Experiment(backend, make_calibration_set(2), qubits, program, mock_data=mock_data)
exp.get_iq() # OR exp.get_trace()
/home/kpclocal/jenkins/workspace/qcs_release_2025B/.venv3.10/lib/python3.10/site-packages/keysight/qcs/programs/program.py:891: UserWarning: get_iq() is deprecated and will be removed in a future version. Use get_iq_array() instead.
warn(
[7]:
(((xy_channels_0))) | ... | (((xy_channels_1))) | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ... | 6 | |||||||||||||||||||
(phase, 0 rad) | ... | (phase, 3.1415926536 rad) | |||||||||||||||||||
(amplitude, 0) | (amplitude, 0.0667) | (amplitude, 0.1333) | (amplitude, 0.2) | (amplitude, 0.2667) | (amplitude, 0.3333) | (amplitude, 0.4) | (amplitude, 0.4667) | (amplitude, 0.5333) | (amplitude, 0.6) | ... | (amplitude, 0.2) | (amplitude, 0.2667) | (amplitude, 0.3333) | (amplitude, 0.4) | (amplitude, 0.4667) | (amplitude, 0.5333) | (amplitude, 0.6) | (amplitude, 0.6667) | (amplitude, 0.7333) | (amplitude, 0.8) | |
0 | -0.012142-0.017452j | -0.021037-0.030431j | 0.003584-0.008268j | -0.009785-0.015071j | -0.007037+0.011171j | 0.031778-0.007017j | -0.023837-0.016846j | 0.019829+0.005912j | -0.003075-0.022688j | 0.027723+0.011929j | ... | -0.022578-0.057672j | 0.006901+0.046226j | 0.015695+0.023737j | 0.026469-0.038733j | -0.018386-0.010636j | 0.021467+0.012530j | -0.014377+0.010932j | 0.007180+0.028563j | -0.000865+0.012230j | -0.019446+0.023353j |
1 rows × 3094 columns
Zipped Sweeps with mock_data#
Zipped sweeps have special structure in program.repetitions.items
[8]:
num_a = 13
# program
qubits = qcs.Qudits(range(n_channels))
channels = qcs.Channels(range(n_channels), "xy_channels", absolute_phase=False)
program = qcs.Program()
amplitude = qcs.Scalar("amplitude", dtype=float)
phase = qcs.Scalar("phase", dtype=float)
amps = qcs.Array("amps", value=np.linspace(0, 0.8, num_a), dtype=float)
phases = qcs.Array("phases", value=np.linspace(0, np.pi, num_a), dtype=float)
waveform = qcs.RFWaveform(
duration=20e-9,
envelope=qcs.GaussianEnvelope(),
amplitude=amplitude,
rf_frequency=5.1e9,
instantaneous_phase=phase,
)
program.add_waveform(waveform, channels)
int_filter = qcs.RFWaveform(10e-8, qcs.ConstantEnvelope(), 1, 5.15e9)
program.add_acquisition(int_filter, channels)
program.sweep((amps, phases), (amplitude, phase)).n_shots(n_shots)
for i in program.repetitions.items:
print(i)
Repeat(7)
Sweep(amplitude=Array(name=amps, shape=(13,), dtype=float, unit=none), phase=Array(name=phases, shape=(13,), dtype=float, unit=none))
Defining and loading mock_data
[9]:
# mock_data
shape = [n_shots, num_a, n_points]
mock_data = dict()
for chan in channels:
mock_data[chan] = np.random.random(shape)
# test for each mock_data
backend = qcs.HclBackend(mapper, fpga_postprocessing=fpga_postprocessing)
exp = Experiment(backend, make_calibration_set(2), qubits, program, mock_data=mock_data)
exp.get_iq() # OR exp.get_trace()
/home/kpclocal/jenkins/workspace/qcs_release_2025B/.venv3.10/lib/python3.10/site-packages/keysight/qcs/programs/program.py:891: UserWarning: get_iq() is deprecated and will be removed in a future version. Use get_iq_array() instead.
warn(
[9]:
(((xy_channels_0))) | ... | (((xy_channels_1))) | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ... | 6 | |||||||||||||||||||
(amplitude, 0), (phase, 0 rad) | (amplitude, 0.0667), (phase, 261.8 mrad) | (amplitude, 0.1333), (phase, 523.6 mrad) | (amplitude, 0.2), (phase, 785.4 mrad) | (amplitude, 0.2667), (phase, 1.0471975512 rad) | (amplitude, 0.3333), (phase, 1.308996939 rad) | (amplitude, 0.4), (phase, 1.5707963268 rad) | (amplitude, 0.4667), (phase, 1.8325957146 rad) | (amplitude, 0.5333), (phase, 2.0943951024 rad) | (amplitude, 0.6), (phase, 2.3561944902 rad) | ... | (amplitude, 0.2), (phase, 785.4 mrad) | (amplitude, 0.2667), (phase, 1.0471975512 rad) | (amplitude, 0.3333), (phase, 1.308996939 rad) | (amplitude, 0.4), (phase, 1.5707963268 rad) | (amplitude, 0.4667), (phase, 1.8325957146 rad) | (amplitude, 0.5333), (phase, 2.0943951024 rad) | (amplitude, 0.6), (phase, 2.3561944902 rad) | (amplitude, 0.6667), (phase, 2.617993878 rad) | (amplitude, 0.7333), (phase, 2.8797932658 rad) | (amplitude, 0.8), (phase, 3.1415926536 rad) | |
0 | 0.007352-0.020070j | -0.014639+0.043102j | 0.010966+0.005918j | 0.015824-0.010360j | 0.024994+0.059510j | -0.022445-0.055323j | 0.028804-0.010298j | 0.006629-0.014569j | -0.000871+0.044678j | 0.009703+0.027075j | ... | 0.005604+0.004943j | 0.028400+0.015427j | -0.001422-0.000796j | 0.044411+0.028131j | 0.026029-0.034315j | 0.000616-0.059646j | 0.027153-0.098916j | -0.020694+0.027185j | 0.044959+0.018638j | 0.030580+0.008713j |
1 rows × 182 columns
Example for a Qubit level program#
For qubit level program, the user need to additionally take care of the calibration set and the application of LinkerPass to the program, as shown below:
[10]:
# channel mapper
n_channels = 2
n_qubits = 2
n_points = 240 # points accepted by the integration filter
mapper = qcs.ChannelMapper("dummy")
# channel_acq required for qubit-type mock_data
# our default function qcs.experiments.make_calibration_set` creates channels with
# name="readout_acquisition", and supporting linkers for it.
channels_acq = qcs.Channels(
range(n_channels), "readout_acquisition", absolute_phase=True
)
# assigning dummy physical addresses to the channels
for i in range(n_channels):
mapper.add_channel_mapping(
channels=channels_acq[i],
addresses=qcs.Address(2, 1, i + 1),
instrument_types=qcs.InstrumentEnum.M5300AWG,
)
Defining the program and other components:
[11]:
n_shots = 7
num_amps = 13 # number of amplitude points to sweep on
fpga_postprocessing = False
qubits = qcs.Qudits(range(n_channels))
channels_acq = qcs.Channels(
range(n_channels), "readout_acquisition", absolute_phase=True
)
program = qcs.Program()
program.add_measurement(qubits)
program.n_shots(n_shots)
# Additional steps: Defining the calibration set and applying LinkerPass
cal_set = make_calibration_set(n_channels)
program = qcs.LinkerPass(*cal_set.linkers.values()).apply(program)
# mock_data for qubits
mock_data = dict()
for qub in qubits:
mock_data[qub] = np.random.random((n_shots, n_points))
backend = qcs.HclBackend(mapper, fpga_postprocessing=fpga_postprocessing)
exp = Experiment(backend, make_calibration_set(2), qubits, program, mock_data=mock_data)
exp.get_iq()
/home/kpclocal/jenkins/workspace/qcs_release_2025B/.venv3.10/lib/python3.10/site-packages/keysight/qcs/programs/program.py:891: UserWarning: get_iq() is deprecated and will be removed in a future version. Use get_iq_array() instead.
warn(
[11]:
(((Qudits(labels=[0], name=qudits, dim=2)))) | (((Qudits(labels=[1], name=qudits, dim=2)))) | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
0 | 0.002727+0.013405j | -0.005646-0.006130j | 0.035178+0.038043j | 0.018771-0.007235j | -0.008187+0.025709j | -0.022232-0.024229j | 0.015294+0.007298j | -0.033034+0.010230j | 0.043947+0.016791j | 0.036037+0.026145j | 0.014593+0.025809j | 0.020845+0.019800j | 0.002018-0.061956j | 0.009540-0.013131j |