Skip to content

Helper

Helper functions and classes, including data-saving, connection, fitting, etc.

Everything directly exported to quick.

🟢connect

soccfg, soc = quick.connect(ip, port=8888, proxy_name="qick")

Connect to a QICK board.

Parameters:

  • ip (str) IP address of the QICK board
  • port=8888 (int) Port of the QICK board
  • proxy_name="qick" (str) Proxy server name of the QICK board

Return:

  • soccfg QICK board socket config
  • soc QICK board socket

🟡getSoc

soccfg, soc = quick.getSoc()

Get the socket objects for the last connected QICK board. Mostly for internal use.

Return:

  • soccfg QICK board socket config
  • soc QICK board socket
quick.print_yaml(data)

Print a Python object in yaml format.

Parameters:

  • data a Python object, such as dict.

🟢load_yaml

data = quick.load_yaml(path)

Load a yaml file.

Parameters:

  • path (str) Path to the yaml file

Return:

  • data a Python object

🟢save_yaml

content = quick.save_yaml(path, data)

Save a Python object to a yaml file.

Parameters:

  • path (str) Path to the proposed yaml file
  • data a Python object to be saved

Return:

  • content (str) the saved yaml string

🟢load_data

data = quick.load_data(*paths)

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

n = quick.feistel_network(i, N, seed)

Generate pseudo-random permutation using Feistel network.

Parameters:

  • i (int) position index
  • N (int) upper limit of the integer range
  • seed (int) seed for the pseudo-random generation

Return:

  • n (int) permuted index

🔵nested_get

res = quick.nested_get(d, key)

Get value from a nested dictionary.

Parameters:

  • d (dict) a nested dictionary
  • key (list) a list of keys to access the nested value

Return:

  • res the accessed value

🟢Sweep

s = quick.Sweep(config, sweepConfig, random=False, progressBar=True)

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

s = quick.Saver(title, path, indep_params=[], dep_params=[], params={})

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_data method below.

- Saver.write_yml

s.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

s.write_data(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 in indep_params and dep_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

gain = quick.dB2gain(dB, ref_gain=1, ref_dB=None)

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_gain will be ignored.

Returns:

  • gain (float) value of gain

🔵gain2dB

power_dB = quick.gain2dB(gain, ref_gain=1, ref_dB=None)

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_gain will be ignored.

Returns:

  • power_dB (float) power value in dB unit

🟢evalStr

res = quick.evalStr(s, var, _var=None)

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 string
  • var (dict) given local variables
  • _var=None (dict) given global variables

Return:

  • res (str) evaluated string, treat the given s as 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

@quick.safe_wrap(retry=3, timeout=300)

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

xc = quick.symmetryCenter(x, y, it=3)

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 in x.

🟢estimateOmega

omega = quick.estimateOmega(x, y)

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-distinguish
  • threshold (float) threshold in I (after the phase change), above which is excited state
  • visibility (float) computed readout visibility
  • Fg (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. None if plot=False.

🟢fitT1

p, perr, r2, fig = quick.fitT1(T, S, plot=True)

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. None if plot=False.

🟢fitT2

p, perr, r2, fig = quick.fitT2(T, S, omega=2*np.pi, T2=20.0, plot=True)

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. None if plot=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 frequency
  • S (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]. If None, 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. None if plot=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.