{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e1dea188",
      "metadata": {},
      "source": [
        "---\n",
        "title: Find the Maximum Independent Set with the Aqarios Constrained Quantum Optimizer\n",
        "description: Use Aqarios Constrained Quantum Optimizer based on iterative warm-starting and XY-mixers to solve the Maximum Independent Set problem on IBM Quantum hardware\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Aqarios QOBLIB Bucher forall */}\n",
        "\n",
        "# Find the Maximum Independent Set with the Aqarios Constrained Quantum Optimizer\n",
        "\n",
        "<Admonition type=\"note\" title=\"Note\">\n",
        "  Qiskit Functions are an experimental feature available only to IBM Quantum® Premium Plan, Flex Plan, and On-Prem (via IBM Quantum Platform API) Plan users. They are in preview release status and subject to change.\n",
        "</Admonition>\n",
        "\n",
        "*Usage estimate: 30 seconds on a Heron r2 processor. (NOTE: This is an estimate only. Your runtime might vary.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be93797d",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "39ff36f6",
      "metadata": {},
      "source": [
        "## Background\n",
        "\n",
        "This tutorial demonstrates how to find the maximum independent set of a graph using the [Aqarios Constrained Quantum Optimizer](/docs/guides/aqarios-constrained-quantum-optimizer) [\\[1\\]](#references), a constrained combinatorial optimization problem.\n",
        "An instance from the QOBLIB [\\[2\\]](#references) benchmark library is formulated as a binary linear program and passed to the Optimizer Application Function.\n",
        "The optimizer handles all reformulation, circuit synthesis, transpilation, and iterative warm starting internally (see [\\[3\\]](#references) for details).\n",
        "\n",
        "The tutorial covers the following steps:\n",
        "\n",
        "1. Define the problem as a linear program using the `OptimizationProblem` from [qiskit-addon-opt-mapper](https://github.com/qiskit/qiskit-addon-opt-mapper)\n",
        "2. Run the quantum optimization using the Aqarios Constrained Quantum Optimizer\n",
        "3. Retrieve and visualize the results\n",
        "\n",
        "### The Maximum Independent Set problem\n",
        "\n",
        "The Maximum Independent Set (MIS) problem is a fundamental challenge in combinatorial optimization.\n",
        "Formally, given a graph $G(V, E)$, the goal is to find the largest subset of vertices $V_I \\subset V$ such that no two vertices in $V_I$ are connected by an edge, as in $\\nexists (u, v) \\in E : v \\in V_I \\wedge u \\in V_I$.\n",
        "Each vertex is assigned a binary decision variable $x_i \\in \\{0, 1\\}$, and a constraint $x_u + x_v \\leq 1$ is introduced for every edge, ensuring that at most one endpoint of each edge is selected.\n",
        "The problem can thus be stated as the following maximization problem:\n",
        "\n",
        "$$\n",
        "\\max_{x_i} \\sum_{i \\in V} x_i \\qquad\\text{(find the largest set)}\\\\\n",
        "\\text{s.t.} \\quad x_u + x_v \\leq 1 \\quad \\forall (u, v) \\in E.\n",
        "$$\n",
        "\n",
        "MIS has a wide range of practical applications. In wireless network planning, an independent set corresponds to a group of transmitters that can all broadcast simultaneously without mutual interference. In scheduling, it models the largest collection of tasks that can execute concurrently given pairwise resource conflicts. In computational biology, it captures sets of mutually non-interacting proteins in a network.\n",
        "\n",
        "Despite its intuitive formulation, MIS is NP-hard, and even for graphs with a few hundred nodes specific instances become difficult to solve exactly or heuristically [\\[2\\]](#references).\n",
        "The problem also gives rise to sparse constraint structures that are well suited for hardware implementations of quantum optimization, making it an attractive benchmark for near-term quantum devices.\n",
        "\n",
        "### Aqarios Constrained Quantum Optimizer\n",
        "\n",
        "The standard approach to embedding a constrained binary problem into quantum optimization transforms the model into an unconstrained format by adding penalty terms: each violated constraint $x_u + x_v \\leq 1$ contributes $2 x_u x_v$ to the minimization objective $-\\sum_i x_i$. This is handled automatically by the Constrained Quantum Optimizer Qiskit Function.\n",
        "\n",
        "Beyond this standard transformation, the optimizer identifies **cliques** in the constraint graph. A clique is a set of nodes $V_C$ where every pair of nodes shares an edge. As a consequence, the $\\binom{|V_C|}{2}$ pairwise constraints $x_u + x_v \\leq 1 \\;\\forall (u,v) \\in E_C$ can be replaced by a single, tighter constraint $\\sum_{i \\in V_C} x_i \\leq 1$. Introducing a slack variable $y$ turns this into an equality $\\sum_i x_i + y = 1$, which takes the form of a one-hot constraint that can be enforced directly in QAOA using **XY-mixers** [\\[3\\]](#references). This reduces the search space and avoids the need for penalty terms for those constraints, improving solution quality.\n",
        "\n",
        "Additionally, variables connected to only a single neighbor are called **pendant** nodes and are fixed deterministically by the algorithm before quantum execution, further reducing the effective problem size.\n",
        "\n",
        "The Constrained Quantum Optimizer employs an **iterative warm-starting** approach compatible with XY-mixers [\\[1\\]](#references), which progressively narrows the search space by biasing the quantum state distribution towards promising solution regions across iterations. This enables the use of **fixed-angle QAOA parameters**, eliminating the need for variational parameter training. The total quantum resource requirements are governed solely by the number of warm-start iterations, which means the quantum cost straightforward to control.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "01da2a6e",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "source": [
        "## Requirements\n",
        "\n",
        "Before starting this tutorial, ensure that you have installed the following requirements:\n",
        "\n",
        "* Qiskit Runtime (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog IBM Client (`pip install qiskit-ibm-catalog`)\n",
        "* Qiskit Addon Optimization Mapper (`pip install qiskit-addon-opt-mapper`)\n",
        "* Numpy (`pip install numpy`)\n",
        "* Matplotlib (`pip install matplotlib`)\n",
        "* NetworkX (`pip install networkx`)\n",
        "\n",
        "Optionally, for the [Appendix](#appendix-problem-statement-with-luna-model) you need to install\n",
        "\n",
        "* Luna Model (`pip install luna-model`)\n",
        "\n",
        "## Setup\n",
        "\n",
        "Import all required dependencies.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "3fe5037e-e02b-40cb-bba7-7349d729df53",
      "metadata": {},
      "outputs": [],
      "source": [
        "import networkx as nx\n",
        "import urllib.request\n",
        "\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "from qiskit_addon_opt_mapper import OptimizationProblem\n",
        "from qiskit_addon_opt_mapper.applications import IndependentSet\n",
        "from qiskit_addon_opt_mapper.translators import to_docplex_mp"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d47802c3",
      "metadata": {},
      "source": [
        "First, authenticate using your [IBM Quantum API key](http://quantum.cloud.ibm.com/). Then, select the Qiskit Function as follows. (This code assumes you've already [saved your account](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) to your local environment.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "2e6b3761",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[QiskitFunction(aqarios/constrained-quantum-optimizer)]"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Verify that you have access to the function\n",
        "catalog.list()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2b3688f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Load the function\n",
        "optimizer = catalog.load(\"aqarios/constrained-quantum-optimizer\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "bd0fd445",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[<IBMBackend('ibm_pittsburgh')>,\n",
              " <IBMBackend('ibm_boston')>,\n",
              " <IBMBackend('ibm_phoenix')>,\n",
              " <IBMBackend('ibm_fez')>,\n",
              " <IBMBackend('ibm_miami')>,\n",
              " <IBMBackend('ibm_marrakesh')>,\n",
              " <IBMBackend('ibm_kingston')>]"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Check the list of backends you have access to\n",
        "catalog.backends()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "e50deb54",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Select the backend you want to use\n",
        "backend = catalog.backend(\"ibm_pittsburgh\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5435d391",
      "metadata": {},
      "source": [
        "## Step 1: Map classical inputs to quantum problem\n",
        "\n",
        "The problem is formulated as an **LP-file**, a common format for optimization problems that serves as the input to the Aqarios Constrained Quantum Optimizer. Besides LP-files, the function also supports **MPS-files** and native **Luna Model** representations.\n",
        "The LP-file is generated through the following steps:\n",
        "\n",
        "1. Fetch a graph instance from the QOBLIB [\\[2\\]](#references)\n",
        "2. Model the optimization problem\n",
        "3. Generate the LP-file\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c773d45b",
      "metadata": {},
      "source": [
        "### Load the problem instance graph\n",
        "\n",
        "The graphs are specified in the DIMACS `.gph` format, a line-based format where lines starting with `e` define edges, lines starting with `p` define the problem header, and lines starting with `c` are comments:\n",
        "\n",
        "```\n",
        "c some-comment\n",
        "p edge 3 2\n",
        "e 1 2\n",
        "e 2 3\n",
        "...\n",
        "```\n",
        "\n",
        "The `.gph` file can be downloaded from the QOBLIB repository using the following function, which also parses it into a NetworkX graph. Note that the DIMACS format uses 1-based node labeling, which is converted to 0-based indexing here.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "8e09bff6-0d5b-4ce5-869b-449755d7edad",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Loading graph with 186 nodes and 280 edges.\n"
          ]
        }
      ],
      "source": [
        "URL_BASE = \"https://raw.githubusercontent.com/ZIB-AOPT/QOBLIB/refs/heads/main/07-independentset/instances/\"\n",
        "\n",
        "\n",
        "def fetch_qoblib_graph(name: str) -> nx.Graph:\n",
        "    \"\"\"Fetch and parse the QOBLIB graph file.\"\"\"\n",
        "    # Download the .gph file\n",
        "    file, _ = urllib.request.urlretrieve(URL_BASE + f\"{name}.gph\")\n",
        "    with open(file) as f:\n",
        "        # Read the file contents\n",
        "        lines = f.readlines()\n",
        "\n",
        "    # Skip comments\n",
        "    lines = [line for line in lines if not line.startswith(\"c\")]\n",
        "\n",
        "    # Read graph definition\n",
        "    _, _, num_nodes, num_edges = lines[0].split()\n",
        "    print(f\"Loading graph with {num_nodes} nodes and {num_edges} edges.\")\n",
        "\n",
        "    # Parse edge information\n",
        "    # The .gph format starts node labeling with 1; we need 0 here, so we subtract one.\n",
        "    split_edges = (line.split() for line in lines[1:])\n",
        "    edges = [(int(u) - 1, int(v) - 1) for _, u, v in split_edges]\n",
        "\n",
        "    return nx.Graph(edges)\n",
        "\n",
        "\n",
        "graph_name = \"es60fst02\"\n",
        "graph = fetch_qoblib_graph(graph_name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2851abad",
      "metadata": {},
      "source": [
        "This example uses the `es60fst02` instance from QOBLIB, a graph with 186 nodes and 280 edges. Thanks to the preprocessing steps employed by the Constrained Quantum Optimizer, this instance is solvable on 156-qubit Heron devices. The graph can be visualized using matplotlib:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "182c672d-bab4-48e7-93c1-5e19dc9226ab",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer/extracted-outputs/182c672d-bab4-48e7-93c1-5e19dc9226ab-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Keep layout for later reuse\n",
        "layout = nx.spring_layout(graph, seed=1)\n",
        "nx.draw(graph, layout, node_size=40)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59456755",
      "metadata": {},
      "source": [
        "### Construct the optimization problem\n",
        "\n",
        "The Maximum Independent Set problem can be formulated directly using `OptimizationProblem`. Each graph node becomes a binary decision variable, and each edge introduces a constraint ensuring that at most one of its endpoints is selected:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "d5363fd3",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [],
      "source": [
        "# Create an OptimizationProblem instance\n",
        "mis_problem = OptimizationProblem(\"MIS\")\n",
        "\n",
        "# Add a binary variable for each node\n",
        "x = mis_problem.binary_var_list(graph.number_of_nodes())\n",
        "\n",
        "# Maximize the sum of all node variables\n",
        "mis_problem.maximize(linear={xi.name: 1 for xi in x})\n",
        "\n",
        "# Add '<= 1' constraints for each edge\n",
        "for u, v in graph.edges:\n",
        "    mis_problem.linear_constraint({x[u].name: 1, x[v].name: 1}, \"<=\", 1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f9a8247",
      "metadata": {},
      "source": [
        "#### A shortcut\n",
        "\n",
        "The [`qiskit-addon-opt-mapper`](https://qiskit.github.io/qiskit-addon-opt-mapper/) package provides a pre-implemented application class for the Maximum Independent Set problem, which simplifies the formulation above into a single call:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "01e2a4e0",
      "metadata": {},
      "outputs": [],
      "source": [
        "mis = IndependentSet(graph)\n",
        "mis_problem = mis.to_optimization_problem()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6870d4e",
      "metadata": {},
      "source": [
        "### Translate the problem to an LP-file\n",
        "\n",
        "The `OptimizationProblem` itself does not support LP-file exports, but it is interoperable with [DOcplex](https://www.ibm.com/docs/de/icos/22.1.2?topic=docplex-python-modeling-api), which does. Generating the LP-file contents requires only two lines:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "bf4d697a",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\\ This file has been generated by DOcplex\n",
            "\\ ENCODING=ISO-8859-1\n",
            "\\Problem name: Independent set\n",
            "\n",
            "Maximize\n",
            " obj: x_0 + x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 + x_8 + x_9 + x_10 + x_11\n",
            "      + x_12 + x_13 + x_14 + x_15 + x_16 + x_17 + x_18 + x_19 + x_20 + x_21\n",
            "      + x_22 + x_23 + x_24 + x_25 + x_26 + x_27 + x_28 + x_29 + x_30 + x_31\n",
            "      + x_32 + x_33 + x_34 + x_35 + x_36 + x_37 + x_38 + x_39 + x_40 + x_41\n",
            "      + x_42 + x_43 + x_44 + x_45 + x_46 + x_47 + x_48 + x_49 + x_50 + x_51\n",
            "      + x_52 + x_53 + x_54 + x_55 + x_56 + x_57 + x_58 + x_59 + x_60 + x_61\n",
            "      + x_62 + x_63 + x_64 + x_65 + x_66 + x_67 + x_68 + x_69 + x_70 + x_71\n",
            "      + x_72 + x_73 + x_74 + x_75 + x_76 + x_77 + x_78 + x_79 + x_80 + x_81\n",
            "      + x_82 + x_83 + x_84 + x_85 + x_86 + x_87 + x_88 + x_89 + x_90 + x_91\n",
            "      + x_92 + x_93 + x_94 + x_95 + x_96 + x_97 + x_98 + x_99 + x_100 + x_101\n",
            "      + x_102 + x_103 + x_104 + x_105 + x_106 + x_107 + x_108 + x_109 + x_110\n",
            "      + x_111 + x_112 + x_113 + x_114 + x_115 + x_116 + x_117 + x_118 + x_119\n",
            "      + x_120 + x_121 + x_122 + x_123 + x_124 + x_125 + x_126 + x_127 + x_128\n",
            "      + x_129 + x_130 + x_131 + x_132 + x_133 + x_134 + x_135 + x_136 + x_137\n",
            "      + x_138 + x_139 + x_140 + x_141 + x_142 + x_143 + x_144 + x_145 + x_146\n",
            "      + x_147 + x_148 + x_149 + x_150 + x_151 + x_152 + x_153 + x_154 + x_155\n",
            "      + x_156 + x_157 + x_158 + x_159 + x_160 + x_161 + x_162 + x_163 + x_164\n",
            "      + x_165 + x_166 + x_167 + x_168 + x_169 + x_170 + x_171 + x_172 + x_173\n",
            "      + x_174 + x_175 + x_176 + x_177 + x_178 + x_179 + x_180 + x_181 + x_182\n",
            "      + x_183 + x_184 + x_185\n",
            "Subject To\n",
            " c0: x_60 + x_61 <= 1\n",
            " c1: x_14 + x_60 <= 1\n",
            " c2: x_7 + x_60 <= 1\n",
            " c3: x_7 + x_61 <= 1\n",
            " c4: x_61 + x_62 <= 1\n",
            " c5: x_61 + x_64 <= 1\n",
            " c6: x_14 + x_62 <= 1\n",
            " c7: x_62 + x_65 <= 1\n",
            " c8: x_23 + x_63 <= 1\n",
            " c9: x_53 + x_63 <= 1\n",
            " c10: x_39 + x_63 <= 1\n",
            " c11: x_7 + x_68 <= 1\n",
            " c12: x_18 + x_68 <= 1\n",
            " c13: x_68 + x_69 <= 1\n",
            " c14: x_68 + x_72 <= 1\n",
            " c15: x_64 + x_65 <= 1\n",
            " c16: x_64 + x_69 <= 1\n",
            " c17: x_65 + x_66 <= 1\n",
            " c18: x_51 + x_53 <= 1\n",
            " c19: x_69 + x_73 <= 1\n",
            " c20: x_66 + x_67 <= 1\n",
            " c21: x_42 + x_66 <= 1\n",
            " c22: x_67 + x_75 <= 1\n",
            " c23: x_43 + x_67 <= 1\n",
            " c24: x_42 + x_75 <= 1\n",
            " c25: x_75 + x_83 <= 1\n",
            " c26: x_12 + x_51 <= 1\n",
            " c27: x_18 + x_70 <= 1\n",
            " c28: x_18 + x_26 <= 1\n",
            " c29: x_70 + x_71 <= 1\n",
            " c30: x_70 + x_76 <= 1\n",
            " c31: x_71 + x_72 <= 1\n",
            " c32: x_72 + x_73 <= 1\n",
            " c33: x_72 + x_78 <= 1\n",
            "...\n"
          ]
        }
      ],
      "source": [
        "mp_model = to_docplex_mp(mis_problem)\n",
        "lp_str = mp_model.export_as_lp_string()\n",
        "\n",
        "print(\"\\n\".join(lp_str.split(\"\\n\")[:60]))\n",
        "print(\"...\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0c6ca33d",
      "metadata": {},
      "source": [
        "This format is native to the Constrained Quantum Optimizer.\n",
        "\n",
        "## Step 2: Optimize problem for quantum hardware execution\n",
        "\n",
        "All circuit synthesis, optimization, and transpilation is handled by the function natively. See the [inputs section](/docs/api/functions/aqarios-constrained-quantum-optimizer#inputs) in the API reference for the arguments with which to call the function.\n",
        "\n",
        "To fine-tune the algorithm's behavior, see the [Options list](/docs/api/functions/aqarios-constrained-quantum-optimizer#options-list) in the API reference.\n",
        "\n",
        "For more information, see the [Aqarios Constrained Quantum Optimizer guide](/docs/guides/aqarios-constrained-quantum-optimizer) and [API reference](/docs/api/functions/aqarios-constrained-quantum-optimizer).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "241b80a6",
      "metadata": {},
      "source": [
        "## Step 3: Execute using Qiskit primitives\n",
        "\n",
        "The LP-file can now be submitted to the optimizer:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "02469f27-1ea9-4fa2-8412-3e7c5ec1a59f",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: 87ec08b9-6275-40fa-be94-340a0a916bf1\n"
          ]
        }
      ],
      "source": [
        "job = optimizer.run(model=lp_str, backend_name=backend.name)\n",
        "\n",
        "print(f\"Job ID: {job.job_id}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4804bb92",
      "metadata": {},
      "source": [
        "Internally, the algorithm proceeds through the following stages:\n",
        "\n",
        "1. **Preprocessing**:\n",
        "   * Reduce fixable variables\n",
        "   * Find cliques\n",
        "   * Identify constraint types\n",
        "   * Evaluate penalty factors for penalty terms\n",
        "   * Apply constraint transformations\n",
        "   * Synthesize circuit with constraint-enforcing methods\n",
        "   * Approximation of the problem and transpilation\n",
        "2. **Parallel chains of iterative loops**:\n",
        "   * Sample from circuit with fixed parameters\n",
        "   * Apply postprocessing\n",
        "   * Evaluate and set new warm-starting probabilities\n",
        "3. **Postprocessing**:\n",
        "   * Find best samples and check for feasibility with regards the input problem\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25fa45d3",
      "metadata": {},
      "source": [
        "### Monitor the progress\n",
        "\n",
        "See the following sections in the Get started with Qiskit Functions page to monitor your job's progress:\n",
        "\n",
        "* [Check job status](/docs/guides/functions-get-started#check-job-status)\n",
        "* [Retrieve results](/docs/guides/functions-get-started#retrieve-results)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "416cf0c0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'QUEUED'"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Monitor the job status\n",
        "job.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8378a14f",
      "metadata": {},
      "source": [
        "## Step 4: Post-process and return results in desired classical format\n",
        "\n",
        "The result output is a dictionary, the fields of which are described in the [Outputs section](/docs/api/functions/aqarios-constrained-quantum-optimizer#outputs) of the API reference.\n",
        "\n",
        "When the [`solutions`](/docs/api/functions/aqarios-constrained-quantum-optimizer#output-structure) list contains more than one entry, multiple degenerate optima have been found. Only the first solution is considered here:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "fe8a3b9d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The found maximum independent set of es60fst02 contains: 88 nodes and is feasible.\n",
            "{100 103 107 109 111 113 115 118 121 123 124 127 129 130 132 133 138 142 144 148 149 155 156 16 161 162 165 167 169 170 175 28 31 33 35 36 47 50 58 59 60 62 64 66 68 71 73 75 78 79 84 85 87 90 91 93 94 95 39 5 27 23 43 15 22 9 4 56 32 30 53 26 17 54 1 37 41 49 34 11 139 153 12 3 6 57 20 44}\n"
          ]
        }
      ],
      "source": [
        "# Retrieve the job result\n",
        "result = job.result()\n",
        "\n",
        "# Retrieve the first solution from the result\n",
        "solution = result[\"solutions\"][0]\n",
        "\n",
        "print(f\"The found maximum independent set of {graph_name} contains:\", end=\" \")\n",
        "print(\n",
        "    f\"{int(result['obj_value'])} nodes and is {'feasible' if result['feasible'] else 'infeasible'}.\"\n",
        ")\n",
        "print(\"{\" + \" \".join(k[2:] for k, v in solution.items() if v == 1) + \"}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e0ac4233",
      "metadata": {},
      "source": [
        "### Visualization\n",
        "\n",
        "The identified independent set can be visualized by highlighting the selected nodes in the graph:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "f068c750",
      "metadata": {
        "lines_to_next_cell": 2
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer/extracted-outputs/f068c750-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Color all selected nodes in orange\n",
        "node_map = {\n",
        "    int(k.split(\"_\")[1]): \"tab:orange\" if v else \"tab:blue\"\n",
        "    for k, v in solution.items()\n",
        "}\n",
        "node_colors = [node_map[k] for k in graph.nodes]\n",
        "\n",
        "# Draw with the same layout used before\n",
        "nx.draw(graph, layout, node_size=40, node_color=node_colors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1463b529",
      "metadata": {},
      "source": [
        "## Appendix: Problem statement with Luna Model\n",
        "\n",
        "In addition to the `qiskit-addon-opt-mapper` approach shown above, the Qiskit Function also accepts models created with Luna Model [\\[4\\]](#references), Aqarios' modeling SDK.\n",
        "After installation of the `luna-model` PyPI package, import it as follows:\n",
        "\n",
        "**Setup:**\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "299a6d7c",
      "metadata": {},
      "outputs": [],
      "source": [
        "from luna_model import Model, Sense\n",
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "03a63f0d",
      "metadata": {},
      "source": [
        "The model is then constructed from the graph in the same way as with `qiskit-addon-opt-mapper`:\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fbf74687",
      "metadata": {},
      "source": [
        "**Build the model:**\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "cf52944f-8908-4b4e-8e65-556fbe3b7a38",
      "metadata": {},
      "outputs": [],
      "source": [
        "edges = np.array(graph.edges)\n",
        "\n",
        "# Create the optimization model with a name\n",
        "model = Model(name=f\"MIS-{graph_name}\", sense=Sense.MAX)\n",
        "# Add binary variables\n",
        "x = model.add_variables(\"x\", graph.number_of_nodes())\n",
        "# Set the objective\n",
        "model.objective = x.sum()\n",
        "\n",
        "# Use numpy like batch generation of constraints\n",
        "model.add_constraints(x[edges].sum(axis=1) <= 1)\n",
        "\n",
        "input_str = model.encode_b64()\n",
        "\n",
        "# optimizer.run(model=input_str, backend_name=\"ibm_fez\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "42730c89",
      "metadata": {},
      "source": [
        "## Next Steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  * Consult the [Aqarios Constrained Quantum Optimizer guide](/docs/guides/aqarios-constrained-quantum-optimizer) for a detailed walkthrough of all function features.\n",
        "  * Explore the [API reference](/docs/api/functions/aqarios-constrained-quantum-optimizer) for the full list of input parameters and output fields.\n",
        "  * Experiment with the algorithm options (`reps`, `num_parallel`, `shots`, `postprocessing`) on your own constrained binary optimization problem to assess their impact on solution quality and runtime.\n",
        "</Admonition>\n",
        "\n",
        "## References\n",
        "\n",
        "1. IBM Quantum, [*Aqarios Constrained Quantum Optimizer Guide*](/docs/guides/aqarios-constrained-quantum-optimizer)\n",
        "2. Koch et al. (2026), *The Quantum Optimization Benchmarking Library* [10.1038/s43588-026-00991-1](https://doi.org/10.1038/s43588-026-00991-1)\n",
        "3. Bucher et al. (2026), *Constrained Quantum Optimization via Iterative Warm-Start XY-Mixers* [10.1088/1367-2630/ae8ea2](https://doi.org/10.1088/1367-2630/ae8ea2)\n",
        "4. Aqarios GmbH, [*Luna Model Docs*](https://docs.aqarios.com/luna-model)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}