{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Averaged Circuit Eigenvalue Sampling with Cirq 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 `cirq-superstaq`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports and API Token" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example tutorial notebook uses `cirq-superstaq`, our Superstaq client for Cirq," ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "try:\n", " import cirq_superstaq as css\n", "except ImportError:\n", " print(\"Installing cirq-superstaq...\")\n", " %pip install --quiet 'cirq-superstaq[examples]'\n", " print(\"Installed cirq-superstaq.\")\n", " print(\"You may need to restart the kernel to import newly installed packages.\")\n", " import cirq_superstaq as css" ] }, { "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 Cirq, we must first instantiate a service provider in `cirq-superstaq` with `Service()`. We then supply a Superstaq API token (or key) by either providing the API token as an argument of `css.Service()` or by setting it as an environment variable (see more details [here](https://superstaq.readthedocs.io/en/latest/get_started/basics/basics_css.html#Set-up-access-to-Superstaq%E2%80%99s-API))." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Get cirq-superstaq service for Superstaq backend\n", "service = css.Service()" ] }, { "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 = service.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": [ "'c4428c69-5048-43f9-99e2-f005620d4134'" ] }, "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 = service.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/cirq_superstaq.html#cirq_superstaq.service.Service.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 = service.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": [ "'d0dfbdd2-6945-4930-bbea-f6538f517004'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "noise_job_id" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "noise_result = service.process_aces(noise_job_id)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1. , 1. , 0.43729089, 1. , 1. ,\n", " 1. , 0.37156509, 1. , 0.62674301, 0.85195808,\n", " 1. , 0.93597888, 0.91775969, 1. , 1. ,\n", " 1. , 0.33541873, 1. , 1. , 1. ,\n", " 0.95034448, 1. , 1. , 1. , 1. ,\n", " 0.40710778, 1. , 1. , 1. , 0.87884797,\n", " 1. , 0.84014817, 0.34949293, 1. , 0.66028861,\n", " 1. , 1. , 1. , 0.49340862, 1. ,\n", " 1. , 0.21858458, 1. , 0.25613083, 1. ,\n", " 1. , 1. , 0.4963732 , 1. , 1. ,\n", " 0.89530943])" ] }, "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 }