From 50bde76e53a086834681240bc81b539a4954a92d Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 19 Sep 2023 14:37:03 -0700 Subject: [PATCH] Add RUS Azure Quantum notebook sample This adds a notebook with a subset of the RUS experiment on Quanitnuum to allow users to repeat this experiment on their own as a hosted notebook. --- .../repeat-until-success/README.md | 21 + .../repeat-until-success.ipynb | 476 ++++++++++++++++++ 2 files changed, 497 insertions(+) create mode 100644 samples/azure-quantum/repeat-until-success/README.md create mode 100644 samples/azure-quantum/repeat-until-success/repeat-until-success.ipynb diff --git a/samples/azure-quantum/repeat-until-success/README.md b/samples/azure-quantum/repeat-until-success/README.md new file mode 100644 index 00000000000..034bb9bbf91 --- /dev/null +++ b/samples/azure-quantum/repeat-until-success/README.md @@ -0,0 +1,21 @@ +--- +page_type: sample +author: swernli +description: Demonstrate the RUS experiment using the Azure Quantum service +ms.author: swernli@microsoft.com +ms.date: 09/19/2023 +languages: +- qsharp +- python +products: +- qdk +- azure-quantum +--- + +# RUS sample + +This sample demonstrates how to use Python, Q#, and the Azure Quantum service together to reproduce a subset of an RUS experiment on Quantinuum hardware. + +## Manifest + +- [repeat-until-success.ipynb](https://github.com/microsoft/quantum/blob/main/samples/azure-quantum/repeat-until-success/repeat-until-success.ipynb): Jupyter notebook for this sample. diff --git a/samples/azure-quantum/repeat-until-success/repeat-until-success.ipynb b/samples/azure-quantum/repeat-until-success/repeat-until-success.ipynb new file mode 100644 index 00000000000..4a1d7a06fc7 --- /dev/null +++ b/samples/azure-quantum/repeat-until-success/repeat-until-success.ipynb @@ -0,0 +1,476 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "# Repeat-Until-Success Sample\n", + "This sample shows the use of branching on measurement and qubit reuse to implement a Repeat-Until-Success algorithm on Quantinuum hardware targets. It is drawn from a collaboration between Microsoft and Quantinuum research teams, specifically the authors Natalie C. Brown, John Peter Campora III, Ciarán Ryan-Anderson, Dominic Lucchetti, and Alex Chernoguzov from Quantinuum; Stefan Wernli, Adam Paetznick, Martin Roetteler, and Krysta Svore from Microsft; as well as Bettina Heim and Cassandra Granade.\n", + "\n", + "The program uses RUS and two sub-circuits to perform the single-qubit unitary V3 = (I + 2iZ)/(√5) as described in detail in [Repeat-Until-Success: Non-deterministic decomposition of single-qubit unitaries](https://arxiv.org/abs/1311.1074) by Adam Paetznik and Krysta Svore.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "## 1. Connect to Azure Quantum and set up target\n", + "\n", + "First, we must configure the qsharp module to connect the azure workspace and specify a target. The Quantinuum syntax checker target `quantinuum.sim.h2-1sc` is chosen by default, which will perform program compilation but always return all zeros. To update to target that performs hardware-modeled noisy simulation, comment out the first target and uncomment the selection of `quantinuum.sim.h2-1e`. Not that this will consume quota as part of executing the simulation.\n", + "\n", + "We also configure the target capability as `AdaptiveExecution` to indicate we use the QIR profile with support for mid-circuit measurement, measurement-based control flow, and classical value support as part of compilation.\n", + "\n", + "Replace the `resourceId` and `location` connection parameters with the values for your configured Azure Quantum Workspace" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "import time\n", + "import qsharp\n", + "import qsharp.azure\n", + "\n", + "# Set the compiler target to the one queried from the workspace\n", + "targets = qsharp.azure.connect(\n", + " resourceId=\"\",\n", + " location=\"\"\n", + ")\n", + "\n", + "\n", + "qsharp.azure.target(\"quantinuum.sim.h2-1sc\") # Syntax-Checker that verifies connectivity and compilation\n", + " # Always returns all zero results to indicate success\n", + "# qsharp.azure.target(\"quantinuum.sim.h2-1e\") # Emulator target that will run the program with hardware-modeled noise\n", + " # Uses HQC/quota\n", + "qsharp.azure.target_capability(\"AdaptiveExecution\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "## 2. Compile the RUS Program\n", + "Note: compiling this code will produce expected warnings about potentially incompatible capabilities from the utility operation `PreparePauliEigenstate`. The incompatible code paths in the library are not used by this sample and teh warning can be safely ignored." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "microsoft": { + "language": "qsharp" + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "vscode": { + "languageId": "qsharp" + } + }, + "outputs": [], + "source": [ + "%%qsharp\n", + "open Microsoft.Quantum.Intrinsic;\n", + "open Microsoft.Quantum.Canon;\n", + "open Microsoft.Quantum.Math;\n", + "open Microsoft.Quantum.Preparation;\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", + "/// # Summary\n", + "/// Example of a Repeat-until-success algorithm implementing a circuit\n", + "/// that achieves exp(i*ArcTan(2)*Z), also known as the \"V gate\"\n", + "/// by Paetznick & Svore.\n", + "///\n", + "/// # References\n", + "/// - [ *Adam Paetznick, Krysta M. Svore*,\n", + "/// Quantum Information & Computation 14(15 & 16): 1277-1301 (2014)\n", + "/// ](https://arxiv.org/abs/1311.1074)\n", + "/// For circuit diagram, see file RUS.png.\n", + "///\n", + "/// # Input\n", + "/// ## inputValue\n", + "/// Boolean value for input qubit (true maps to One, false maps to Zero)\n", + "/// ## inputBasis\n", + "/// Pauli basis in which to prepare input qubit\n", + "/// ## limit\n", + "/// Integer limit to number of repeats of circuit\n", + "///\n", + "/// # Remarks\n", + "/// The program executes a circuit on a \"target\" qubit using an \"auxiliary\"\n", + "/// and \"resource\" qubit. The circuit consists of two parts (red and blue\n", + "/// in image).\n", + "/// The goal is to measure Zero for both the auxiliary and resource qubit.\n", + "/// If this succeeds, the program will have effectively applied an\n", + "/// Rz(arctan(2)) gate (also known as V_3 gate) on the target qubit.\n", + "/// If this fails, the program reruns the circuit up to times.\n", + "operation AllocateQubitsAndApplyRzArcTan2 (\n", + " inputValue : Bool,\n", + " inputBasis : Pauli,\n", + " measurementBasis : Pauli,\n", + " limit : Int\n", + ") : Result[] {\n", + " use (auxiliary, resource, target) = (Qubit(), Qubit(), Qubit());\n", + " \n", + " // Prepare target qubit in a zero- or one-state, based on input value\n", + " if (inputValue) {\n", + " X(target);\n", + " }\n", + " PreparePauliEigenstate(inputBasis, target);\n", + " \n", + " within {\n", + " H(auxiliary);\n", + " H(resource);\n", + " }\n", + " apply {\n", + " ApplyRzArcTan2(limit, auxiliary, resource, target);\n", + "\n", + " // let rotation angle = 2.0 * ArcTan(2.0);\n", + " let rotationAngle =\n", + " 2.2142974355881810060341309203570740801400952908028652933530784148;\n", + " Rz(rotationAngle, target); // Rotate back to initial state\n", + " }\n", + " \n", + " return [M(auxiliary), M(resource), Measure([measurementBasis], [target])];\n", + "}\n", + "\n", + "/// # Summary\n", + "/// Apply Rz(arctan(2)) on qubits using repeat until success algorithm.\n", + "/// Updated to use for-loop sytle.\n", + "operation ApplyRzArcTan2 (\n", + " limit : Int,\n", + " auxiliary : Qubit,\n", + " resource : Qubit,\n", + " target : Qubit\n", + ") : Unit {\n", + " mutable (result1, result2) = (false, false);\n", + " \n", + " // Run Part 1 of the program.\n", + " for _ in 1..limit {\n", + " if not result1 or not result2 {\n", + " set result1 = ApplyAndMeasurePart1(auxiliary, resource) == Zero;\n", + " \n", + " // We’ll only run Part 2 if Part 1 returns Zero.\n", + " // Otherwise, we’ll skip and rerun Part 1 again.\n", + " if result1 {\n", + " set result2 = ApplyAndMeasurePart2(resource, target) == Zero;\n", + " \n", + " if not result2 {\n", + " Z(resource); // Reset resource\n", + " Adjoint Z(target); // Correct effective Z rotation on target\n", + " }\n", + " } else {\n", + " Z(auxiliary);\n", + " Reset(resource);\n", + " H(resource);\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "/// # Summary\n", + "/// Apply part 1 of RUS circuit and measure\n", + "/// auxiliary qubit in Pauli X basis\n", + "operation ApplyAndMeasurePart1(auxiliary : Qubit, resource : Qubit) : Result {\n", + " within {\n", + " T(auxiliary);\n", + " } \n", + " apply {\n", + " CNOT(resource, auxiliary);\n", + " }\n", + " return Measure([PauliX], [auxiliary]);\n", + "}\n", + "\n", + "/// # Summary\n", + "/// Apply part 2 of RUS circuit and measure\n", + "/// resource qubit in Pauli X basis\n", + "operation ApplyAndMeasurePart2(resource : Qubit, target : Qubit) : Result {\n", + " T(target);\n", + " Z(target);\n", + " CNOT(target, resource);\n", + " T(resource);\n", + " return Measure([PauliX], [resource]);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "## 3. Setup the Experiment\n", + "We create three wrapper operations for the code above, each one increasting the number of allowed retries compared to the previous one, so we can see the percentage of resulting success when the repeat logic is allowed to execute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "# We declare the functions we will define in Q# as a callable so that Python recognizes the symbols,\n", + "# then define those functions calling code from the algorithm above.\n", + "TestRUSLimit0: any = None\n", + "TestRUSLimit1: any = None\n", + "TestRUSLimit2: any = None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "microsoft": { + "language": "qsharp" + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "vscode": { + "languageId": "qsharp" + } + }, + "outputs": [], + "source": [ + "%%qsharp\n", + "\n", + "/// # Summary\n", + "/// Test the RzArcTan2 algorithm using Pauli-X for preparation and measurement,\n", + "/// with no repeat retries.\n", + "operation TestRUSLimit0() : Result[] {\n", + " return AllocateQubitsAndApplyRzArcTan2(false, PauliX, PauliX, 0);\n", + "}\n", + "\n", + "/// # Summary\n", + "/// Test the RzArcTan2 algorithm using Pauli-X for preparation and measurement,\n", + "/// using a limit of 1 looping retries.\n", + "operation TestRUSLimit1() : Result[] {\n", + " return AllocateQubitsAndApplyRzArcTan2(false, PauliX, PauliX, 1);\n", + "}\n", + "\n", + "/// # Summary\n", + "/// Test the RzArcTan2 algorithm using Pauli-X for preparation and measurement,\n", + "/// using a limit of 2 looping retries.\n", + "operation TestRUSLimit2() : Result[] {\n", + " return AllocateQubitsAndApplyRzArcTan2(false, PauliX, PauliX, 2);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "## 4. Run the Experiment\n", + "We can now submit and wait on each of the above experiments, gathering the resulting histograms into repective variables." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "job0 = qsharp.azure.submit(TestRUSLimit0, jobName=\"RUS Limit 0\", shots=100)\n", + "while (job0.status not in [\"Succeeded\", \"Failed\", \"Cancelled\"]):\n", + " print(\".\", end=\"\", flush=True)\n", + " time.sleep(5)\n", + " job0 = qsharp.azure.status()\n", + "results0 = qsharp.azure.output()\n", + "results0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "job1 = qsharp.azure.submit(TestRUSLimit1, jobName=\"RUS Limit 1\", shots=100)\n", + "while (job1.status not in [\"Succeeded\", \"Failed\", \"Cancelled\"]):\n", + " print(\".\", end=\"\", flush=True)\n", + " time.sleep(5)\n", + " job1 = qsharp.azure.status()\n", + "results1 = qsharp.azure.output()\n", + "results1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "job2 = qsharp.azure.submit(TestRUSLimit2, jobName=\"RUS Limit 2\", shots=100)\n", + "while (job2.status not in [\"Succeeded\", \"Failed\", \"Cancelled\"]):\n", + " print(\".\", end=\"\", flush=True)\n", + " time.sleep(5)\n", + " job2 = qsharp.azure.status()\n", + "results2 = qsharp.azure.output()\n", + "results2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "## 5. Plot the Results\n", + "Finally, we plot the results. When run against the simulator, each successive experiment should show increasing probability of success, indicating that the more allowed runs of RUS further refine the effectiveness of the program. Note that when run with the syntax checker target that returns all zeros, the graph should show a 1.0 result for every repeat limit value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "outputs_hidden": false, + "source_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "\n", + "success = \"[0, 0, 0]\"\n", + "xticks = [0, 1, 2]\n", + "pyplot.title(\"Probability Success\")\n", + "pyplot.bar(xticks, [results0[success], results1[success], results2[success]])\n", + "pyplot.xticks([0, 1, 2])\n", + "pyplot.xlabel(\"Repeat Limit\")\n", + "pyplot.ylabel(\"Probability\")\n", + "\n", + "\n", + "pyplot.show()" + ] + } + ], + "metadata": { + "kernel_info": { + "name": "python3" + }, + "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.9.18" + }, + "nteract": { + "version": "nteract-front-end@1.0.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}