Tutorial 1) Parameterizing sequences with arbok

0. Introduction

This tutorial gives a first insight into the architecture and philosophy behind arbok.

Arbok is a top-level python control framework based on QCoDeS compiling into FPGA instructions (QM-QUA SDK) for quantum machines hardware. The core idea behind arbok is to write qubit control sequences in a device and measurement setup agnostic manner that are configured/ scaled to larger systems by providing the respective configurations that characterize that given system.

QCoDeS is a full stack data acquisition framework that handles instrument communication, parameterization, data storage and visualization. Arbok leverages this existing infrastructure and

The following tutorial proivides:

  1. An overview of the basic architecture of arbok

  2. Demonstration of building a custom square pulse from scratch

  3. Defining parameter sweeps

  4. Using arbok QUA helpers to write sequences

The other tutorials in this documentation cover the topics:

  • Tutorial 0) Measurement notebook example

  • Tutorial 2) Readout sequences and abstract readouts

  • Tutorial 3) Input streaming of parameters

1. The basic architecture

To get started, four types of classes are needed:

Device:
The Device holds the quantum machines configuration that is quantifying elements, waveforms, mixers, etc. Also contains information about voltage dividers between the quantum machine outputs and the given device. Each of the following classes requires an instance of a sample to be instanciated.

ArbokDriver:
This instance can be understood as the actual qcodes instrument and manages the hardware connection, and all modular (Sub)Sequences.

SubSequence:
The SubSequence has and modular snippet of qua code which is written in a device agnostic way and can be re used on any system. The SubSequence is then parameterized by a configuration in the form a python dictionairy that make the executed code device specific.

Measurement:
The Measurement does not contain qua code itself but holds one or more SubSequences that are compiled together. A Measurement is meant to asseble its sub-modules to a full experiment.

How the mentioned classes relate to each other is sketched in the schematic below:

2. Parameterizing a simple square pulse

Firstly we import ArbokDriver, Device and Sequence classes that were discussed before. As a SubSequence, we import one of the given examples. We are starting with a simple square pulse sequence.

We also import a dummy configuration which has been taken from the quantum machines github repository.

from rich import print as rprint
from IPython.display import display, HTML
from arbok_driver import ArbokDriver, Device, Measurement
from arbok_driver.examples.sequences import SquarePulse
from arbok_driver.examples.configurations.hardware import opx1000_config
2026-04-05 08:03:43,935 - qm - INFO     - Starting session: b9c42964-8402-4a8c-9f78-1563eb264fac
display(HTML('<img src="./figures/schematic_nested_square.svg" width="500"/>'))

2.1 Configuring the Device

In the first step, a Device object will be configured and is used in every following sequence. The sample holds the configuration of the quantum machine that you probably already have from your experiments.

Besides this configuration a further ‘divider_config’ is required. This dictionary represents voltage divider that are in between the quantum machine and your sample. All voltage values in arbok are meant to be what is applied to the sample, not the output of the machine. This is implemented by the scale attribute of qcodes parameters.

A simple example is given below. A dict entry is required for every element in the quantum machines config that has an output port configured. For every element we multiply the factor opx_scale which compensates the output range of the OPX (-0.5V -> 0.5V)

opx_scale = 2
divider_config = {
    'gate_1': {
        'division': 1*opx_scale,
    },
    'gate_2': {
        'division': 1*opx_scale,
    },
    'readout_element': {
        'division': 1*opx_scale
    }
}

Both of those configs are now used to instantiate the given device.

mock_device = Device(
    name = 'mock_device',
    opx_config = opx1000_config,
    divider_config = divider_config)

2.2 Building the Arbok_driver and a Measurement

The sample we created previously is the only requirement to build a basic arbok_driver.

qm_driver = ArbokDriver('qm_driver', mock_device)

Measurements can now be registered into this driver. Measurements are meant to act as a container for all sub-sequences that are required to run a single type of measurement. Currently single sequences per driver are supported.

mock_measurement = Measurement(qm_driver, 'mock_measurement')

2.1 Configuring a simple square pulse sequence

So far, predefined classes were created and instantiated. The following SquarePulse class will demonstrate inheritance, let us have a look at the source code.

The SquarePulse inherits from SubSequence and the only thing added/overwritten is the qua_sequence method. The qua commands to execute are written within that method and the given arguments are filled with other attribute calls (like self.amplitude()). Those attributes are in fact qcodes parameters and can be tracked and varied throughout an experiment.

