{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Streamlined Randomized Benchmarking (SRB)\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These examples demonstrate how to use SRB, the most basic QCVV protocol, to estimate\nthe process infidelity of a single-qubit or two-qubit gateset [#f1]_ . See\n:tqdoc:`SRB` for more information about this protocol.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hello World\nThe simplest thing we can do is run SRB on a single-qubit. Qubits are labeled by\nnon-negative integers, and here we target qubit 0. We choose to use sequence lengths\n``[4, 30, 50]``, and, by default, 30 random circuits are generated per sequence\nlength. Below, we display the first circuit where the 4 random Clifford gates can be\nseen, plus a fifth gate which is the product of their inverses and a random Pauli\nmatrix, where the additional Pauli matrix is introduced to make the fit more robust.\nThe empty cycles with non-zero markers delimit the random Cliffords, and more\ngenerally, in other protocols like :meth:`~trueq.make_cb()` and\n:meth:`~trueq.make_irb()`, contain interleaved cycles of interest.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import trueq as tq\nimport trueq.simulation as tqs\n\ncircuits = tq.make_srb(0, [4, 30, 50])\ncircuits[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we simulate each of the circuits with the built-in simulator. We can see the\nbitstring counts of the first circuit below.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "tq.Simulator().add_overrotation(0.04).add_depolarizing(0.01).run(circuits)\ncircuits[0].results.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is important to look at the decay curve to verify that appropriate sequence lengths\nwere chosen. The most informative sequence length to measure occurs around\n$m=1/(1-p)$ where the decay is $y=Ap^m$. If $A\\approx 1$ and\n$p\\approx 1$, then this corresponds to $y\\approx1/e\\approx0.37$.\nTherefore, in this example, our sequence lengths are a bit too short, and ``[4, 70]``\nwould, in retrospect, have been more appropriate.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "circuits.plot.raw()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can look at the values of the estimate:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "circuits.fit()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Isolated vs. Simultaneous SRB\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The core function :meth:`~trueq.make_srb()` generates a circuit collection that\nimplements simultaneous SRB, where gates are placed on all specified qubits of a\nquantum device within each circuit. Different random gates are independently chosen\nduring each cycle. Qubit labels are entered into its first argument and specify which\nqubits are acted on simultaneously. You can choose which qubits get single-qubit\ntwirls, and which qubits get two-qubit twirls, by nesting the qubit labels\nappropriately; see the following examples.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# simultaneous single-qubit RB on qubits 0 and 1; 60 circuits total\ncircuits = tq.make_srb([0, 1], [4, 100], 30)\n\n# isolated single-qubit RB on qubits 0 and 1; 120 circuits total\ncircuits = tq.make_srb([0], [4, 100], 30).append(tq.make_srb([1], [4, 100], 30))\n\n# two-qubit RB on 0 and 1, simultaneous with single-qubit RB on 2; 60 circuits total\ncircuits = tq.make_srb([[0, 1], 2], [4, 100], 30)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The invocations above all use two sequence lengths (the number of random gate cycles\nin a circuit), 4 and 100, and produce 30 random circuits per sequence length. Note\nthat specifying qubit labels ``[0, 1, 2]`` is a convenient short-hand for\n``[[0], [1], [2]]``. This imples, for example, that ``[[0,1]]`` has a much different\nmeaning than ``[0,1]``.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Isolated SRB\nIn isolated SRB, we characterize qubits or qubit-pairs one at a time with disjoint\nexperiments. This is done to assess gate quality on a small subregister in the context\nof an idling environment. In this example, we build a single circuit collection to\nestimate the average gate infidelity of three pairs on a 6 qubit device.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# generate a circuit collection\ncircuits = tq.CircuitCollection()\nfor pair in [[0, 1], [2, 3], [4, 5]]:\n circuits += tq.make_srb([pair], [4, 10, 20])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we transpile the circuits into native gates and simulate them.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Initialize a simulator\nsim = tq.Simulator().add_overrotation(0.01, 0.02)\nsim.add_stochastic_pauli(px=0.01, match=tqs.LabelMatch((0, 1)))\nsim.add_stochastic_pauli(pz=0.005, match=tqs.LabelMatch((2, 3, 4, 5)))\n\n# initialize a transpiler into native gates (U3 and CNOT)\ncnot_factory = tq.config.GateFactory.from_matrix(\"cnot\", tq.Gate.cnot.mat)\nconfig = tq.Config(factories=[tq.config.u3_factory, cnot_factory])\nt = tq.Compiler.from_config(config)\n\n# transpile and simulate\ncircuits = t.compile(circuits)\nsim.run(circuits)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the results.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "circuits.plot.raw([[0, 1], [2, 3], [4, 5]])\ncircuits.plot.compare_rb()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simultaneous SRB\nIn simultaneous SRB, we characterize qubits or qubit-pairs in a single set of\nexperiments. This captures crosstalk between these subsystems which is usually not\npresent when other qubits are idle. In this example, we add more circuits to the\ncircuits from the previous section.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# add simultaneous circuits to our old collection\nsimul_circuits = t.compile(tq.make_srb([[0, 1], [2, 3], [4, 5]], [4, 10, 20]))\ncircuits += simul_circuits\nsim.run(simul_circuits)\n\n# plot the results\ncircuits.plot.raw([[0, 1], [2, 3], [4, 5]])\ncircuits.plot.compare_rb()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Changing the Twirling Group Gateset\nThe above examples characterize the average gate fidelity of the Clifford group, which\nis historically by far the most common group to use. However, any unitary 2-design\nwill suffice, and so for SRB, the other notable choice is the set of all unitaries\nsampled using the Haar measure. We make this alternate choice below.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "su_circuits = tq.make_srb([[0, 1], [2, 3], [4, 5]], [4, 6, 12], twirl=\"U\")\nsu_circuits = t.compile(su_circuits)\nsim.run(su_circuits)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our native gates here are still CNOT and U3. It takes on average 1.5 CNOTs to\nsynthesize a two-qubit Clifford gate, but it almost always takes 3 CNOTs to\nsynthesize a Haar-random two-qubit unitary group. Thus we see higher error rates\nthan in the section above.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simulate and plot the results.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "su_circuits.plot.raw([[0, 1], [2, 3], [4, 5]])\nsu_circuits.plot.compare_rb()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. rubric:: Footnotes\n\n.. [#f1] \"Streamlined\" refers to the method we use to eliminate the constant offset\n (the $B$ in $Ap^m+B$) that typically appears in standard RB.\n\n" ] } ], "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.6.8" } }, "nbformat": 4, "nbformat_minor": 0 }