{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "dde95705",
      "metadata": {},
      "source": [
        "---\n",
        "title: Constrained Quantum Optimizer - A Qiskit Function by Aqarios\n",
        "description: Solve constrained binary optimization problems on IBM Quantum hardware by using the iterative warm-starting QAOA with XY-mixers by Aqarios.\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Aqarios QOBLIB Bucher docplex forall Zachary quicksum */}\n",
        "\n",
        "# Constrained Quantum Optimizer - A Qiskit Function by Aqarios\n",
        "\n",
        "*See the [API reference](/docs/api/functions/aqarios-constrained-quantum-optimizer)*\n",
        "\n",
        "<Admonition type=\"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",
        "## Overview\n",
        "\n",
        "With the Constrained Quantum Optimizer by Aqarios, you can solve constrained binary optimization problems on IBM® quantum hardware without manual reformulation and circuit mapping. Simply supply a problem as an LP file, an MPS file, or a [Luna Model](https://docs.aqarios.com/luna-model). The function handles reformulation, circuit synthesis, transpilation, and hardware execution automatically. The solver utilizes iterative warm-starting [\\[1\\]](#references) of the [FlexQAOA](https://docs.aqarios.com/luna-solve/algorithms/quantum-gate/flexqaoa/) algorithm, a constraint-native extension of the Quantum Approximate Optimization Algorithm (QAOA).\n",
        "\n",
        "The optimizer targets binary problems with constraints. In particular, one-hot constraints and set-packing constraints can be natively enforced by using XY-mixers. Other constraint types are automatically reformulated as penalty terms.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9f4110b",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bcd63ea",
      "metadata": {},
      "source": [
        "## Description\n",
        "\n",
        "<Admonition type=\"caution\" title=\"Disclaimer\">\n",
        "  Performance can depend on both the problem instance and subsequent processing steps. In some cases, classical samples and quantum-generated samples might achieve similar final solution quality after equivalent post-processing. Evaluation should therefore consider the complete optimization workflow.\n",
        "</Admonition>\n",
        "\n",
        "The Constrained Quantum Optimizer implements the Iterative warm-start XY-mixer QAOA(IWS-QAOA) [\\[1\\]](#references). The algorithm runs fixed-angle QAOA circuits on quantum hardware and updates the initial warm-start probabilities of each circuit using measurement outcomes from the previous iteration. No variational parameter training is required; instead, a fixed linear ramp parametrization is chosen.\n",
        "\n",
        "By using XY-mixers to enforce one-hot constraints, you can drastically improve solution quality for constrained problems, because the search space is practically reduced to a smaller feasible subspace — and no additional complexity, due to penalty terms, is introduced in the optimization landscape.\n",
        "\n",
        "### Workflow\n",
        "\n",
        "The workflow of the function is described below:\n",
        "\n",
        "1. **Preprocessing**: Before circuit synthesis, the optimizer analyzes the constraint structure of the input problem:\n",
        "   * *Graph preprocessing:* When applicable to the problem, it is analyzed as a graph, where edges determine the relationship between variables. This graph analysis enables two preprocessing problem enhancements. First, single constraints can be aggregated to one large constraint by using clique merging, so that they are represented by a single XY-mixer. Second, variables with only a single neighbor can be fixed deterministically, reducing the problem size and complexity.\n",
        "   * *Constraint resolution:* The preprocessor handles each constraint separately: If the constraint is a one-hot constraint, it can be enforced through XY-mixers. If it is a set-packing constraint, it will first be transformed to a one-hot constraint. Equality constraints are handled through quadratic penalties, and inequality constraints through [unbalanced penalization](https://doi.org/10.1088/2058-9565/ad35e4).\n",
        "   * *Penalty derivation:* Constraints that cannot be expressed as one-hot equality constraints are converted to penalty terms. The optimizer derives the penalty strength automatically from the problem structure.\n",
        "2. **Iterative warm-starting**: The optimizer runs multiple *parallel warm-start chains*. Each chain holds a probability distribution over variable assignments, which is used as the starting probability of the QAOA circuit (warm-start state). At each iteration, a chain samples from the quantum circuit, applies classical postprocessing, and updates its distribution based on the occurrence of samples. Across iterations, this focuses the search on high-quality solutions without a variational optimization loop.\n",
        "\n",
        "The number of chains (`num_parallel`), the total shot budget per chain (`total_shots`), and the number of QAOA repetitions (`reps`) are the primary controls for trading solution quality against runtime.\n",
        "\n",
        "![Workflow diagram of the Constrained Quantum Optimizer](https://quantum.cloud.ibm.com/docs/images/guides/aqarios-constrained-quantum-optimizer/cqo-diagram.svg)\n",
        "\n",
        "## Benchmarks\n",
        "\n",
        "The benchmarks below show that the Constrained Quantum Optimizer Qiskit Function is capable of finding high quality, feasible solutions to constrained optimization problems with well above 100 binary variables. Accuracy is the ratio of the found objective value to the known optimum. Runtime is wall-clock time from function invocation to result delivery. QPU usage is the billable Quantum Compute time.\n",
        "\n",
        "Two problems were examined: The [Maximum Independent Set](/docs/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer) (MIS) problem and the Max-$k$-Cut, which is an extension of the well-known MaxCut problem to more than two categories. Max-$k$-Cut is also the optimization problem equivalent of the graph coloring decision problem (when all edges are cut, the graph is $k$-colorable).\n",
        "\n",
        "| Problem               | Variables | Constraints | Qubits | Accuracy | Feasibility | Total runtime (s) | QPU usage (s) | Total shots |\n",
        "| --------------------- | --------- | ----------- | ------ | -------- | ----------- | ----------------- | ------------- | ----------- |\n",
        "| MIS es60fst02         | 186       | 280         | 124    | 100.0%   | True        | 940.2             | 147.0         | 25.0k       |\n",
        "| MIS sloane\\_2dc\\_128  | 128       | 5173        | 74     | 100.0%   | True        | 347.6             | 106.0         | 12.5k       |\n",
        "| Max-3-Cut Karate Club | 99        | 33          | 99     | 98.7%    | True        | 279.5             | 166.0         | 25.0k       |\n",
        "| Max-4-Cut Karate Club | 132       | 33          | 132    | 100.0%   | True        | 258.3             | 200.0         | 25.0k       |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f761442",
      "metadata": {},
      "source": [
        "### Benchmark Notes\n",
        "\n",
        "* The above results were obtained on the `ibm_marrakesh` Heron r2 processor with default options and `use_session` enabled. For the `sloane_2dc_128` instance, `total_shots=12500` was used.\n",
        "* The reduced qubit count for MIS instances (74 and 124 versus 128 and 186 variables) reflects variable fixing in preprocessing.\n",
        "* The problem instances for MIS are taken from the [Quantum Optimization Benchmarking Library](https://doi.org/10.1038/s43588-026-00991-1). - The instance for the Max-$k$-Cut problem is the [Zachary Karate Club graph](https://en.wikipedia.org/wiki/Zachary%27s_karate_club), which has a chromatic number of 5.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73390a19",
      "metadata": {},
      "source": [
        "## Get started\n",
        "\n",
        "This guide demonstrates basic usage of the Constrained Quantum Optimizer by showing how to load the function and execute simple problems. For a more involved tutorial, see [Find the Maximum Independent Set with the Aqarios Constrained Quantum Optimizer](/docs/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer).\n",
        "\n",
        "Authenticate with your IBM Quantum API key and instance CRN, then load the function from the catalog.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  The following code assumes you have saved your credentials. To set them up, follow the instructions in [Save your IBM Cloud account credentials](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "95a715d2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[QiskitFunction(aqarios/constrained-quantum-optimizer)]"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Verify that you have access to the function\n",
        "catalog.list()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fe865158",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Load the function\n",
        "optimizer = catalog.load(\"aqarios/constrained-quantum-optimizer\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "d976e101",
      "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": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Check the list of backends you have access to\n",
        "catalog.backends()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "6b0f5c79",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Select the backend you want to use\n",
        "backend = catalog.backend(\"ibm_phoenix\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e4445979",
      "metadata": {},
      "source": [
        "## Example 1: A simple constrained problem\n",
        "\n",
        "The first example shows how to use the function with [LP-files](https://www.ibm.com/docs/en/icos/22.1.1?topic=cplex-lp-file-format-algebraic-representation), a common optimization problem specification used in operations research. You will formulate the following arbitrary optimization problem of five binary variables $x_i \\in \\{0,1\\}$ subject to two constraints:\n",
        "\n",
        "$$\n",
        "    \\min x_1 x_2 + 2 x_2 x_3 - x_3 x_4 - 2 x_4 x_5 - 2 x_1 x_3 - 4 x_2 x_5 \\\\\n",
        "    \\text{s.t.}\\quad x_1 + x_2 + x_3 \\leq 1 \\\\\n",
        "    \\quad x_3 - x_4 + x_5 = 0\n",
        "$$\n",
        "\n",
        "### 1. Formulate the problem\n",
        "\n",
        "Directly state the mathematical expression as the following LP-string format:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "7c3af25d",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "outputs": [],
      "source": [
        "lp_str = \"\"\"\\\\Problem name: Example\n",
        "Minimize\n",
        "  obj: [ 2 x_1 x_2 + 4 x_2 x_3 - 2 x_3 x_4 - 4 x_4 x_5 - 4 x_1 x_3 - 8 x_2 x_5 ] / 2\n",
        "Subject To\n",
        "  c1: x_1 + x_2 + x_3 <= 1\n",
        "  c2: x_3 - x_4 + x_5 = 0\n",
        "Binaries\n",
        "  x_1 x_2 x_3 x_4 x_5\n",
        "End\n",
        "\"\"\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f6c75f9d",
      "metadata": {},
      "source": [
        "Note that `[ ... ] / 2` is a required notation for quadratic objectives from the LP standard.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "003a933c",
      "metadata": {},
      "source": [
        "### 2. Run the optimizer\n",
        "\n",
        "Solve the problem with the optimizer.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "242f1f89",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: 2e06a3a8-7c29-4c76-94d7-194293363f4a\n"
          ]
        }
      ],
      "source": [
        "# Set up low-resource options for the simple problem\n",
        "options = {\"total_shots\": 2000, \"num_parallel\": 5, \"use_session\": True}\n",
        "\n",
        "# Dispatch the job\n",
        "job_1 = optimizer.run(\n",
        "    model=lp_str, backend_name=backend.name, options=options\n",
        ")\n",
        "\n",
        "print(f\"Job ID: {job_1.job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "4bd4790a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'RUNNING: WAITING_FOR_QPU'"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Monitor the job status\n",
        "job_1.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "acff0671",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "source": [
        "### 3. Retrieve the result\n",
        "\n",
        "The solution, mapping the variable names to the associated assignments, is provided by the result.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6a121188",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'solutions': [{'x_1': 0, 'x_2': 1, 'x_3': 0, 'x_4': 1, 'x_5': 1}],\n",
              " 'raw_energy': -6.0,\n",
              " 'obj_value': -6.0,\n",
              " 'feasible': True,\n",
              " 'metadata': {'resource_usage': {'RUNNING: MAPPING': {'CPU': 5.405},\n",
              "   'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU': 9.902},\n",
              "   'RUNNING: WAITING_FOR_QPU': {'CPU': 25.456},\n",
              "   'RUNNING: EXECUTING_QPU': {'CPU': 25.202},\n",
              "   'RUNNING: POST_PROCESSING': {'CPU': 6.481}},\n",
              "  'circuit_metrics': {'depth': 100.95,\n",
              "   '2Q-depth': 33.0,\n",
              "   'sx': 122.25,\n",
              "   'rz': 78.85,\n",
              "   'cz': 48.0,\n",
              "   'x': 0.4}}}"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = job_1.result()\n",
        "\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e8837f5f",
      "metadata": {},
      "source": [
        "The `solutions` field in the return dictionary contains all degenerate best solutions found. Alongside the solution data, the `obj_value`, a `feasible` flag, and additional `metadata` are provided.\n",
        "\n",
        "## Example 2: Maximum Independent Set\n",
        "\n",
        "The Maximum Independent Set (MIS) problem asks for the largest subset of vertices in a graph such that no two vertices in the subset share an edge. This example solves MIS on a 12-node regular graph. For more information on the problem and a larger example, refer to the [tutorial](/docs/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer).\n",
        "\n",
        "The problem is built with [qiskit-addon-opt-mapper](https://github.com/qiskit/qiskit-addon-opt-mapper).\n",
        "Install the required packages if needed:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "a1e2f301",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install networkx qiskit-addon-opt-mapper"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d56e1440",
      "metadata": {},
      "source": [
        "### Step 1: Define the problem\n",
        "\n",
        "Build a random 3-regular graph and create the MIS optimization problem. Each graph node becomes a binary variable, and each edge introduces a constraint ensuring that at most one endpoint is selected.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "03998691",
      "metadata": {},
      "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",
            "Subject To\n",
            " c0: x_0 + x_4 <= 1\n",
            " c1: x_0 + x_9 <= 1\n",
            " c2: x_0 + x_8 <= 1\n",
            " c3: x_1 + x_2 <= 1\n",
            " c4: x_1 + x_7 <= 1\n",
            " c5: x_1 + x_3 <= 1\n",
            " c6: x_2 + x_4 <= 1\n",
            " c7: x_2 + x_11 <= 1\n",
            " c8: x_3 + x_10 <= 1\n",
            " c9: x_3 + x_11 <= 1\n",
            " c10: x_4 + x_5 <= 1\n",
            " c11: x_5 + x_10 <= 1\n",
            " c12: x_5 + x_6 <= 1\n",
            " c13: x_6 + x_8 <= 1\n",
            " c14: x_6 + x_7 <= 1\n",
            " c15: x_7 + x_9 <= 1\n",
            " c16: x_8 + x_11 <= 1\n",
            " c17: x_9 + x_10 <= 1\n",
            "\n",
            "Bounds\n",
            " 0 <= x_0 <= 1\n",
            " 0 <= x_1 <= 1\n",
            " 0 <= x_2 <= 1\n",
            " 0 <= x_3 <= 1\n",
            " 0 <= x_4 <= 1\n",
            " 0 <= x_5 <= 1\n",
            " 0 <= x_6 <= 1\n",
            " 0 <= x_7 <= 1\n",
            " 0 <= x_8 <= 1\n",
            " 0 <= x_9 <= 1\n",
            " 0 <= x_10 <= 1\n",
            " 0 <= x_11 <= 1\n",
            "\n",
            "Binaries\n",
            " 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",
            "End\n",
            "\n"
          ]
        }
      ],
      "source": [
        "import networkx as nx\n",
        "from qiskit_addon_opt_mapper.applications import IndependentSet\n",
        "from qiskit_addon_opt_mapper.translators import to_docplex_mp\n",
        "\n",
        "# 12-node random 3-regular graph\n",
        "G = nx.random_regular_graph(3, 12, seed=7)\n",
        "\n",
        "# Build MIS problem and export as LP string\n",
        "mis = IndependentSet(G)\n",
        "mis_problem = mis.to_optimization_problem()\n",
        "lp_str = to_docplex_mp(mis_problem).export_as_lp_string()\n",
        "\n",
        "print(lp_str)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e6f7a8b9",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "source": [
        "### Step 2: Run the optimizer\n",
        "\n",
        "Solve the problem with the optimizer.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "04236c83",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: f041431c-b42e-4627-afe5-522fea61faf6\n"
          ]
        }
      ],
      "source": [
        "options = {\"total_shots\": 2000, \"use_session\": True}\n",
        "\n",
        "# Dispatch the job\n",
        "job_2 = optimizer.run(\n",
        "    model=lp_str, backend_name=backend.name, options=options\n",
        ")\n",
        "\n",
        "print(f\"Job ID: {job_2.job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "079ebc2f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'QUEUED'"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Monitor the job status\n",
        "job_2.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90c6de7c",
      "metadata": {},
      "source": [
        "### Step 3: Retrieve and interpret results\n",
        "\n",
        "Finally we retrieve the solution and verify for correctness.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "f1a2b3c4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Feasible: True\n",
            "Independent set size: 5\n",
            "Selected nodes: [1, 4, 6, 9, 11]\n",
            "Edge conflicts: []\n"
          ]
        }
      ],
      "source": [
        "result = job_2.result()\n",
        "\n",
        "print(f\"Feasible: {result['feasible']}\")\n",
        "print(f\"Independent set size: {int(result['obj_value'])}\")\n",
        "\n",
        "# Extract the selected nodes\n",
        "solution = result[\"solutions\"][0]\n",
        "independent_set = [\n",
        "    int(k.split(\"_\")[1]) for k, v in solution.items() if v == 1\n",
        "]\n",
        "print(f\"Selected nodes: {sorted(independent_set)}\")\n",
        "\n",
        "# Verify no two selected nodes share an edge\n",
        "conflicts = [\n",
        "    (u, v)\n",
        "    for u, v in G.edges()\n",
        "    if u in independent_set and v in independent_set\n",
        "]\n",
        "print(f\"Edge conflicts: {conflicts}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2b3c4d5",
      "metadata": {},
      "source": [
        "## Example 3: Graph 3-coloring (Max-3-Cut)\n",
        "\n",
        "The Max-$k$-Cut problem partitions the vertices of a graph into $k$ groups to maximize the number of edges whose endpoints belong to different groups. For $k = 3$ this is equivalent to finding a 3-coloring that maximizes cross-color edges.\n",
        "\n",
        "Formally, assign each node $i \\in V$ to one of $k$ groups with binary variables $x_{i,c}$ for color $c \\in \\{1, \\ldots, k\\}$. The one-hot constraint $\\sum_c x_{i,c} = 1$ for each node enforces a unique assignment. The objective maximizes cross-group edges:\n",
        "\n",
        "$$\n",
        "\\min \\sum_{(u,v) \\in E} \\sum_c x_{u,c} \\, x_{v,c}\\\\\n",
        "\\text{s.t.} \\sum_{c} x_{i,c} = 1 \\quad \\forall i \\in  V\n",
        "$$\n",
        "\n",
        "The one-hot constraints are ideal for XY-mixers, which the optimizer handles natively.\n",
        "\n",
        "This example uses the [Zachary Karate Club graph](https://en.wikipedia.org/wiki/Zachary%27s_karate_club) (34 nodes, 78 edges) and builds the model with the [Luna Model](https://docs.aqarios.com/luna-model/).\n",
        "Install the required packages if needed:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "b3c4d5e6",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install networkx matplotlib luna-model"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4d5e6f7",
      "metadata": {},
      "source": [
        "### Step 1: Define the problem\n",
        "\n",
        "First, generate the binary variables with the `luna-model` package. Then, define the objective and add the constraints to the model.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "d5e6f7a8",
      "metadata": {},
      "outputs": [],
      "source": [
        "import networkx as nx\n",
        "from luna_model import Model, quicksum\n",
        "\n",
        "k = 3\n",
        "G = nx.karate_club_graph()\n",
        "\n",
        "# Instantiate the Model object\n",
        "model = Model(name=\"Max-3-Cut\")\n",
        "\n",
        "# Add binary variables\n",
        "x = model.add_variables(\"x\", shape=(G.number_of_nodes(), k))\n",
        "\n",
        "# Add the objective by using the numpy dot abstraction\n",
        "model.objective = quicksum(x[i].dot(x[j]) for i, j in G.edges)\n",
        "\n",
        "# Bulk-add the one-hot constraints\n",
        "model.add_constraints(x.sum(axis=1) == 1)\n",
        "\n",
        "# Encode the model to send via API\n",
        "model_enc = model.encode_b64()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "263ccb5d",
      "metadata": {
        "lines_to_next_cell": 0
      },
      "source": [
        "### Step 2: Run the optimizer\n",
        "\n",
        "Solve the problem with the optimizer.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f7a8b9c0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: df911d83-9df7-44a1-bfad-5d66e08676c3\n"
          ]
        }
      ],
      "source": [
        "options = {\"use_session\": True}\n",
        "\n",
        "# Dispatch the job\n",
        "job_3 = optimizer.run(\n",
        "    model=model_enc, backend_name=backend.name, options=options\n",
        ")\n",
        "\n",
        "print(f\"Job ID: {job_3.job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "e2ff9b51",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'RUNNING: WAITING_FOR_QPU'"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Monitor the job status\n",
        "job_3.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a8b9c0d1",
      "metadata": {},
      "source": [
        "### Step 3: Retrieve and visualize results\n",
        "\n",
        "Retrieve and visualize the results.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "b9c0d1e2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Feasible: True\n",
            "Violations: 4\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/aqarios-constrained-quantum-optimizer/extracted-outputs/b9c0d1e2-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "result = job_3.result()\n",
        "solution = result[\"solutions\"][0]\n",
        "\n",
        "# Map each node to its assigned color group\n",
        "node_group = {}\n",
        "for i in G.nodes:\n",
        "    for c in range(k):\n",
        "        if solution[f\"x{i},{c}\"] == 1:\n",
        "            node_group[i] = c\n",
        "\n",
        "print(f\"Feasible: {result['feasible']}\")\n",
        "print(f\"Violations: {int(result['obj_value'])}\")\n",
        "\n",
        "# Visualize the partition\n",
        "colors = [\"tab:blue\", \"tab:orange\", \"tab:green\"]\n",
        "node_colors = [colors[node_group[i]] for i in G.nodes()]\n",
        "nx.draw(G, nx.kamada_kawai_layout(G), node_color=node_colors, node_size=200)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9ec2e67",
      "metadata": {},
      "source": [
        "## Get support\n",
        "\n",
        "For questions or issues, contact [support@aqarios.com](mailto:support@aqarios.com) with your job ID.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a6a25c8",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  * [Request access to the Constrained Quantum Optimizer by Aqarios](https://aqarios.com/qiskit-function-access/).\n",
        "  * Visit the [API reference](/docs/api/functions/aqarios-constrained-quantum-optimizer) for the complete parameter and output documentation.\n",
        "  * Try the [Find the Maximum Independent Set with the Aqarios Constrained Quantum Optimizer](/docs/tutorials/find-the-maximum-independent-set-with-aqarios-constrained-quantum-optimizer) tutorial for a deep-dive into a real QOBLIB benchmark instance.\n",
        "  * Review the publication [Constrained Quantum Optimization via Iterative Warm-Start XY-Mixers](https://doi.org/10.1088/1367-2630/ae8ea2).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6c0a20cb",
      "metadata": {},
      "source": [
        "## References\n",
        "\n",
        "1. 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",
        "\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
}