from arbok_driver.examples.sequences.square_pulse import SquarePulse
SquarePulse??
Init signature:
SquarePulse(
    parent,
    name: 'str',
    sequence_config: 'dict | None' = None,
    check_step_requirements: 'bool' = False,
    **kwargs,
)
Source:        
class SquarePulse(SubSequence):
    """
    Class containing parameters and sequence for a simple square pulse
    """
    PARAMETER_CLASS = SquarePulseParameters
    arbok_params: SquarePulseParameters
    def qua_sequence(self):
        """Macro that will be played within the qua.program() context"""
        qua.align()
        qua.play(
            pulse = 'ramp'*qua.amp(self.arbok_params.amplitude.qua),
            element = self.arbok_params.element.qua,
            duration = self.arbok_params.t_ramp.qua
            )
        qua.wait(
            self.arbok_params.t_square_pulse.qua,
            self.arbok_params.element.qua)
        qua.play(
            pulse = 'ramp'*qua.amp(self.arbok_params.amplitude.qua),
            element = self.arbok_params.element.qua,
            duration = self.arbok_params.t_ramp.qua
            )
File:           ~/git-repos/arbok_driver/arbok_driver/examples/sequences/square_pulse.py
Type:           ABCMeta
Subclasses:     

The SquarePulse SubSequence requires parameters (e.g. defined in a sequence_config file) that adds a qcodes.parameter for each entry like the one given here (note that we import Voltage and Time to specify our parameter units and other variables automatically):

from arbok_driver.parameter_types import Voltage, Time, String

square_conf = {
    'parameters': {
        'amplitude': {
            'value': 0.5,
            'type': Voltage,
        },
        't_square_pulse': {
            'value': 100,
            'type': Time
        },

        'element': {
            'value': 'gate_1',
            'unit': 'gate label',
            'type':  String
        },
        't_ramp': {
            'value': 20,
            'type': Time
        },
    }
}

square_conf fully configures parameters with an initial value and unit. Optionally you can add the variable type within qua, an axis label for data saving and validators like this:

_ = {
    't_square_pulse': {
        'value': 100,
        'type': Time,
        'label': 'Square pulse width'
    },
}

After creating the square pulse SubSequence with the respective config we can take a look at its snapshot.

Again, as all (sub-) sequences so far it requires a sample object for instantiation. This makes sure that all added sub-sequences are configured for the same device. Finally we add the SquarePulse of type SubSequence to our Sequence which is registered to the ArbokDriver.

square_pulse = SquarePulse(mock_measurement, 'square_pulse', square_conf)
qm_driver.mock_measurement.square_pulse.print_readable_snapshot()
qm_driver_mock_measurement_square_pulse:
	parameter     value
--------------------------------------------------------------------------------
amplitude      :	0.5 (V)
element        :	gate_1 (gate label)
t_ramp         :	20 (s)
t_square_pulse :	100 (s)

Another way to see all available parameters on a (sub-) sequence is by checking the parameters attribute:

square_pulse.parameters

Parameters can be easily modified and read out. Let us half the duration of the square pulse.

square_pulse.t_square_pulse.set(50)
square_pulse.t_square_pulse.get()
50.0

2.3 Compiling the qua program

All (sub-) sequences are prepared and the QUA program can be compiled. The method get_qua_program of the ArbokDriver recursively goes through the sequences and subsequences and returns the qua source code. The output can now be printed to a file with the method print_qua_program_to_file. Let us have a look at the result:

rprint(mock_measurement.get_qua_program_as_str())
# Single QUA script generated at 2026-04-05 08:03:45.845687
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(int, value=0)
    with infinite_loop_():
        pause()
        assign(v1, 0)
        align()
        play("ramp"*amp(0.5), "gate_1", duration=20)
        wait(50, "gate_1")
        play("ramp"*amp(0.5), "gate_1", duration=20)
        align()
        assign(v1, (v1+1))
        r1 = declare_stream()
        save(v1, r1)
        align()
    with stream_processing():
        r1.buffer(1).save("qm_driver_mock_measurement_shots")

config = None

loaded_config = None


The implicit parameters of the SquarePulse class became explicit upon QUA compilation with the values we have set.

