{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Direct Randomized Benchmarking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial contains a few details on how to run [Direct Randomized Benchmarking](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.123.030503) that are not covered in the [RB overview tutorial](RB-Overview.ipynb).\n", "\n", "## What is Direct RB? \n", "\n", "In essence, Direct RB is a streamlined, generalized version of the popular [Clifford RB](RB-CliffordRB.ipynb) method. It has the same core purpose - quantifying average gate performance - but it is feasable on more qubits, and it provides more directly useful information.\n", "\n", "The basic requirements for running Clifford RB and Direct RB are the same. Both methods can be implemented on a set of $n$ qubits whenever the $n$-qubit Clifford group can be **generated** by the native gates on those $n$ qubits. Clifford RB runs circuits containing $m+1$ uniformly random $n$-qubit Cliffords followed by the unique inversion $n$-qubit Clifford gate (all of which must be compiled into the native gates of the device), where $m \\geq 0$. In contrast, Direct RB circuits consist of:\n", "\n", "1. A sub-circuit that generates a uniformly random $n$-qubit stabilizer state. \n", "2. $m$ independently sampled layers of the native gates in the device, with these layers sampled according to a user-specified distribution $\\Omega$ over all possible circuit layers. \n", "3. A sub-circuit that maps the ideal output of the preceeding circuit to a uniformly random computational basis state (or, if preferred, to the all-zeros state).\n", "\n", "This construction means that Direct RB circuits can be shorter than Clifford RB circuits - for the same $m$ a Direct RB circuit is typically much shorter, including for the shortest allowed depth $m=0$. This means that Direct RB can be run on more qubits (without just obtaining a useless, entirely decohered output). But Direct RB circuits still contain sufficient randomization (if $\\Omega$ is chosen appropriately) to retain the core features of Clifford RB (exponential decays, etc).\n", "\n", "For more information on what Direct RB is and why it is useful, see [*Direct randomized benchmarking for multi-qubit devices*](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.123.030503)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function #python 2 & 3 compatibility\n", "import pygsti" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Direct RB experiment design\n", "\n", "The data analysis in Direct RB is exactly as in Clifford RB, and how to do this analysis is covered in the [RB overview tutorial](RB-Overview.ipynb). The differences and flexibility in Direct RB are all at the experiment design stage, and so this is what is covered in this tutorial. \n", "\n", "### 1. Generic RB inputs\n", "\n", "The first inputs to create a Direct RB experiment design are the same as in all RB protocols, and these are covered in the [RB overview tutorial](RB-Overview.ipynb). They are:\n", "\n", "- The device to benchmark (`pspec`).\n", "- The \"RB depths\" at which we will sample circuits (`depths`). For Direct RB, these depths are the number of layers in the \"core\" circuit, outlined in step (2) above. These depths can be any non-negative integers.\n", "- The number of circuits to sample at each length (`k`).\n", "- The qubits to benchmark (`qubits`)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "nQubits = 4\n", "qubit_labels = ['Q0','Q1','Q2','Q3'] \n", "gate_names = ['Gxpi2', 'Gxmpi2', 'Gypi2', 'Gympi2', 'Gcphase'] \n", "availability = {'Gcphase':[('Q0','Q1'), ('Q1','Q2'), ('Q2','Q3'), ('Q3','Q0')]}\n", "pspec = pygsti.obj.ProcessorSpec(nQubits, gate_names, availability=availability, \n", " qubit_labels=qubit_labels, construct_models=('clifford',))\n", "\n", "depths = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512]\n", "k = 10\n", "qubits = ['Q0','Q1','Q2', 'Q3']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All other arguments to the Direct RB experiment design generation function `DirectRBDesign` are optional. But to make the most out of Direct RB it is typically important to manually set at least some of them.\n", "\n", "### 2. The circuit layer sampler\n", "The Direct RB circuit layer sampling distribution $\\Omega$ is perhaps the most important input to the Direct RB experiment design. This is because, by construction, the Direct RB error rate $r$ is $\\Omega$-dependent. This is because $r$ quantifies gate performance over circuits that are sampled according to $\\Omega$. This $\\Omega$-dependence is useful, because by carefully choosing or varying $\\Omega$ we can learn a lot about device performance. But it also means that the $\\Omega$ has to be carefully chosen! At the very least, **you need to know what sampling distribution you are using in order to interpret the results!**\n", "\n", "This might seem like a drawback in comparison to Clifford RB, but note that this $\\Omega$-dependence is analogous to the Clifford-compiler dependence of the Clifford RB error rate (with the advantage that it is more easily controlled and understood).\n", "\n", "The sampling distribution is specified via the optional arguements `sampler` and `samplerargs`. Here we use what we call the \"edge grab\" sampler. \n", "\n", "Because Mirror RB has an equivalent sampling-distribution dependence, there is a separate [random circuit sampling tutorial](RB-Samplers.ipynb) that introduces the different built-in sampling algorithms within pyGSTi (which includes details of the \"edge grab\" algorithm)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "sampler = 'edgegrab'\n", "samplerargs = [0.5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. The target output\n", "By design, a specific Direct RB circuit should always return a particular bit-string if there is no errors, which we call it's target bit-string. This target bit-string can either be randomized (so that it is a uniformly random bit-string), or it can be set to always be the all-zeros bit-string. This is specified via the `randomizeout` argument. We advise randomizing the target output." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "randomizeout = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. The stabilizer state compilation algorithm\n", "To generate a Direct RB circuit in terms of native gates, it is necessary for pyGSTi to compile the sub-circuits in steps (1) and (3) that implement a randomly sampled stabilizer state preparation and measurement, respectively. We do this compilation using a randomized algorithm, and the number of randomization is controlled via `citerations`. Increasing this will reduce the average depth of these subcircuits, up to a point, making Direct RB feasable on more qubits. \n", "But note that time to generate the circuits increases linearly with `citerations` (so we'll leave it at the default value of 20 here). For the experiments presented in [Direct randomized benchmarking for multi-qubit devices](https://arxiv.org/abs/1807.07975) it was increased to 200, and values around this are probably advisable if you want to push the limits of how many qubits you can holistically benchmark with Direct RB for given gate fidelities.\n", "\n", "Note that, unlike Clifford RB, there is (approximately) no compiler dependence to the Direct RB error rate. So the value of `citerations` only effects the feasability of Direct RB not its ouput error rate." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "citerations = 20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From here, everything proceeds as in the RB overview tutorial (except for adding in the optional arguments)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# Here we construct an error model with 0.1% local depolarization on each qubit after each gate.\n", "gate_error_rate = 0.001\n", "def simulate_taking_data(data_template_filename):\n", " \"\"\"Simulate taking data and filling the results into a template dataset.txt file\"\"\"\n", " pspec = pygsti.obj.ProcessorSpec(nQubits, gate_names, availability=availability, \n", " qubit_labels=qubit_labels, construct_models=('TP',))\n", " noisemodel = pspec.models['TP'].copy()\n", " for gate in noisemodel.operation_blks['gates'].values():\n", " if gate.dim == 16:\n", " gate.depolarize(1 - pygsti.tools.rbtools.r_to_p(1 - (1-gate_error_rate)**2, 4))\n", " if gate.dim == 4:\n", " gate.depolarize(1 - pygsti.tools.rbtools.r_to_p(gate_error_rate, 2))\n", " pygsti.io.fill_in_empty_dataset_with_fake_data(noisemodel, data_template_filename, nSamples=1000, seed=1234)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "design = pygsti.protocols.DirectRBDesign(pspec, depths, k, qubit_labels=qubits, sampler=sampler, \n", " samplerargs=samplerargs, randomizeout=randomizeout,\n", " citerations=citerations)\n", "\n", "pygsti.io.write_empty_protocol_data(design, '../tutorial_files/test_rb_dir', clobber_ok=True)\n", "\n", "# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --\n", "simulate_taking_data('../tutorial_files/test_rb_dir/data/dataset.txt') # REPLACE with actual data-taking\n", "\n", "data = pygsti.io.load_data_from_dir('../tutorial_files/test_rb_dir')\n", "\n", "protocol = pygsti.protocols.RB()\n", "results = protocol.run(data)\n", "ws = pygsti.report.Workspace()\n", "ws.init_notebook_mode(autodisplay=True)\n", "ws.RandomizedBenchmarkingPlot(results)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# The error rate we approximately expect accord to Direct RB theory\n", "print(1 - (1 - gate_error_rate)**len(qubits))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.0" } }, "nbformat": 4, "nbformat_minor": 1 }