1. Getting started

1.1. Defining a calibration

1.1.1. Exopy template

Typical usage:

  • Set default_path = $my_default_path in calib_name_template.meas.ini and define my_default_path value in assumptions.py

1.1.2. Jupyter notebook template

[ ]:
# Calibration name ({TYPE})
## {PLACEHOLDER}
[ ]:
file = h5py.File('HDF5_PATH', 'r', swmr=True)
xdata = file['data']['x'][()]
ydata = file['data']['y'][()]
[ ]:
popt, pcov = opt.curve_fit(function, xdata, ydata, guesses=(0, 1, 2))
f'a, b, c = {popt}'
[ ]:
fig, ax = plt.subplots()
ax.plot(xdata, ydata, '.-', label='Data')
ax.plot(xdata, function(xdata, *popt), label=f'Fit: a = {popt[0]:f}')
ax.legend();
[ ]:
# optional
_opt = popt
_cov = pcov

# optional
_err = {'Custom error', _opt[0] < 0}

# mandatory
_results = {'a': popt[0]}
_results

This Jupyter notebook should be saved as calib_name_template.ipynb under qualib/calibrations/calib_name.

1.1.3. Python script

Template:

from .default import DefaultCalibration

class Calibration(DefaultCalibration):
    def handle_substitutions(self) -> None:
        # Define substitutions here
        super().handle_substitutions()

    def pre_process(self) -> None:
        super().pre_process(mapping = {})

    def process(self) -> None:
        super().process()
        # Update assumptions here

    def post_process(self) -> None:
        super().post_process(mapping = {})

Example (“Rabi” calibration):

from ..default import DefaultCalibration

class Calibration(DefaultCalibration):
    def handle_substitutions(self) -> None:
        super().handle_substitutions()

    def pre_process(self) -> None:
        super().pre_process({
            'LINEARITY_AMP_LIMIT': str(self.assumptions['rabi'][f'{self.substitutions["PULSE"]}_linearity_amp_limit'])
        })

    def process(self) -> None:
        super().process()
        self.assumptions['qubit'][f'{self.substitutions["PULSE"]}_amp'] = self.results['a_rabi']

    def post_process(self) -> None:
        factor = 1
        if '_pi_'  in self.substitutions['PULSE']: factor = 2
        if '_pi2_' in self.substitutions['PULSE']: factor = 4
        
        super().post_process({
            'TYPE':         self.assumptions['TYPE'],
            'PULSE_AMP':    f'{self.results["a_rabi"]/factor:f}',
            'PULSE_LENGTH': str(self.assumptions['qubit'][f'{self.substitutions["PULSE"]}_length'])
        })

1.2. Defining a calibration sequence

Example:

[
   {"name": "spectro_ro",
    "substitutions": {"NAME": "phase_only",
                      "TYPE": "phase only"}},
   
   {"name": "spectro_ro",
    "substitutions": {"NAME": "circle_fit",
                      "TYPE": "circle fit"}},
   
   {"name": "spectro_qubit"},
   
   {"name": "rabi_probe"},
   
   {"name": "rabi",
    "substitutions": {"NAME":  "uncond_pi2",
                      "PULSE": "unconditional_pi2_pulse",
                      "TYPE":  "unconditional pi/2 pulse"}},
   
   {"name": "rabi",
    "substitutions": {"NAME":  "uncond_pi",
                      "PULSE": "unconditional_pi_pulse",
                      "TYPE":  "unconditional pi pulse"}},
   
   {"name": "rabi",
    "substitutions": {"NAME":  "cond_pi",
                      "PULSE": "conditional_pi_pulse",
                      "TYPE":  "conditional pi pulse"}},
   
    {"name": "ramsey"},
    
    {"name": "t1_qubit"}
]

1.3. Installation/usage

1.3.1. CLI/module usage

python -m qualib.main calibration_scheme.py

1.3.2. Package usage

pip install qualib
from qualib.main import Qualib

qualib = Qualib()
qualib.run_all('calibration_scheme.py')