Two lines defining an infinite_loop and pause of the given QUA program were not written in the square pulse class and are created by the driver in every case. The given logic is required to perform multiple shots of a sequence for example with different parameters or just to average a result. The pause statement keeps the client PC in sync with the execution of the quantum machine for live data plotting, input streaming, etc.

3. Parameter sweeps

So far we have learnt how to create a qcodes Instrument which parameterises a QUA sequence. Often experiments are characterising a physical system by sweeping parameters. In the next step we want to get familiar with defining parameter sweeps. Parameters can be swept concurrently along the same axis and the sweeps can be nested arbitrarily deep.

First we look at the most simple example of a 1D sweep (a sweep of only one parameter). Remember that the sequence acts as the container for all subseqeunces and is meant to represent one experiment. Therefore we define the sweep on it.

import numpy as np
mock_measurement.set_sweeps(
    {
        square_pulse.amplitude: np.linspace(0.1, 1, 5)
    }
)
Declared 1-dimensional parameter sweep of size 5 [5]

We print the compiled program to a file to inspect. One finds that the SquarePulse instructions are indented by an additional for loop which represents the defined sweep. In comparison to the example above, the parameter we sweep is not defined explicitly anymore but given by a QUA variable everywhere it is called.

Arbok tries to parameterize input arrays into start, step and stop to save FPGA memory. A user warning is raised every time this is done. The threshold for parameterisation for this is currently that the variance of the input array item steps is 10 times smaller than the step size itself. Arbok will always raise a warning when input arrays are parameterised.

