{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "a338885a", "metadata": {}, "outputs": [], "source": [ "from typing import TYPE_CHECKING\n", "\n", "import numpy as np\n", "\n", "from qcodes.dataset import (\n", " Measurement,\n", " initialise_or_create_database_at,\n", " load_or_create_experiment,\n", ")\n", "from qcodes.parameters import (\n", " ManualParameter,\n", " Parameter,\n", " ParameterBase,\n", ")\n", "\n", "if TYPE_CHECKING:\n", " from qcodes.dataset.data_set_protocol import ValuesType\n", " from qcodes.parameters import ParameterBase, ParamRawDataType" ] }, { "cell_type": "markdown", "id": "fd4cb8f8", "metadata": {}, "source": [ "# Parameter-defined InterDependencies\n", "\n", "This example demonstrates how to use the `depends_on`, `has_control_of`, and `is_controlled_by` properties to define granular implicit interdependencies between Parameters. These are described in greater detail in the [Interdependent Parameters](../../dataset/interdependentparams.rst)." ] }, { "cell_type": "markdown", "id": "a7967c55", "metadata": {}, "source": [ "## Interdependency Definitions:\n", "- `depends_on`: (also `setpoints`) An experimental relationship, usually the focus of the measurement. A dependent parameter will generally `depend_on` one or more independent parameters\n", "- `is_controlled_by`: (also `basis` and `inferred_from`) A well-known or defined relationship, with an explicit mathematical function to describe it. The directionality is important: We say a parameter A is inferred from B if there exists a function f such that f(B) = A.\n", "- `has_control_of`: The opposite direction of the `is_controlled_by` relationship\n", "\n", "In this example, we will first create a `ControllingParameter` class that operates two component parameters in tandem according to simple linear equations. We will look at how it uses the `has_control_of` and `is_controlled_by` properties to ensure that these components are properly registered in a `Measurement`. Finally, we will examine its custom `unpack_self` method which allows `datasaver.add_result` to add component results even if they are not explicitly added.\n", "\n", "Then we will show how to bind a `depends_on` relationship to a parameter, and demonstrate how this simplifies handling of fixed and constant dependencies." ] }, { "cell_type": "markdown", "id": "7a188625", "metadata": {}, "source": [ "# ControllingParameter Example" ] }, { "cell_type": "code", "execution_count": 2, "id": "32651dfa", "metadata": {}, "outputs": [], "source": [ "class ControllingParameter(Parameter):\n", " def __init__(\n", " self, name: str, components: dict[Parameter, tuple[float, float]]\n", " ) -> None:\n", " super().__init__(name=name, get_cmd=False)\n", " # dict of Parameter to (slope, offset) of components\n", " self._components_dict: dict[Parameter, tuple[float, float]] = components\n", " for param in self._components_dict.keys():\n", " self._has_control_of.add(param)\n", " param.is_controlled_by.add(self)\n", "\n", " def set_raw(self, value: \"ParamRawDataType\") -> None:\n", " # Set all dependent parameters based on their slope and offsets\n", " for param, slope_offset in self._components_dict.items():\n", " param(value * slope_offset[0] + slope_offset[1])\n", "\n", " def get_raw(self) -> \"ParamRawDataType\":\n", " return self.cache.get()\n", "\n", " def unpack_self(\n", " self, value: \"ValuesType\"\n", " ) -> list[tuple[\"ParameterBase\", \"ValuesType\"]]:\n", " assert isinstance(value, float)\n", " unpacked_results = super().unpack_self(value)\n", " for param, slope_offset in self._components_dict.items():\n", " unpacked_results.append((param, value * slope_offset[0] + slope_offset[1]))\n", " return unpacked_results" ] }, { "cell_type": "code", "execution_count": 3, "id": "9af7d477", "metadata": {}, "outputs": [], "source": [ "param1 = ManualParameter(\"param1\", initial_value=0)\n", "param2 = ManualParameter(\"param2\", initial_value=0)\n", "control = ControllingParameter(\"control\", components={param1: (1, 0), param2: (-1, 10)})\n", "\n", "meas_param = Parameter(\"meas\", get_cmd=lambda: param1() + param2() - 5.0)" ] }, { "cell_type": "markdown", "id": "9241ee47", "metadata": {}, "source": [ "## ControllingParameter self-registration of components\n", "\n", "In the ``__init__`` method of the `ControllingParameter`, we use two new attributes to define its built-in InterDependencies. The `has_control_of` property is an ordered set of its internal components. We also add the `ControllingParameter` instance to the `is_controlled_by` sets of the components. This lets us register just _one_ of the set `param1, param2, control` and get the other two for free." ] }, { "cell_type": "code", "execution_count": 4, "id": "bb26c0f0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'control': ParamSpecBase('control', 'numeric', 'control', ''),\n", " 'param1': ParamSpecBase('param1', 'numeric', 'param1', ''),\n", " 'param2': ParamSpecBase('param2', 'numeric', 'param2', '')}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "initialise_or_create_database_at(\"experiments.db\")\n", "exp = load_or_create_experiment(\"InterDependencies_ examples\")\n", "meas = Measurement(exp=exp, name=\"self registration example\")\n", "meas.register_parameter(control)\n", "\n", "meas.parameters" ] }, { "cell_type": "markdown", "id": "dce92a39", "metadata": {}, "source": [ "In addition to the `has_control_of` and `is_controlled_by` properties, there is also a similar `depends_on` property that can be used to flexibly create something like the `ParameterWithSetpoints`. The `setpoints` of a `ParameterWithSetpoints` are now added to its internal `depends_on` set, where they are automatically self-registered with the same machinery as we demonstrated above." ] }, { "cell_type": "markdown", "id": "870e9165", "metadata": {}, "source": [ "## ControllingParameter self-unpacking\n", "\n", "For qcodes measurements, parameter registration is only the first part of the story. Inside the measurement loop itself, we use `datasaver.add_result` to save new data to the resulting database. The `unpack_self` method defined in the `ControllingParameter` class handles unpacking a `ControllingParameter` result tuple, so that the data for its components is also saved." ] }, { "cell_type": "code", "execution_count": 5, "id": "1eb9f5e2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting experimental run with id: 6. \n" ] } ], "source": [ "with meas.run() as datasaver:\n", " for i in np.linspace(0, 1, 11):\n", " control(i)\n", " datasaver.add_result((control, control()))\n", " ds = datasaver.dataset" ] }, { "cell_type": "code", "execution_count": 6, "id": "c48afcbe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'param1': {'param1': array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]),\n", " 'control': array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])},\n", " 'param2': {'param2': array([10. , 9.9, 9.8, 9.7, 9.6, 9.5, 9.4, 9.3, 9.2, 9.1, 9. ]),\n", " 'control': array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])}}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ds.get_parameter_data()" ] }, { "cell_type": "markdown", "id": "95f34917", "metadata": {}, "source": [ "### But does it work with dond?\n", "\n", "Yes." ] }, { "cell_type": "code", "execution_count": 10, "id": "73823b84", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting experimental run with id: 8. Using 'qcodes.dataset.dond'\n" ] }, { "data": { "text/plain": [ "{'meas': {'meas': array([5., 5., 5., 5., 5., 5., 5., 5., 5., 5., 5.]),\n", " 'control': array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]),\n", " 'param1': array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]),\n", " 'param2': array([10. , 9.9, 9.8, 9.7, 9.6, 9.5, 9.4, 9.3, 9.2, 9.1, 9. ])}}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from qcodes.dataset import LinSweep, dond\n", "\n", "ds, _, _ = dond(LinSweep(control, 0, 1, 11), meas_param)\n", "ds.get_parameter_data()" ] } ], "metadata": { "kernelspec": { "display_name": "py311", "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.11.8" } }, "nbformat": 4, "nbformat_minor": 5 }