Helper
Helper functions and classes, including data-saving, connection, fitting, etc.
Everything directly exported to
quick.
🟢connect
Connect to a QICK board.
Parameters:
ip(str) IP address of the QICK boardport=8888(int) Port of the QICK boardproxy_name="qick"(str) Proxy server name of the QICK board
Return:
soccfgQICK board socket configsocQICK board socket
🟡getSoc
Get the socket objects for the last connected QICK board. Mostly for internal use.
Return:
soccfgQICK board socket configsocQICK board socket
🟢print_yaml
Print a Python object in yaml format.
Parameters:
dataa Python object, such as dict.
🟢load_yaml
Load a yaml file.
Parameters:
path(str) Path to the yaml file
Return:
dataa Python object
🟢save_yaml
Save a Python object to a yaml file.
Parameters:
path(str) Path to the proposed yaml filedataa Python object to be saved
Return:
content(str) the saved yaml string
🟢load_data
Load arbitrary number of data files (.csv).
Parameters:
*paths(str) path to the data files
Return:
data(2D Array) combined data. Data rows from all files will be concatenated.
🟡feistel_network
Generate pseudo-random permutation using Feistel network.
Parameters:
i(int) position indexN(int) upper limit of the integer rangeseed(int) seed for the pseudo-random generation
Return:
n(int) permuted index
🔵nested_get
Get value from a nested dictionary.
Parameters:
d(dict) a nested dictionarykey(list) a list of keys to access the nested value
Return:
resthe accessed value
🟢Sweep
The class to construct an iterable. In each iteration, new dictionary will be generated according to the template dictionary config and sweeping list set in sweepConfig. If there is no sweeping item, the original config will be yielded once.
Parameters:
config(dict) The template dictionary. It will NOT be modified.sweepConfig(dict) The sweeping list. See the following example.random=False(bool) Whether to randomize the sweeping order.progressBar=True(bool) Whether to show progress bar.
Example:
v = {
"a": 0, "b": 1, "c": 2,
"nested": { "d": 3, "e": 4 }
}
sweepConfig = {
"a": np.arange(0, 1, 0.1), # empty list raises error
"nested": { "d": [1, 8, 9] }, # "nested" must exist in v
"const": 1, # non-iterable values substitutes directly
}
for _v in quick.Sweep(v, sweepConfig):
print(_v) # dict with "a", "d", "const" modified.
Details:
Randomization uses Feistel network to generate pseudo-random permutation during runtime. It does not cache the permutation. For each instance of Sweep, the random order is fixed by using the same seed generated at construction.
🟢Saver
The class to construct a data saver. The meta information will be saved in a yml file and data points will be saved in a csv file.
Parameters:
title(str) filename (also the title) of the data.path(str) path to the directory to save the data.indep_params=[](list) a list of 2-tuples, specifying meta information for independent variables, in the format of("Name", "Unit")dep_params=[](list) a list of 2-tuples, specifying meta information for dependent variables, in the format of("Name", "Unit")params={}(dict) a dictionary of meta information and other parameters.
Most variables and methods are for internal use and therefore not documented here. To save data, use the
write_datamethod below.
- Saver.write_yml
Write a yml file, recording the metainformation of the current saver. This will overwrite any existing yml file produced by the same saver. This function will be called during the saver initialization. It is recommended to call this function after all data writings to update the completed time.
- Saver.write_data
Write data to a data saver. The data will be immediately appended to the data file. Actual file writing is performed in this method. Numerical data will be saved as scientific notation with 10 digits significant figures, eg. 1.234567890e-3. This function can be called repetitively to keep appending data.
Parameters:
data(2D ArrayLike) a list of data rows. Each row should be a list of numerical data, in the exact order defined inindep_paramsanddep_params. See the example below.
Example:
s = quick.Saver("Test Saving", "path/to/directry",
indep_params=[("Frequency", "MHz")], # a list of ("Name", "Unit")
dep_params=[("Amplitude", "dB"), ("Data", "")], # a list of ("Name", "Unit")
params={"quick.__version__": quick.__version__} # any meta information
)
s.write_data([ # data to save is a 2D array: a list of rows
[1, 100, 200], # match "Frequency", "Amplitude", "Data" in order
[1.5, 101, 201] # defined in indep_params and dep_params
])
# OPTIONAL: after complete, call s.write_yml()
s.write_yml() # this will update the completed time.
print(s.file_name + ".csv") # the full path of data file.
🔵dB2gain
Convert power from dB unit to gain value. By default, 0 dB corresponds to gain of 1.
Parameters:
dB(float) power value in dB unit.ref_gain=1(float) reference gain value.ref_dB=None(float) reference power value in dB unit. If provided,ref_gainwill be ignored.
Returns:
gain(float) value of gain
🔵gain2dB
Convert power from gain value to dB unit. By default, gain of 1 corresponds to 0 dB.
Parameters:
gain(float) gain value.ref_gain=1(float) reference gain value.ref_dB=None(float) reference power value in dB unit. If provided,ref_gainwill be ignored.
Returns:
power_dB(float) power value in dB unit
🟢evalStr
Evaluate a string as f-string with the given local and global variables. All and only the things within {} will be evaluated as Python expression. Everything outside {} will not be changed. The string cannot include any other bracket than those that need to be parsed as Python expressions.
Parameters:
s(str) a given template stringvar(dict) given local variables_var=None(dict) given global variables
Return:
res(str) evaluated string, treat the givensas f-string.
Example:
print(quick.evalStr("{k} + 1 = {k + 1}", { "k": 3 })) # This prints: 3 + 1 = 4
mercator_protocol = """
soft_avg: 100
p0_freq: {r_freq}
p0_length: {r_length}
p0_power: {r_power}
r{rr}_p: 0
r{rr}_length: {r_length / 2} # support any expression
r{rr}_phase: {r_phase + 180}
steps:
- type: pulse
p: 0
g: {r}
- type: trigger
t: {r_offset}
- type: wait_auto
- type: delay_auto
t: {r_relax}
"""
v = dict(quick.experiment.var) # default variables dictionary
cfg = yaml.safe_load(quick.evalStr(mercator_protocol, v))
🔵safe_wrap
Decorator generator to wrap a function with retry and timeout mechanism. If failed, it will raise the last exception.
Parameters:
retry=3(int) number of retries if exception occurs.timeout=300(float) timeout in seconds for each function call.
Return:
Decorator function to wrap the target function.
Example:
@quick.safe_wrap()
def critical_addition(a, b):
# do something critical that may fail
return a + b
critical_addition(1, 2) # will retry up to 3 times with timeout of 300s each time
🔵symmetryCenter
Very fancy technique to evaluate the most probable symmetry center for a signal.
Parameters:
x(1D ArrayLike) data of independent variable. Better to be equally spaced.y(1D ArrayLike) data of dependent variable.it=3(int) number of iteration.
Return:
xc(float) estimated symmetry center. Interpolated, not inx.
🟢estimateOmega
Rough estimate the angular frequency using FFT. Useful for initial parameters in curve fitting.
Parameters:
x(1D ArrayLike) data of independent variable. Must be equally spaced.y(1D ArrayLike) data of dependent variable.
Return:
omega(float) estimated angular frequency.
🟢iq_scatter
phase, threshold, visibility, Fg, Fe, c0, c1, fig = quick.iq_scatter(S0s, S1s, c0=None, c1=None, plot=True)
Compute the center for 0-state and 1-state from measured data. Compute the visibility and readout fidelity. Plot the IQ scatter and histogram.
Parameters:
S0s(1D ArrayLike[complex]) a list of IQ values in IQ complex plane, as the Qubit is in 0 state.S1s(1D ArrayLike[complex]) a list of IQ values in IQ complex plane, as the Qubit is in 1 state.c0=None(complex) 0-state center in IQ complex plane. Can be determined if not provided.c1=None(complex) 1-state center in IQ complex plane. Can be determined if not provided.plot=True(bool) whether to plot the IQ scatter and histogram.
Return:
phase(float) [deg] phase change to be added on ADC to get horizontal state-distinguishthreshold(float) threshold in I (after the phase change), above which is excited statevisibility(float) computed readout visibilityFg(float) computed readout fidelity for ground state (0-state)Fe(float) computed readout fidelity for excited state (1-state)c0(complex) 0-state center in IQ complex plane.c1(complex) 1-state center in IQ complex plane.fig(matplotlib.figure) plotted IQ scatter and histogram.Noneifplot=False.
🟢fitT1
Fit and plot the Qubit T1 from data.
Parameters:
T(1D ArrayLike) a list of pulse delay time.S(1D ArrayLike) a list of corresponding qubit population.plot=True(bool) whether to plot the fitting.
Return:
p(np.Array(3)) Fitted parameter values.p[1]is the value for T1.perr(np.Array(3)) Fitted parameter errors.perr[1]is the error for T1.r2(float) R-squared of the fitting.fig(matplotlib.figure) fitting plot.Noneifplot=False.
🟢fitT2
Fit and plot the Qubit T2 from data.
Parameters:
T(1D ArrayLike) a list of pulse delay time.S(1D ArrayLike) a list of corresponding qubit population.omega=2*np.pi(float) initial value for angular frequency.T2=20.0(float) initial guess value for T2.plot=True(bool) whether to plot the fitting.
Return:
p(np.Array(4)) Fitted parameter values.p[1]is the value for T2.perr(np.Array(4)) Fitted parameter errors.perr[1]is the error for T2.r2(float) R-squared of the fitting.fig(matplotlib.figure) fitting plot.Noneifplot=False.
Example:
data = quick.load_data("path/to/your/data.csv").T
omega = quick.estimateOmega(data[0], data[1])
p, perr, r2, fig = quick.fitT2(data[0], data[1], omega=omega)
🟢fitResonator
p, perr, r2, fig = quick.fitResonator(F, S, fit="circle", p0=[None, None, None, None], plot=True, normalize_background=True, normalize_phase=True)
Circle fit of inverse S21 for quality factor of resonator.
Parameters:
F(1D ArrayLike) a list of frequencyS(1D ArrayLike complex) a list of corresponding complex S21.fit="circle"(str) "circle", "amp" or "arg". the target of the fitting.p0=[None, None, None, None](1D ArrayLike) initial value of the fitting parameters in the order of[Qi, Qc, fr, phi]. IfNone, then default value will be used.plot=True(bool) whether to plot the fitting.normalize_background=True(bool) whether to normalize the background of S21 before fitting.normalize_phase=True(bool) whether to normalize the phase of S21 before fitting.
Return:
p(np.Array(4)) Fitted parameter values in the order of[Qi, Qc, fr, phi]perr(np.Array(4)) Fitted parameter errors.r2(float) R-squared of the fitting.fig(matplotlib.figure) fitting plot.Noneifplot=False.
Example:
data = quick.load_data("path/to/data1.csv", "path/to/data2.csv").T # combine two scan
p, perr, r2, fig = quick.fitResonator(data[0], data[3] + 1j * data[4])
Details:
If your fitting phase is opposite to the expected, set initial values of Qi and Qc with negative values in p0. This is caused by unphysical data that encloses the origin in the complex plane, which is typically due to imprecision in calibrating the off-resonant point for near-critically-damped resonators.