{ "cells": [ { "cell_type": "markdown", "id": "b4db0bfb-f716-41a4-a0e5-a7761c3ec941", "metadata": {}, "source": [ "# Tutorial 0) Arbok, a dynamicially generated qcodes driver" ] }, { "cell_type": "markdown", "id": "162bc0ec-9b00-48ea-871f-b38703a739d3", "metadata": {}, "source": [ "## 0.1 Introduction and overwiew" ] }, { "cell_type": "markdown", "id": "adaec79a-a403-4fce-ae88-72dd303cd149", "metadata": {}, "source": [ "Welcome to the `arbok_driver` tutorials!" ] }, { "cell_type": "markdown", "id": "4ccff823-1345-4220-85fe-6fa6350bfbe1", "metadata": {}, "source": [ "In this series we will explore how this python package generates a qcodes\n", "driver on the fly to modularize and parameterize our complex measurement sequences.\n", "\n", "Arbok is a top-level python control framework based on [QCoDeS](https://microsoft.github.io/Qcodes/index.html) compiling into FPGA instructions ([QM-QUA SDK](https://pypi.org/project/qm-qua/1.1.7/)) for [quantum machines hardware](https://www.quantum-machines.co/). 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**.\n", "\n", "

\n", " \n", "

\n", "\n", "Tutorial 0 briefly introduces arbok and its main user facing classes:\n", "- `ArbokDriver:` The qcodes instrument managing the hardware connection to the OPX\n", "- `Measurement:` The drivers instrument-module(s) orchestrating measurements\n", "- `SubSequence:` The modular building blocks populating measurements with instructions (qua)\n", "- `Device:` Manages device specific configurations\n", "\n", "**QCoDeS** is a full stack data acquisition framework that handles instrument communication, parameterization, data storage and visualization. For now we will not dive into the scalability and features of arbok but will try to understand its connection to QCoDeS and how it leverages its well tested, excellent infrastructure.\n", "\n", "

\n", " \n", "

\n" ] }, { "cell_type": "markdown", "id": "20591693-1005-4139-9c1d-5f58488a2a53", "metadata": {}, "source": [ "The following tutorials will give insights into the following topics:\n", "- **Tutorial 1)** -- Scale up and parameterization\n", "- **Tutorial 2)** -- Readout and live data processing\n", "- **Tutorial 3)** -- Live input streaming of parameters\n", "- **Tutorial 4)** -- Asynchronous operations" ] }, { "cell_type": "markdown", "id": "55cf8861-0348-4c57-8140-6110b6794f1d", "metadata": {}, "source": [ "## 0.2 Generating a dynamic `arbok_driver`" ] }, { "cell_type": "code", "execution_count": 1, "id": "49638df9-0721-4798-9a6f-ee119007c9d6", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:30.932520Z", "iopub.status.busy": "2025-09-03T05:16:30.932520Z", "iopub.status.idle": "2025-09-03T05:16:30.936680Z", "shell.execute_reply": "2025-09-03T05:16:30.936171Z", "shell.execute_reply.started": "2025-09-03T05:16:30.932520Z" } }, "outputs": [], "source": [ "import inspect\n", "from IPython.display import Code, display, display_html, Image, SVG" ] }, { "cell_type": "markdown", "id": "dd7ea246-b3cf-48e0-90cc-7c68cfd69459", "metadata": {}, "source": [ "### 0.2.1 Creating a `Device` and `ArbokDriver` instance" ] }, { "cell_type": "code", "execution_count": 5, "id": "d1a1b2d4-79f4-4d77-ba98-aa3212855829", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:31.011589Z", "iopub.status.busy": "2025-09-03T05:16:31.011589Z", "iopub.status.idle": "2025-09-03T05:16:35.543476Z", "shell.execute_reply": "2025-09-03T05:16:35.543476Z", "shell.execute_reply.started": "2025-09-03T05:16:31.011589Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2025-09-03 15:16:33,950 - qm - INFO - Starting session: e1b1508a-f2ef-45ad-8814-9b985acc4072\n" ] } ], "source": [ "from arbok_driver import ArbokDriver, Device, Measurement\n", "\n", "from configurations.opx_config import opx_config\n", "from configurations.divider_config import divider_config" ] }, { "cell_type": "code", "execution_count": 6, "id": "789cd51c-e993-4fd0-84a9-0b8f431109f7", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.545481Z", "iopub.status.busy": "2025-09-03T05:16:35.544478Z", "iopub.status.idle": "2025-09-03T05:16:35.548291Z", "shell.execute_reply": "2025-09-03T05:16:35.548291Z", "shell.execute_reply.started": "2025-09-03T05:16:35.545481Z" } }, "outputs": [], "source": [ "mock_device = Device(\n", " name = 'mock_device',\n", " opx_config = opx_config,\n", " divider_config = divider_config)\n", "\n", "mock_driver = ArbokDriver(\n", " name = 'mock_driver',\n", " device = mock_device\n", ")" ] }, { "cell_type": "markdown", "id": "882661b7-540f-4aad-b03e-fc0c7e10db1d", "metadata": {}, "source": [ "### 0.2.2 Adding a `Measurement` to the driver" ] }, { "cell_type": "markdown", "id": "79697e83-1950-47f1-9a30-39465c886742", "metadata": {}, "source": [ "Let's look back on what we did so far! We created a `Device` object that holds the configuration for the OPX as well as a configuration describing the installed dividers on our lines.\n", "That device is then used to create an `ArbokDriver` instance. So far so good but now we want to create some actual measurements." ] }, { "cell_type": "code", "execution_count": 7, "id": "84811edc-4e7b-4ca1-800b-bc446705d5ec", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.549295Z", "iopub.status.busy": "2025-09-03T05:16:35.549295Z", "iopub.status.idle": "2025-09-03T05:16:35.559092Z", "shell.execute_reply": "2025-09-03T05:16:35.559092Z", "shell.execute_reply.started": "2025-09-03T05:16:35.549295Z" } }, "outputs": [], "source": [ "mock_measurement = Measurement(\n", " parent = mock_driver,\n", " name = \"mock_measurement\",\n", " device = mock_device\n", ")" ] }, { "cell_type": "markdown", "id": "78d9392f-ab13-4a9f-a5a7-1111989244c0", "metadata": {}, "source": [ "### 0.2.3 Populating the `Measurement` with a `SubSequence`" ] }, { "cell_type": "markdown", "id": "c61fe1c8-ecd9-4c0c-9110-7d0d7337acf9", "metadata": {}, "source": [ "The fundamental building blocks of arbok are called `SubSequence`s. Those can be arbitrarily simple or complicated.\n", "Below we are inspecting an easy `SquarePulse` class inheriting from `SubSequence`. An element is ramped to a certain amplitude, a wait time is being passed and finally the element is ramped back to its inital voltage. This is assuming the given element is ['sticky'](https://docs.quantum-machines.co/latest/docs/Guides/features/?h=sticky#sticky-element).\n", "\n", "You already see that some of the attributes are being called with brackets e.g self.element(), self.ramp_time(), etc. Those will become important in a bit!" ] }, { "cell_type": "code", "execution_count": 8, "id": "eeb90c3e-46ee-444b-85c3-eab064c4153d", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.560088Z", "iopub.status.busy": "2025-09-03T05:16:35.559092Z", "iopub.status.idle": "2025-09-03T05:16:35.613454Z", "shell.execute_reply": "2025-09-03T05:16:35.613454Z", "shell.execute_reply.started": "2025-09-03T05:16:35.560088Z" } }, "outputs": [ { "data": { "text/html": [ "
class SquarePulse(SubSequence):\n",
       "    """\n",
       "    Class containing parameters and sequence for a simple square pulse\n",
       "    """\n",
       "\n",
       "    def qua_sequence(self):\n",
       "        """Macro that will be played within the qua.program() context"""\n",
       "        qua.align()\n",
       "        qua.play('ramp'*qua.amp(self.amplitude()), self.element(), duration = self.t_ramp())\n",
       "        qua.wait(self.t_square_pulse(), self.element())\n",
       "        qua.play('ramp'*qua.amp(-self.amplitude()), self.element(), duration = self.t_ramp())\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{k}{class}\\PY{+w}{ }\\PY{n+nc}{SquarePulse}\\PY{p}{(}\\PY{n}{SubSequence}\\PY{p}{)}\\PY{p}{:}\n", "\\PY{+w}{ }\\PY{l+s+sd}{\\PYZdq{}\\PYZdq{}\\PYZdq{}}\n", "\\PY{l+s+sd}{ Class containing parameters and sequence for a simple square pulse}\n", "\\PY{l+s+sd}{ \\PYZdq{}\\PYZdq{}\\PYZdq{}}\n", "\n", " \\PY{k}{def}\\PY{+w}{ }\\PY{n+nf}{qua\\PYZus{}sequence}\\PY{p}{(}\\PY{n+nb+bp}{self}\\PY{p}{)}\\PY{p}{:}\n", "\\PY{+w}{ }\\PY{l+s+sd}{\\PYZdq{}\\PYZdq{}\\PYZdq{}Macro that will be played within the qua.program() context\\PYZdq{}\\PYZdq{}\\PYZdq{}}\n", " \\PY{n}{qua}\\PY{o}{.}\\PY{n}{align}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{n}{qua}\\PY{o}{.}\\PY{n}{play}\\PY{p}{(}\\PY{l+s+s1}{\\PYZsq{}}\\PY{l+s+s1}{ramp}\\PY{l+s+s1}{\\PYZsq{}}\\PY{o}{*}\\PY{n}{qua}\\PY{o}{.}\\PY{n}{amp}\\PY{p}{(}\\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{amplitude}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\\PY{p}{,} \\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{element}\\PY{p}{(}\\PY{p}{)}\\PY{p}{,} \\PY{n}{duration} \\PY{o}{=} \\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{t\\PYZus{}ramp}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\n", " \\PY{n}{qua}\\PY{o}{.}\\PY{n}{wait}\\PY{p}{(}\\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{t\\PYZus{}square\\PYZus{}pulse}\\PY{p}{(}\\PY{p}{)}\\PY{p}{,} \\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{element}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\n", " \\PY{n}{qua}\\PY{o}{.}\\PY{n}{play}\\PY{p}{(}\\PY{l+s+s1}{\\PYZsq{}}\\PY{l+s+s1}{ramp}\\PY{l+s+s1}{\\PYZsq{}}\\PY{o}{*}\\PY{n}{qua}\\PY{o}{.}\\PY{n}{amp}\\PY{p}{(}\\PY{o}{\\PYZhy{}}\\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{amplitude}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\\PY{p}{,} \\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{element}\\PY{p}{(}\\PY{p}{)}\\PY{p}{,} \\PY{n}{duration} \\PY{o}{=} \\PY{n+nb+bp}{self}\\PY{o}{.}\\PY{n}{t\\PYZus{}ramp}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\n", "\\end{Verbatim}\n" ], "text/plain": [ "class SquarePulse(SubSequence):\n", " \"\"\"\n", " Class containing parameters and sequence for a simple square pulse\n", " \"\"\"\n", "\n", " def qua_sequence(self):\n", " \"\"\"Macro that will be played within the qua.program() context\"\"\"\n", " qua.align()\n", " qua.play('ramp'*qua.amp(self.amplitude()), self.element(), duration = self.t_ramp())\n", " qua.wait(self.t_square_pulse(), self.element())\n", " qua.play('ramp'*qua.amp(-self.amplitude()), self.element(), duration = self.t_ramp())" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from example_sequences.square_pulse import SquarePulse\n", "from configurations.square_pulse_config import square_pulse_config\n", "\n", "display(Code(inspect.getsource(SquarePulse), language=\"python\"))" ] }, { "cell_type": "markdown", "id": "6af2cfb0-1e7c-47c9-8674-c192924cb5ea", "metadata": {}, "source": [ "In order to populate a measurement with sub-sequences, we have to create a dict reflecting the structure of our measurement. Here we are just considering a single `SubSequence` in the form of a `SquarePulse`. It is parameterized by a configuration. We will ignore this part for now and will go in depth in tutorial 1." ] }, { "cell_type": "code", "execution_count": 9, "id": "fc8296ed-0ff9-4b36-92e5-a6590d5859be", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.614454Z", "iopub.status.busy": "2025-09-03T05:16:35.614454Z", "iopub.status.idle": "2025-09-03T05:16:35.618049Z", "shell.execute_reply": "2025-09-03T05:16:35.618049Z", "shell.execute_reply.started": "2025-09-03T05:16:35.614454Z" } }, "outputs": [], "source": [ "sub_sequence_dict = {\n", " 'square_pulse': {\n", " 'sequence': SquarePulse,\n", " 'config': square_pulse_config\n", " }\n", "}\n", "\n", "mock_measurement.add_subsequences_from_dict(sub_sequence_dict)" ] }, { "cell_type": "markdown", "id": "341fa3fc-1999-4c56-a1c1-836d3e9cbf4c", "metadata": {}, "source": [ "The current structure is visualized by the figure below. We created an `arbok_driver` instance and added a measurement to it. This empty measurement was populated with a `SquarePulse` `SubSequence`.\n", "The `ArbokDriver` inherits from `qcodes.Instrument` and each box in the figure is a `qcodes.InstrumentModule` of the box that contains it.\n", "\n", "''" ] }, { "cell_type": "markdown", "id": "71918167-f092-4ea9-9407-3d2b688b21f0", "metadata": {}, "source": [ "If you want to check the resulting structure of your measurement you can either list its sub_sequences or you draw the sub_sequence tree. Since the arbok_driver is based on QCoDeS, we can use all their helper functions as well to inspect our dynamicaly created instrument. As you see below, so far our driver has one measurement which is implemented as an [`InstrumentModule`](https://microsoft.github.io/Qcodes/api/instrument/index.html#qcodes.instrument.InstrumentModule) which has a sub-module (SquarePulse) itself. `print_readable_snapshot` also lists all available parameters and their current values." ] }, { "cell_type": "code", "execution_count": 10, "id": "624e504c-9093-4fb9-a05b-e32c33cc87e1", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.619045Z", "iopub.status.busy": "2025-09-03T05:16:35.618049Z", "iopub.status.idle": "2025-09-03T05:16:35.633188Z", "shell.execute_reply": "2025-09-03T05:16:35.633188Z", "shell.execute_reply.started": "2025-09-03T05:16:35.619045Z" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mock_measurement.sub_sequences" ] }, { "cell_type": "code", "execution_count": 11, "id": "46c71cf5-9c03-4a79-b042-1424082e73c8", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.634190Z", "iopub.status.busy": "2025-09-03T05:16:35.634190Z", "iopub.status.idle": "2025-09-03T05:16:35.644386Z", "shell.execute_reply": "2025-09-03T05:16:35.644386Z", "shell.execute_reply.started": "2025-09-03T05:16:35.634190Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mock_measurement\n", " └─ square_pulse\n" ] } ], "source": [ "mock_measurement.draw_sub_sequence_tree()" ] }, { "cell_type": "code", "execution_count": 27, "id": "24030b97-fc61-4f92-b0fc-df830e49ce9d", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T06:32:32.884223Z", "iopub.status.busy": "2025-09-03T06:32:32.884223Z", "iopub.status.idle": "2025-09-03T06:32:32.891255Z", "shell.execute_reply": "2025-09-03T06:32:32.890740Z", "shell.execute_reply.started": "2025-09-03T06:32:32.884223Z" } }, "outputs": [ { "data": { "text/plain": [ "{'mock_driver_mock_measurement': }" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mock_driver.submodules" ] }, { "cell_type": "code", "execution_count": 12, "id": "6e8c0aff-4926-476b-9bbd-a05c15f2a966", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T05:16:35.645387Z", "iopub.status.busy": "2025-09-03T05:16:35.645387Z", "iopub.status.idle": "2025-09-03T05:16:35.654956Z", "shell.execute_reply": "2025-09-03T05:16:35.654956Z", "shell.execute_reply.started": "2025-09-03T05:16:35.645387Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mock_driver:\n", "\tparameter value\n", "--------------------------------------------------------------------------------\n", "IDN :\tNone \n", "iteration :\tNone \n", "mock_driver_mock_measurement:\n", "\tparameter value\n", "--------------------------------------------------------------------------------\n", "mock_driver_mock_measurement_square_pulse:\n", "\tparameter value\n", "--------------------------------------------------------------------------------\n", "amplitude :\t0.1 (V)\n", "element :\tgate_1 (N/A)\n", "t_ramp :\t200 (s)\n", "t_square_pulse :\t100 (s)\n" ] } ], "source": [ "mock_driver.print_readable_snapshot()" ] }, { "cell_type": "markdown", "id": "4395c1f5-0057-4088-97b3-901ea02e8cde", "metadata": {}, "source": [ "## 0.3 Generating a qua program to run" ] }, { "cell_type": "markdown", "id": "a2a8f086-7dee-42d1-9f62-5c258e84365b", "metadata": {}, "source": [ "As soon as we have populated a `Measurment`, we can generate a qua program. Lets do that, save it to a file and discuss what we are seeing." ] }, { "cell_type": "markdown", "id": "79da463b-c8ba-4aff-8c75-08199f02a653", "metadata": {}, "source": [ "Looks familiar, right? I'm sure you are recognizing the square pulse we inspected earlier.\n", "\n", "You see that all the parameters were converted to the values we observed in the snapshot.\n", "\n", "However, we see a bit more than we expected! The entire code is nested in an `infinite_loop` starting with a `pause` statement. This will become important in the next part in order to synchronize the running program with the driver running on your local machine. The same is true for the `stream_processing` section in the bottom." ] }, { "cell_type": "code", "execution_count": 30, "id": "9c05f606-5cf3-49bb-8c9d-7b4e84796532", "metadata": { "execution": { "iopub.execute_input": "2025-09-03T06:51:02.207656Z", "iopub.status.busy": "2025-09-03T06:51:02.206659Z", "iopub.status.idle": "2025-09-03T06:51:02.359148Z", "shell.execute_reply": "2025-09-03T06:51:02.359148Z", "shell.execute_reply.started": "2025-09-03T06:51:02.207656Z" } }, "outputs": [ { "data": { "text/html": [ "
# Single QUA script generated at 2025-09-03 16:51:02.305902\n",
       "# QUA library version: 1.2.1\n",
       "\n",
       "from qm import CompilerOptionArguments\n",
       "from qm.qua import *\n",
       "\n",
       "with program() as prog:\n",
       "    v1 = declare(int, value=0)\n",
       "    with infinite_loop_():\n",
       "        pause()\n",
       "        assign(v1, 0)\n",
       "        align()\n",
       "        play("ramp"*amp(0.1), "gate_1", duration=200)\n",
       "        wait(100, "gate_1")\n",
       "        play("ramp"*amp(-0.1), "gate_1", duration=200)\n",
       "        align()\n",
       "        assign(v1, (v1+1))\n",
       "        r0 = declare_stream()\n",
       "        save(v1, r0)\n",
       "        align()\n",
       "    with stream_processing():\n",
       "        r0.buffer(1).save("mock_driver_mock_measurement_shots")\n",
       "\n",
       "\n",
       "config = None\n",
       "\n",
       "loaded_config = None\n",
       "
\n" ], "text/latex": [ "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", "\\PY{c+c1}{\\PYZsh{} Single QUA script generated at 2025\\PYZhy{}09\\PYZhy{}03 16:51:02.305902}\n", "\\PY{c+c1}{\\PYZsh{} QUA library version: 1.2.1}\n", "\n", "\\PY{k+kn}{from}\\PY{+w}{ }\\PY{n+nn}{qm}\\PY{+w}{ }\\PY{k+kn}{import} \\PY{n}{CompilerOptionArguments}\n", "\\PY{k+kn}{from}\\PY{+w}{ }\\PY{n+nn}{qm}\\PY{n+nn}{.}\\PY{n+nn}{qua}\\PY{+w}{ }\\PY{k+kn}{import} \\PY{o}{*}\n", "\n", "\\PY{k}{with} \\PY{n}{program}\\PY{p}{(}\\PY{p}{)} \\PY{k}{as} \\PY{n}{prog}\\PY{p}{:}\n", " \\PY{n}{v1} \\PY{o}{=} \\PY{n}{declare}\\PY{p}{(}\\PY{n+nb}{int}\\PY{p}{,} \\PY{n}{value}\\PY{o}{=}\\PY{l+m+mi}{0}\\PY{p}{)}\n", " \\PY{k}{with} \\PY{n}{infinite\\PYZus{}loop\\PYZus{}}\\PY{p}{(}\\PY{p}{)}\\PY{p}{:}\n", " \\PY{n}{pause}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{n}{assign}\\PY{p}{(}\\PY{n}{v1}\\PY{p}{,} \\PY{l+m+mi}{0}\\PY{p}{)}\n", " \\PY{n}{align}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{n}{play}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{ramp}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{*}\\PY{n}{amp}\\PY{p}{(}\\PY{l+m+mf}{0.1}\\PY{p}{)}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{gate\\PYZus{}1}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{,} \\PY{n}{duration}\\PY{o}{=}\\PY{l+m+mi}{200}\\PY{p}{)}\n", " \\PY{n}{wait}\\PY{p}{(}\\PY{l+m+mi}{100}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{gate\\PYZus{}1}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n", " \\PY{n}{play}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{ramp}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{*}\\PY{n}{amp}\\PY{p}{(}\\PY{o}{\\PYZhy{}}\\PY{l+m+mf}{0.1}\\PY{p}{)}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{gate\\PYZus{}1}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{,} \\PY{n}{duration}\\PY{o}{=}\\PY{l+m+mi}{200}\\PY{p}{)}\n", " \\PY{n}{align}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{n}{assign}\\PY{p}{(}\\PY{n}{v1}\\PY{p}{,} \\PY{p}{(}\\PY{n}{v1}\\PY{o}{+}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\n", " \\PY{n}{r0} \\PY{o}{=} \\PY{n}{declare\\PYZus{}stream}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{n}{save}\\PY{p}{(}\\PY{n}{v1}\\PY{p}{,} \\PY{n}{r0}\\PY{p}{)}\n", " \\PY{n}{align}\\PY{p}{(}\\PY{p}{)}\n", " \\PY{k}{with} \\PY{n}{stream\\PYZus{}processing}\\PY{p}{(}\\PY{p}{)}\\PY{p}{:}\n", " \\PY{n}{r0}\\PY{o}{.}\\PY{n}{buffer}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{o}{.}\\PY{n}{save}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{mock\\PYZus{}driver\\PYZus{}mock\\PYZus{}measurement\\PYZus{}shots}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n", "\n", "\n", "\\PY{n}{config} \\PY{o}{=} \\PY{k+kc}{None}\n", "\n", "\\PY{n}{loaded\\PYZus{}config} \\PY{o}{=} \\PY{k+kc}{None}\n", "\\end{Verbatim}\n" ], "text/plain": [ "\n", "# Single QUA script generated at 2025-09-03 16:51:02.305902\n", "# QUA library version: 1.2.1\n", "\n", "from qm import CompilerOptionArguments\n", "from qm.qua import *\n", "\n", "with program() as prog:\n", " v1 = declare(int, value=0)\n", " with infinite_loop_():\n", " pause()\n", " assign(v1, 0)\n", " align()\n", " play(\"ramp\"*amp(0.1), \"gate_1\", duration=200)\n", " wait(100, \"gate_1\")\n", " play(\"ramp\"*amp(-0.1), \"gate_1\", duration=200)\n", " align()\n", " assign(v1, (v1+1))\n", " r0 = declare_stream()\n", " save(v1, r0)\n", " align()\n", " with stream_processing():\n", " r0.buffer(1).save(\"mock_driver_mock_measurement_shots\")\n", "\n", "\n", "config = None\n", "\n", "loaded_config = None\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "qua_program = mock_measurement.get_qua_program()\n", "mock_driver.print_qua_program_to_file(\n", " './qua_programs/single_square_program.py', qua_program)\n", "\n", "from qua_programs import single_square_program\n", "\n", "display(Code(inspect.getsource(single_square_program), language=\"python\"))" ] }, { "cell_type": "markdown", "id": "7412983c-bb38-43a0-a388-34e155e7b060", "metadata": {}, "source": [ "## 0.4 Summary" ] }, { "cell_type": "markdown", "id": "8bf6fba8-e663-4800-82b4-ec4219a70cfe", "metadata": {}, "source": [ "In this tutorial, we introduced the core concepts and workflow of the `arbok_driver` package. We demonstrated how to set up a mock device and driver using configuration files, and how to add a measurement to the driver. We explored the modular structure of measurements by adding a simple `SquarePulse` sub-sequence, and visualized the resulting instrument hierarchy. Finally, we generated a QUA program from the configured measurement, showing how the high-level abstractions in `arbok_driver` translate into executable code for quantum hardware. This foundation prepares us for more advanced topics in subsequent tutorials, such as scaling up measurements, data readout and processing as well as asynchronous operations like feedback or heraled operations." ] }, { "cell_type": "markdown", "id": "5407e790-04e9-460c-97d6-5edde4b43da0", "metadata": {}, "source": [ "Continue with tutorial 1" ] }, { "cell_type": "code", "execution_count": null, "id": "cfe9f0bf-d0d2-4fb3-9040-274541819354", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" } }, "nbformat": 4, "nbformat_minor": 5 }