rprint(mock_measurement.get_qua_program_as_str(recompile = True))
/home/nixos/git-repos/arbok_driver/arbok_driver/sweep.py:381: RuntimeWarning: invalid value encountered in add
  sss['start'] + Cast.mul_fixed_by_int(
# Single QUA script generated at 2026-04-05 08:03:45.975981
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(fixed, )
    v2 = declare(int, value=0)
    v3 = declare(int, )
    with infinite_loop_():
        pause()
        assign(v2, 0)
        assign(v3, 0)
        with while_((v3<5)):
            assign(v1, (0.1+Cast.mul_fixed_by_int(0.224775,v3)))
            align()
            align()
            play("ramp"*amp(v1), "gate_1", duration=20)
            wait(50, "gate_1")
            play("ramp"*amp(v1), "gate_1", duration=20)
            align()
            assign(v2, (v2+1))
            r1 = declare_stream()
            save(v2, r1)
            align()
            assign(v3, (v3+1))
    with stream_processing():
        r1.buffer(1).save("qm_driver_mock_measurement_shots")

config = None

loaded_config = None


Now let us see how a two dimensional sweep would work where we sweep two parameters along a certain axis. When sweeping two params together it is important to pass input arrays that have the same length.

mock_measurement.set_sweeps(
    {
        square_pulse.amplitude: np.linspace(0.1, 1, 5)
    },
    {
        square_pulse.t_square_pulse: np.arange(10, 50, 10, dtype = int),
        square_pulse.t_ramp: np.arange(10, 50, 10, dtype = int)
    }
)
Declared 2-dimensional parameter sweep of size 20 [5, 4]

We see that there is yet another for loop nested into the other. For multi param sweeps, arrays are always defined explicitly.

rprint(mock_measurement.get_qua_program_as_str(recompile = True))
/home/nixos/git-repos/arbok_driver/arbok_driver/sweep.py:381: RuntimeWarning: invalid value encountered in add
  sss['start'] + Cast.mul_fixed_by_int(
# Single QUA script generated at 2026-04-05 08:03:46.181197
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(fixed, )
    v2 = declare(int, )
    a1 = declare(int, value=[10, 20, 30, 40])
    v3 = declare(int, )
    a2 = declare(int, value=[10, 20, 30, 40])
    v4 = declare(int, value=0)
    v5 = declare(int, )
    v6 = declare(int, )
    with infinite_loop_():
        pause()
        assign(v4, 0)
        assign(v5, 0)
        with while_((v5<5)):
            assign(v1, (0.1+Cast.mul_fixed_by_int(0.224775,v5)))
            align()
            assign(v6, 0)
            with while_((v6<4)):
                assign(v2, a1)
                assign(v3, a2)
                align()
                play("ramp"*amp(v1), "gate_1", duration=v3)
                wait(v2, "gate_1")
                play("ramp"*amp(v1), "gate_1", duration=v3)
                align()
                assign(v4, (v4+1))
                r1 = declare_stream()
                save(v4, r1)
                align()
                assign(v6, (v6+1))
            assign(v5, (v5+1))
    with stream_processing():
        r1.buffer(1).save("qm_driver_mock_measurement_shots")

config = None

loaded_config = None


square_pulse.print_readable_snapshot()
qm_driver_mock_measurement_square_pulse:
	parameter     value
--------------------------------------------------------------------------------
amplitude      :	[0.1   0.325 0.55  0.775 1.   ] (V)
element        :	gate_1 (gate label)
t_ramp         :	[4.0e-08 8.0e-08 1.2e-07 1.6e-07] (s)
t_square_pulse :	[4.0e-08 8.0e-08 1.2e-07 1.6e-07] (s)

4. QUA helpers for system scale up

4.1 Creating parameters for multiple elements at the same time

If multiple output gates have to be configured at the same time, for example when one wants to move in a high dimensional voltage space, parameters can be defined with a gate prefix as shown below. Looking at ‘vHome’ and ‘vSquare’ you will see that no ‘value’ key is given here but ‘elements’. For this given configuration four different parameters will be defined for both ‘vHome’ and ‘vSquare’ as seen in the snapshot below.

from arbok_driver.examples.sequences import SquarePulseScalable
from arbok_driver.parameter_types import List
square_conf_scaled = {
    'sequence': SquarePulseScalable,
    'parameters': {
        'sticky_elements': {
            'value': ['gate_1', 'gate_2', 'gate_3', 'gate_4'],
            'unit': 'gate label',
            'type': List
        },
        'v_home': {
            'type': Voltage,
            "label": 'Default voltage point during the sequence',
            'elements': {
                'gate_1': 0,
                'gate_2': 0,
                'gate_3': 0,
                'gate_4': 0,
            }
        },
        'v_square': {
            'type': Voltage,
            "label": 'Voltage amplitude of square pulse',
            'elements': {
                'gate_1': 0.1,
                'gate_2': -0.05,
                'gate_3': 0.08,
                'gate_4': 0.25,
            }
        },
        't_square_pulse': {
            'value': 100,
            'type': Time
        },
        't_ramp': {
            'value': 20,
            'type': Time
        },
    }
}

After instantiating SquarePulseScalable with the given configuration we see that for example ‘vSquare’ created one paramer for each given gate. Resulting parameters are ‘v_square_gate_1’, ‘v_square_gate_2’, ‘v_square_gate_3’, ‘v_square_gate_4’.

meas_scaled_square = Measurement(qm_driver, 'meas_scaled_square')
square_pulse_scaled = SquarePulseScalable(
    meas_scaled_square, 'square_pulse_scaled', square_conf_scaled)
square_pulse_scaled.print_readable_snapshot()
qm_driver_meas_scaled_square_square_pulse_scaled:
	parameter      value
--------------------------------------------------------------------------------
sticky_elements :	['gate_1', 'gate_2', 'gate_3', 'gate_4'] (gate label)
t_ramp          :	20 (s)
t_square_pulse  :	100 (s)
v_home_gate_1   :	0 (V)
v_home_gate_2   :	0 (V)
v_home_gate_3   :	0 (V)
v_home_gate_4   :	0 (V)
v_square_gate_1 :	0.1 (V)
v_square_gate_2 :	-0.05 (V)
v_square_gate_3 :	0.08 (V)
v_square_gate_4 :	0.25 (V)

All elements that we were added in the config are now their own individual parameters. You can either access them directly via QCoDeS

square_pulse_scaled.v_square_gate_2.get()
-0.05

.. or via the arbok parameter mapping:

square_pulse_scaled.arbok_params.v_square['gate_2'].get()
-0.05

Let’s add a sweep of one of the voltages!

meas_scaled_square.set_sweeps(
    {square_pulse_scaled.arbok_params.v_square['gate_3']: np.arange(0, 0.1, 0.01)}
)
Declared 1-dimensional parameter sweep of size 10 [10]

4.2 Playing pulses on multiple gates at the same time

The square pulse we imported now is slightly different to the one we have seen before. Play statements are not called explicitly but with the qua_helper arbok_go that takes the gate prefix and a list of gates. Play commands are then executed for all of those combinations that are available. Explicit align statements with the given gates can be executed optionally as well. See the resulting program for reference below.

rprint(meas_scaled_square.get_qua_program_as_str())
# Single QUA script generated at 2026-04-05 08:03:46.522360
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(fixed, )
    v2 = declare(int, value=0)
    v3 = declare(int, )
    with infinite_loop_():
        pause()
        assign(v2, 0)
        assign(v3, 0)
        with while_((v3<10)):
            assign(v1, (0.0+Cast.mul_fixed_by_int(0.00999,v3)))
            align()
            align("gate_1", "gate_2", "gate_3", "gate_4")
            align("gate_1", "gate_2", "gate_3", "gate_4")
            play("unit_ramp"*amp(0.2), "gate_1", duration=20)
            play("unit_ramp"*amp(-0.1), "gate_2", duration=20)
            play("unit_ramp"*amp((v1-0)), "gate_3", duration=20)
            play("unit_ramp"*amp(0.25), "gate_4", duration=20)
            align("gate_1", "gate_2", "gate_3", "gate_4")
            wait(100, "gate_1", "gate_2", "gate_3", "gate_4")
            align("gate_1", "gate_2", "gate_3", "gate_4")
            play("unit_ramp"*amp(-0.2), "gate_1", duration=20)
            play("unit_ramp"*amp(0.1), "gate_2", duration=20)
            play("unit_ramp"*amp((0-v1)), "gate_3", duration=20)
            play("unit_ramp"*amp(-0.25), "gate_4", duration=20)
            align("gate_1", "gate_2", "gate_3", "gate_4")
            align()
            assign(v2, (v2+1))
            r1 = declare_stream()
            save(v2, r1)
            align()
            assign(v3, (v3+1))
    with stream_processing():
        r1.buffer(1).save("qm_driver_meas_scaled_square_shots")

config = None

loaded_config = None


5. Real world example: charge stability map

So good so far in theory but lets look at a real world example. A simple measurement to characterize gate defined quantum dots is a so called charge stability map (TODO: add citations). Below it is shown how to prepare such a measurement for two qubits and how to scale it up to 8 qubits by only updating the configuration file.

from arbok_driver.examples.sequences import StabilityMap
from arbok_driver.examples.configurations.sequence import (
    stability_2q_conf, stability_8q_conf
)
meas_stab_2q = Measurement(qm_driver, 'meas_stab_2q')
stability_map_2q = StabilityMap(
    meas_stab_2q, 'stability_map_2q', stability_2q_conf
)

prg_str_2q = meas_stab_2q.get_qua_program_as_str(recompile = True)
rprint(prg_str_2q)
# Single QUA script generated at 2026-04-05 08:03:46.836241
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(int, value=0)
    v2 = declare(fixed, )
    v3 = declare(fixed, )
    v4 = declare(fixed, )
    v5 = declare(int, )
    v6 = declare(fixed, )
    v7 = declare(fixed, )
    with infinite_loop_():
        pause()
        assign(v1, 0)
        align("P1", "J1", "P2")
        align("P1", "J1", "P2")
        play("unit_ramp"*amp(0.3), "P1")
        play("unit_ramp"*amp(-0.7), "J1")
        play("unit_ramp"*amp(0.6), "P2")
        align("P1", "J1", "P2")
        wait(12500, "P1", "J1", "P2")
        align("P1", "J1", "P2")
        assign(v4, 0.0)
        assign(v6, 0.0)
        assign(v7, 0.0)
        with for_(v5,0,(v5<20),(v5+1)):
            align()
            align("P1", "P2")
            play("unit_ramp"*amp(0.01), "P1")
            play("unit_ramp"*amp(-0.01), "P2")
            align("P1", "P2")
            align("P1", "P2", "SET1")
            wait(5000, "P1", "P2", "SET1")
            align("P1", "P2", "SET1")
            measure("measure", "SET1", integration.full("x_const", v3, ""))
            align("P1", "P2", "SET1")
            align("P1", "P2")
            play("unit_ramp"*amp(-0.01), "P1")
            play("unit_ramp"*amp(0.01), "P2")
            align("P1", "P2")
            align("P1", "P2", "SET1")
            wait(5000, "P1", "P2", "SET1")
            align("P1", "P2", "SET1")
            measure("measure", "SET1", integration.full("x_const", v2, ""))
            align("P1", "P2", "SET1")
            assign(v6, (v6+(v3*0.05)))
            assign(v7, (v7+(v2*0.05)))
        align()
        assign(v3, v6)
        assign(v2, v7)
        assign(v4, (v7-v6))
        align("P1", "P2", "SET1")
        r2 = declare_stream()
        save(v2, r2)
        r3 = declare_stream()
        save(v3, r3)
        r4 = declare_stream()
        save(v4, r4)
        align("P1", "J1", "P2")
        ramp_to_zero("P1")
        ramp_to_zero("J1")
        ramp_to_zero("P2")
        align("P1", "J1", "P2")
        align()
        assign(v1, (v1+1))
        r1 = declare_stream()
        save(v1, r1)
        align()
    with stream_processing():
        r1.buffer(1).save("qm_driver_meas_stab_2q_shots")
        r2.buffer(1).save("qm_driver_meas_stab_2q_stability_map_2q_p1p2_read")
        r3.buffer(1).save("qm_driver_meas_stab_2q_stability_map_2q_p1p2_ref")
        r4.buffer(1).save("qm_driver_meas_stab_2q_stability_map_2q_p1p2_diff")

config = None

loaded_config = None


Now lets do the exact same thing as before but with a config for 8 qubits and see how much the resulting QUA has expanded.

meas_stab_8q = Measurement(qm_driver, 'meas_stab_8q')
stability_map_8q = StabilityMap(
    meas_stab_8q, 'stability_map_8q', stability_8q_conf
)

prg_str_8q = meas_stab_8q.get_qua_program_as_str(recompile = True)
rprint(prg_str_8q)
# Single QUA script generated at 2026-04-05 08:03:47.458089
# QUA library version: 1.2.5


from qm import CompilerOptionArguments
from qm.qua import *

with program() as prog:
    v1 = declare(int, value=0)
    v2 = declare(fixed, )
    v3 = declare(fixed, )
    v4 = declare(fixed, )
    v5 = declare(fixed, )
    v6 = declare(fixed, )
    v7 = declare(fixed, )
    v8 = declare(fixed, )
    v9 = declare(fixed, )
    v10 = declare(fixed, )
    v11 = declare(fixed, )
    v12 = declare(fixed, )
    v13 = declare(fixed, )
    v14 = declare(int, )
    v15 = declare(fixed, )
    v16 = declare(fixed, )
    v17 = declare(fixed, )
    v18 = declare(fixed, )
    v19 = declare(fixed, )
    v20 = declare(fixed, )
    v21 = declare(fixed, )
    v22 = declare(fixed, )
    with infinite_loop_():
        pause()
        assign(v1, 0)
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        play("unit_ramp"*amp(0.3), "P1")
        play("unit_ramp"*amp(-0.7), "J1")
        play("unit_ramp"*amp(0.6), "P2")
        play("unit_ramp"*amp(-0.2), "J2")
        play("unit_ramp"*amp(0.4), "P3")
        play("unit_ramp"*amp(-0.8), "J3")
        play("unit_ramp"*amp(0.7), "P4")
        play("unit_ramp"*amp(-0.1), "J4")
        play("unit_ramp"*amp(0.2), "P5")
        play("unit_ramp"*amp(-0.6), "J5")
        play("unit_ramp"*amp(0.8), "P6")
        play("unit_ramp"*amp(-0.3), "J6")
        play("unit_ramp"*amp(0.5), "P7")
        play("unit_ramp"*amp(-0.5), "J7")
        play("unit_ramp"*amp(0.9), "P8")
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        wait(12500, "P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        assign(v4, 0.0)
        assign(v15, 0.0)
        assign(v16, 0.0)
        assign(v7, 0.0)
        assign(v17, 0.0)
        assign(v18, 0.0)
        assign(v10, 0.0)
        assign(v19, 0.0)
        assign(v20, 0.0)
        assign(v13, 0.0)
        assign(v21, 0.0)
        assign(v22, 0.0)
        with for_(v14,0,(v14<20),(v14+1)):
            align()
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8")
            play("unit_ramp"*amp(0.01), "P1")
            play("unit_ramp"*amp(-0.01), "P2")
            play("unit_ramp"*amp(0.01), "P3")
            play("unit_ramp"*amp(-0.01), "P4")
            play("unit_ramp"*amp(0.01), "P5")
            play("unit_ramp"*amp(-0.01), "P6")
            play("unit_ramp"*amp(0.01), "P7")
            play("unit_ramp"*amp(-0.01), "P8")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            wait(5000, "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            measure("measure", "SET1", integration.full("x_const", v3, ""))
            measure("measure", "SET2", integration.full("x_const", v6, ""))
            measure("measure", "SET3", integration.full("x_const", v9, ""))
            measure("measure", "SET4", integration.full("x_const", v12, ""))
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8")
            play("unit_ramp"*amp(-0.01), "P1")
            play("unit_ramp"*amp(0.01), "P2")
            play("unit_ramp"*amp(-0.01), "P3")
            play("unit_ramp"*amp(0.01), "P4")
            play("unit_ramp"*amp(-0.01), "P5")
            play("unit_ramp"*amp(0.01), "P6")
            play("unit_ramp"*amp(-0.01), "P7")
            play("unit_ramp"*amp(0.01), "P8")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            wait(5000, "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            measure("measure", "SET1", integration.full("x_const", v2, ""))
            measure("measure", "SET2", integration.full("x_const", v5, ""))
            measure("measure", "SET3", integration.full("x_const", v8, ""))
            measure("measure", "SET4", integration.full("x_const", v11, ""))
            align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
            assign(v15, (v15+(v3*0.05)))
            assign(v16, (v16+(v2*0.05)))
            assign(v17, (v17+(v6*0.05)))
            assign(v18, (v18+(v5*0.05)))
            assign(v19, (v19+(v9*0.05)))
            assign(v20, (v20+(v8*0.05)))
            assign(v21, (v21+(v12*0.05)))
            assign(v22, (v22+(v11*0.05)))
        align()
        assign(v3, v15)
        assign(v2, v16)
        assign(v4, (v16-v15))
        assign(v6, v17)
        assign(v5, v18)
        assign(v7, (v18-v17))
        assign(v9, v19)
        assign(v8, v20)
        assign(v10, (v20-v19))
        assign(v12, v21)
        assign(v11, v22)
        assign(v13, (v22-v21))
        align("P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "SET1", "SET2", "SET3", "SET4")
        r2 = declare_stream()
        save(v2, r2)
        r3 = declare_stream()
        save(v3, r3)
        r4 = declare_stream()
        save(v4, r4)
        r5 = declare_stream()
        save(v5, r5)
        r6 = declare_stream()
        save(v6, r6)
        r7 = declare_stream()
        save(v7, r7)
        r8 = declare_stream()
        save(v8, r8)
        r9 = declare_stream()
        save(v9, r9)
        r10 = declare_stream()
        save(v10, r10)
        r11 = declare_stream()
        save(v11, r11)
        r12 = declare_stream()
        save(v12, r12)
        r13 = declare_stream()
        save(v13, r13)
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        ramp_to_zero("P1")
        ramp_to_zero("J1")
        ramp_to_zero("P2")
        ramp_to_zero("J2")
        ramp_to_zero("P3")
        ramp_to_zero("J3")
        ramp_to_zero("P4")
        ramp_to_zero("J4")
        ramp_to_zero("P5")
        ramp_to_zero("J5")
        ramp_to_zero("P6")
        ramp_to_zero("J6")
        ramp_to_zero("P7")
        ramp_to_zero("J7")
        ramp_to_zero("P8")
        align("P1", "J1", "P2", "J2", "P3", "J3", "P4", "J4", "P5", "J5", "P6", "J6", "P7", "J7", "P8")
        align()
        assign(v1, (v1+1))
        r1 = declare_stream()
        save(v1, r1)
        align()
    with stream_processing():
        r1.buffer(1).save("qm_driver_meas_stab_8q_shots")
        r2.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p1p2_read")
        r3.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p1p2_ref")
        r4.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p1p2_diff")
        r5.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p3p4_read")
        r6.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p3p4_ref")
        r7.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p3p4_diff")
        r8.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p5p6_read")
        r9.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p5p6_ref")
        r10.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p5p6_diff")
        r11.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p7p8_read")
        r12.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p7p8_ref")
        r13.buffer(1).save("qm_driver_meas_stab_8q_stability_map_8q_p7p8_diff")

config = None

loaded_config = None