{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Mirror Randomized Benchmarking with Universal Gate Sets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial contains a few details on how to run *Mirror Randomized Benchmarking* with universal gate sets, that are not covered in the [RB overview tutorial](RB-Overview.ipynb) or the [Clifford MRB tutorial](RB-MirrorRB.ipynb).\n", "\n", "## What is Mirror RB? \n", "\n", "Mirror RB is a streamlined, computationally-efficient RB method. It has the same core purpose as Clifford RB - quantifying average gate performance - but it is feasable on more qubits, and it provides more directly useful information. Unlike oter RB protocols, Mirror RB can be implemented with non-Clifford gates on many qubits. The general structure of MRB circuits with non-Clifford gates is similar to that of MRB circuits with Clifford gates. The structure of a depth $m$ ($m\\geq 0$) mirror RB circuit is:\n", "1. A Haar-random 1-qubit gate (or random 1-qubit Clifford gate) on every qubit. \n", "2. A \"compute\" circuit consisting of $m/2$ independently sampled layers of gates, sampled according to a user-specified distribution $\\Omega$. Each of these layers is a *composite layer* consisting of (1) randomly-sampled native two-qubit gates, followed by (2) Haar-random 1-qubit gates (or random 1-qubit Clifford gates) on each qubit. Variations on this structure are possible, but they are not currently implemented in pyGSTi. \n", "4. An \"uncompute\" circuit consisting of the $m/2$ layers from step (2) in the reverse order with each gate replaced with its inverse. \n", "5. The inverse of the random 1-qubit gates in step (1).\n", "This circuit then undergoes a version of randomized compilation to get the final circuit, in which random Pauli gates are compiled into the composite layers. \n", "\n", "See [Demonstrating scalable randomized benchmarking of universal gate sets](https://arxiv.org/abs/2207.07272) for further details on MRB with universal gate sets. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function #python 2 & 3 compatibility\n", "import pygsti\n", "from pygsti.processors import QubitProcessorSpec as QPS\n", "from pygsti.processors import CliffordCompilationRules as CCR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Mirror RB experiment design\n", "\n", "Generating a Mirror RB experiment design for universal gate sets is very similar to creating an experiment design for other RB methods or for Clifford Mirror RB. \n", "\n", "### 1. Generic RB inputs\n", "\n", "The first inputs to create a Mirror 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`). Universal gate set MRB in pyGSTi currently requires the `Gzr` and `Xpi2` gates to be in the list of gate names.\n", "- The \"RB depths\" at which we will sample circuits (`depths`). For Mirror RB, these depths must be even integers. They correspond to the number of total layers in the \"compute\" and \"uncompute\" sub-circuits.\n", "- The number of circuits to sample at each length (`k`).\n", "- The qubits to benchmark (`qubits`)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Mirror RB can be run on many many more qubit than this, but this notebook creates simulated data. As \n", "# we are using a full density matrix simulator this limits the number of qubits we can use here.\n", "n_qubits = 3\n", "qubit_labels = ['Q'+str(i) for i in range(n_qubits)] \n", "gate_names = ['Gi', 'Gxpi2', 'Gzr', 'Gcphase'] \n", "availability = {'Gcphase':[('Q'+str(i),'Q'+str((i+1) % n_qubits)) for i in range(n_qubits)]}\n", "pspec = QPS(n_qubits, gate_names, availability=availability, qubit_labels=qubit_labels)\n", "\n", "depths = [0, 2, 4, 8, 16, 32]\n", "k = 40\n", "qubits = qubit_labels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mirror RB is implemented in pyGSTi for specific random circuit structures, specified via the option argument `circuit_type`:\n", "1. `clifford+zxzxz-haar`: Clifford two-qubit gates and Haar-random 1-qubit gates\n", "2. `clifford+zxzxz-clifford`: Clifford two-qubit gates and random Clifford 1-qubit gates (this is *not* a universal gate set)\n", "3. `cz(theta)+zxzxz-haar`: Controlled Z axis rotation gates CZ(theta) and Haar-random 1-qubit gates. This option requires that both CZ(theta) and CZ(-theta) are in the allowed two-qubit gates. \n", "\n", "All single-qubit gates are decomposed into a series of five single qubit gates: Two $X_{\\pi/2}$ gates and three $Z(\\theta)$ gates. \n", "\n", "### 2. The circuit layer sampler\n", "As with Clifford Mirror RB, the Mirror RB error rate $r$ depends on the circuit layer sampling distribution $\\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", "Universal gate set MRB in pyGSTi uses the \"edge grab\" sampler. The frequency of two-qubit gates in the layers can be changed via the optional arguement `samplerargs`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "samplerargs = [0.5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From here, generating the design and collecting data proceeds as in the RB overview tutorial." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "qubit_error_rate = 0.002\n", "def simulate_taking_data(data_template_filename):\n", " \"\"\"Simulate taking data and filling the results into a template dataset.txt file\"\"\"\n", " error_rates = {}\n", " for gn in pspec.gate_names:\n", " n = pspec.gate_num_qubits(gn)\n", " gate_error_rate = n * qubit_error_rate\n", " error_rates[gn] = [gate_error_rate/(4**n - 1)] * (4**n - 1)\n", " noisemodel = pygsti.models.create_crosstalk_free_model(pspec, stochastic_error_probs=error_rates)\n", " pygsti.io.fill_in_empty_dataset_with_fake_data(data_template_filename, noisemodel, num_samples=1000, seed=1234)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- Sampling 40 circuits at MRB length 0 (1 of 6 depths) with seed 459117\n", "- Sampling 40 circuits at MRB length 2 (2 of 6 depths) with seed 459157\n", "- Sampling 40 circuits at MRB length 4 (3 of 6 depths) with seed 459197\n", "- Sampling 40 circuits at MRB length 8 (4 of 6 depths) with seed 459237\n", "- Sampling 40 circuits at MRB length 16 (5 of 6 depths) with seed 459277\n", "- Sampling 40 circuits at MRB length 32 (6 of 6 depths) with seed 459317\n" ] } ], "source": [ "design = pygsti.protocols.MirrorRBDesign(pspec, depths, k, qubit_labels=qubits,\n", " circuit_type='clifford+zxzxz-haar', samplerargs=samplerargs)\n", "\n", "pygsti.io.write_empty_protocol_data('../tutorial_files/test_mrb_dir', design, clobber_ok=True)\n", "\n", "# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --MirrorRBDesign\n", "simulate_taking_data('../tutorial_files/test_mrb_dir/data/dataset.txt') # REPLACE with actual data-taking\n", "\n", "data = pygsti.io.read_data_from_dir('../tutorial_files/test_mrb_dir')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running the Mirror RB protocol\n", "As with all RB methods in pyGSTi, to analyze the data we instantiate an `RB` protocol and `.run` it on our data object. However, there is a slight difference for Mirror RB. Mirror RB doesn't fit simple success/fail format data: instead it fits what we call *Hamming weight adjusted success probabilities* to an exponential decay ($P_m = A + B p^m$ where $P_m$ is the average adjusted success probability at RB length $m$). \n", "\n", "To obtain this data analysis we simply specify the data type when instantiate an `RB` protocol: we set `datatype = adjusted_success_probabilities`." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
Loading...
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "
\n", "
\n", "
\n", "\n", "
\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "protocol = pygsti.protocols.RB(datatype = 'adjusted_success_probabilities', defaultfit='A-fixed')\n", "results = protocol.run(data)\n", "ws = pygsti.report.Workspace()\n", "ws.init_notebook_mode(autodisplay=True)\n", "ws.RandomizedBenchmarkingPlot(results)" ] } ], "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.8.0" } }, "nbformat": 4, "nbformat_minor": 4 }