diff --git a/docs/tutorials/advanced-techniques-for-qaoa.ipynb b/docs/tutorials/advanced-techniques-for-qaoa.ipynb index e26640d3b74..6c11f55ff28 100644 --- a/docs/tutorials/advanced-techniques-for-qaoa.ipynb +++ b/docs/tutorials/advanced-techniques-for-qaoa.ipynb @@ -9,15 +9,33 @@ "title: Advanced techniques for QAOA\n", "description: This notebook introduces advanced techniques to improve the performance of the Quantum Approximate Optimization Algorithm (QAOA) with a large number of qubits.\n", "---\n", - "\n", - "\n", - "{/* cspell:ignore setminus pysat lbrack IEICE frameon */}\n", + "{/* cspell:ignore pysat, lbrack, frameon, IEICE */}\n", "\n", "# Advanced techniques for QAOA\n", "\n", "*Usage estimate: 3 minutes on a Heron r2 processor (NOTE: This is an estimate only. Your runtime may vary.)*" ] }, + { + "cell_type": "markdown", + "id": "1a6bbeef-966f-45e1-89aa-e964ef891eeb", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "Users should be familiar with the basics of the Quantum Approximate Optimization Algorithm (QAOA). See the following resources for an introduction to QAOA:\n", + "\n", + "* The [Solve utility-scale quantum optimization problems](/docs/tutorials/quantum-approximate-optimization-algorithm) tutorial\n", + "* The [Utility-scale QAOA](/learning/courses/quantum-computing-in-practice/utility-scale-qaoa#utility-scale-qaoa) lesson (part of the [Quantum computing in practice](/learning/courses/quantum-computing-in-practice) course) in IBM Quantum® Learning\n", + "\n", + "## Learning outcomes\n", + "\n", + "After going through this tutorial, users should be able to do the following:\n", + "* Use advanced techniques that help improve QAOA transpilation and provide a means for improving QAOA performance\n", + "\n", + "For a detailed walkthrough of the contents of this tutorial, see this [Qiskit video](https://youtu.be/rBfK-l-qSNk?si=PC28gFAdu4JYSYdk)." + ] + }, { "cell_type": "markdown", "id": "ea97567d-810f-4cca-8edf-a47d70ea870a", @@ -25,14 +43,16 @@ "source": [ "## Background\n", "\n", - "This notebook introduces advanced techniques to improve the performance of the **Quantum Approximate Optimization Algorithm (QAOA)** with a large number of qubits.\n", - "See the [Solve utility-scale quantum optimization problems](/docs/tutorials/quantum-approximate-optimization-algorithm) tutorial for an introduction to QAOA.\n", + "This tutorial introduces two advanced techniques to improve the performance of the **Quantum Approximate Optimization Algorithm (QAOA)** at a large number of qubits.\n", + "\n", "\n", "The advanced techniques in this notebook include:\n", "\n", - "* **SWAP strategy with SAT initial mapping**: This is a specifically designed transpiler pass for QAOA that uses a SWAP strategy and a SAT solver together to improve the selection of which physical qubits on the QPU to use. The SWAP strategy exploits the commutativity of the QAOA operators to reorder gates so that layers of SWAP gates can be simultaneously executed, thus reducing the depth of the circuit [\\[1\\]](#references). The SAT solver is used to find an initial mapping that minimizes the number of SWAP operations needed to map the qubits in the circuit to the physical qubits on the device [\\[2\\]](#references) .\n", - "* **CVaR cost function**: Typically the expected value of the cost Hamiltonian is used as the cost function for QAOA, but as was shown in [\\[3\\]](#references) , focusing on the tail of the distribution, rather than the expected value, can improve the performance of QAOA for combinatorial optimization problems. The CVaR accomplishes this. For a given set of shots with corresponding objective values of the considered optimization problem, the Conditional Value at Risk (CVaR) with confidence level $\\alpha \\in [0, 1]$ is defined as the average of the $\\alpha$ best shots [\\[3\\]](#references).\n", - "Thus, $\\alpha = 1$ corresponds to the standard expected value, while $\\alpha=0$ corresponds to the minimum of the given shots, and $\\alpha \\in (0, 1)$ is a tradeoff between focusing on better shots, while still applying some averaging to smooth out the optimization landscape. Additionally, the CVaR can be used as an error mitigation technique to improve the quality of the objective value estimation [\\[4\\]](#references)." + "* **SWAP strategy with SAT initial mapping**: This is a specifically designed transpiler pass for QAOA that uses a SWAP strategy and a SAT solver together to improve the selection of which physical qubits on the QPU to use. The SWAP strategy exploits the commutativity of the QAOA operators to reorder gates so that layers of SWAP gates can be simultaneously executed, thus reducing the depth of the circuit [\\[1\\]](#references). The SAT solver is used to find an initial mapping that minimizes the number of SWAP operations needed to map the qubits in the circuit to the physical qubits on the device [\\[2\\]](#references).\n", + "* **CVaR cost function**: Typically the expected value of the cost Hamiltonian is used as the cost function for QAOA, but as was shown in [\\[3\\]](#references), focusing on the tail of the distribution, rather than the expected value, can improve the performance of QAOA for combinatorial optimization problems. The CVaR accomplishes this. For a given set of shots with corresponding objective values of the considered optimization problem, the Conditional Value at Risk (CVaR) with confidence level $\\alpha \\in [0, 1]$ is defined as the average of the $\\alpha$ best shots [\\[3\\]](#references).\n", + "Thus, $\\alpha = 1$ corresponds to the standard expected value, while $\\alpha=0$ corresponds to the minimum of the given shots, and $\\alpha \\in (0, 1)$ is a tradeoff between focusing on better shots, while still applying some averaging to smooth out the optimization landscape. Additionally, the CVaR can be used as an error mitigation technique to improve the quality of the objective value estimation [\\[4\\]](#references).\n", + "\n", + "By the end of this tutorial, you should be able to use these techniques to get the best results from running QAOA for your optimization problems." ] }, { @@ -105,14 +125,34 @@ "In this example, we use a graph with 100 nodes that is based on a hardware coupling map." ] }, + { + "cell_type": "markdown", + "id": "663d13f5-a5f7-4b67-a89b-26deb41224ec", + "metadata": {}, + "source": [ + "## Small-scale simulator example\n", + "\n", + "Since the goal of this tutorial is to show how QAOA performs at scales beyond what a simulator can handle, we will forgo this step.\n", + "\n", + "f you would like to try a simulator-based QAOA workflow, try out the [Quantum approximate optimization algorithm](/docs/tutorials/quantum-approximate-optimization-algorithm) tutorial." + ] + }, + { + "cell_type": "markdown", + "id": "6ca05a7c-5fde-4485-a3ad-c0ba02844e50", + "metadata": {}, + "source": [ + "## Large-scale hardware example" + ] + }, { "cell_type": "markdown", "id": "b825afbf-10fd-4926-bd65-05272044f107", "metadata": {}, "source": [ - "## Step 1: Map classical inputs to a quantum problem\n", + "### Step 1: Map classical inputs to a quantum problem\n", "\n", - "### Graph → Hamiltonian\n", + "#### Graph → Hamiltonian\n", "\n", "First, map the problem onto a quantum circuit that is suited for the QAOA. Details on this process can be found in the [introductory QAOA tutorial](/docs/tutorials/quantum-approximate-optimization-algorithm)." ] @@ -142,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": null, "id": "e04ce982-8294-4ebe-8dcc-f74205938800", "metadata": {}, "outputs": [ @@ -158,12 +198,15 @@ } ], "source": [ + "# Check if the coupling map is symmetric. We will add a conditional below\n", + "# to avoid over-counting edges for symmetric/bi-directional coupling maps.\n", + "\n", "backend.coupling_map.is_symmetric" ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": null, "id": "8b864d32-9483-45ea-831f-60488e330adb", "metadata": {}, "outputs": [ @@ -186,7 +229,12 @@ "\n", "for edge in backend.coupling_map:\n", " if (edge[0] < n) and (edge[1] < n):\n", - " if (edge[1], edge[0], w) not in elist:\n", + " # Conditional to avoid over-counting edges\n", + " if (\n", + " edge[1],\n", + " edge[0],\n", + " w,\n", + " ) not in elist:\n", " elist.append((edge[0], edge[1], w))\n", "\n", "graph_100.add_edges_from(elist)\n", @@ -252,9 +300,9 @@ "id": "4e576068-53e7-4a06-a83b-87e95de141e9", "metadata": {}, "source": [ - "## Step 2: Optimize problem for quantum hardware execution\n", + "### Step 2: Optimize problem for quantum hardware execution\n", "\n", - "### SWAP strategy with the SAT initial mapping\n", + "#### SWAP strategy with the SAT initial mapping\n", "\n", "We will demonstrate how to build and optimize QAOA circuits using the **SWAP strategy with SAT initial mapping**, a specifically designed transpiler pass for QAOA applied to quadratic problems.\n", "\n", @@ -299,7 +347,7 @@ "source": [ "#### Remap the graph using a SAT mapper\n", "\n", - "Even when a circuit consists of commuting gates (this is the case for the QAOA circuit, but also for Trotterized simulations of Ising Hamiltonians), finding a good initial mapping is a challenging task. The SAT-based approach presented in [\\[2\\]](#references) enables the discovery of effective initial mappings for circuits with commuting gates, resulting in a significant reduction in the number of required SWAP layers. This approach has been demonstrated to scale to up to *500 qubits*, as illustrated in the paper.\n", + "Even when a circuit consists of commuting gates (this is the case for the QAOA circuit, but also for Trotterized simulations of Ising Hamiltonians), finding a good initial mapping is a challenging task. When we use the SAT-based approach presented in [\\[2\\]](#references), we can discover effective initial mappings for circuits with commuting gates, resulting in a significant reduction in the number of required SWAP layers. This approach has been demonstrated to scale to up to *500 qubits*, as illustrated in the paper.\n", "\n", "The following code demonstrates how to use the `SATMapper` from Matsuo et al. to remap the graph. This process allows the problem to be mapped to a more optimal initial state for a specified SWAP strategy, resulting in a significant reduction in the number of SWAP layers required to execute the circuit.\n", "\n", @@ -608,7 +656,7 @@ "We only want to apply the SWAP strategies to the cost operator layer, so we start by creating the isolated block that we will later transform and append to the final QAOA circuit.\n", "\n", "For this, we can use the [`QAOAAnsatz`](/docs/api/qiskit/qiskit.circuit.library.QAOAAnsatz) class from Qiskit. We input an empty circuit to the `initial_state` and `mixer_operator` fields to make sure we are building an isolated cost operator layer.\n", - "We also define the edge_coloring map so that RZZ gates are positioned next to SWAP gates. This strategic placement allows us to exploit CX cancellations, optimizing the circuit for better performance.\n", + "We also define the `edge_coloring` map so that RZZ gates are positioned next to SWAP gates. This strategic placement allows us to exploit CX cancellations, optimizing the circuit for better performance.\n", "This process is executed within the `create_qaoa_swap_circuit` function." ] }, @@ -813,14 +861,6 @@ "id": "82ae28b3-85eb-4487-8100-1e622e93cccf", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/mirko/Workspace/documentation/.venv/lib/python3.13/site-packages/qiskit/circuit/quantumcircuit.py:4625: UserWarning: Trying to add QuantumRegister to a QuantumCircuit having a layout\n", - " circ.add_register(qreg)\n" - ] - }, { "data": { "text/plain": [ @@ -847,11 +887,9 @@ "id": "e2afd1a7-0980-433b-a3a8-303d7e7718b1", "metadata": {}, "source": [ - "## Step 3: Execute using Qiskit primitives\n", + "## Step 3: Execute using Qiskit Runtime primitives\n", "\n", - "### Define a CVaR cost function\n", - "\n", - "This example shows how to use the Conditional Value at Risk (CVaR) cost function introduced in [\\[3\\]](#references) within the variational quantum optimization algorithms.\n", + "Let's now prepare for hardware execution. Our first step will be to define a Conditional Value at Risk (CVaR) cost function, which was introduced in [\\[3\\]](#references) for use within the paradigm of variational quantum optimization algorithms.\n", "\n", "The CVaR of a random variable $X$ for a confidence level $α ∈ (0, 1]$ is defined as\n", "$CVaR_{\\alpha}(X) = \\mathbb{E} \\lbrack X | X \\leq F_X^{-1}(\\alpha) \\rbrack$\n", @@ -1026,7 +1064,7 @@ "id": "63fa2ab4-5354-4022-ab46-e9bbf73870de", "metadata": {}, "source": [ - "The CVaR can be used as an error mitigation technique as previously discussed [\\[4\\]](#references). In this example, we determine $\\alpha$ and the number of shots according to the circuit's error rate." + "The CVaR can be used as an error mitigation technique as previously discussed [\\[4\\]](#references). In this example, we determine $\\alpha$ and the number of shots according to the [error per layered gate](/docs/guides/qpu-information#2q-error-layered) (EPLG) associated with the circuit." ] }, { @@ -1073,7 +1111,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": null, "id": "382e8acd-d0d0-4302-99aa-b64e5dd31e17", "metadata": {}, "outputs": [ @@ -1127,6 +1165,9 @@ " sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n", " sampler.options.twirling.enable_gates = True\n", " sampler.options.twirling.enable_measure = True\n", + " sampler.options.environment.job_tags = [\n", + " \"TUT_AQAOA\"\n", + " ] # add tag for your job execution\n", "\n", " result = minimize(\n", " qaoa_sampler_cost_fun,\n", @@ -1148,7 +1189,9 @@ "id": "1d190fa4-3bbe-412a-b296-6dddd3ad2b12", "metadata": {}, "source": [ - "## Step 4: Post-process and return result in desired classical format" + "## Step 4: Post-process and return result in desired classical format\n", + "\n", + "Let's now visualize our results and then post-process them to find the value of the cut." ] }, { @@ -1190,12 +1233,12 @@ "id": "38aadfcb-aec9-4dbb-a9d3-319239eae196", "metadata": {}, "source": [ - "The following retrieves the best solution from the sampled bitstrings" + "The following retrieves the best solution from the sampled bitstrings:" ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": null, "id": "7e8af29e-c99b-41f2-b6dd-2be471e1af21", "metadata": {}, "outputs": [ @@ -1208,7 +1251,7 @@ } ], "source": [ - "# sort the result_dict[iter_counts]['evaluated'] by the CVaR value\n", + "# Sort the result_dict[iter_counts]['evaluated'] by the CVaR value\n", "sorted_result_dict = [\n", " (k, v)\n", " for k, v in sorted(\n", @@ -1229,10 +1272,10 @@ "Consider the Hamiltonian $H_C$ for the **max-cut** problem. Let each vertex of the graph be associated with a qubit in state $|0\\rangle$ or $|1\\rangle$, where the value denotes the set the vertex is in. The goal of the problem is to maximize the number of edges $(v_1, v_2)$ for which $v_1 = |0\\rangle$ and $v_2 = |1\\rangle$, or vice versa. If we associate the $Z$ operator with each qubit, where\n", "\n", "$$\n", - " Z|0\\rangle = |0\\rangle \\qquad Z|1\\rangle = -|1\\rangle\n", + " Z|0\\rangle = |0\\rangle \\qquad Z|1\\rangle = -|1\\rangle,\n", "$$\n", "\n", - "then an edge $(v_1, v_2)$ belongs to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = -1$; in other words, the qubits associated with $v_1$ and $v_2$ are different. Similarly, $(v_1, v_2)$ does not belong to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = 1$" + "then an edge $(v_1, v_2)$ belongs to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = -1$; in other words, the qubits associated with $v_1$ and $v_2$ are different. Similarly, $(v_1, v_2)$ does not belong to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = 1$." ] }, { @@ -1346,14 +1389,18 @@ }, { "cell_type": "markdown", - "id": "c048cd84-d6e8-4b8a-926e-a7f7724a86e2", + "id": "a6a5bbfe-a159-4dc1-9333-488737aff503", "metadata": {}, "source": [ - "## Tutorial survey\n", + "## Next steps\n", + "\n", + "\n", "\n", - "Please take one minute to provide feedback on this tutorial. Your insights will help us improve our content offerings and user experience.\n", + "If you found this work interesting, you might be interested in the following material:\n", "\n", - "[Link to survey](https://your.feedback.ibm.com/jfe/form/SV_cZwpScxyXVDpIeq)" + "* [The intractable decathlon](https://arxiv.org/pdf/2504.03832): a listing of 10 optimization problems that are difficult for classical optimization algorithms, and which may be good use cases to test the techniques introduced in this tutorial.\n", + "* [A repo of best practices for quantum optimization](https://github.com/qiskit-community/qopt-best-practices) to further improve the results of your QAOA-based workflow.\n", + "" ] } ], diff --git a/docs/tutorials/quantum-approximate-optimization-algorithm.ipynb b/docs/tutorials/quantum-approximate-optimization-algorithm.ipynb index e1b6d1357a8..e0493c4dac3 100644 --- a/docs/tutorials/quantum-approximate-optimization-algorithm.ipynb +++ b/docs/tutorials/quantum-approximate-optimization-algorithm.ipynb @@ -2,29 +2,37 @@ "cells": [ { "cell_type": "markdown", - "id": "5d52815f", - "metadata": { - "tags": [ - "remove-cell" - ] - }, + "id": "bc51e7bf-e582-49ba-93f8-035624d56ccf", + "metadata": {}, "source": [ "---\n", "title: Quantum approximate optimization algorithm\n", - "description: Learn the basics of quantum computing, and how to use IBM Quantum services and QPUs to solve real-world problems.\n", + "description: Solve max-cut using QAOA with a Qiskit pattern at utility scale.\n", "---\n", "\n", + "{/* cspell:ignore frameon popcount fval */}\n", + "\n", + "# Quantum approximate optimization algorithm\n", "\n", - "{/* cspell:ignore rarr, QUBO, elist, fval, frameon */}" + "*Usage estimate: 22 minutes on a Heron r3 processor (NOTE: This is an estimate only. Your runtime might vary.)*" ] }, { "cell_type": "markdown", - "id": "bc51e7bf-e582-49ba-93f8-035624d56ccf", + "id": "learning-outcomes-prereqs", "metadata": {}, "source": [ - "# Quantum approximate optimization algorithm\n", - "*Usage estimate: 22 minutes on a Heron r3 processor (NOTE: This is an estimate only. Your runtime might vary.)*" + "## Learning outcomes\n", + "After completing this tutorial, you can expect to understand the following information:\n", + "- How to map a classical combinatorial optimization problem (max-cut) to a quantum Hamiltonian\n", + "- How to implement and run the Quantum Approximate Optimization Algorithm (QAOA) using Qiskit Runtime sessions\n", + "- How to scale a QAOA workflow from a small simulator example to utility-scale hardware execution\n", + "\n", + "## Prerequisites\n", + "It is recommended that you familiarize yourself with these topics:\n", + "- [Basics of quantum circuits](/learning/courses/basics-of-quantum-information)\n", + "- [Variational algorithms](/learning/courses/variational-algorithm-design)\n", + "- [QAOA in depth](/learning/courses/quantum-computing-in-practice/utility-scale-qaoa) — for a comprehensive treatment of the QAOA algorithm and applying it at utility scale" ] }, { @@ -33,12 +41,36 @@ "metadata": {}, "source": [ "## Background\n", - "This tutorial demonstrates how to implement the **Quantum Approximate Optimization Algorithm (QAOA)** – a hybrid (quantum-classical) iterative method – within the context of Qiskit patterns. You will first solve the **maximum-cut** (or **max-cut**) problem for a small graph and then learn how to execute it at utility scale. All the hardware executions in the tutorial should run within the time limit for the freely-accessible Open Plan.\n", "\n", + "The **Quantum Approximate Optimization Algorithm (QAOA)** is a hybrid quantum-classical iterative method for solving combinatorial optimization problems. In this tutorial, you will use QAOA to solve the **maximum-cut (max-cut)** problem — an NP-hard optimization problem with applications in clustering, network science, and statistical physics. Given a graph of nodes connected by edges, the goal is to partition the nodes into two sets such that the number of edges crossing the partition is maximized.\n", + "\n", + "![Illustration of a max-cut problem](/docs/images/tutorials/quantum-approximate-optimization-algorithm/maxcut-illustration.avif)\n", + "\n", + "### From classical optimization to quantum circuits\n", + "\n", + "Max-cut can be expressed as a classical binary optimization problem. Each node is assigned a binary variable $x_i \\in \\{0, 1\\}$ indicating which set it belongs to. The objective is to maximize the number of edges where the endpoints are in different sets:\n", + "\n", + "$$\n", + "\\max_{x \\in \\{0,1\\}^n} \\sum_{(i,j)} x_i + x_j - 2x_ix_j.\n", + "$$\n", + "\n", + "This is equivalently a **Quadratic Unconstrained Binary Optimization (QUBO)** problem of the form $\\min_x\\, x^T Q x$. Through a standard variable substitution ($x_i \\to (1 - Z_i)/2$), the QUBO can be rewritten as a **cost Hamiltonian** whose ground state encodes the optimal solution. In general, this Hamiltonian has both quadratic and linear terms:\n", + "\n", + "$$\n", + "H_C = \\sum_{ij} Q_{ij} \\, Z_i Z_j + \\sum_i b_i \\, Z_i.\n", + "$$\n", + "\n", + "For the unweighted max-cut problem considered here, the linear coefficients vanish ($b_i = 0$) and $Q_{ij} = 1$ for each edge, leaving the simpler form $H_C = \\sum_{(i,j) \\in E} Z_i Z_j$ that you will build in code below. The more general form above is what you would need to adapt this workflow to weighted graphs or other QUBO-expressible problems.\n", "\n", - "The max-cut problem is an optimization problem that is hard to solve (more specifically, it is an *NP-hard* problem) with a number of different applications in clustering, network science, and statistical physics. This tutorial considers a graph of nodes connected by edges, and aims to partition the nodes into two sets such that the number of edges traversed by this cut is maximized.\n", + "### How QAOA works\n", "\n", - "![Illustration of a max-cut problem](/docs/images/tutorials/quantum-approximate-optimization-algorithm/maxcut-illustration.avif)" + "QAOA prepares candidate solutions by applying alternating layers of two operators to an initial superposition state $H^{\\otimes n}|0\\rangle$: the **cost operator** $e^{-i\\gamma_k H_C}$ and a **mixer operator** $e^{-i\\beta_k H_m}$. The angles $\\gamma_k$ and $\\beta_k$ are optimized in a classical feedback loop; the quantum computer evaluates the cost function, and a classical optimizer updates the parameters until convergence. This iterative loop runs within a Qiskit Runtime **session**, which keeps the quantum device reserved across iterations for lower latency.\n", + "\n", + "![Circuit diagram with QAOA layers](/docs/images/tutorials/quantum-approximate-optimization-algorithm/circuit-diagram.svg)\n", + "\n", + "For a deeper treatment of QAOA theory, including the full QUBO-to-Hamiltonian derivation, see the [QAOA course module](/learning/courses/utility-scale-quantum-computing/variational-quantum-algorithms#1-introduction).\n", + "\n", + "In this tutorial you will first solve max-cut on a small five-node graph, then scale the same workflow to a 100-node utility-scale problem on real hardware. *Note on plan access:* This tutorial uses Qiskit Runtime [sessions](/docs/guides/execution-modes#session-mode), which are only available on the Premium Plan. If you are on the Open Plan, you cannot run this tutorial as written; instead, you will need to swap `Session` for [job mode](/docs/guides/execution-modes#job-mode) (as in, submit each iteration as an independent job rather than wrapping the optimization loop in with `Session(...)`). The workflow still runs, but each iteration incurs the full queue latency rather than reusing a reserved device. See [Overview of available plans](/docs/guides/plans-overview) for more information." ] }, { @@ -49,10 +81,11 @@ "## Requirements\n", "\n", "Before starting this tutorial, be sure you have the following installed:\n", - "- Qiskit SDK v1.0 or later, with [visualization](/docs/api/qiskit/visualization) support\n", - "- Qiskit Runtime v0.22 or later (`pip install qiskit-ibm-runtime`)\n", "\n", - "In addition, you will need access to an instance on [IBM Quantum Platform](/docs/guides/cloud-setup). Note that this tutorial cannot be executed on the Open Plan, because it runs workloads using [sessions](/docs/api/qiskit-ibm-runtime/session), which are only available with Premium Plan access." + "* Qiskit SDK v2.0 or later, with [visualization](/docs/api/qiskit/visualization) support\n", + "* Qiskit Runtime v0.22 or later (`pip install qiskit-ibm-runtime`)\n", + "\n", + "In addition, you will need access to an instance on [IBM Quantum® Platform](/docs/guides/cloud-setup)." ] }, { @@ -70,7 +103,6 @@ "metadata": {}, "outputs": [], "source": [ - "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import rustworkx as rx\n", "from rustworkx.visualization import mpl_draw as draw_graph\n", @@ -94,30 +126,9 @@ "id": "68fd0b4f-baa4-45dc-9f4c-d9cdff01a651", "metadata": {}, "source": [ - "## Part I. Small-scale QAOA\n", - "The first part of this tutorial uses a small-scale max-cut problem to illustrate the steps to solve an optimization problem using a quantum computer.\n", - "\n", - "To give some context before mapping this problem to a quantum algorithm, you can better understand how the max-cut problem becomes a classical combinatorial optimization problem by first considering the minimization of a function $f(x)$\n", - "\n", - "$$\n", - "\\min_{x\\in \\{0, 1\\}^n}f(x),\n", - "$$\n", + "## Small-scale example\n", "\n", - "where the input $x$ is a vector whose components correspond to each node of a graph. Then, constrain each of these components to be either $0$ or $1$ (which represent being included or not included in the cut). This small-scale example case uses a graph with $n=5$ nodes.\n", - "\n", - "You could write a function of a pair of nodes $i,j$ which indicates whether the corresponding edge $(i,j)$ is in the cut. For example, the function $x_i + x_j - 2 x_i x_j$ is 1 only if one of either $x_i$ or $x_j$ are 1 (which means that the edge is in the cut) and zero otherwise. The problem of maximizing the edges in the cut can be formulated as\n", - "\n", - "$$\n", - "\\max_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)} x_i + x_j - 2 x_i x_j,\n", - "$$\n", - "\n", - "which can be rewritten as a minimization of the form\n", - "\n", - "$$\n", - "\\min_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)} 2 x_i x_j - x_i - x_j.\n", - "$$\n", - "\n", - "The minimum of $f(x)$ in this case is when the number of edges traversed by the cut is maximal. As you can see, there is nothing relating to quantum computing yet. You need to reformulate this problem into something that a quantum computer can understand." + "This section walks through each step of the QAOA workflow on a small five-node max-cut instance. Despite the \"small-scale\" label, this example still runs on real IBM Quantum hardware — the code selects a backend with 127 or more qubits and executes the circuit there." ] }, { @@ -145,10 +156,10 @@ } ], "source": [ - "n = 5\n", + "n_small = 5\n", "\n", "graph = rx.PyGraph()\n", - "graph.add_nodes_from(np.arange(0, n, 1))\n", + "graph.add_nodes_from(np.arange(0, n_small, 1))\n", "edge_list = [\n", " (0, 1, 1.0),\n", " (0, 2, 1.0),\n", @@ -168,38 +179,7 @@ "source": [ "### Step 1: Map classical inputs to a quantum problem\n", "\n", - "The first step of the pattern is to map the classical problem (graph) into quantum **circuits** and **operators**. To do this, there are three main steps to take:\n", - "\n", - "1. Utilize a series of mathematical reformulations, to represent this problem using the Quadratic Unconstrained Binary Optimization (QUBO) problems notation.\n", - "2. Rewrite the optimization problem as a Hamiltonian for which the ground state corresponds to the solution which minimizes the cost function.\n", - "3. Create a quantum circuit which will prepare the ground state of this Hamiltonian via a process similar to quantum annealing.\n", - "\n", - "\n", - "\n", - "**Note:** In the QAOA methodology, you ultimately want to have an operator (**Hamiltonian**) that represents the **cost function** of our hybrid algorithm, as well as a parametrized circuit (**Ansatz**) that represents quantum states with candidate solutions to the problem. You can sample from these candidate states and then evaluate them using the cost function.\n", - "\n", - "\n", - "#### Graph → optimization problem\n", - "\n", - "The first step of the mapping is a notation change, The following expresses the problem in QUBO notation:\n", - "\n", - "$$\n", - "\\min_{x\\in \\{0, 1\\}^n}x^T Q x,\n", - "$$\n", - "\n", - "where $Q$ is a $n\\times n$ matrix of real numbers, $n$ corresponds to the number of nodes in your graph, $x$ is the vector of binary variables introduced above, and $x^T$ indicates the transpose of the vector $x$.\n", - "\n", - "```\n", - "Maximize\n", - " -2*x_0*x_1 - 2*x_0*x_2 - 2*x_0*x_4 - 2*x_1*x_2 - 2*x_2*x_3 - 2*x_3*x_4 + 3*x_0\n", - " + 2*x_1 + 3*x_2 + 2*x_3 + 2*x_4\n", - "\n", - "Subject to\n", - " No constraints\n", - "\n", - " Binary variables (5)\n", - " x_0 x_1 x_2 x_3 x_4\n", - "```" + "Map the classical graph into quantum **circuits** and **operators**. As described in the [Background](#background), for unweighted max-cut the cost Hamiltonian reduces to $H_C = \\sum_{(i,j) \\in E} Z_i Z_j$, and QAOA uses a parametrized ansatz circuit to prepare candidate ground states of $H_C$." ] }, { @@ -207,80 +187,14 @@ "id": "a5b9e551-38a1-4543-b9f1-caaefb0ef3a9", "metadata": {}, "source": [ - "### Optimization problem → Hamiltonian\n", - "\n", - "You can then reformulate the QUBO problem as a **Hamiltonian** (here, a matrix that represents the energy of a system):\n", - "\n", - "$$\n", - "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n", - "$$\n", - "\n", - "\n", - "\n", - "\n", - "To demonstrate how the QAOA problem can be rewritten in this way, first replace the binary variables $x_i$ to a new set of variables $z_i\\in\\{-1, 1\\}$ via\n", + "#### Build the cost Hamiltonian\n", "\n", - "$$\n", - "x_i = \\frac{1-z_i}{2}.\n", - "$$\n", - "\n", - "Here you can see that if $x_i$ is $0$, then $z_i$ must be $1$. When the $x_i$'s are substituted for the $z_i$'s in the optimization problem ($x^TQx$), an equivalent formulation can be obtained.\n", - "\n", - "$$\n", - "x^TQx=\\sum_{ij}Q_{ij}x_ix_j \\\\ =\\frac{1}{4}\\sum_{ij}Q_{ij}(1-z_i)(1-z_j) \\\\=\\frac{1}{4}\\sum_{ij}Q_{ij}z_iz_j-\\frac{1}{4}\\sum_{ij}(Q_{ij}+Q_{ji})z_i + \\frac{n^2}{4}.\n", - "$$\n", - "\n", - "Now if we define $b_i=-\\sum_{j}(Q_{ij}+Q_{ji})$, remove the prefactor, and the constant $n^2$ term, we arrive at the two equivalent formulations of the same optimization problem.\n", - "\n", - "$$\n", - "\\min_{x\\in\\{0,1\\}^n} x^TQx\\Longleftrightarrow \\min_{z\\in\\{-1,1\\}^n}z^TQz + b^Tz\n", - "$$\n", - "\n", - "Here, $b$ depends on $Q$. Note that to obtain $z^TQz + b^Tz$ we dropped the factor of 1/4 and a constant offset of $n^2$ which do not play a role in the optimization.\n", - "\n", - "\n", - "Now, to obtain a quantum formulation of the problem, promote the $z_i$ variables to a Pauli $Z$ matrix, such as a $2\\times 2$ matrix of the form\n", - "\n", - "$$\n", - "Z_i = \\begin{pmatrix}1 & 0 \\\\ 0 & -1\\end{pmatrix}.\n", - "$$\n", - "\n", - "When you substitute these matrices in the optimization problem above, you obtain the following Hamiltonian\n", - "\n", - "$$\n", - "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n", - "$$\n", - "\n", - "*Also recall that the $Z$ matrices are embedded in the quantum computer's computational space, that is, a Hilbert space of size $2^n\\times 2^n$. Therefore, you should understand terms such as $Z_iZ_j$ as the tensor product $Z_i\\otimes Z_j$ embedded in the $2^n\\times 2^n$ Hilbert space. For example, in a problem with five decision variables the term $Z_1Z_3$ is understood to mean $I\\otimes Z_3\\otimes I\\otimes Z_1\\otimes I$ where $I$ is the $2\\times 2$ identity matrix.*\n", - " \n", - "\n", - "\n", - "This Hamiltonian is called the **cost function Hamiltonian**. It has the property that its ground state corresponds to the solution that **minimizes the cost function $f(x)$**.\n", - "Therefore, to solve your optimization problem you now need to prepare the ground state of $H_C$ (or a state with a high overlap with it) on the quantum computer. Then, sampling from this state will, with a high probability, yield the solution to $min~f(x)$." - ] - }, - { - "cell_type": "markdown", - "id": "183f3603-1e5d-4839-a23c-31d17f5489a0", - "metadata": {}, - "source": [ - "Now let us consider the Hamiltonian $H_C$ for the **max-cut** problem. Let each vertex of the graph be associated with a qubit in state $|0\\rangle$ or $|1\\rangle$, where the value denotes the set the vertex is in. The goal of the problem is to maximize the number of edges $(v_1, v_2)$ for which $v_1 = |0\\rangle$ and $v_2 = |1\\rangle$, or vice-versa. If we associate the $Z$ operator with each qubit, where\n", - "\n", - "$$\n", - " Z|0\\rangle = |0\\rangle \\qquad Z|1\\rangle = -|1\\rangle\n", - "$$\n", - "\n", - "then an edge $(v_1, v_2)$ belongs to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = -1$; in other words, the qubits associated with $v_1$ and $v_2$ are different. Similarly, $(v_1, v_2)$ does not belong to the cut if the eigenvalue of $(Z_1|v_1\\rangle) \\cdot (Z_2|v_2\\rangle) = 1$. Note that, we do not care about the exact qubit state associated with each vertex, rather we care only whether they are same or not across an edge. The max-cut problem requires us to find an assignment of the qubits on the vertices such that the eigenvalue of the following Hamiltonian is minimized\n", - "$$\n", - " H_C = \\sum_{(i,j) \\in e} Q_{ij} \\cdot Z_i Z_j.\n", - "$$\n", - "\n", - "In other words, $b_i = 0$ for all $i$ in the max-cut problem. The value of $Q_{ij}$ denotes the weight of the edge. In this tutorial we consider an unweighted graph, that is, $Q_{ij} = 1.0$ for all $i, j$." + "Convert the graph edges into Pauli $Z_iZ_j$ terms to construct $H_C$ (see [Background](#background) for the derivation)." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "52d1ba92", "metadata": {}, "outputs": [ @@ -297,9 +211,11 @@ "def build_max_cut_paulis(\n", " graph: rx.PyGraph,\n", ") -> list[tuple[str, list[int], float]]:\n", - " \"\"\"Convert the graph to Pauli list.\n", + " \"\"\"Convert graph edges to a list of ZZ Pauli terms.\n", "\n", - " This function does the inverse of `build_max_cut_graph`\n", + " The returned list is in the sparse format expected by\n", + " ``SparsePauliOp.from_sparse_list``: each element is\n", + " ``(pauli_string, qubit_indices, coefficient)``.\n", " \"\"\"\n", " pauli_list = []\n", " for edge in list(graph.edge_list()):\n", @@ -309,7 +225,7 @@ "\n", "\n", "max_cut_paulis = build_max_cut_paulis(graph)\n", - "cost_hamiltonian = SparsePauliOp.from_sparse_list(max_cut_paulis, n)\n", + "cost_hamiltonian = SparsePauliOp.from_sparse_list(max_cut_paulis, n_small)\n", "print(\"Cost Function Hamiltonian:\", cost_hamiltonian)" ] }, @@ -318,7 +234,7 @@ "id": "33f71b0d-4a2a-4082-8c1a-ce9d2b769048", "metadata": {}, "source": [ - "#### Hamiltonian → quantum circuit" + "#### Build the QAOA ansatz circuit" ] }, { @@ -326,17 +242,7 @@ "id": "00431c46-30c2-40f9-99df-40baf8da98f6", "metadata": {}, "source": [ - "The Hamiltonian $H_C$ contains the quantum definition of your problem. Now you can create a quantum circuit that will help *sample* good solutions from the quantum computer. The QAOA is inspired by quantum annealing and applies alternating layers of operators in the quantum circuit.\n", - "\n", - "The general idea is to start in the ground state of a known system, $H^{\\otimes n}|0\\rangle$ above, and then steer the system into the ground state of the cost operator that you are interested in. This is done by applying the operators $\\exp\\{-i\\gamma_k H_C\\}$ and $\\exp\\{-i\\beta_k H_m\\}$ with angles $\\gamma_1,...,\\gamma_p$ and $\\beta_1,...,\\beta_p~$.\n", - "\n", - "\n", - "The quantum circuit that you generate is **parametrized** by $\\gamma_i$ and $\\beta_i$, so you can try out different values of $\\gamma_i$ and $\\beta_i$ and sample from the resulting state.\n", - "\n", - "![Circuit diagram with QAOA layers](/docs/images/tutorials/quantum-approximate-optimization-algorithm/circuit-diagram.svg)\n", - "\n", - "\n", - "In this case, you will try an example with one QAOA layer that contains two parameters: $\\gamma_1$ and $\\beta_1$." + "Use `QAOAAnsatz` to construct the parametrized QAOA circuit from the cost Hamiltonian. Here we use `reps=2` (two QAOA layers, four parameters: $\\beta_0, \\beta_1, \\gamma_0, \\gamma_1$)." ] }, { @@ -397,21 +303,7 @@ "id": "c08be444-e3ed-4178-a10b-414069b1b411", "metadata": {}, "source": [ - "The circuit above contains a series of abstractions useful to think about quantum algorithms, but not possible to run on the hardware. To be able to run on a QPU, the circuit needs to undergo a series of operations that make up the **transpilation** or **circuit optimization** step of the pattern.\n", - "\n", - "The Qiskit library offers a series of **transpilation passes** that cater to a wide range of circuit transformations. You need to make sure that your circuit is **optimized** for your purpose.\n", - "\n", - "Transpilation may involves several steps, such as:\n", - "\n", - "* **Initial mapping** of the qubits in the circuit (such as decision variables) to physical qubits on the device.\n", - "* **Unrolling** of the instructions in the quantum circuit to the hardware-native instructions that the backend understands.\n", - "* **Routing** of any qubits in the circuit that interact to physical qubits that are adjacent with one another.\n", - "* **Error suppression** by adding single-qubit gates to suppress noise with dynamical decoupling.\n", - "\n", - "\n", - "More information about transpilation is available in our [documentation](/docs/guides/transpile).\n", - "\n", - "The following code transforms and optimizes the abstract circuit into a format that is ready for execution on one of devices accessible through the cloud using the **Qiskit IBM Runtime service**." + "Transpile the abstract circuit into hardware-native instructions. This step handles qubit mapping, gate decomposition, routing, and error suppression. See the transpilation [documentation](/docs/guides/transpile) for more information." ] }, { @@ -424,7 +316,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n" + "\n" ] }, { @@ -445,7 +337,9 @@ ")\n", "print(backend)\n", "\n", - "# Create pass manager for transpilation\n", + "# Create pass manager for transpilation. Level 3 is the most aggressive\n", + "# preset: slower to transpile, but produces shorter circuits that are\n", + "# more robust to hardware noise.\n", "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n", "\n", "candidate_circuit = pm.run(circuit)\n", @@ -465,11 +359,9 @@ "id": "9b99ce67-f121-4244-b62a-536be38fea86", "metadata": {}, "source": [ - "In the QAOA workflow, the optimal QAOA parameters are found in an iterative optimization loop, which runs a series of circuit evaluations and uses a classical optimizer to find the optimal $\\beta_k$ and $\\gamma_k$ parameters. This execution loop is executed via the following steps:\n", + "The QAOA optimization loop runs inside a Qiskit Runtime [session](/docs/guides/execution-modes) to keep the device reserved across iterations. An Estimator evaluates $\\langle H_C \\rangle$ at each step, and a classical optimizer (COBYLA) updates the parameters until convergence.\n", "\n", - "1. Define the initial parameters\n", - "2. Instantiate a new `Session` containing the optimization loop and the primitive used to sample the circuit\n", - "3. Once an optimal set of parameters is found, execute the circuit a final time to obtain a final distribution which will be used in the post-process step." + "![Illustration showing the behavior of Single job, Batch, and Session runtime modes.](/docs/images/tutorials/quantum-approximate-optimization-algorithm/runtime-modes.avif)" ] }, { @@ -477,8 +369,7 @@ "id": "00b2b0f1-9bad-4ad3-b93e-5cbf40395dbf", "metadata": {}, "source": [ - "#### Define circuit with initial parameters\n", - "We start with arbitrary chosen parameters." + "Define initial parameters and run the optimization loop:" ] }, { @@ -488,34 +379,17 @@ "metadata": {}, "outputs": [], "source": [ + "# QAOA doesn't prescribe principled default angles — any bounded choice\n", + "# works as a warm start for problems this small. beta and gamma are\n", + "# periodic (beta in [0, pi] and gamma in [0, 2*pi] modulo the underlying\n", + "# Pauli-rotation periods), and pi/2 and pi are just midpoints of those\n", + "# ranges. For harder problems you would typically warm start from known\n", + "# good angles or transfer parameters from smaller instances.\n", "initial_gamma = np.pi\n", "initial_beta = np.pi / 2\n", "init_params = [initial_beta, initial_beta, initial_gamma, initial_gamma]" ] }, - { - "cell_type": "markdown", - "id": "b867f1b0-7196-4d34-9b28-e3fb1de8221c", - "metadata": {}, - "source": [ - "#### Define backend and execution primitive\n", - "Use the **Qiskit Runtime primitives** to interact with IBM® backends. The two primitives are Sampler and Estimator, and the choice of primitive depends on what type of measurement you want to run on the quantum computer. For the minimization of $H_C$, use Estimator since the measurement of the cost function is simply the expectation value of $\\langle H_C \\rangle$." - ] - }, - { - "cell_type": "markdown", - "id": "ebe288e7-ce87-4b6d-949c-041db09c7c47", - "metadata": {}, - "source": [ - "#### Run\n", - "\n", - "The primitives offer a variety of [execution modes](/docs/guides/execution-modes) to schedule workloads on quantum devices, and a QAOA workflow runs iteratively in a session.\n", - "\n", - "![Illustration showing the behavior of Single job, Batch, and Session runtime modes.](/docs/images/tutorials/quantum-approximate-optimization-algorithm/runtime-modes.avif)\n", - "\n", - "You can plug the Sampler-based cost function into the SciPy minimizing routine to find the optimal parameters." - ] - }, { "cell_type": "code", "execution_count": 8, @@ -552,9 +426,9 @@ " message: Return from COBYLA because the trust region radius reaches its lower bound.\n", " success: True\n", " status: 0\n", - " fun: -1.6295230263157894\n", - " x: [ 1.530e+00 1.439e+00 4.071e+00 4.434e+00]\n", - " nfev: 26\n", + " fun: -2.0402211719947774\n", + " x: [ 3.041e+00 1.212e+00 2.081e+00 4.471e+00]\n", + " nfev: 36\n", " maxcv: 0.0\n" ] } @@ -571,6 +445,7 @@ " estimator.options.dynamical_decoupling.sequence_type = \"XY4\"\n", " estimator.options.twirling.enable_gates = True\n", " estimator.options.twirling.num_randomizations = \"auto\"\n", + " estimator.options.environment.job_tags = [\"TUT_QAOA\"]\n", "\n", " result = minimize(\n", " cost_func_estimator,\n", @@ -587,7 +462,9 @@ "id": "01d6b81c", "metadata": {}, "source": [ - "The optimizer was able to reduce the cost and find better parameters for the circuit." + "The optimizer was able to reduce the cost and find better parameters for the circuit.\n", + "\n", + "A smoothly decreasing curve that plateaus is the signature of convergence. A noisy, non-monotonic curve usually indicates that something upstream needs attention; common causes are too few shots per evaluation (high estimator variance), poor initial parameters, or a circuit whose depth is dominated by hardware noise. COBYLA is derivative-free and fairly robust to moderate noise, but when the noise swamps the actual cost improvements per step, its linear-approximation model can no longer tell real descent from random jitter and the optimizer wanders." ] }, { @@ -619,9 +496,7 @@ "id": "1f9c8a9c", "metadata": {}, "source": [ - "Once you have found the optimal parameters for the circuit, you can assign these parameters and sample the final distribution obtained with the optimized parameters. Here is where the *Sampler* primitive should be used since it is the probability distribution of bitstring measurements which correspond to the optimal cut of the graph.\n", - "\n", - "**Note:** This means preparing a quantum state $\\psi$ in the computer and then measuring it. A measurement will collapse the state into a single computational basis state - for example, `010101110000...` - which corresponds to a candidate solution $x$ to our initial optimization problem ($\\max f(x)$ or $\\min f(x)$ depending on the task)." + "Assign the optimized parameters and sample the final distribution using the Sampler primitive." ] }, { @@ -656,7 +531,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{28: 0.0328, 11: 0.0343, 2: 0.0296, 25: 0.0308, 16: 0.0303, 27: 0.0302, 13: 0.0323, 7: 0.0312, 4: 0.0296, 9: 0.0295, 26: 0.0321, 30: 0.031, 23: 0.0324, 31: 0.0303, 21: 0.0335, 15: 0.0317, 12: 0.0309, 29: 0.0297, 3: 0.0313, 5: 0.0312, 6: 0.0274, 10: 0.0329, 22: 0.0353, 0: 0.0315, 20: 0.0326, 8: 0.0322, 14: 0.0306, 17: 0.0295, 18: 0.0279, 1: 0.0325, 24: 0.0334, 19: 0.0295}\n" + "{18: 0.039, 5: 0.0665, 20: 0.0973, 29: 0.0063, 9: 0.0899, 13: 0.0379, 2: 0.0047, 1: 0.0153, 11: 0.0932, 14: 0.0327, 12: 0.0314, 25: 0.0193, 21: 0.0398, 6: 0.0224, 4: 0.0197, 10: 0.0387, 3: 0.0181, 26: 0.07, 17: 0.0327, 19: 0.0332, 22: 0.0914, 24: 0.007, 0: 0.0033, 8: 0.0066, 30: 0.0158, 28: 0.0169, 27: 0.0222, 16: 0.0073, 7: 0.0057, 23: 0.0062, 15: 0.0054, 31: 0.0041}\n" ] } ], @@ -671,6 +546,8 @@ "sampler.options.twirling.enable_gates = True\n", "sampler.options.twirling.num_randomizations = \"auto\"\n", "\n", + "sampler.options.environment.job_tags = [\"TUT_QAOA\"]\n", + "\n", "pub = (optimized_circuit,)\n", "job = sampler.run([pub], shots=int(1e4))\n", "counts_int = job.result()[0].data.meas.get_int_counts()\n", @@ -688,7 +565,7 @@ "source": [ "### Step 4: Post-process and return result in desired classical format\n", "\n", - "The post-processing step interprets the sampling output to return a solution for your original problem. In this case, you are interested in the bitstring with the highest probability as this determines the optimal cut. The symmetries in the problem allow for four possible solutions, and the sampling process will return one of them with a slightly higher probability, but you can see in the plotted distribution below that four of the bitstrings are distinctively more likely than the rest." + "Extract the most likely bitstring from the sampled distribution. This represents the best cut found by QAOA." ] }, { @@ -701,7 +578,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Result bitstring: [0, 1, 1, 0, 1]\n" + "Result bitstring: [0, 0, 1, 0, 1]\n" ] } ], @@ -738,7 +615,7 @@ } ], "source": [ - "matplotlib.rcParams.update({\"font.size\": 10})\n", + "plt.rcParams.update({\"font.size\": 10})\n", "final_bits = final_distribution_bin\n", "values = np.abs(list(final_bits.values()))\n", "top_4_values = sorted(values, reverse=True)[:4]\n", @@ -764,7 +641,7 @@ "source": [ "#### Visualize best cut\n", "\n", - "From the optimal bit string, you can then visualize this cut on the original graph." + "From the optimal bitstring, you can then visualize this cut on the original graph." ] }, { @@ -801,7 +678,7 @@ "id": "2119575f-f3cf-45bc-ae2b-93c046391eb6", "metadata": {}, "source": [ - "And calculate the value of the cut:" + "Now, calculate the value of the cut:" ] }, { @@ -835,200 +712,209 @@ }, { "cell_type": "markdown", - "id": "2e2a89de-cef3-46ea-b201-cf931b65dfea", + "id": "76a7241e", "metadata": {}, "source": [ - "## Part II. Scale it up!\n", - "\n", - "You have access to many devices with over 100 qubits on IBM Quantum® Platform. Select one on which to solve max-cut on a 100-node weighted graph. This is a \"utility-scale\" problem. The steps to build the workflow are followed the same as above, but with a much larger graph." + "For a graph this small, the true optimum is easy to brute-force, so you can double-check the results by comparing the QAOA result against the exact answer." ] }, { "cell_type": "code", "execution_count": 17, - "id": "590fe2ce", + "id": "0a3b5267", "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "Classical optimum (brute force): 5\n", + "QAOA cut value: 5\n" + ] } ], "source": [ - "n = 100 # Number of nodes in graph\n", - "graph_100 = rx.PyGraph()\n", - "graph_100.add_nodes_from(np.arange(0, n, 1))\n", - "elist = []\n", - "for edge in backend.coupling_map:\n", - " if edge[0] < n and edge[1] < n:\n", - " elist.append((edge[0], edge[1], 1.0))\n", - "graph_100.add_edges_from(elist)\n", - "draw_graph(graph_100, node_size=200, with_labels=True, width=1)" + "# Classical baseline: enumerate all 2**n_small bitstrings and take the best cut.\n", + "def brute_force_max_cut(graph: rx.PyGraph) -> tuple[int, list[int]]:\n", + " n = len(list(graph.nodes()))\n", + " best_cut = -1\n", + " best_x: list[int] = []\n", + " for i in range(2**n):\n", + " x = [(i >> k) & 1 for k in range(n)]\n", + " cut = evaluate_sample(x, graph)\n", + " if cut > best_cut:\n", + " best_cut = int(cut)\n", + " best_x = x\n", + " return best_cut, best_x\n", + "\n", + "\n", + "classical_best, classical_x = brute_force_max_cut(graph)\n", + "print(f\"Classical optimum (brute force): {classical_best}\")\n", + "print(f\"QAOA cut value: {cut_value}\")" ] }, { "cell_type": "markdown", - "id": "31bb3bc1-f19e-4553-9e93-a89e92ea5469", + "id": "large-scale-header", "metadata": {}, "source": [ - "### Step 1: Map classical inputs to a quantum problem" + "## Large-scale hardware example\n", + "\n", + "You have access to many devices with over 100 qubits on IBM Quantum Platform. Select one on which to solve max-cut on a 100-node weighted graph. This is a \"utility-scale\" problem. The workflow follows the same steps as above, applied to a much larger graph." ] }, { "cell_type": "markdown", - "id": "3dacef1f", + "id": "large-scale-steps-intro", "metadata": {}, "source": [ - "#### Graph → Hamiltonian\n", - "First, convert the graph you want to solve directly into a Hamiltonian that is suited for QAOA." + "### End-to-end workflow at utility scale\n", + "\n", + "All four steps are shown below, applied to the 100-node graph. The structure is the same as the small-scale walkthrough: map, transpile, execute, post-process — but with a larger problem and split across the four cells below for clarity." ] }, { "cell_type": "code", - "execution_count": 18, - "id": "a6bdceed", + "execution_count": null, + "id": "large-scale-helpers", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cost Function Hamiltonian: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n", - " coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", - " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])\n" - ] - } - ], + "outputs": [], "source": [ - "max_cut_paulis_100 = build_max_cut_paulis(graph_100)\n", + "# Precomputed parity lookup table: _PARITY[b] = +1 if popcount(b) is even, else -1.\n", + "# We use this to vectorize expectation-value evaluation across all Pauli terms.\n", + "_PARITY = np.array(\n", + " [-1 if bin(i).count(\"1\") % 2 else 1 for i in range(256)],\n", + " dtype=np.complex128,\n", + ")\n", + "\n", "\n", - "cost_hamiltonian_100 = SparsePauliOp.from_sparse_list(max_cut_paulis_100, 100)\n", - "print(\"Cost Function Hamiltonian:\", cost_hamiltonian_100)" + "def evaluate_sparse_pauli(state: int, observable: SparsePauliOp) -> complex:\n", + " \"\"\"Expectation value of a SparsePauliOp on a single computational-basis state.\n", + "\n", + " For a Z-only observable (which QAOA cost Hamiltonians are, after the\n", + " QUBO-to-Hamiltonian mapping), the eigenvalue of each Pauli term on a\n", + " computational-basis state is simply (-1)**popcount(z_mask AND state),\n", + " i.e., the parity of the bitwise-AND of the term's Z-support and the\n", + " measured bitstring.\n", + "\n", + " This routine packs the Z-support of every Pauli term into bytes, ANDs\n", + " them against the measured state in a single vectorized op, and looks up\n", + " the parity in _PARITY. For a 100-qubit / ~hundreds-of-terms Hamiltonian\n", + " over 10_000 samples, this is dramatically faster than calling\n", + " SparsePauliOp.expectation_value per sample.\n", + " \"\"\"\n", + " packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder=\"little\")\n", + " state_bytes = np.frombuffer(\n", + " state.to_bytes(packed_uint8.shape[1], \"little\"), dtype=np.uint8\n", + " )\n", + " reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1)\n", + " return np.sum(observable.coeffs * _PARITY[reduced])\n", + "\n", + "\n", + "def best_solution(samples, hamiltonian):\n", + " \"\"\"Return the sampled bitstring (as int) with the lowest Hamiltonian cost.\"\"\"\n", + " min_cost = float(\"inf\")\n", + " min_sol = None\n", + " for bit_str in samples.keys():\n", + " candidate_sol = int(bit_str)\n", + " fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real\n", + " if fval <= min_cost:\n", + " min_cost = fval\n", + " min_sol = candidate_sol\n", + " return min_sol\n", + "\n", + "\n", + "def _plot_cdf(objective_values: dict, ax, color):\n", + " x_vals = sorted(objective_values.keys(), reverse=True)\n", + " y_vals = np.cumsum([objective_values[x] for x in x_vals])\n", + " ax.plot(x_vals, y_vals, color=color)\n", + "\n", + "\n", + "def plot_cdf(dist, ax, title):\n", + " _plot_cdf(dist, ax, \"C1\")\n", + " ax.vlines(min(list(dist.keys())), 0, 1, \"C1\", linestyle=\"--\")\n", + " ax.set_title(title)\n", + " ax.set_xlabel(\"Objective function value\")\n", + " ax.set_ylabel(\"Cumulative distribution function\")\n", + " ax.grid(alpha=0.3)\n", + "\n", + "\n", + "def samples_to_objective_values(samples, hamiltonian):\n", + " \"\"\"Convert the samples to values of the objective function.\"\"\"\n", + " objective_values = defaultdict(float)\n", + " for bit_str, prob in samples.items():\n", + " candidate_sol = int(bit_str)\n", + " fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real\n", + " objective_values[fval] += prob\n", + " return objective_values" ] }, { "cell_type": "markdown", - "id": "ba1796e6", + "id": "4dc0c8ff", "metadata": {}, "source": [ - "#### Hamiltonian → quantum circuit" + "**Step 1**: Build the graph, cost Hamiltonian, and ansatz." ] }, { "cell_type": "code", "execution_count": 19, - "id": "9693adfc", + "id": "94190344", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "circuit_100 = QAOAAnsatz(cost_operator=cost_hamiltonian_100, reps=1)\n", - "circuit_100.measure_all()\n", + "# Step 1: build the 100-node graph, cost Hamiltonian, and QAOA ansatz.\n", + "n_large = 100\n", + "graph_100 = rx.PyGraph()\n", + "graph_100.add_nodes_from(np.arange(0, n_large, 1))\n", + "elist = []\n", + "for edge in backend.coupling_map:\n", + " if edge[0] < n_large and edge[1] < n_large:\n", + " elist.append((edge[0], edge[1], 1.0))\n", + "graph_100.add_edges_from(elist)\n", + "\n", + "max_cut_paulis_100 = build_max_cut_paulis(graph_100)\n", + "cost_hamiltonian_100 = SparsePauliOp.from_sparse_list(\n", + " max_cut_paulis_100, n_large\n", + ")\n", "\n", - "circuit_100.draw(\"mpl\", fold=False, scale=0.2, idle_wires=False)" + "circuit_100 = QAOAAnsatz(cost_operator=cost_hamiltonian_100, reps=1)\n", + "circuit_100.measure_all()" ] }, { "cell_type": "markdown", - "id": "cf31d488-d672-4c91-8a80-867273502396", + "id": "c91f0c16", "metadata": {}, "source": [ - "### Step 2: Optimize problem for quantum execution\n", - "To scale the circuit optimization step to utility-scale problems, you can take advantage of the high performance transpilation strategies introduced in Qiskit SDK v1.0. Other tools include the new transpiler service with [AI enhanced transpiler passes](/docs/guides/ai-transpiler-passes)." + "**Step 2**: Transpile for the selected hardware backend." ] }, { "cell_type": "code", "execution_count": 20, - "id": "3a14e7ad", + "id": "2b59da0e", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ + "# Step 2: transpile for hardware.\n", "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n", - "\n", - "candidate_circuit_100 = pm.run(circuit_100)\n", - "candidate_circuit_100.draw(\"mpl\", fold=False, scale=0.1, idle_wires=False)" - ] - }, - { - "cell_type": "markdown", - "id": "8a8e65f0-9089-4237-b833-6f99da859ce2", - "metadata": {}, - "source": [ - "### Step 3: Execute using Qiskit primitives\n", - "\n", - "To run QAOA, you must know the optimal parameters $\\gamma_k$ and $\\beta_k$ to put in the variational circuit. Optimize these parameters by running an optimization loop on the device. The cell submits jobs until the cost function value has converged and the optimal parameters for $\\gamma_k$ and $\\beta_k$ are determined." - ] - }, - { - "cell_type": "markdown", - "id": "5e11ce39-a046-4f65-a8e6-bc9ca123eb9a", - "metadata": {}, - "source": [ - "#### Find candidate solution by running the optimization on the device" + "candidate_circuit_100 = pm.run(circuit_100)" ] }, { "cell_type": "markdown", - "id": "7ac4b5df", + "id": "eb2c4d66", "metadata": {}, "source": [ - "First, run the optimization loop for the circuit parameters on a device." + "**Step 3**: Run the QAOA optimization loop inside a session, then sample." ] }, { "cell_type": "code", "execution_count": 21, - "id": "9521a963", + "id": "e5aceab3", "metadata": {}, "outputs": [ { @@ -1038,23 +924,23 @@ " message: Return from COBYLA because the trust region radius reaches its lower bound.\n", " success: True\n", " status: 0\n", - " fun: -3.9939191365979383\n", - " x: [ 1.571e+00 3.142e+00]\n", - " nfev: 29\n", + " fun: -17.172689238986344\n", + " x: [ 2.574e+00 4.166e+00]\n", + " nfev: 28\n", " maxcv: 0.0\n" ] } ], "source": [ + "# Step 3: run the QAOA optimization loop on the device, then sample the\n", + "# final distribution with the optimized parameters.\n", "initial_gamma = np.pi\n", "initial_beta = np.pi / 2\n", "init_params = [initial_beta, initial_gamma]\n", "\n", "objective_func_vals = [] # Global variable\n", "with Session(backend=backend) as session:\n", - " # If using qiskit-ibm-runtime<0.24.0, change `mode=` to `session=`\n", " estimator = Estimator(mode=session)\n", - "\n", " estimator.options.default_shots = 1000\n", "\n", " # Set simple error suppression/mitigation options\n", @@ -1062,6 +948,7 @@ " estimator.options.dynamical_decoupling.sequence_type = \"XY4\"\n", " estimator.options.twirling.enable_gates = True\n", " estimator.options.twirling.num_randomizations = \"auto\"\n", + " estimator.options.environment.job_tags = [\"TUT_QAOA\"]\n", "\n", " result = minimize(\n", " cost_func_estimator,\n", @@ -1069,55 +956,11 @@ " args=(candidate_circuit_100, cost_hamiltonian_100, estimator),\n", " method=\"COBYLA\",\n", " )\n", - " print(result)" - ] - }, - { - "cell_type": "markdown", - "id": "1ec8bdc0-48ed-42e2-a8f2-55d41432b383", - "metadata": {}, - "source": [ - "Once the optimal parameters from running QAOA on the device have been found, assign the parameters to the circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "1c432c2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ + " print(result)\n", + "\n", + "# Assign optimal parameters and sample the final distribution.\n", "optimized_circuit_100 = candidate_circuit_100.assign_parameters(result.x)\n", - "optimized_circuit_100.draw(\"mpl\", fold=False, idle_wires=False)" - ] - }, - { - "cell_type": "markdown", - "id": "f3a9a3be-60db-41f0-9058-451c1f41a8a7", - "metadata": {}, - "source": [ - "Finally, execute the circuit with the optimal parameters to sample from the corresponding distribution." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "a5cc531b", - "metadata": {}, - "outputs": [], - "source": [ - "# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `backend=`\n", + "\n", "sampler = Sampler(mode=backend)\n", "sampler.options.default_shots = 10000\n", "\n", @@ -1127,12 +970,13 @@ "sampler.options.twirling.enable_gates = True\n", "sampler.options.twirling.num_randomizations = \"auto\"\n", "\n", + "# Add a unique tag to the job execution\n", + "sampler.options.environment.job_tags = [\"TUT_QAOA\"]\n", "\n", "pub = (optimized_circuit_100,)\n", "job = sampler.run([pub], shots=int(1e4))\n", "\n", "counts_int = job.result()[0].data.meas.get_int_counts()\n", - "counts_bin = job.result()[0].data.meas.get_counts()\n", "shots = sum(counts_int.values())\n", "final_distribution_100_int = {\n", " key: val / shots for key, val in counts_int.items()\n", @@ -1141,239 +985,75 @@ }, { "cell_type": "markdown", - "id": "7a8f4463", - "metadata": {}, - "source": [ - "Check that the cost minimized in the optimization loop has converged to a certain value." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "0fda3611", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.figure(figsize=(12, 6))\n", - "plt.plot(objective_func_vals)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"Cost\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "c6b01763", - "metadata": {}, - "source": [ - "### Step 4: Post-process and return result in desired classical format" - ] - }, - { - "cell_type": "markdown", - "id": "da37beca", + "id": "7f0c5980", "metadata": {}, "source": [ - "Given that the likelihood of each solution is low, extract the solution that corresponds to the lowest cost." + "**Step 4**: Post-process the sampled distribution to extract the best cut." ] }, { "cell_type": "code", - "execution_count": 25, - "id": "080e93a9", + "execution_count": 22, + "id": "010571f7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Result bitstring: [1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1]\n" + "Result bitstring: [1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0]\n", + "The value of the cut is: 156\n" ] } ], "source": [ - "_PARITY = np.array(\n", - " [-1 if bin(i).count(\"1\") % 2 else 1 for i in range(256)],\n", - " dtype=np.complex128,\n", - ")\n", - "\n", - "\n", - "def evaluate_sparse_pauli(state: int, observable: SparsePauliOp) -> complex:\n", - " \"\"\"Utility for the evaluation of the expectation value of a measured state.\"\"\"\n", - " packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder=\"little\")\n", - " state_bytes = np.frombuffer(\n", - " state.to_bytes(packed_uint8.shape[1], \"little\"), dtype=np.uint8\n", - " )\n", - " reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1)\n", - " return np.sum(observable.coeffs * _PARITY[reduced])\n", - "\n", - "\n", - "def best_solution(samples, hamiltonian):\n", - " \"\"\"Find solution with lowest cost\"\"\"\n", - " min_cost = 1000\n", - " min_sol = None\n", - " for bit_str in samples.keys():\n", - " # Qiskit use little endian hence the [::-1]\n", - " candidate_sol = int(bit_str)\n", - " # fval = qp.objective.evaluate(candidate_sol)\n", - " fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real\n", - " if fval <= min_cost:\n", - " min_sol = candidate_sol\n", - "\n", - " return min_sol\n", - "\n", - "\n", + "# Step 4: find the best-cost sample and evaluate its cut value.\n", "best_sol_100 = best_solution(final_distribution_100_int, cost_hamiltonian_100)\n", "best_sol_bitstring_100 = to_bitstring(int(best_sol_100), len(graph_100))\n", "best_sol_bitstring_100.reverse()\n", "\n", - "print(\"Result bitstring:\", best_sol_bitstring_100)" + "print(\"Result bitstring:\", best_sol_bitstring_100)\n", + "\n", + "cut_value_100 = evaluate_sample(best_sol_bitstring_100, graph_100)\n", + "print(\"The value of the cut is:\", cut_value_100)" ] }, { "cell_type": "markdown", - "id": "7d0cac91", + "id": "large-scale-convergence-md", "metadata": {}, "source": [ - "Next, visualize the cut. Nodes of the same color belong to the same group." + "Check that the cost minimized in the optimization loop has converged, and visualize results." ] }, { "cell_type": "code", - "execution_count": 26, - "id": "b4a25e28", + "execution_count": 23, + "id": "large-scale-viz", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "plot_result(graph_100, best_sol_bitstring_100)" - ] - }, - { - "cell_type": "markdown", - "id": "95507cce-16ec-433c-956b-c77f70d7a3ab", - "metadata": {}, - "source": [ - "Calculate the the value of the cut." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "dd015e77-c1b9-4d06-9163-3ef56cc810f7", - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "The value of the cut is: 124\n" - ] - } - ], - "source": [ - "cut_value_100 = evaluate_sample(best_sol_bitstring_100, graph_100)\n", - "print(\"The value of the cut is:\", cut_value_100)" - ] - }, - { - "cell_type": "markdown", - "id": "be09c8ea", - "metadata": {}, - "source": [ - "Now you need to compute the objective value of each sample that you measured on the quantum computer. The sample with the lowest objective value is the solution returned by the quantum computer." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "27db70eb", - "metadata": {}, - "outputs": [], - "source": [ - "# auxiliary function to help plot cumulative distribution functions\n", - "def _plot_cdf(objective_values: dict, ax, color):\n", - " x_vals = sorted(objective_values.keys(), reverse=True)\n", - " y_vals = np.cumsum([objective_values[x] for x in x_vals])\n", - " ax.plot(x_vals, y_vals, color=color)\n", - "\n", - "\n", - "def plot_cdf(dist, ax, title):\n", - " _plot_cdf(\n", - " dist,\n", - " ax,\n", - " \"C1\",\n", - " )\n", - " ax.vlines(min(list(dist.keys())), 0, 1, \"C1\", linestyle=\"--\")\n", - "\n", - " ax.set_title(title)\n", - " ax.set_xlabel(\"Objective function value\")\n", - " ax.set_ylabel(\"Cumulative distribution function\")\n", - " ax.grid(alpha=0.3)\n", - "\n", - "\n", - "# auxiliary function to convert bit-strings to objective values\n", - "def samples_to_objective_values(samples, hamiltonian):\n", - " \"\"\"Convert the samples to values of the objective function.\"\"\"\n", - "\n", - " objective_values = defaultdict(float)\n", - " for bit_str, prob in samples.items():\n", - " candidate_sol = int(bit_str)\n", - " fval = evaluate_sparse_pauli(candidate_sol, hamiltonian).real\n", - " objective_values[fval] += prob\n", - "\n", - " return objective_values" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "33f0580d", - "metadata": {}, - "outputs": [], - "source": [ - "result_dist = samples_to_objective_values(\n", - " final_distribution_100_int, cost_hamiltonian_100\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "20b3480e", - "metadata": {}, - "source": [ - "Finally, you can plot the cumulative distribution function to visualize how each sample contributes to the total probability distribution and the corresponding objective value. The horizontal spread shows the range of objective values of the samples in the final distribution. Ideally, you would see that the cumulative distribution function has \"jumps\" at the lower end of the objective function value axis. This would mean that few solutions with low cost have high probability of being sampled. A smooth, wide curve indicates that each sample is similarly likely, and they can have very different objective values, low or high." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "4381a2b3", - "metadata": {}, - "outputs": [ + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, "metadata": {}, @@ -1381,8 +1061,22 @@ } ], "source": [ + "# Plot convergence\n", + "plt.figure(figsize=(12, 6))\n", + "plt.plot(objective_func_vals)\n", + "plt.xlabel(\"Iteration\")\n", + "plt.ylabel(\"Cost\")\n", + "plt.show()\n", + "\n", + "# Visualize the cut\n", + "plot_result(graph_100, best_sol_bitstring_100)\n", + "\n", + "# Plot cumulative distribution function\n", + "result_dist = samples_to_objective_values(\n", + " final_distribution_100_int, cost_hamiltonian_100\n", + ")\n", "fig, ax = plt.subplots(1, 1, figsize=(8, 6))\n", - "plot_cdf(result_dist, ax, \"Eagle device\")" + "plot_cdf(result_dist, ax, backend.name)" ] }, { @@ -1390,21 +1084,14 @@ "id": "69ebc85b-6a29-4671-8d16-1ac97f089607", "metadata": {}, "source": [ - "## Conclusion\n", - "\n", - "This tutorial demonstrated how to solve an optimization problem with a quantum computer using the Qiskit patterns framework. The demonstration included a utility-scale example, with circuit sizes that cannot be exactly simulated classically. Currently, quantum computers do not outperform classical computers for combinatorial optimization because of noise. However, the hardware is steadily improving, and new algorithms for quantum computers are continually being developed. Indeed, much of the research working on quantum heuristics for combinatorial optimization is tested with classical simulations that only allow for a small number of qubits, typically around 20 qubits. Now, with larger qubit counts and devices with less noise, researchers will be able to start benchmarking these quantum heuristics at large problem sizes on quantum hardware." - ] - }, - { - "cell_type": "markdown", - "id": "b4a9cf43", - "metadata": {}, - "source": [ - "## Tutorial survey\n", - "\n", - "Please take this short survey to provide feedback on this tutorial. Your insights will help us improve our content offerings and user experience.\n", - "\n", - "[Link to survey](https://your.feedback.ibm.com/jfe/form/SV_cNHi0H1YIagQZ9Q)" + "## Next steps\n", + "If you found this work interesting, you might be interested in the following material:\n", + "\n", + "- [Advanced techniques for QAOA](/docs/tutorials/advanced-techniques-for-qaoa) — explores advanced strategies for improving QAOA performance\n", + "- [Multi-objective optimization challenge](https://github.com/qiskit-community/qdc-challenges-2025/blob/main/challenges/Track_B/qmoo/qmoo_qdc25.ipynb) — put your skills to the test with this community challenge on multi-objective quantum optimization\n", + "- [Transpilation documentation](/docs/guides/transpile) for fine-tuning circuit optimization\n", + "- [Error suppression and mitigation](/docs/guides/error-mitigation-and-suppression-techniques) for improving hardware results\n", + "" ] } ], diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/0fda3611-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/0fda3611-0.avif deleted file mode 100644 index f821e21a3ae..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/0fda3611-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/1c432c2e-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/1c432c2e-0.avif deleted file mode 100644 index 238ee8f12bb..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/1c432c2e-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/2989e76e-4296-4dd8-b065-2b8fced064cf-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/2989e76e-4296-4dd8-b065-2b8fced064cf-0.avif index e530100908f..9efbd86f9c0 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/2989e76e-4296-4dd8-b065-2b8fced064cf-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/2989e76e-4296-4dd8-b065-2b8fced064cf-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/33135970-8bc4-4fb2-ab87-08726a432ce4-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/33135970-8bc4-4fb2-ab87-08726a432ce4-0.avif index 23b5597828d..7c20dfa753b 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/33135970-8bc4-4fb2-ab87-08726a432ce4-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/33135970-8bc4-4fb2-ab87-08726a432ce4-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3a14e7ad-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3a14e7ad-0.avif deleted file mode 100644 index 3096eb9084d..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3a14e7ad-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3f28a422-805c-4d3d-b5f6-62539e9133bd-1.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3f28a422-805c-4d3d-b5f6-62539e9133bd-1.avif index 6ead56772bf..77f613bde99 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3f28a422-805c-4d3d-b5f6-62539e9133bd-1.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/3f28a422-805c-4d3d-b5f6-62539e9133bd-1.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/4381a2b3-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/4381a2b3-0.avif deleted file mode 100644 index 92c5f949b9e..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/4381a2b3-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/590fe2ce-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/590fe2ce-0.avif deleted file mode 100644 index 09fb5bd2d3c..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/590fe2ce-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/650875e9-adbc-43bd-9505-556be2566278-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/650875e9-adbc-43bd-9505-556be2566278-0.avif index fe5e7bbed31..f036ef205e2 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/650875e9-adbc-43bd-9505-556be2566278-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/650875e9-adbc-43bd-9505-556be2566278-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/6ced6bea-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/6ced6bea-0.avif index 534bfca91a7..aa73896b908 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/6ced6bea-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/6ced6bea-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif index 1743ffccf7e..0e7c36d627c 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/9693adfc-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/9693adfc-0.avif deleted file mode 100644 index f7b2388ca20..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/9693adfc-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/b4a25e28-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/b4a25e28-0.avif deleted file mode 100644 index 89b28373b1a..00000000000 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/b4a25e28-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/e14ecc92-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/e14ecc92-0.avif index 919e65f5552..e0dce6bc96c 100644 Binary files a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/e14ecc92-0.avif and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/e14ecc92-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-0.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-0.avif new file mode 100644 index 00000000000..64c1a8f3330 Binary files /dev/null and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-0.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-1.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-1.avif new file mode 100644 index 00000000000..21bef365a63 Binary files /dev/null and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-1.avif differ diff --git a/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-2.avif b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-2.avif new file mode 100644 index 00000000000..1d283a00451 Binary files /dev/null and b/public/docs/images/tutorials/quantum-approximate-optimization-algorithm/extracted-outputs/large-scale-viz-2.avif differ diff --git a/scripts/config/cspell/dictionaries/qiskit.txt b/scripts/config/cspell/dictionaries/qiskit.txt index 79dc454686e..3e9ab218b7a 100644 --- a/scripts/config/cspell/dictionaries/qiskit.txt +++ b/scripts/config/cspell/dictionaries/qiskit.txt @@ -428,6 +428,7 @@ cidx rbrack bitorder cvar +setminus qudits qutrits quasiprobabilities