{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "11ea9666",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Optimization Solver - Una funzione Qiskit di Q-CTRL Fire Opal\"\n",
        "description: \"Inizia a risolvere i problemi di ottimizzazione su scala industriale con Fire Opal Optimization Solver di Q-CTRL\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Sachdeva */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dde95705",
      "metadata": {},
      "source": [
        "<span id=\"optimization-solver-a-qiskit-function-by-q-ctrl-fire-opal\" />\n",
        "\n",
        "# Optimization Solver: una funzione Qiskit di Q-CTRL Fire Opal\n",
        "\n",
        "*Consulta la [documentazione dell'API](/docs/api/functions/q-ctrl-optimization-solver)*\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Le funzioni Qiskit sono una funzione sperimentale disponibile solo per gli utenti di IBM Quantum® Premium Plan, Flex Plan e On-Prem (tramite IBM Quantum Platform API) Plan. Sono in stato di anteprima e sono soggetti a modifiche.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "40c2085c",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versioni del pacchetto\">\n",
        "    Il codice presente in questa pagina è stato sviluppato sulla base dei seguenti requisiti.\n",
        "    Si consiglia di utilizzare queste versioni o quelle più recenti.\n",
        "\n",
        "    ```\n",
        "    qiskit-ibm-runtime~=0.47.0\n",
        "    sympy~=1.14.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e30c7881",
      "metadata": {},
      "source": [
        "<span id=\"overview\" />\n",
        "\n",
        "## Panoramica\n",
        "\n",
        "Con il Fire Opal Optimization Solver, è possibile risolvere problemi di ottimizzazione su scala industriale su hardware quantistico senza richiedere competenze in materia. È sufficiente inserire la definizione del problema di alto livello e il risolutore si occuperà del resto. L'intero flusso di lavoro è consapevole del rumore e sfrutta il [Performance Management di Fire Opal](/docs/guides/q-ctrl-performance-management). Il Solver fornisce costantemente soluzioni accurate a problemi classici, anche su scala full-device sulle più grandi QPU IBM®.\n",
        "\n",
        "Il Solver è flessibile e può essere utilizzato per risolvere problemi di ottimizzazione combinatoria definiti come funzioni obiettivo o grafi arbitrari. Non è necessario associare i problemi alla topologia dei dispositivi. È possibile risolvere sia i problemi senza vincoli che quelli con vincoli, applicando questi ultimi come vincoli rigidi di tipo “ Hamming-weight-1 ” anziché come termini di penalità. Gli esempi riportati in questa guida illustrano come risolvere un problema di ottimizzazione su larga scala, sia senza vincoli che con vincoli, utilizzando diversi tipi di input per il Solver. Il primo esempio riguarda un problema di taglio massimo definito su un grafo a 156 nodi e di grado 3, mentre il secondo esempio affronta un problema di partizionamento di un grafo a 50 nodi definito da una funzione di costo.\n",
        "\n",
        "Per accedere al risolutore di ottimizzazione, [contattare Q-CTRL](https://form.typeform.com/to/uOAVDnGg?typeform-source=q-ctrl.com).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f761442",
      "metadata": {},
      "source": [
        "<span id=\"function-description\" />\n",
        "\n",
        "## Descrizione funzione\n",
        "\n",
        "Il risolutore ottimizza e automatizza completamente l'intero algoritmo, dalla soppressione degli errori a livello hardware all'efficiente mappatura del problema e all'ottimizzazione classica ad anello chiuso. Dietro le quinte, la pipeline del risolutore riduce gli errori in ogni fase, consentendo di migliorare le prestazioni necessarie per scalare in modo significativo. Il flusso di lavoro sottostante si ispira al Quantum Approximate Optimization Algorithm (QAOA), che è un algoritmo ibrido quantistico-classico. Per un riepilogo dettagliato dell'intero flusso di lavoro di Optimization Solver, consultare il [manoscritto pubblicato](https://arxiv.org/abs/2406.01743).\n",
        "\n",
        "![Visualizzazione del flusso di lavoro del risolutore di ottimizzazione](https://quantum.cloud.ibm.com/docs/images/guides/qctrl-optimization/solver_workflow.svg)\n",
        "\n",
        "Per risolvere un problema generico con il Risolutore di ottimizzazione:\n",
        "\n",
        "1. Definire il problema come una funzione obiettivo, un grafico o una catena di spin `SparsePauliOp` .\n",
        "2. Collegarsi alla funzione attraverso il Catalogo delle funzioni di Qiskit.\n",
        "3. Eseguire il problema con il Risolutore e recuperare i risultati.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d20af236",
      "metadata": {},
      "source": [
        "<span id=\"accepted-problem-formats\" />\n",
        "\n",
        "### Formati di problema accettati\n",
        "\n",
        "* Rappresentazione dell'espressione polinomiale di una funzione obiettivo. Idealmente creato in Python con un oggetto Poly esistente in SymPy e formattato in una stringa usando [sympy.srepr](https://docs.sympy.org/latest/tutorials/intro-tutorial/printing.html#srepr).\n",
        "* Rappresentazione grafica di un tipo specifico di problema. Il grafico dovrebbe essere creato utilizzando la libreria networkx disponibile all'indirizzo Python. Dovrebbe quindi essere convertito in una stringa utilizzando la funzione di NetworkX [`nx.readwrite.json_graph.adjacency_data`](https://networkx.org/documentation/stable/reference/readwrite/generated/networkx.readwrite.json_graph.adjacency_data.html).\n",
        "* Rappresentazione a catena di spin di un problema specifico. La catena di spin deve essere rappresentata come un oggetto `SparsePauliOp` ; si veda la [documentazione](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) per maggiori dettagli.\n",
        "\n",
        "<Admonition type=\"note\" title=\"Questa funzione supporta tutti i backend di IBM?\">\n",
        "  Se desideri utilizzare un backend che questa funzione non supporta al momento, [contatta Q-CTRL](https://form.typeform.com/to/iuujEAEI?typeform-source=q-ctrl.com) per richiedere l'aggiunta del supporto.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1165f4f0",
      "metadata": {},
      "source": [
        "<span id=\"benchmarks\" />\n",
        "\n",
        "## Benchmark\n",
        "\n",
        "[I risultati dei benchmark pubblicati](https://arxiv.org/abs/2406.01743) dimostrano che il Solver risolve con successo problemi con oltre 120 qubit, superando persino i risultati precedentemente pubblicati sull'annealing quantistico e sui dispositivi a ioni intrappolati. Le seguenti metriche di benchmark forniscono un'indicazione approssimativa dell'accuratezza e della scalabilità dei tipi di problemi sulla base di alcuni esempi. Le metriche effettive possono variare in base a varie caratteristiche del problema, come il numero di termini della funzione obiettivo (densità) e la loro localizzazione, il numero di variabili e l'ordine polinomiale.\n",
        "\n",
        "Il \"Numero di qubit\" indicato non è un limite rigido, ma rappresenta una soglia approssimativa in cui ci si può aspettare una precisione di soluzione estremamente costante. Sono stati risolti con successo problemi di dimensioni maggiori e si incoraggia la sperimentazione oltre questi limiti.\n",
        "\n",
        "La connettività arbitraria dei qubit è supportata in tutti i tipi di problemi.\n",
        "\n",
        "| Tipo di problema                           | Numero di qubit | Esempio                                                             | Accuratezza | Tempo totale | Utilizzo del tempo di esecuzione (s) | Numero di iterazioni |\n",
        "| ------------------------------------------ | --------------- | ------------------------------------------------------------------- | ----------- | ------------ | ------------------------------------ | -------------------- |\n",
        "| Problemi quadratici scarsamente connessi   | 156             | 3-regolare max-cut                                                  | 100%        | 1764         | 293                                  | 16                   |\n",
        "| Ottimizzazione binaria di ordine superiore | 156             | Modello Ising spin-glass                                            | 100%        | 1461         | 272                                  | 16                   |\n",
        "| Problemi quadratici densamente connessi    | 50              | Taglio massimo con connessione completa                             | 100%        | 1758         | 268                                  | 12                   |\n",
        "| Problema con vincoli rigidi                | 50              | Partizionamento di grafi ponderati con una densità di archi dell'8% | 100%        | 1074         | 215                                  | 10                   |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73390a19",
      "metadata": {},
      "source": [
        "<span id=\"get-started\" />\n",
        "\n",
        "## Introduzione\n",
        "\n",
        "Innanzitutto, autenticati utilizzando la tua [chiave API IBM Quantum](http://quantum.cloud.ibm.com/). Quindi, selezionare la funzione Qiskit come segue. (Questo frammento di codice presuppone che tu abbia già [salvato il tuo account](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) nell'ambiente locale.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "95a715d2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[QiskitFunction(qunova/hivqe-chemistry),\n",
              " QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),\n",
              " QiskitFunction(algorithmiq/tem),\n",
              " QiskitFunction(qedma/qesem),\n",
              " QiskitFunction(multiverse/singularity),\n",
              " QiskitFunction(ibm/circuit-function),\n",
              " QiskitFunction(q-ctrl/optimization-solver),\n",
              " QiskitFunction(colibritd/quick-pde),\n",
              " QiskitFunction(q-ctrl/performance-management),\n",
              " QiskitFunction(kipu-quantum/iskay-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": 2,
      "id": "8acf84f6",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Access Function\n",
        "solver = catalog.load(\"q-ctrl/optimization-solver\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e8837f5f",
      "metadata": {},
      "source": [
        "<span id=\"example-unconstrained-optimization\" />\n",
        "\n",
        "## Esempio: Ottimizzazione senza vincoli\n",
        "\n",
        "Esegui il problema [del taglio massimo](https://en.wikipedia.org/wiki/Maximum_cut) (max-cut). L'esempio seguente illustra le funzionalità del Solver su un problema di taglio massimo in un grafo non ponderato a 3 regolari con 156 nodi, ma è possibile risolvere anche problemi relativi a grafi ponderati.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a699235b",
      "metadata": {},
      "source": [
        "Oltre a `qiskit-ibm-catalog`, per eseguire questo esempio si useranno anche i seguenti pacchetti: `networkx` e `numpy`. Potete installare questi pacchetti decommentando la seguente cella se state eseguendo questo esempio in un notebook che utilizza il kernel IPython.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a74fe6a9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install networkx numpy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a41c4a0d",
      "metadata": {},
      "source": [
        "<span id=\"1-define-the-problem\" />\n",
        "\n",
        "### 1. Definire il problema\n",
        "\n",
        "È possibile risolvere un problema di taglio massimo definendo un problema grafico e specificando `problem_type='maxcut'`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "d56e1440",
      "metadata": {},
      "outputs": [],
      "source": [
        "import networkx as nx\n",
        "import numpy as np\n",
        "\n",
        "# Generate a random graph with 156 nodes\n",
        "maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "0a7255e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/q-ctrl-optimization-solver/extracted-outputs/0a7255e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Optionally, visualize the graph\n",
        "nx.draw_networkx(\n",
        "    maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e42a8d55",
      "metadata": {},
      "source": [
        "Il risolutore accetta una stringa come input per la definizione del problema.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2546e3e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Convert graph to string\n",
        "problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ab6f24f",
      "metadata": {},
      "source": [
        "<span id=\"2-run-the-problem\" />\n",
        "\n",
        "### 2. Esegui il problema\n",
        "\n",
        "Quando si utilizza il metodo di input basato sul grafico, specificare il tipo di problema.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "16c66d64",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Solve the problem\n",
        "maxcut_job = solver.run(\n",
        "    problem=problem_as_str,\n",
        "    problem_type=\"maxcut\",\n",
        "    backend_name=backend_name,  # E.g. \"ibm_fez\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "48069093",
      "metadata": {},
      "source": [
        "Per verificare [lo stato](/docs/guides/functions-get-started#check-job-status) del carico di lavoro della funzione Qiskit o per ottenere [i risultati, procedere](/docs/guides/functions-get-started#retrieve-results) come segue:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "856fe992",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "34b53970-d95a-4e24-8763-fc6f3d112843\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "QUEUED\n"
          ]
        }
      ],
      "source": [
        "# Print the ID so you can use it later, if necessary\n",
        "print(maxcut_job.job_id)\n",
        "\n",
        "# Get job status\n",
        "print(maxcut_job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74f48eab",
      "metadata": {},
      "source": [
        "<span id=\"3-retrieve-the-result\" />\n",
        "\n",
        "### 3. Recupera il risultato\n",
        "\n",
        "Recupera il valore di taglio ottimale dal dizionario dei risultati.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  La mappatura delle variabili alla stringa di bit potrebbe essere cambiata. Il dizionario di output contiene un `variables_to_bitstring_index_map` sottodizionario che aiuta a verificare l'ordinamento.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "6b571411",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Optimal cut value: 210.0\n"
          ]
        }
      ],
      "source": [
        "# Poll for results\n",
        "maxcut_result = maxcut_job.result()\n",
        "\n",
        "# Take the absolute value of the solution since the cost function is minimized\n",
        "qctrl_maxcut = abs(maxcut_result[\"solution_bitstring_cost\"])\n",
        "\n",
        "# Print the optimal cut value found by the Optimization Solver\n",
        "print(f\"Optimal cut value: {qctrl_maxcut}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "508e29b0",
      "metadata": {},
      "source": [
        "È possibile verificare l'accuratezza del risultato risolvendo il problema in modo classico con solutori open-source come [PuLP](https://coin-or.github.io/pulp/) se il grafo non è densamente connesso. I problemi ad alta densità possono richiedere solutori classici avanzati per convalidare la soluzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2817b13",
      "metadata": {},
      "source": [
        "<span id=\"example-constrained-optimization\" />\n",
        "\n",
        "## Esempio: Ottimizzazione vincolata\n",
        "\n",
        "L'esempio precedente relativo al metodo max-cut è un comune problema di ottimizzazione binaria quadratica senza vincoli. Il risolutore di ottimizzazione di Q-CTRL è in grado di risolvere anche problemi di ottimizzazione con vincoli, passando i vincoli rigidi direttamente al risolutore tramite l'input `constraint` , anziché codificarli come termini di penalità nella funzione obiettivo. Attualmente il Solver supporta i vincoli di tipo \" Hamming-weight-1 \": ciascun vincolo specifica un gruppo di variabili in cui esattamente una variabile deve essere uguale a 1 e le altre devono essere uguali a 0.\n",
        "\n",
        "L'esempio seguente illustra come costruire una funzione di costo e un insieme di vincoli rigidi per un problema di ottimizzazione con vincoli, ovvero [la partizionamento di un grafo](https://en.wikipedia.org/wiki/Graph_partition), assegnando ogni nodo di un grafo esattamente a uno dei diversi gruppi e minimizzando al contempo il peso totale degli archi i cui estremi appartengono allo stesso gruppo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a9fbc0e9",
      "metadata": {},
      "source": [
        "Oltre ai pacchetti `qiskit-ibm-catalog` e `qiskit` , per eseguire questo esempio si utilizzeranno anche i seguenti pacchetti: `numpy`, `networkx`, e `sympy`. Potete installare questi pacchetti decommentando la seguente cella se state eseguendo questo esempio in un notebook che utilizza il kernel IPython.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "c6428fd0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install numpy networkx sympy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "98d3bfd3",
      "metadata": {},
      "source": [
        "<span id=\"1-define-the-problem\" />\n",
        "\n",
        "### 1. Definire il problema\n",
        "\n",
        "Definire un problema di partizionamento casuale di un grafo generando un grafo con nodi ponderati in modo casuale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "c2ce65e3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/q-ctrl-optimization-solver/extracted-outputs/c2ce65e3-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import networkx as nx\n",
        "from sympy import Symbol, Poly, srepr\n",
        "\n",
        "# To change the weights, change the seed to any integer.\n",
        "rng_seed = 18\n",
        "_rng = np.random.default_rng(rng_seed)\n",
        "node_count = 50\n",
        "edge_probability = 0.08\n",
        "graph = nx.erdos_renyi_graph(\n",
        "    node_count, edge_probability, seed=rng_seed, directed=False\n",
        ")\n",
        "\n",
        "# add node weights\n",
        "min_weight = -1.0\n",
        "max_weight = 1.0\n",
        "for i in graph.nodes:\n",
        "    weight = (max_weight - min_weight) * _rng.random() + min_weight\n",
        "    graph.add_node(i, weight=weight)\n",
        "\n",
        "# Optionally, visualize the graph\n",
        "nx.draw_networkx(graph, nx.kamada_kawai_layout(graph), node_size=200)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89c91472",
      "metadata": {},
      "source": [
        "Un modello standard di ottimizzazione per la partizionamento dei grafi ponderati può essere formulato come segue. Suddividiamo i nodi del grafo in tre gruppi $g \\in \\{0, 1, 2\\}$ e poniamo che $n_{i,g} = 1$ se il nodo $i$ è assegnato al gruppo $g$, e $n_{i,g} = 0$ in caso contrario. L'obiettivo è ridurre al minimo il peso totale dei bordi i cui estremi appartengono allo stesso gruppo, dove il peso di un bordo $(i,j)$ è dato dalla somma dei pesi dei suoi due estremi, $\\omega_{i,j} = \\omega_i + \\omega_j$ :\n",
        "\n",
        "$\\textbf{Minimize}\\qquad y = \\sum_{(i,j)\\in E} \\omega_{i,j} \\sum_{g} n_{i,g}\\, n_{j,g}$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "91586ee9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Construct the cost function.\n",
        "group_count = 3\n",
        "variables = [\n",
        "    Symbol(f\"n[{i},{g}]\")\n",
        "    for i in range(node_count)\n",
        "    for g in range(group_count)\n",
        "]\n",
        "node_group_var = {\n",
        "    (i, g): variables[i * group_count + g]\n",
        "    for i in range(node_count)\n",
        "    for g in range(group_count)\n",
        "}\n",
        "cost_function = Poly(0, *variables)\n",
        "\n",
        "for i, j in graph.edges():\n",
        "    edge_weight = graph.nodes[i][\"weight\"] + graph.nodes[j][\"weight\"]\n",
        "    for g in range(group_count):\n",
        "        cost_function += (\n",
        "            edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]\n",
        "        )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9d01684b",
      "metadata": {},
      "source": [
        "Ogni nodo deve essere assegnato esattamente a uno dei tre gruppi. Si tratta di un vincolo di tipo “ Hamming-weight-1 ”: per ogni nodo $i$, esattamente uno tra $n_{i,0}, n_{i,1}, n_{i,2}$ deve essere pari a 1, mentre gli altri devono essere pari a 0:\n",
        "\n",
        "$n_{i,0} + n_{i,1} + n_{i,2} = 1 \\texttt{ for all } i \\in V$\n",
        "\n",
        "Anziché codificare questo requisito come termine di penalità nella funzione di costo, inseriscilo direttamente nel Solver come vincolo rigido utilizzando il campo di immissione `constraint` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "1ff1ffcc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Problem constraints: {\"('n[0,0]', 'n[0,1]', 'n[0,2]')\": 1, \"('n[1,0]', 'n[1,1]', 'n[1,2]')\": 1, \"('n[2,0]', 'n[2,1]', 'n[2,2]')\": 1, \"('n[3,0]', 'n[3,1]', 'n[3,2]')\": 1, \"('n[4,0]', 'n[4,1]', 'n[4,2]')\": 1, \"('n[5,0]', 'n[5,1]', 'n[5,2]')\": 1, \"('n[6,0]', 'n[6,1]', 'n[6,2]')\": 1, \"('n[7,0]', 'n[7,1]', 'n[7,2]')\": 1, \"('n[8,0]', 'n[8,1]', 'n[8,2]')\": 1, \"('n[9,0]', 'n[9,1]', 'n[9,2]')\": 1, \"('n[10,0]', 'n[10,1]', 'n[10,2]')\": 1, \"('n[11,0]', 'n[11,1]', 'n[11,2]')\": 1, \"('n[12,0]', 'n[12,1]', 'n[12,2]')\": 1, \"('n[13,0]', 'n[13,1]', 'n[13,2]')\": 1, \"('n[14,0]', 'n[14,1]', 'n[14,2]')\": 1, \"('n[15,0]', 'n[15,1]', 'n[15,2]')\": 1, \"('n[16,0]', 'n[16,1]', 'n[16,2]')\": 1, \"('n[17,0]', 'n[17,1]', 'n[17,2]')\": 1, \"('n[18,0]', 'n[18,1]', 'n[18,2]')\": 1, \"('n[19,0]', 'n[19,1]', 'n[19,2]')\": 1, \"('n[20,0]', 'n[20,1]', 'n[20,2]')\": 1, \"('n[21,0]', 'n[21,1]', 'n[21,2]')\": 1, \"('n[22,0]', 'n[22,1]', 'n[22,2]')\": 1, \"('n[23,0]', 'n[23,1]', 'n[23,2]')\": 1, \"('n[24,0]', 'n[24,1]', 'n[24,2]')\": 1, \"('n[25,0]', 'n[25,1]', 'n[25,2]')\": 1, \"('n[26,0]', 'n[26,1]', 'n[26,2]')\": 1, \"('n[27,0]', 'n[27,1]', 'n[27,2]')\": 1, \"('n[28,0]', 'n[28,1]', 'n[28,2]')\": 1, \"('n[29,0]', 'n[29,1]', 'n[29,2]')\": 1, \"('n[30,0]', 'n[30,1]', 'n[30,2]')\": 1, \"('n[31,0]', 'n[31,1]', 'n[31,2]')\": 1, \"('n[32,0]', 'n[32,1]', 'n[32,2]')\": 1, \"('n[33,0]', 'n[33,1]', 'n[33,2]')\": 1, \"('n[34,0]', 'n[34,1]', 'n[34,2]')\": 1, \"('n[35,0]', 'n[35,1]', 'n[35,2]')\": 1, \"('n[36,0]', 'n[36,1]', 'n[36,2]')\": 1, \"('n[37,0]', 'n[37,1]', 'n[37,2]')\": 1, \"('n[38,0]', 'n[38,1]', 'n[38,2]')\": 1, \"('n[39,0]', 'n[39,1]', 'n[39,2]')\": 1, \"('n[40,0]', 'n[40,1]', 'n[40,2]')\": 1, \"('n[41,0]', 'n[41,1]', 'n[41,2]')\": 1, \"('n[42,0]', 'n[42,1]', 'n[42,2]')\": 1, \"('n[43,0]', 'n[43,1]', 'n[43,2]')\": 1, \"('n[44,0]', 'n[44,1]', 'n[44,2]')\": 1, \"('n[45,0]', 'n[45,1]', 'n[45,2]')\": 1, \"('n[46,0]', 'n[46,1]', 'n[46,2]')\": 1, \"('n[47,0]', 'n[47,1]', 'n[47,2]')\": 1, \"('n[48,0]', 'n[48,1]', 'n[48,2]')\": 1, \"('n[49,0]', 'n[49,1]', 'n[49,2]')\": 1}\n"
          ]
        }
      ],
      "source": [
        "# Build the hard constraint: exactly one group per node.\n",
        "constraint_dict = {\n",
        "    str(tuple(f\"n[{i},{g}]\" for g in range(group_count))): 1\n",
        "    for i in range(node_count)\n",
        "}\n",
        "print(f\"Problem constraints: {constraint_dict}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "753e5514",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Problemi parzialmente vincolati\">\n",
        "  Non è necessario aggiungere tutte le variabili a `constraint`. Qualsiasi variabile non inclusa nel dizionario rimane non vincolata; è quindi possibile combinare, nello stesso problema, gruppi di variabili con vincoli rigidi e variabili libere.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9fd0ba9e",
      "metadata": {},
      "source": [
        "<span id=\"2-run-the-problem\" />\n",
        "\n",
        "### 2. Esegui il problema\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "f380b56d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Solve the problem\n",
        "partition_job = solver.run(\n",
        "    problem=srepr(cost_function),\n",
        "    constraint=constraint_dict,\n",
        "    backend_name=\"ibm_marrakesh\",  # E.g. \"ibm_marrakesh\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d154c226",
      "metadata": {},
      "source": [
        "Per verificare [lo stato](/docs/guides/functions-get-started#check-job-status) del carico di lavoro della funzione Qiskit o per ottenere [i risultati, procedere](/docs/guides/functions-get-started#retrieve-results) come segue:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "6de77b14",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "b8085944-f313-444e-be39-ea61b1b47ebd\n",
            "QUEUED\n"
          ]
        }
      ],
      "source": [
        "# Print the ID so you can use it later, if necessary\n",
        "print(partition_job.job_id)\n",
        "\n",
        "# Get job status\n",
        "print(partition_job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8a95618e",
      "metadata": {},
      "source": [
        "<span id=\"3-get-the-result\" />\n",
        "\n",
        "### 3. Ottieni il risultato\n",
        "\n",
        "Recuperare la soluzione e analizzare i risultati. Il costo della soluzione rappresenta il peso totale dei bordi i cui estremi sono finiti nello stesso gruppo; pertanto, un costo inferiore indica una migliore partizionamento del grafo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a924bf93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total weight of same-group edges: -36.5539\n",
            "Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100\n"
          ]
        }
      ],
      "source": [
        "partition_result = partition_job.result()\n",
        "qctrl_cost = partition_result[\"solution_bitstring_cost\"]\n",
        "solution_bitstring = partition_result[\"solution_bitstring\"]\n",
        "\n",
        "# Print results\n",
        "print(f\"Total weight of same-group edges: {qctrl_cost}\")\n",
        "print(f\"Solution bitstring: {solution_bitstring}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9ec2e67",
      "metadata": {},
      "source": [
        "<span id=\"get-support\" />\n",
        "\n",
        "## Ottenere supporto\n",
        "\n",
        "Per qualsiasi domanda o problema, [contattate Q-CTRL](https://form.typeform.com/to/iuujEAEI?typeform-source=q-ctrl.com).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "993aa226c6ec9f5e",
      "metadata": {},
      "source": [
        "<span id=\"changelog\" />\n",
        "\n",
        "## Log di modifica\n",
        "\n",
        "* 10 agosto 2026: è stato aggiunto il supporto per i vincoli rigidi (peso di Hamming pari a 1) tramite il parametro di input `constraint` , ed è stato aggiornato l'esempio di ottimizzazione con vincoli per utilizzarli.\n",
        "* 11 febbraio 2026: ora supportiamo `ibm_miami`\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a6a25c8",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  * Richiedi l'accesso al [Q-CTRL Optimization Solver](/functions?id=q-ctrl-optimization-solver).\n",
        "  * Consulta la documentazione [di riferimento dell'API](/docs/api/functions/q-ctrl-optimization-solver) relativa a questa funzione di Qiskit.\n",
        "  * Provate l'esercitazione [Risolvere problemi di ottimizzazione binaria di ordine superiore con il risolutore di ottimizzazione di Q-CTRL](/docs/tutorials/solve-higher-order-binary-optimization-problems-with-q-ctrls-optimization-solver).\n",
        "  * Recensione [Sachdeva, N., et al. (2024).  L'ottimizzazione quantistica che utilizza un computer quantistico IBM con modello gate a 127 qubit può superare le prestazioni degli annealer quantistici per problemi di ottimizzazione binaria non banali. arXiv preprint arXiv:2406.01743](https://arxiv.org/abs/2406.01743).\n",
        "  * Recensione [Loco, D., et al. (2026).  Previsione pratica dei siti di idratazione delle tasche proteiche per la scoperta di farmaci su un computer quantistico. arXiv preprint arXiv:2512.08390](https://arxiv.org/abs/2512.08390).\n",
        "  * Esamina il caso di studio [Mazda](https://q-ctrl.com/case-study/tackling-a-costly-bottleneck-in-automotive-design).\n",
        "  * Esamina il caso di studio [relativo a Network Rail](https://q-ctrl.com/case-study/accelerating-the-schedule-for-quantum-enhanced-rail).\n",
        "  * Esamina il caso di studio [dell'esercito australiano](https://q-ctrl.com/case-study/improving-army-logistics-with-quantum-computing).\n",
        "  * Esamina il caso di studio [relativo ai trasporti nel Nuovo Galles del Sud](https://q-ctrl.com/case-study/delivering-quantum-computing-for-faster-commuting).\n",
        "</Admonition>\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
}