{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Quantum Characterisation, Verification and Validation" ] }, { "attachments": {}, "cell_type": "markdown", "id": "approximate-aurora", "metadata": {}, "source": [ "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Infleqtion/client-superstaq/blob/main/docs/source/apps/supermarq/qcvv/qcvv_css.ipynb) [![Launch Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/Infleqtion/client-superstaq/HEAD?labpath=docs/source/apps/supermarq/qcvv/qcvv_css.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To demonstrate how to implement new benchmarking experiments within the Superstaq QCVV framework,\n", "consider implementing a naive benchmarking routine where we try to estimate the fidelity of a single\n", "qubit Z gate by repeatedly applying the gate to a qubit in the ground state (such that the Z-gate\n", "should have no effect) and observing if any observations of the excited state occur. If the excited \n", "state is observed this indicates an error has occurred. Assuming that each time the Z-gate is\n", "applied the probability of a bit flip error is $e$ then after $d$ gates the probability of \n", "observing the ground state is $$p(0) = \\frac{1}{2}(1-e)^d + \\frac{1}{2}$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can create an experiment to measure this as follows" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The autoreload extension is already loaded. To reload it, use:\n", " %reload_ext autoreload\n" ] } ], "source": [ "%load_ext autoreload\n", "%autoreload 2\n", "from __future__ import annotations" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " import supermarq\n", "except ImportError:\n", " print(\"Installing supermarq...\")\n", " %pip install --quiet supermarq\n", " print(\"Installed supermarq.\")\n", " print(\"You may need to restart the kernel to import newly installed packages.\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from supermarq.qcvv import BenchmarkingExperiment, Sample, BenchmarkingResults\n", "from dataclasses import dataclass\n", "from collections.abc import Sequence\n", "from typing import Iterable\n", "from tqdm.contrib.itertools import product\n", "import pandas as pd\n", "\n", "import cirq\n", "\n", "from scipy.stats import linregress\n", "import numpy as np\n", "import seaborn as sns\n", "import matplotlib.pyplot as plt\n", "\n", "\n", "@dataclass(frozen=True)\n", "class NaiveExperimentResult(BenchmarkingResults):\n", " gate_fidelity: float\n", " gate_error: float\n", "\n", " experiment_name = \"NaiveExperiment\"\n", "\n", "\n", "class NaiveExperiment(BenchmarkingExperiment[NaiveExperimentResult]):\n", " def __init__(self):\n", " super().__init__(num_qubits=1)\n", "\n", " def _build_circuits(self, num_circuits: int, layers: Iterable[int]) -> Sequence[Sample]:\n", " \"\"\"Build the circuits by composing multiple Z gates together into circuits. The\n", " number of gates to compose is given by the `layers` parameter.\n", " \"\"\"\n", " samples = []\n", " for _, depth in product(range(num_circuits), layers, desc=\"Building circuits.\"):\n", " circuit = cirq.Circuit([cirq.Z(*self.qubits) for _ in range(depth)])\n", " circuit += cirq.measure(*self.qubits)\n", " samples.append(Sample(raw_circuit=circuit, data={\"depth\": depth}))\n", "\n", " return samples\n", "\n", " def _process_probabilities(self, samples) -> None:\n", " \"\"\"Copy the data and observed probabilities into a pandas DataFrame.\"\"\"\n", " records = []\n", " for sample in samples:\n", " records.append({**sample.data, **sample.probabilities})\n", " return pd.DataFrame(records)\n", "\n", " def analyze_results(self, plot_results: bool = True) -> NaiveExperiment:\n", " \"\"\"To analyse the results to fit a simple exponential decay. This can be done easily\n", " by fitting a linear model to the logarithm of the equation above.\n", " \"\"\"\n", "\n", " model = linregress(x=self.raw_data[\"depth\"], y=np.log(2 * self.raw_data[\"0\"] - 1))\n", "\n", " fidelity = np.exp(model.slope)\n", "\n", " self._results = NaiveExperimentResult(\n", " target=\"& \".join(self.targets),\n", " total_circuits=len(self.samples),\n", " gate_fidelity=fidelity,\n", " gate_error=1 - fidelity,\n", " )\n", "\n", " if plot_results:\n", " self.plot_results()\n", "\n", " return self.results\n", "\n", " def plot_results(self) -> None:\n", " \"\"\"Plot the data with the fit superimposed on top.\"\"\"\n", "\n", " fig, axs = plt.subplots(\n", " 1,\n", " )\n", "\n", " sns.scatterplot(self.raw_data, x=\"depth\", y=\"0\", ax=axs)\n", "\n", " x = np.linspace(0, max(self.raw_data.depth))\n", " y = 0.5 * self.results.gate_fidelity**x + 0.5\n", " axs.plot(x, y)\n", " axs.set_xlabel(\"Circuit depth\")\n", " axs.set_ylabel(\"Probability of ground state\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To test this basic experiment, we use a depolarising noise model and a density matrix simulator.\n", "Note that if we use a single qubit depolarising channel with pauli error rate $p$ this will result in an \n", "error with probability of $4p/3$." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "23732e00c62f46b88b6b8d612c1e2e16", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Building circuits.: 0%| | 0/30 [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "if experiment.collect_data():\n", " results = experiment.analyze_results(plot_results=True)\n", " print(results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Checking this result we have" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.009974775529465019\n" ] } ], "source": [ "pauli_error_rate = experiment.results.gate_error\n", "print(pauli_error_rate)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Which agrees very closely with our channel which we set up with $p=0.01$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could also run the same experiment on a device through Superstaq. In this case we use the \n", "`run_on_device()` method instead, although for this example we use the Superstaq simulator device. \n", "Note we also use the `force=True` option to overwrite our existing\n", "results." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "5049f6b29a3f411585033aa0809bad01", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Building circuits.: 0%| | 0/15 [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "if experiment.collect_data():\n", " results = experiment.analyze_results(plot_results=True)\n", " print(results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As expected, since the Superstaq simulator is exact, we obtain a gate fidelity of 1.0" ] } ], "metadata": { "kernelspec": { "display_name": "client_superstaq", "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.9" } }, "nbformat": 4, "nbformat_minor": 2 }