{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "347e07c3",
      "metadata": {},
      "source": [
        "---\n",
        "title: Solve a maximum independent set problem with more variables than qubits using JIJ's SQOA Solver\n",
        "description: Solve a maximum independent set problem from QOBLIB with SQOA Solver, a sampling-based Qiskit Function by JIJ.\n",
        "---\n",
        "\n",
        "{/* cspell:ignore SQOA, sqoa, QRAO, QRAC, QOBLIB, OMMX, ommx, presolve, Presolve, noncommuting, Bernal, Blom, Cadavid, Cortiana, Hadfield, Heese, Hegade, Imai, Jiao, Kagele, Kohda, Mizukami, Neira, Schieber, Yamashiro */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "97c614ef",
      "metadata": {},
      "source": [
        "# Solve a maximum independent set problem with more variables than qubits using JIJ's SQOA Solver\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d6d75d3e",
      "metadata": {},
      "source": [
        "<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",
        "*Usage estimate: 10 seconds on a Heron r2 processor. (NOTE: This is an estimate only. Your runtime might vary.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62f56fd5",
      "metadata": {},
      "source": [
        "## Background\n",
        "\n",
        "This tutorial shows how to use [SQOA Solver, a Qiskit Function by JIJ](/docs/guides/jij-sqoa-solver), to solve a maximum independent set (MIS) problem with more binary variables than the selected QPU has qubits. The basic steps are to load an MIS instance from the [Quantum Optimization Benchmarking Library (QOBLIB)](https://github.com/ZIB-AOPT/QOBLIB) [\\[1\\]](#references), formulate it in quadratic unconstrained binary optimization (QUBO) form with [qiskit-addon-opt-mapper](https://qiskit.github.io/qiskit-addon-opt-mapper/), submit it to SQOA Solver for execution on an IBM® QPU, and validate the returned solution.\n",
        "\n",
        "### The maximum independent set problem\n",
        "\n",
        "An independent set is a subset of nodes in a graph such that no two selected nodes share an edge. A maximum independent set (MIS) is an independent set with the largest possible number of nodes. Applications of MIS include scheduling, resource allocation, and network design.\n",
        "\n",
        "MIS is NP-hard. In an exhaustive search, every additional node doubles the number of subsets to consider, so this approach is impractical even for moderately sized graphs. QOBLIB includes MIS as one of its ten problem classes, so it is a useful benchmark for evaluating quantum optimization workflows on real hardware.\n",
        "\n",
        "#### Mathematical formulation\n",
        "\n",
        "Given an undirected graph $G = (V, E)$, assign a binary variable $x_v$ to each node $v$:\n",
        "\n",
        "* $x_v = 1$ if node $v$ is selected.\n",
        "* $x_v = 0$ otherwise.\n",
        "\n",
        "The [binary linear formulation](https://github.com/ZIB-AOPT/QOBLIB/tree/main/07-independentset/models/binary_linear) maximizes the number of selected nodes while preventing both endpoints of any edge from being selected:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\max_{x} \\quad & \\sum_{v \\in V} x_v \\\\\n",
        "\\text{subject to} \\quad & x_v + x_w \\leq 1 && \\forall (v,w) \\in E, \\\\\n",
        "& x_v \\in \\{0,1\\} && \\forall v \\in V.\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "The objective counts the selected nodes. Together, the edge constraints ensure that the selected nodes form an independent set.\n",
        "\n",
        "This tutorial uses QOBLIB's [binary unconstrained formulation](https://github.com/ZIB-AOPT/QOBLIB/tree/main/07-independentset/models/binary_unconstrained), which replaces the edge constraints with quadratic penalties:\n",
        "\n",
        "$$\n",
        "\\max_{x \\in \\{0,1\\}^{|V|}} \\quad \\sum_{v \\in V} x_v - 2 \\sum_{(v,w) \\in E} x_v x_w.\n",
        "$$\n",
        "\n",
        "Whenever both endpoints of an edge are selected, the second term subtracts two from the objective. This penalty makes a conflicting selection less favorable than keeping either endpoint alone. The resulting objective is a QUBO, which is the input format that SQOA Solver accepts.\n",
        "\n",
        "### Why use SQOA Solver?\n",
        "\n",
        "Many quantum optimization approaches assign one qubit to each binary variable, so the capacity of the quantum hardware can directly limit the problem size. SQOA Solver combines several reduction and hybrid-computing techniques to ease this restriction:\n",
        "\n",
        "* **It can tackle problems with more binary variables than the QPU has qubits.** Presolve applies reduction rules that can fix variables and simplify the remaining QUBO while preserving at least one optimal solution. Empirically, presolve can also make the remaining QUBO sparser, which helps the QRAC-based encoding that follows it.\n",
        "\n",
        "  The solver then uses a QRAC-based encoding derived from Quantum Random Access Optimization (QRAO) [\\[3\\]](#references) to represent multiple variables (up to three) with no direct QUBO interaction on the same qubit.\n",
        "\n",
        "  The reductions from presolve and encoding depend on the problem structure. For example, in this tutorial, a 200-variable problem uses only 67 qubits.\n",
        "* **It can be less sensitive to hardware noise.** Sample-based quantum diagonalization [\\[4\\]](#references), [\\[5\\]](#references) uses the QPU to sample bitstrings but performs the diagonalization classically. This avoids repeated QPU evaluations for variational parameter updates and can reduce the impact of noise on the optimization workflow.\n",
        "* **It automates the workflow from presolve through post-processing.** You provide a correctly formatted QUBO instance and select a backend. SQOA Solver then runs the whole workflow automatically. It applies presolve, maps the classical variables to a relaxed quantum problem, constructs and transpiles the sampling circuits, performs quantum sampling and classical diagonalization, rounds and refines the assignment, and reconstructs and evaluates the classical solution.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "76a1b7f9",
      "metadata": {},
      "source": [
        "## Requirements\n",
        "\n",
        "Before you start this tutorial, make sure that you have the following packages installed:\n",
        "\n",
        "* Qiskit Functions Catalog client (`pip install qiskit-ibm-catalog`).\n",
        "* Qiskit addon optimization mapper, used to formulate the problem (`pip install qiskit-addon-opt-mapper`).\n",
        "* NetworkX and Matplotlib, used to handle and plot the graph (`pip install networkx matplotlib`).\n",
        "\n",
        "You also need access to SQOA Solver from the Qiskit Functions Catalog. [Fill out the form](/functions?id=jij-sqoa-solver) to request access to the function.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a4375bbe",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install qiskit-ibm-catalog qiskit-addon-opt-mapper networkx matplotlib"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8b812370",
      "metadata": {},
      "source": [
        "## Setup\n",
        "\n",
        "First, import the required packages and tools.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e2972d86",
      "metadata": {},
      "outputs": [],
      "source": [
        "import urllib.request\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import networkx as nx\n",
        "from qiskit_addon_opt_mapper import OptimizationProblem\n",
        "from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f66e681-0259-4a09-bc6e-d68bdc2168a9",
      "metadata": {},
      "source": [
        "SQOA Solver accepts a QUBO problem as a coefficient dictionary in the tuple-string format used by IBM® optimization functions. Define a helper function that converts the QUBO returned by `OptimizationProblemToQubo` into this format.\n",
        "\n",
        "The converter always returns a minimization problem, which matches how SQOA Solver treats a dictionary problem. The keys use the variable indices of the QUBO, and every variable receives a linear entry, even with a zero coefficient, so that the returned solution covers all variables.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "69415c97-41be-42d0-bbb5-74fdbc10ad39",
      "metadata": {},
      "outputs": [],
      "source": [
        "def to_problem_dict(qubo: OptimizationProblem) -> dict[str, float]:\n",
        "    \"\"\"Convert a QUBO from OptimizationProblemToQubo to the SQOA Solver dictionary format.\"\"\"\n",
        "    objective = qubo.objective\n",
        "    payload = {\"()\": objective.constant}\n",
        "    # Keep every variable in the problem, even with a zero linear coefficient.\n",
        "    payload.update({f\"({i},)\": 0.0 for i in range(qubo.get_num_vars())})\n",
        "    for i, coefficient in objective.linear.to_dict().items():\n",
        "        payload[f\"({i},)\"] += coefficient\n",
        "    for (i, j), coefficient in objective.quadratic.to_dict().items():\n",
        "        # Fold a binary diagonal term x_i * x_i = x_i into the linear term.\n",
        "        key = f\"({i},)\" if i == j else f\"({i}, {j})\"\n",
        "        payload[key] = payload.get(key, 0.0) + coefficient\n",
        "    return {key: float(value) for key, value in payload.items()}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d1fe4ec",
      "metadata": {},
      "source": [
        "## Step 1: Map classical inputs to a quantum problem\n",
        "\n",
        "SQOA Solver accepts a QUBO problem and performs the quantum mapping inside the function. In this tutorial, you download an MIS instance from QOBLIB and formulate it as a QUBO with `qiskit-addon-opt-mapper`. This step covers the following:\n",
        "\n",
        "* Connect to SQOA Solver.\n",
        "* Understand the SQOA framework.\n",
        "* Load the problem instance.\n",
        "* Formulate the QUBO and convert it for submission.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cb7a7871",
      "metadata": {},
      "source": [
        "### Connect to SQOA Solver\n",
        "\n",
        "Connect to the Qiskit Functions Catalog with your IBM Quantum Platform credentials, then load SQOA Solver. The following code assumes that you already [saved your account](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) to your local environment.\n",
        "\n",
        "If the connection fails, confirm that your IBM Quantum Platform account administrator has granted the selected service instance access to SQOA Solver.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "89bcd2ef",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "solver = catalog.load(\"jij/sqoa-solver\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4db00408",
      "metadata": {},
      "source": [
        "### Understand the SQOA framework\n",
        "\n",
        "The Sampling-based Quantum Optimization Algorithm with Quantum Relaxation (SQOA-QR) [\\[2\\]](#references) combines Quantum Random Access Optimization (QRAO) with sample-based quantum diagonalization:\n",
        "\n",
        "1. **Quantum relaxation and encoding:** SQOA Solver converts the QUBO to a minimization Ising objective of the form $c + \\sum_i h_i z_i + \\sum_{i<j} J_{ij} z_i z_j$, where $z_i \\in \\{-1, +1\\}$. QRAO [\\[3\\]](#references) then maps each spin variable to a Pauli observable. For the $(3,1)$-QRAC encoding used here, the relaxed Hamiltonian is\n",
        "\n",
        "   $$\n",
        "   \\widetilde{H} = cI + \\sum_i \\sqrt{k_i} h_i P_{f(i)} + \\sum_{i<j} \\sqrt{k_i k_j} J_{ij} P_{f(i)} P_{f(j)},\n",
        "   $$\n",
        "\n",
        "   where $I$ is the identity operator, $f(i)$ assigns variable $i$ to a qubit and one of $X$, $Y$, or $Z$, and $k_i \\in \\{1,2,3\\}$ is the number of variables mapped to the same qubit as $i$. When both relevant qubits contain three variables, the linear and quadratic scaling factors become $\\sqrt{3}$ and $3$, respectively.\n",
        "\n",
        "   Variables that share a qubit are assigned different noncommuting Pauli observables. Variables that interact in the QUBO are kept on different qubits, while variables with no direct QUBO interaction can share a qubit. The reduction in qubit count therefore depends on the structure of the problem.\n",
        "2. **Quantum sampling:** A quantum processor samples bitstrings from circuits designed to explore low-energy states of the relaxed Hamiltonian.\n",
        "3. **Sample-based diagonalization:** The solver projects the relaxed Hamiltonian onto the subspace spanned by basis states obtained from the sampled bitstrings and diagonalizes the projected Hamiltonian classically [\\[4\\]](#references), [\\[5\\]](#references). For a fixed set of sampled bitstrings, let $\\mathcal{S}_r$ be the subspace spanned by $r$ distinct basis states, so $\\dim(\\mathcal{S}_r) \\leq r$. Its estimated ground-state energy is:\n",
        "\n",
        "   $$\n",
        "   E_r = \\min_{\\substack{|\\psi\\rangle \\in \\mathcal{S}_r \\\\ \\langle\\psi|\\psi\\rangle=1}} \\langle\\psi|\\widetilde{H}|\\psi\\rangle, \\qquad \\mathcal{S}_r \\subseteq \\mathcal{S}_{r'} \\Longrightarrow E_{\\mathrm{exact}} \\leq E_{r'} \\leq E_r \\quad (r' \\geq r),\n",
        "   $$\n",
        "\n",
        "   where $E_{\\mathrm{exact}}$ is the ground-state energy of the full relaxed Hamiltonian. Therefore, a larger retained subspace cannot increase the estimated ground-state energy.\n",
        "\n",
        "   This ordering assumes the same sampling result and concerns the relaxed-Hamiltonian energy before rounding. It does not theoretically guarantee a better returned classical solution. However, in QRAO, a lower-energy state is heuristically expected to provide a better classical solution.\n",
        "4. **Rounding:** The relaxed state obtained from the diagonalization is converted into a classical binary assignment.\n",
        "\n",
        "SQOA Solver surrounds this framework with classical processing. Before the quantum mapping, presolve applies reduction rules that can fix variables and simplify the remaining QUBO while preserving at least one optimal solution. After the diagonalization and rounding, a classical local search refines the assignment, and OMMX reconstructs and evaluates the complete solution.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2f0bedf8",
      "metadata": {},
      "source": [
        "### Load the instance\n",
        "\n",
        "QOBLIB distributes each MIS instance as a graph in the DIMACS `.gph` format, a line-based format in which the line starting with `p` gives the numbers of nodes and edges and each line starting with `e` defines an edge.\n",
        "\n",
        "QOBLIB also publishes an optimal or best-known reference solution for each instance. The following functions download both from the QOBLIB repository. DIMACS labels nodes starting from 1, so the graph is relabeled to start from 0.\n",
        "\n",
        "If you want to start from your own graph instead, skip the download and formulate the problem from your NetworkX graph in the same way.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "39f996c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "QOBLIB_URL = \"https://raw.githubusercontent.com/ZIB-AOPT/QOBLIB/refs/heads/main/07-independentset\"\n",
        "\n",
        "\n",
        "def fetch_qoblib_graph(name: str) -> nx.Graph:\n",
        "    \"\"\"Download a QOBLIB independent-set instance as a NetworkX graph.\"\"\"\n",
        "    with urllib.request.urlopen(\n",
        "        f\"{QOBLIB_URL}/instances/{name}.gph\"\n",
        "    ) as response:\n",
        "        lines = response.read().decode().splitlines()\n",
        "    graph = nx.Graph()\n",
        "    for fields in map(str.split, lines):\n",
        "        if fields and fields[0] == \"p\":\n",
        "            # Header: p edge <number of nodes> <number of edges>\n",
        "            graph.add_nodes_from(range(int(fields[2])))\n",
        "        elif fields and fields[0] == \"e\":\n",
        "            # DIMACS labels nodes from 1; relabel them to start from 0.\n",
        "            graph.add_edge(int(fields[1]) - 1, int(fields[2]) - 1)\n",
        "    return graph\n",
        "\n",
        "\n",
        "def fetch_reference_objective(name: str) -> float:\n",
        "    \"\"\"Read the objective value of the QOBLIB reference solution.\"\"\"\n",
        "    with urllib.request.urlopen(\n",
        "        f\"{QOBLIB_URL}/solutions/{name}.opt.sol\"\n",
        "    ) as response:\n",
        "        for line in response.read().decode().splitlines():\n",
        "            if line.startswith(\"# Objective value\"):\n",
        "                return float(line.split(\"=\")[1])\n",
        "    raise ValueError(f\"No objective value found for {name}.\")\n",
        "\n",
        "\n",
        "INSTANCE_NAME = \"gen200_p0-9_44\"\n",
        "G = fetch_qoblib_graph(INSTANCE_NAME)\n",
        "reference_objective = fetch_reference_objective(INSTANCE_NAME)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "28051348",
      "metadata": {},
      "source": [
        "You use the objective value of the reference solution later to assess the result from SQOA Solver. This tutorial solves this problem on a device with 156 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "ad2a205c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "gen200_p0-9_44: 200 nodes, 1990 edges\n",
            "Reference objective: 44\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"{INSTANCE_NAME}: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges\"\n",
        ")\n",
        "print(f\"Reference objective: {reference_objective:g}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d91c9beb",
      "metadata": {},
      "source": [
        "Visualize the graph. Later, you also use it to verify that the nodes returned by SQOA Solver form an independent set.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "7c0c796c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/maximum-independent-set-jij-sqoa/extracted-outputs/7c0c796c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "layout = nx.forceatlas2_layout(\n",
        "    G, max_iter=500, scaling_ratio=3.0, gravity=1.0, seed=7\n",
        ")\n",
        "node_size = max(8, min(60, 4000 / max(G.number_of_nodes(), 1)))\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(9, 7))\n",
        "nx.draw_networkx_edges(G, layout, ax=ax, alpha=0.2, width=0.6)\n",
        "nx.draw_networkx_nodes(\n",
        "    G, layout, node_size=node_size, node_color=\"lightgray\", ax=ax\n",
        ")\n",
        "ax.set_title(\n",
        "    f\"{INSTANCE_NAME}: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges\"\n",
        ")\n",
        "ax.set_axis_off()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b8decb55",
      "metadata": {},
      "source": [
        "### Formulate the QUBO\n",
        "\n",
        "Build the binary linear formulation from the Background section as an `OptimizationProblem` with one binary variable per node, an objective that counts the selected nodes, and one constraint per edge. Then convert it with `OptimizationProblemToQubo`.\n",
        "\n",
        "With a penalty weight of 2, the converter replaces each edge constraint with the penalty term $2 x_v x_w$ and turns the maximization into a minimization, so the converted problem is QOBLIB's binary unconstrained formulation with its sign reversed. The QUBO's variable indices are the graph's node labels. The `IndependentSet` application class in `qiskit_addon_opt_mapper.applications` builds the same constrained model with `IndependentSet(G).to_optimization_problem()`.\n",
        "\n",
        "Finally, convert the QUBO to the dictionary format with `to_problem_dict()`. Because the problem is a binary QUBO, the request in Step 2 declares its variable domain as `\"binary\"`. The function performs presolve, quantum mapping, and circuit construction after submission.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "8fcbbfd6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "QUBO variables: 200\n",
            "Dictionary entries: 2191\n"
          ]
        }
      ],
      "source": [
        "mis = OptimizationProblem(INSTANCE_NAME)\n",
        "mis.binary_var_list(G.number_of_nodes(), name=\"x\")\n",
        "mis.maximize(linear={v: 1 for v in G.nodes})\n",
        "for u, v in G.edges:\n",
        "    mis.linear_constraint({u: 1, v: 1}, \"<=\", 1)\n",
        "\n",
        "converter = OptimizationProblemToQubo(penalty=2)\n",
        "qubo = converter.convert(mis)\n",
        "problem_dict = to_problem_dict(qubo)\n",
        "\n",
        "print(f\"QUBO variables: {qubo.get_num_vars()}\")\n",
        "print(f\"Dictionary entries: {len(problem_dict)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7fc23a05",
      "metadata": {},
      "source": [
        "## Step 2: Optimize the problem for quantum hardware execution\n",
        "\n",
        "SQOA Solver performs the quantum mapping and hardware preparation inside the function. It first applies presolve to the QUBO, then maps the remaining variables to a relaxed Hamiltonian by using a QRAC-based encoding.\n",
        "\n",
        "For quantum sampling, it constructs circuits based on the relaxed Hamiltonian and transpiles them for the selected backend. The problem-size and qubit reductions from presolve and encoding depend on the problem, so configure the solver without assuming a fixed reduction factor.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "387a134b",
      "metadata": {},
      "source": [
        "### Configure the solver inputs\n",
        "\n",
        "SQOA Solver requires two inputs:\n",
        "\n",
        "* `problem`, the dictionary from Step 1.\n",
        "* `variable_type`, which is `\"binary\"` for this QUBO.\n",
        "\n",
        "It also accepts two optional inputs:\n",
        "\n",
        "* `backend_name` selects the QPU. When it is omitted or `None`, the function selects the least busy operational QPU that is accessible to your instance.\n",
        "* `options` is a dictionary that controls the sampling budget (`shots`), the classical seed (`seed`), session usage (`session`), the classical local search (`postprocessing_level`), and job tags (`job_tags`). If you increase `shots`, the sampling statistics can improve.\n",
        "\n",
        "For all inputs, their default values, and validation rules, see the [API reference](/docs/api/functions/jij-sqoa-solver).\n",
        "\n",
        "This tutorial names the backend explicitly, uses the default values for `shots`, `session`, and `postprocessing_level`, and sets the classical seed. Collect the inputs in a dictionary so that you can pass them to `solver.run()` in Step 3.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "7797d5b7",
      "metadata": {},
      "outputs": [],
      "source": [
        "# ibm_fez has 156 qubits\n",
        "backend_name = (\n",
        "    \"ibm_fez\"  # Omit this key or pass None for automatic selection.\n",
        ")\n",
        "\n",
        "sqoa_input = {\n",
        "    \"problem\": problem_dict,\n",
        "    \"variable_type\": \"binary\",\n",
        "    \"backend_name\": backend_name,\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "af634b0a",
      "metadata": {},
      "outputs": [],
      "source": [
        "sqoa_input[\"options\"] = {\n",
        "    \"shots\": 4096,\n",
        "    \"seed\": 42,\n",
        "    \"session\": False,\n",
        "    \"postprocessing_level\": 1,\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88aa5a12",
      "metadata": {},
      "source": [
        "## Step 3: Execute using primitives\n",
        "\n",
        "Submit the job with the SQOA Solver connection created in Step 1. The function runs the IBM Quantum Sampler primitive on the selected backend, collects the measured bitstrings, and continues with the classical diagonalization and post-processing. You interact with a single function job.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "132af854",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: dcee8e26-b8ac-4dd7-a76e-5adb5bc388e5\n"
          ]
        }
      ],
      "source": [
        "job = solver.run(**sqoa_input)\n",
        "print(f\"Job ID: {job.job_id}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4446811",
      "metadata": {},
      "source": [
        "To check the status of the workload, see [Check job status](/docs/guides/functions-get-started#check-job-status). Internally, the function proceeds through mapping, hardware optimization, queue waiting, QPU execution, and post-processing phases.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "7b93cd2b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'INITIALIZING'"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6fedb728",
      "metadata": {},
      "source": [
        "`job.result()` blocks until the job completes and returns a dictionary with the solution, its objective value and feasibility, and metadata that holds solver information and resource usage. For the full definition of the inputs and outputs, see the [API reference](/docs/api/functions/jij-sqoa-solver).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "fabef6c3",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "142deb24",
      "metadata": {},
      "source": [
        "## Step 4: Post-process the result and convert it to a classical format\n",
        "\n",
        "Inspect the complete function output, map the returned assignment back to the graph, verify the selected nodes, and visualize the result.\n",
        "\n",
        "### Inspect the result\n",
        "\n",
        "The result dictionary contains four top-level fields:\n",
        "\n",
        "* `solution` is the complete variable assignment, keyed by `x_i`, where *i* is the variable index (for example, `x_17`). In this tutorial, the variable index is the node label.\n",
        "* `objective_value` is the objective of that assignment evaluated on the submitted dictionary, that is, on the minimization QUBO.\n",
        "* `feasible` refers only to the variable domain, because the dictionary has no constraints.\n",
        "* `metadata` contains `solver_info` and `resource_usage`. `solver_info` records the effective `seed` of the run. If the classical local search ran, `solver_info` also records the rounded assignment from before that refinement in `solution_before_postprocessing`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "69f9f5b1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result fields: ['solution', 'objective_value', 'feasible', 'metadata']\n",
            "Solution entries: 200\n",
            "First entries: {'x_0': 0.0, 'x_1': 0.0, 'x_2': 1.0}\n",
            "Objective value: -35.0\n",
            "Feasible: True\n",
            "Solver-info fields: ['seed', 'solution_before_postprocessing']\n",
            "Effective seed: 42\n",
            "Resource-usage phases: ['RUNNING: MAPPING', 'RUNNING: OPTIMIZING_FOR_HARDWARE', 'RUNNING: WAITING_FOR_QPU', 'RUNNING: EXECUTING_QPU', 'RUNNING: POST_PROCESSING']\n"
          ]
        }
      ],
      "source": [
        "solver_info = result[\"metadata\"][\"solver_info\"]\n",
        "\n",
        "print(f\"Result fields: {list(result)}\")\n",
        "print(f\"Solution entries: {len(result['solution'])}\")\n",
        "print(f\"First entries: {dict(list(result['solution'].items())[:3])}\")\n",
        "print(f\"Objective value: {result['objective_value']}\")\n",
        "print(f\"Feasible: {result['feasible']}\")\n",
        "print(f\"Solver-info fields: {list(solver_info)}\")\n",
        "print(f\"Effective seed: {solver_info['seed']}\")\n",
        "print(f\"Resource-usage phases: {list(result['metadata']['resource_usage'])}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e74d4d0",
      "metadata": {},
      "source": [
        "### Map the solution to the graph\n",
        "\n",
        "Collect the returned values in the order of the variable indices, and map them back to the variables of the original problem with `interpret()`. The selected nodes are the variables with the value 1, and `is_feasible()` checks the assignment against the edge constraints of the original problem.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "98bdba4e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Selected nodes: 35\n",
            "Satisfies every edge constraint: True\n"
          ]
        }
      ],
      "source": [
        "x_qubo = [result[\"solution\"][f\"x_{i}\"] for i in range(qubo.get_num_vars())]\n",
        "x = converter.interpret(x_qubo)\n",
        "selected_nodes = [node for node, value in enumerate(x) if value > 0.5]\n",
        "\n",
        "print(f\"Selected nodes: {len(selected_nodes)}\")\n",
        "print(f\"Satisfies every edge constraint: {mis.is_feasible(x)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b9273410",
      "metadata": {},
      "source": [
        "### Compare with the solution before post-processing\n",
        "\n",
        "Because `postprocessing_level` is `1`, `solver_info` also records the rounded assignment from before the classical local search in `solution_before_postprocessing`. It uses the same keys as `solution`.\n",
        "\n",
        "Evaluate it on the QUBO to see how much the refinement improved the result. Both values are objectives of the submitted minimization QUBO, so a lower value is better. The field is omitted when the local search does not run, for example, when presolve fixes every variable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "662c7158",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Objective before local search: -12.0\n",
            "Objective after local search: -35.0\n",
            "Variables changed by local search: 19\n"
          ]
        }
      ],
      "source": [
        "before = solver_info.get(\"solution_before_postprocessing\")\n",
        "if before is None:\n",
        "    print(\"The classical local search did not run for this problem.\")\n",
        "else:\n",
        "    x_before = [before[f\"x_{i}\"] for i in range(qubo.get_num_vars())]\n",
        "    changed = sum(\n",
        "        before[key] != value for key, value in result[\"solution\"].items()\n",
        "    )\n",
        "    print(\n",
        "        f\"Objective before local search: {qubo.objective.evaluate(x_before)}\"\n",
        "    )\n",
        "    print(f\"Objective after local search: {result['objective_value']}\")\n",
        "    print(f\"Variables changed by local search: {changed}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7cc3d9f2",
      "metadata": {},
      "source": [
        "### Verify the independence constraint\n",
        "\n",
        "Because the QUBO represents adjacency through penalty terms rather than explicit constraints, a heuristic result can still select adjacent nodes. Check the returned nodes directly against the graph. For the comparison and visualization in the following sections, copy the returned set and greedily remove one endpoint of each detected conflict.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "098b91ac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Conflicting edges in the returned set: 0\n",
            "Independent set size after repair: 35\n"
          ]
        }
      ],
      "source": [
        "returned_set = set(selected_nodes)\n",
        "conflicts = [\n",
        "    (u, v) for u, v in G.edges if u in returned_set and v in returned_set\n",
        "]\n",
        "print(f\"Conflicting edges in the returned set: {len(conflicts)}\")\n",
        "\n",
        "independent_set = returned_set.copy()\n",
        "for u, v in conflicts:\n",
        "    if u in independent_set and v in independent_set:\n",
        "        # Drop the endpoint that has more selected neighbors.\n",
        "        drop = max(\n",
        "            (u, v),\n",
        "            key=lambda node: sum(nb in independent_set for nb in G[node]),\n",
        "        )\n",
        "        independent_set.remove(drop)\n",
        "\n",
        "assert all(\n",
        "    not (u in independent_set and v in independent_set) for u, v in G.edges\n",
        ")\n",
        "print(f\"Independent set size after repair: {len(independent_set)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db9072ea",
      "metadata": {},
      "source": [
        "### Compare with the reference solution\n",
        "\n",
        "In Step 1, you downloaded the objective value of the [QOBLIB reference solution](https://github.com/ZIB-AOPT/QOBLIB/tree/main/07-independentset/solutions). QOBLIB reference solutions can be optimal or best-known, so compare against the reference objective without assuming that it is always a certified optimum.\n",
        "\n",
        "For this maximization problem, a ratio of 1.0 means that the repaired result matches the reference objective.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "dcea1081",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Reference objective: 44\n",
            "Ratio to reference: 0.795\n"
          ]
        }
      ],
      "source": [
        "ratio_to_reference = len(independent_set) / reference_objective\n",
        "\n",
        "print(f\"Reference objective: {reference_objective:g}\")\n",
        "print(f\"Ratio to reference: {ratio_to_reference:.3f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4b3146d1",
      "metadata": {},
      "source": [
        "### Visualize the result\n",
        "\n",
        "Plot the graph again with the repaired independent set highlighted. No two highlighted nodes share an edge.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "41f062f0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/maximum-independent-set-jij-sqoa/extracted-outputs/41f062f0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, ax = plt.subplots(figsize=(9, 7))\n",
        "nx.draw_networkx_edges(G, layout, ax=ax, alpha=0.2, width=0.6)\n",
        "nx.draw_networkx_nodes(\n",
        "    G,\n",
        "    layout,\n",
        "    nodelist=[v for v in G if v not in independent_set],\n",
        "    node_size=node_size,\n",
        "    node_color=\"lightgray\",\n",
        "    ax=ax,\n",
        ")\n",
        "nx.draw_networkx_nodes(\n",
        "    G,\n",
        "    layout,\n",
        "    nodelist=sorted(independent_set),\n",
        "    node_size=node_size * 1.4,\n",
        "    node_color=\"tab:blue\",\n",
        "    ax=ax,\n",
        ")\n",
        "ax.set_title(\n",
        "    f\"Independent set size: {len(independent_set)} \"\n",
        "    f\"(reference: {reference_objective:g})\"\n",
        ")\n",
        "ax.set_axis_off()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bafb34d2",
      "metadata": {},
      "source": [
        "### Read the resource usage\n",
        "\n",
        "The result metadata reports `CPU_TIME` for phases other than `RUNNING: EXECUTING_QPU`, when it is available. The `RUNNING: EXECUTING_QPU` phase can report `QPU_TIME`, the QPU usage returned by Quantum Compute, if the final usage is available for every Quantum Compute job.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "1a4ed12d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "RUNNING: MAPPING                     CPU     11.8 s\n",
            "RUNNING: OPTIMIZING_FOR_HARDWARE     CPU     15.4 s\n",
            "RUNNING: WAITING_FOR_QPU             CPU     16.5 s\n",
            "RUNNING: EXECUTING_QPU               QPU      3.0 s\n",
            "RUNNING: POST_PROCESSING             CPU     55.8 s\n"
          ]
        }
      ],
      "source": [
        "usage = result[\"metadata\"][\"resource_usage\"]\n",
        "\n",
        "for phase, metrics in usage.items():\n",
        "    line = f\"{phase:36s}\"\n",
        "    if \"CPU_TIME\" in metrics:\n",
        "        line += f\" CPU {metrics['CPU_TIME']:8.1f} s\"\n",
        "    if \"QPU_TIME\" in metrics:\n",
        "        line += f\" QPU {metrics['QPU_TIME']:8.1f} s\"\n",
        "    print(line)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f19cf8e4",
      "metadata": {},
      "source": [
        "## Conclusion\n",
        "\n",
        "In this tutorial, you downloaded an MIS instance from QOBLIB, formulated it as a QUBO with `qiskit-addon-opt-mapper`, connected to SQOA Solver, configured and submitted the workload, mapped the returned assignment back to the graph, and checked the selected nodes against the original graph.\n",
        "\n",
        "You also compared the result with the QOBLIB reference solution, examined the contribution of the classical post-processing, and inspected the resource usage of each execution phase.\n",
        "\n",
        "SQOA Solver combines presolve, QRAO with a QRAC-based encoding, sampling-circuit construction, hardware-aware transpilation, sample-based diagonalization, rounding, and classical refinement. This workflow can map more binary variables than a one-variable-per-qubit encoding would allow. The amount of reduction from presolve and encoding depends on the problem structure, and solution quality depends on the samples obtained.\n",
        "\n",
        "The `shots` and `postprocessing_level` settings control the quantum sampling budget and the classical refinement, respectively, without changing the submission workflow.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "86d6f25d-b1b2-4c36-9e23-d4d28827ffa2",
      "metadata": {},
      "source": [
        "## Appendix: Submit the problem as an OMMX instance\n",
        "\n",
        "SQOA Solver also accepts an `ommx.v1.Instance` from the [OMMX](https://jij-inc.github.io/ommx/en/introduction.html) SDK. The [OMMX Quantum Benchmarks](https://jij-inc.github.io/OmmxQuantumBenchmarks/) package distributes the QOBLIB problems as OMMX instances together with their reference solutions, so you can load the same QUBO without formulating it yourself.\n",
        "\n",
        "With an OMMX input, `objective_value` is evaluated in the sense of the instance, and the result also contains the canonical `ommx.v1.Solution`. Install the package, which also installs the OMMX SDK:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "36bbe766-501d-44ac-aa1c-c5ef24d54220",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install ommx-quantum-benchmarks"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64f7323a-93bf-4cdd-89ce-44df55042bf7",
      "metadata": {},
      "source": [
        "Load the `binary_unconstrained` model of the same instance. The dataset loader prints progress messages, so the code captures them to keep the output compact.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "97fd53f2-cafe-44ae-81e7-b006e0a0c6ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "%%capture\n",
        "from ommx_quantum_benchmarks.qoblib import IndependentSet\n",
        "\n",
        "ommx_instance, _ = IndependentSet()(\"binary_unconstrained\", INSTANCE_NAME)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f73453d3-a9bc-4915-91a8-c2938d7a607c",
      "metadata": {},
      "source": [
        "SQOA Solver receives an OMMX instance as a standard padded Base64 string and returns the canonical solution in the same format. Define two helper functions for this conversion:\n",
        "\n",
        "* `to_problem_payload()` serializes an `ommx.v1.Instance` into the string that you pass as `problem`.\n",
        "* `to_ommx_solution()` decodes the canonical `ommx.v1.Solution` from the result.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "2add2740-140f-4aff-95ef-77639df9a418",
      "metadata": {},
      "outputs": [],
      "source": [
        "import base64\n",
        "\n",
        "from ommx.v1 import Instance, Solution\n",
        "\n",
        "\n",
        "def to_problem_payload(instance: Instance) -> str:\n",
        "    \"\"\"Serialize an OMMX instance into the `problem` string for SQOA Solver.\"\"\"\n",
        "    return base64.b64encode(instance.to_bytes()).decode()\n",
        "\n",
        "\n",
        "def to_ommx_solution(result: dict) -> Solution:\n",
        "    \"\"\"Decode the canonical OMMX solution from a SQOA Solver result.\"\"\"\n",
        "    payload = result[\"metadata\"][\"solver_info\"][\"ommx_solution\"]\n",
        "    return Solution.from_bytes(base64.b64decode(payload, validate=True))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "166fa32d-8098-4d9e-a8d8-d79b0054b684",
      "metadata": {},
      "source": [
        "Submit the instance with the same inputs as before, replacing only `problem`. Then decode the canonical solution and read the selected nodes by variable name.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "92fc4b25-abae-4f93-9c23-820f7fbf0ee9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Objective value: 35.0\n",
            "Selected nodes: 35\n"
          ]
        }
      ],
      "source": [
        "ommx_job = solver.run(\n",
        "    **{**sqoa_input, \"problem\": to_problem_payload(ommx_instance)}\n",
        ")\n",
        "ommx_result = ommx_job.result()\n",
        "\n",
        "ommx_solution = to_ommx_solution(ommx_result)\n",
        "x_values = ommx_solution.extract_decision_variables(\"x\")\n",
        "ommx_nodes = sorted(\n",
        "    node for (node,), value in x_values.items() if value > 0.5\n",
        ")\n",
        "\n",
        "print(f\"Objective value: {ommx_result['objective_value']}\")\n",
        "print(f\"Selected nodes: {len(ommx_nodes)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a6f82a21",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  * Apply the workflow to your own QUBO problems. Build an `OptimizationProblem` as shown in the [SQOA Solver guide](/docs/guides/jij-sqoa-solver). The steps for conversion and submission remain the same, but adapt the validation to the structure of your problem.\n",
        "  * Submit the problem as an OMMX instance, as shown in the [appendix](#appendix-submit-the-problem-as-an-ommx-instance). See the [API reference](/docs/api/functions/jij-sqoa-solver) for the accepted problem formats and the returned fields.\n",
        "  * Explore other independent-set instances in the [QOBLIB repository](https://github.com/ZIB-AOPT/QOBLIB/tree/main/07-independentset) or through [OMMX Quantum Benchmarks](https://jij-inc.github.io/OmmxQuantumBenchmarks/).\n",
        "  * Learn more about modeling with [qiskit-addon-opt-mapper](https://qiskit.github.io/qiskit-addon-opt-mapper/) or [JijModeling](https://jij-inc-jijmodeling-tutorials-en.readthedocs-hosted.com/en/latest/introduction.html) and exchanging problems with [OMMX](https://ommx-en-book.readthedocs.io/en/latest/).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3337d125",
      "metadata": {},
      "source": [
        "## References\n",
        "\n",
        "\\[1] Koch, T., Bernal Neira, D. E., Chen, Y., Cortiana, G., Egger, D. J., Heese, R., Hegade, N. N., Gomez Cadavid, A., Huang, R., Itoko, T., et al. (2026). Quantum optimization benchmarking library: The intractable decathlon. Nature Computational Science, 6, 653–671. [DOI](https://doi.org/10.1038/s43588-026-00991-1), [arXiv:2504.03832](https://arxiv.org/abs/2504.03832)\n",
        "\n",
        "\\[2] Matsuyama, H., & Yamashiro, Y. (2025). Sampling-based quantum optimization algorithm with quantum relaxation. 2025 IEEE International Conference on Quantum Computing and Engineering (QCE), 1, 2323–2333. [DOI](https://doi.org/10.1109/QCE65121.2025.00253), [arXiv:2504.12629](https://arxiv.org/abs/2504.12629)\n",
        "\n",
        "\\[3] Fuller, B., Hadfield, C., Glick, J. R., Imamichi, T., Itoko, T., Thompson, R. J., Jiao, Y., Kagele, M. M., Blom-Schieber, A. W., Raymond, R., et al. (2024). Approximate solutions of combinatorial problems via quantum relaxations. IEEE Transactions on Quantum Engineering, 5, 1–15. [DOI](https://doi.org/10.1109/TQE.2024.3421294), [arXiv:2111.03167](https://arxiv.org/abs/2111.03167)\n",
        "\n",
        "\\[4] Kanno, K., Kohda, M., Imai, R., Koh, S., Mitarai, K., Mizukami, W., & Nakagawa, Y. O. (2026). Quantum-selected configuration interaction: Classical diagonalization of Hamiltonians in subspaces selected by quantum computers. Physical Review Research, 8(2), 023268. [DOI](https://doi.org/10.1103/dmn4-snfx), [arXiv:2302.11320](https://arxiv.org/abs/2302.11320)\n",
        "\n",
        "\\[5] Robledo-Moreno, J., Motta, M., Haas, H., Javadi-Abhari, A., Jurcevic, P., Kirby, W., et al. (2025). Chemistry beyond the scale of exact diagonalization on a quantum-centric supercomputer. Science Advances, 11(25), eadu9991. [DOI](https://doi.org/10.1126/sciadv.adu9991), [arXiv:2405.05068](https://arxiv.org/abs/2405.05068)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "description": "Solve a maximum independent set problem from QOBLIB with SQOA Solver, a sampling-based Qiskit Function by JIJ.",
    "hours": 1,
    "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"
    },
    "title": "Solve a maximum independent set problem with more variables than qubits using JIJ's SQOA Solver"
  },
  "nbformat": 4,
  "nbformat_minor": 5
}