{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Averaged Circuit Eigenvalue Sampling with Qiskit Superstaq" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/SupertechLabs/client-superstaq/blob/main/docs/source/apps/aces/aces_css.ipynb) [![Launch Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/SupertechLabs/client-superstaq/HEAD?labpath=docs/source/apps/aces/aces_css.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook demonstrates how to characterize a quantum device using the averaged circuit eigenvalue sampling (ACES) protocol through Superstaq. This protocol is integrated into Superstaq following the [*original paper*](https://arxiv.org/abs/2108.05803) by Steven T. Flammia and can be accessed using `qiskit-superstaq`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports and API Token" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example tutorial notebook uses `qiskit-superstaq`, our Superstaq client for Qiskit," ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "try:\n", " import qiskit_superstaq as qss\n", "except ImportError:\n", " print(\"Installing qiskit-superstaq...\")\n", " %pip install --quiet 'qiskit-superstaq[examples]'\n", " print(\"Installed qiskit-superstaq.\")\n", " print(\"You may need to restart the kernel to import newly installed packages.\")\n", " import qiskit_superstaq as qss" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Required imports\n", "\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To interface Superstaq via Qiskit, we must first instantiate a provider in `qiskit-superstaq` with `SuperstaqProvider()`. We then supply a Superstaq API token (or key) by either providing the API token as an argument of `qss.SuperstaqProvider()` or by setting it as an environment variable (see more details [here](https://superstaq.readthedocs.io/en/latest/get_started/basics/basics_qss.html#Set-up-access-to-Superstaq%E2%80%99s-API))." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Get the qiskit superstaq provider for Superstaq backend\n", "provider = qss.SuperstaqProvider()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ACES" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To submit an ACES job, you will need the following information:\n", "\n", "* `target`: device that you want to characterize.\n", "* `qubits`: indices of the qubits to characterize.\n", "* `shots`: number of shots to use per circuit to run.\n", "* `num_circuits`: number of random circuits to sample in order to get the gate eigenvalues.\n", "* `mirror_depth`: for each circuit, how many mirrored moments to include.\n", "* `extra_depth`: for each circuit, how many random moments to include.\n", "* `method`: the type of execution method. If `method=\"noise-sim\"`, then optional arguments `noise` and `error_prob` must be passed as well.\n", "\n", "With this information, you can submit an ACES job through the `submit_aces` method. This will take care of constructing the circuits needed to perform the protocol and will execute them. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "job_id = provider.submit_aces(\n", " target=\"ss_unconstrained_simulator\",\n", " qubits=[0, 1],\n", " shots=100,\n", " num_circuits=5,\n", " mirror_depth=4,\n", " extra_depth=7,\n", " method=\"dry-run\",\n", ")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'68f5d0a8-92ff-4430-b6ee-5d4200b07a58'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "job_id" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We note that the circuits created by this protocol can take long to execute, especially if submitted to a real device, so it might be convenient to save the job id somewhere. Once the jobs have finished running, calling `process_aces` and passing it the job id will compute the individual gate eigenvalues." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "result = provider.process_aces(job_id)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A gate eigenvalue is a measure of how well a gate is executed by the device we are characterizing. It is given by the following equation\n", "$$ \\tilde{G}(P) = \\Lambda_{G, P} G(P) $$\n", "where $\\tilde{G}$ is the noisy implementation of the ideal gate $G$, $P$ is a Pauli operator, and $\\Lambda_{G, P}$ is the gate eigenvalue corresponding to that gate and Pauli pair. $G(P)$ is the conjugation of $P$ by $G$, i.e., $G P G^\\dagger$.\n", "\n", "The output is a list of estimated circuit eigenvalues. For each qubit, we consider six Clifford gates, given by the XZ maps: XZ, ZX, -YZ, -XY, ZY, and YX. For each of these, there are three eigenvalues: X, Y, and Z. All the one-qubit eigenvalues are returned first. Then, the only two-qubit gate considered is the CZ in linear connectivity. For this gate, there are 15 eigenvalues: XX, XY, XZ, XI, YX, YY, YZ, YI, ZX, ZY, ZZ, ZI, IX, IY, and IZ. Therefore, for the above example of two qubits, there are $18\\cdot2 + 14 = 51$ eigenvalues. \n", "\n", "Since we used `\"ss_unconstrained_simulator\"` as a target, the results are not very interesting because all the circuits are simulated without a noise model, so we get that every circuit eigenvalue is 1 since every gate is simulated perfectly." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ACES with noise" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A more interesting example is given when we use `method=\"noise-sim\"`. We now have to specify the arguments `noise` and `error_prob` (see the [documentation](https://superstaq.readthedocs.io/en/latest/qiskit_superstaq.html#qiskit_superstaq.SuperstaqBackend.submit_aces) for more information on these). Here, we are going to use an asymmetric depolarizing channel, with the error probabilities 0.05, 0.11, and 0.08 for the X, Y, and Z gates respectively." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "noise_job_id = provider.submit_aces(\n", " target=\"ss_unconstrained_simulator\",\n", " qubits=[0, 1],\n", " shots=100,\n", " num_circuits=5,\n", " mirror_depth=4,\n", " extra_depth=7,\n", " method=\"noise-sim\",\n", " noise=\"asymmetric_depolarize\",\n", " error_prob=(0.05, 0.11, 0.08),\n", ")" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'5c04c005-ac3c-478f-b3e9-4ae2c8e775b3'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "noise_job_id" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "noise_result = provider.process_aces(noise_job_id)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.22506795, 0.75664239, 1. , 1. , 0.87474481,\n", " 1. , 1. , 0.94793303, 0.86153607, 0.58656859,\n", " 1. , 1. , 1. , 0.6490385 , 0.80166365,\n", " 1. , 1. , 1. , 0.51738399, 1. ,\n", " 0.72798301, 1. , 1. , 1. , 1. ,\n", " 0.65592175, 1. , 1. , 1. , 1. ,\n", " 0.95206672, 0.93797758, 1. , 0.59267365, 1. ,\n", " 1. , 0.35966265, 0.88632776, 0.63868724, 1. ,\n", " 0.39035178, 1. , 0.27895623, 1. , 1. ,\n", " 0.35345374, 1. , 0.74321218, 1. , 0.78580319,\n", " 0.76095787])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array(noise_result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output is now much more interesting, since we have a more diverse set of eigenvalues due to the noise channel we have introduced." ] } ], "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.11.5" } }, "nbformat": 4, "nbformat_minor": 4 }