{ "cells": [ { "cell_type": "markdown", "id": "e5a260ac", "metadata": {}, "source": [ "(sec-tutorial-conditional-reset)=\n", "\n", "\n", "```{seealso}\n", "The complete source code of this tutorial can be found in\n", "\n", "{nb-download}`Conditional Reset.ipynb`\n", "\n", "```\n", "\n", "# Tutorial: Conditional Reset\n", "\n", "In this tutorial, we show how to perform a conditional reset using the [conditional control flow framework](sec-control-flow). A conditional reset consists of measuring the state of a qubit, and then:\n", "- sending a pulse to rotate the qubit to the ground state in case the qubit is found to be in an excited state\n", "- not sending any pulse if the qubit is found to be in the ground state\n", "\n", "This conditional reset is potentially much faster than an idle qubit reset (i.e. waiting for a time {math}`\\gg \\tau_1`).\n", "Quantify discriminates between excited and ground state at the `Measure` operation using a [thresholded acquisition](thresholded_acquisition_explanation), and uses a default {math}`\\pi` pulse to set the qubit to its ground state.\n", "In this tutorial, we demonstrate conditional reset using a Qblox cluster that contains a readout module (`QRM_RF`), responsible for the measurement of the qubit, and a control module (`QCM_RF`), responsible for conditionally sending out the {math}`\\pi` pulse.\n", "\n", "To run a conditional reset, we perform the following steps:\n", "\n", "1. Set up the quantum device, dummy hardware, and hardware configuration.\n", "2. Configure thresholded acquisition parameters to separate the {math}`|0\\rangle` and {math}`|1\\rangle` states.\n", "3. Verify that these parameters are set correctly.\n", "4. Run a conditional reset.\n", "\n", "\n", "```{note}\n", "Currently, the conditional reset is only implemented for the Qblox hardware.\n", "```\n", "\n", "(cond_reset_initial_setup)=\n", "\n", "## Initial Setup\n", "\n", "We follow here the same setup as in the {ref}`sec-tutorial-experiment` tutorial.\n", "\n", "First, we define a single transmon qubit as an element ({{ BasicTransmonElement }}) of the {{ QuantumDevice }} and populate the parameters with some reasonable values:\n", "\n", "```{seealso}\n", "If you want to learn more about how to set up the {{ QuantumDevice }} and hardware configuration, please see our other tutorials, in particular {ref}`sec-tutorial-experiment` and {ref}`sec-tutorial-compiling`.\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "id": "9711e031", "metadata": { "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "\"\"\"Disable transparent backgrounds for analysis figures to make them look nicer\n", "in both dark and bright mode on the website.\"\"\"\n", "\n", "from quantify_core.analysis import base_analysis as ba\n", "\n", "ba.settings[\"mpl_transparent_background\"] = False" ] }, { "cell_type": "code", "execution_count": 2, "id": "495e6351", "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "from qblox_instruments import Cluster, ClusterType\n", "import tempfile\n", "\n", "from quantify_core.data import handling as dh\n", "from quantify_core.measurement.control import MeasurementControl\n", "from quantify_scheduler import BasicTransmonElement, InstrumentCoordinator, QuantumDevice\n", "from quantify_scheduler.qblox import ClusterComponent\n", "\n", "measurement_control = MeasurementControl(\"measurement_control\")\n", "instrument_coordinator = InstrumentCoordinator(\"instrument_coordinator\")\n", "\n", "# Create a temporary directory for this tutorial\n", "temp_dir = tempfile.mkdtemp()\n", "\n", "# First, don't forget to set the data directory!\n", "dh.set_datadir(temp_dir)\n", "\n", "# Device parameters\n", "ACQ_DELAY = 100e-9\n", "FREQ_01 = 4e9\n", "READOUT_AMP = 0.1\n", "READOUT_FREQ = 4.3e9\n", "PI_PULSE_AMP = 0.15\n", "LO_FREQ_QUBIT = 3.9e9\n", "LO_FREQ_READOUT = 4.5e9\n", "\n", "single_qubit_device = QuantumDevice(\"single_qubit_device\")\n", "\n", "q0 = BasicTransmonElement(\"q0\")\n", "single_qubit_device.add_element(q0)\n", "\n", "# Assign device parameters to transmon element\n", "q0.measure.pulse_amp(READOUT_AMP)\n", "q0.clock_freqs.readout(READOUT_FREQ)\n", "q0.clock_freqs.f01(FREQ_01)\n", "q0.measure.acq_delay(ACQ_DELAY)\n", "q0.rxy.amp180(PI_PULSE_AMP)" ] }, { "cell_type": "markdown", "id": "c04f7e3b", "metadata": {}, "source": [ "Next, we connect to a dummy {{ Cluster }}. If you are connecting to an actual cluster, you would provide the\n", " `identifier` argument (the IP address, device name or serial number) instead\n", " of the `dummy_cfg` argument." ] }, { "cell_type": "code", "execution_count": 3, "id": "ecaf598c", "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "cluster = Cluster(\n", " \"cluster\",\n", " dummy_cfg={\n", " 1: ClusterType.CLUSTER_QRM_RF,\n", " 2: ClusterType.CLUSTER_QCM_RF,\n", " },\n", ")\n", "\n", "ic_cluster = ClusterComponent(cluster)\n", "\n", "instrument_coordinator.add_component(ic_cluster)\n", "\n", "single_qubit_device.instr_instrument_coordinator(instrument_coordinator.name)" ] }, { "cell_type": "markdown", "id": "fb22f13c", "metadata": {}, "source": [ "Finally, we define the hardware configuration:" ] }, { "cell_type": "code", "execution_count": 4, "id": "998b2f17", "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "hardware_cfg = {\n", " \"version\": \"0.2\",\n", " \"config_type\": \"quantify_scheduler.backends.qblox_backend.QbloxHardwareCompilationConfig\",\n", " \"hardware_description\": {\n", " f\"{cluster.name}\": {\n", " \"instrument_type\": \"Cluster\",\n", " \"modules\": {\n", " 1: {\"instrument_type\": \"QRM_RF\"},\n", " 2: {\"instrument_type\": \"QCM_RF\"},\n", " },\n", " \"ref\": \"internal\",\n", " }\n", " },\n", " \"hardware_options\": {\n", " \"modulation_frequencies\": {\n", " \"q0:res-q0.ro\": {\"lo_freq\": LO_FREQ_READOUT},\n", " \"q0:mw-q0.01\": {\"lo_freq\": LO_FREQ_QUBIT},\n", " }\n", " },\n", " \"connectivity\": {\n", " \"graph\": [\n", " (f\"{cluster.name}.module1.complex_output_0\", \"q0:res\"),\n", " (f\"{cluster.name}.module1.complex_input_0\", \"q0:res\"),\n", " (f\"{cluster.name}.module2.complex_output_0\", \"q0:mw\"),\n", " ]\n", " },\n", "}\n", "\n", "single_qubit_device.hardware_config(hardware_cfg)" ] }, { "cell_type": "markdown", "id": "443bde32", "metadata": {}, "source": [ "(cond_reset_create_schedule)=\n", "\n", "## Readout Calibration\n", "\n", "To discriminate between the ground state and the excited state with {{ ConditionalReset }}, we first need to configure the {{ ThresholdedAcquisition }} parameters `acq_threshold` and `acq_rotation` (see [Tutorial: Acquisitions](thresholded_acquisition_explanation)). We do so by preparing a qubit in either its ground state or its excited state, performing a measurement, and repeating this process 500 times. In the measured IQ plane we expect to find all data points clustered in two distinct groups that correspond to the two different states, and the `acq_threshold` and `acq_rotation` parameters define the line between the two groups. \n", "\n", "We run this calibration using {{ MeasurementControl }} and a predefined {{ Schedule }} called {{ readout_calibration_sched }}:" ] }, { "cell_type": "code", "execution_count": 5, "id": "3bb42825", "metadata": { "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "### Generate dummy data\n", "\n", "import numpy as np\n", "from qblox_instruments.ieee488_2.dummy_transport import DummyBinnedAcquisitionData\n", "from quantify_scheduler.qblox import start_dummy_cluster_armed_sequencers\n", "\n", "def get_dummy_binned_acquisition_data(\n", " real: float, imag: float, theta: float, threshold: float\n", "):\n", " angle = 2 * np.pi * theta / (360)\n", " threshold *= 1000 # different normalization (integration length) on qblox instruments\n", " if real * np.cos(angle) + imag * np.sin(angle) > -threshold:\n", " thres = 0\n", " else:\n", " thres = 1\n", " return DummyBinnedAcquisitionData(data=(real, imag), thres=thres, avg_cnt=0)\n", "\n", "\n", "# Means and standard deviations\n", "x0, xs0 = -2.7, 2.5\n", "y0, ys0 = 2.9, 2.5\n", "x1, xs1 = -14, 2.5\n", "y1, ys1 = 1.9, 2.5\n", "\n", "# Number of points per data cluster\n", "n_points = 500\n", "\n", "# Generate random samples\n", "x0_samples = np.random.normal(x0, xs0, n_points)\n", "y0_samples = np.random.normal(y0, ys0, n_points)\n", "x1_samples = np.random.normal(x1, xs1, n_points)\n", "y1_samples = np.random.normal(y1, ys1, n_points)\n", "\n", "# interleave the random samples such that we get\n", "# x = [x0_samples[0], x1_samples[0], x0_samples[1],...]\n", "# y = [y0_samples[0], y1_samples[0], y0_samples[1],...]\n", "\n", "x = np.vstack((x0_samples, x1_samples)).reshape(-1, order=\"F\")\n", "y = np.vstack((y0_samples, y1_samples)).reshape(-1, order=\"F\")\n", "states = np.array([0, 1] * 500)\n", "\n", "# prepare cluster with dummy data that will be returned \n", "# after retrieving acquisitions.\n", "cluster.delete_dummy_binned_acquisition_data(1)\n", "cluster.set_dummy_binned_acquisition_data(\n", " slot_idx=1,\n", " sequencer=0,\n", " acq_index_name=\"0\",\n", " data=[\n", " get_dummy_binned_acquisition_data(float(re), float(im), 0, 0)\n", " for re, im in zip(x, y)\n", " ],\n", ")\n", "\n", "cluster.start_sequencer = lambda: start_dummy_cluster_armed_sequencers(ic_cluster)" ] }, { "cell_type": "code", "execution_count": 6, "id": "903fb0f9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
<xarray.Dataset> Size: 24kB\n", "Dimensions: (dim_0: 1000)\n", "Coordinates:\n", " x0 (dim_0) int64 8kB 0 1 0 1 0 1 0 1 0 1 0 1 ... 1 0 1 0 1 0 1 0 1 0 1\n", "Dimensions without coordinates: dim_0\n", "Data variables:\n", " y0 (dim_0) float64 8kB 0.0007881 -0.01229 ... -0.005863 -0.01218\n", " y1 (dim_0) float64 8kB 0.003489 -0.0001137 ... 0.003838 0.0008266\n", "Attributes:\n", " tuid: 20241121-223917-960-975ef1\n", " name: Readout Calibration\n", " grid_2d: False\n", " grid_2d_uniformly_spaced: False\n", " 1d_2_settables_uniformly_spaced: False