Optimization Solver: una Qiskit Function di Q-CTRL Fire Opal
Le Qiskit Functions sono una funzionalità sperimentale disponibile esclusivamente per gli utenti del piano IBM Quantum® Premium, del piano Flex e del piano On-Prem (tramite IBM Quantum Platform API). Sono in stato di anteprima e soggette a modifiche.
Panoramica​
Con il Fire Opal Optimization Solver puoi risolvere problemi di ottimizzazione su scala utility su hardware quantistico senza dover possedere competenze specifiche nel campo quantistico. Inserisci semplicemente la definizione del problema ad alto livello e il Solver si occupa del resto. L'intero flusso di lavoro è noise-aware e sfrutta internamente il Performance Management di Fire Opal. Il Solver fornisce in modo costante soluzioni accurate a problemi classicamente difficili, anche alla scala completa del dispositivo sui più grandi QPU IBM®.
Il Solver è flessibile e può essere utilizzato per risolvere problemi di ottimizzazione combinatoria definiti come funzioni obiettivo o come grafi arbitrari. I problemi non devono necessariamente essere mappati alla topologia del dispositivo. Sia i problemi non vincolati che quelli vincolati sono risolvibili, a condizione che i vincoli possano essere formulati come termini di penalità . Gli esempi inclusi in questa guida mostrano come risolvere un problema di ottimizzazione su scala utility, sia non vincolato che vincolato, utilizzando diversi tipi di input per il Solver. Il primo esempio riguarda un problema Max-Cut definito su un grafo 3-regolare a 156 nodi, mentre il secondo affronta un problema di Minimum Vertex Cover a 50 nodi definito da una funzione di costo.
Per ottenere l'accesso all'Optimization Solver, contatta Q-CTRL.
Descrizione della funzione​
Il Solver ottimizza e automatizza completamente l'intero algoritmo, dalla soppressione degli errori a livello hardware alla mappatura efficiente del problema e all'ottimizzazione classica a ciclo chiuso. Dietro le quinte, la pipeline del Solver riduce gli errori a ogni fase, abilitando le prestazioni avanzate necessarie per scalare in modo significativo. Il flusso di lavoro sottostante è ispirato al Quantum Approximate Optimization Algorithm (QAOA), un algoritmo ibrido quantistico-classico. Per un riepilogo dettagliato dell'intero flusso di lavoro dell'Optimization Solver, consulta il manoscritto pubblicato.
Per risolvere un problema generico con l'Optimization Solver:
- Definisci il tuo problema come funzione obiettivo, come grafo o come catena di spin
SparsePauliOp. - Collegati alla funzione tramite il Qiskit Functions Catalog.
- Esegui il problema con il Solver e recupera i risultati.
Input e output​
Input​
| Nome | Tipo | Descrizione | Obbligatorio | Predefinito | Esempio |
|---|---|---|---|---|---|
| problem | str o SparsePauliOp | Una delle rappresentazioni elencate in "Formati di problema accettati" | Sì | N/A | Poly(2.0*n[0]*n[1] + 2.0*n[0]*n[2] - 3.13520241298341*n[0] + 2.0*n[1]*n[2] - 3.14469748506794*n[1] - 3.18897660121566*n[2] + 6.0, n[0], n[1], n[2], domain='RR') |
| problem_type | str | Nome della classe del problema; usato solo per le definizioni di problema basate su grafo e catena di spin, limitate a "maxcut" o "spin_chain"; non richiesto per definizioni di funzione obiettivo arbitraria | No | None | "maxcut" |
| backend_name | str | Nome del backend da utilizzare | No | Il backend meno occupato a cui la tua istanza ha accesso | "ibm_fez" |
| options | dict | Opzioni di input, tra cui: (Facoltativo) session_id: str; il comportamento predefinito crea una nuova Session | No | None | {"session_id": "cw2q70c79ws0008z6g4g"} |
Formati di problema accettati:
- Rappresentazione in espressione polinomiale di una funzione obiettivo. Idealmente creata in Python con un oggetto SymPy Poly esistente e formattata in stringa utilizzando sympy.srepr.
- Rappresentazione tramite grafo di un tipo di problema specifico. Il grafo deve essere creato utilizzando la libreria networkx in Python e poi convertito in stringa tramite la funzione networkx
[nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.). - Rappresentazione in catena di spin di un problema specifico. La catena di spin deve essere rappresentata come oggetto
SparsePauliOp; consulta la documentazione per maggiori dettagli.
Backend supportati: Esegui il codice seguente per visualizzare l'elenco dei backend attualmente supportati. Se il tuo dispositivo non è presente nell'elenco, contatta Q-CTRL per aggiungere il supporto.
# Added by doQumentation — required packages for this notebook
!pip install -q networkx numpy qiskit-ibm-catalog qiskit-ibm-runtime sympy
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
[<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_kingston')>,
<IBMBackend('ibm_torino')>,
<IBMBackend('ibm_marrakesh')>]
Opzioni:
| Nome | Tipo | Descrizione | Predefinito |
|---|---|---|---|
| session_id | str | ID di una sessione Qiskit Runtime esistente | "cw4r3je6f0t010870y3g" |
| job_tags | list[str] | Un elenco di tag per i job | [] |
Output​
| Nome | Tipo | Descrizione | Esempio |
|---|---|---|---|
| result | dict[str, Any] | Soluzione e metadati elencati in "Contenuto del dizionario dei risultati" | {'solution_bitstring_cost': 3.0, 'final_bitstring_distribution': {'000001': 100, '000011': 2}, 'iteration_count': 3, 'solution_bitstring': '000001', 'variables_to_bitstring_index_map': {n[1]': 5, 'n[2]': 4, 'n[3]': 3, 'n[4]': 2, 'n[5]': 1}, 'best_parameters': [0.19628831763697097, -1.047052334523102], 'warnings': []} |
Contenuto del dizionario dei risultati:
| Nome | Tipo | Descrizione |
|---|---|---|
| solution_bitstring_cost | float | Il miglior costo minimo tra tutte le iterazioni |
| final_bitstring_distribution | CountsDict | Il dizionario dei conteggi delle bitstring associato al costo minimo |
| solution_bitstring | str | Bitstring con il costo migliore nella distribuzione finale |
| iteration_count | int | Il numero totale di iterazioni QAOA eseguite dal Solver |
| variables_to_bitstring_index_map | float | La mappatura dalle variabili al bit equivalente nella bitstring |
| best_parameters | list[float] | I parametri beta e gamma ottimizzati tra tutte le iterazioni |
| warnings | list[str] | Gli avvisi prodotti durante la compilazione o l'esecuzione del QAOA (predefinito: None) |
Benchmark​
I risultati di benchmarking pubblicati mostrano che il Solver risolve con successo problemi con oltre 120 qubit, superando persino i risultati precedentemente pubblicati su dispositivi a quantum annealing e ad ioni intrappolati. Le seguenti metriche di benchmark forniscono un'indicazione approssimativa dell'accuratezza e della scalabilità dei tipi di problema sulla base di alcuni esempi. Le metriche effettive possono variare in base a diverse caratteristiche del problema, come il numero di termini nella funzione obiettivo (densità ) e la loro località , il numero di variabili e l'ordine polinomiale.
Il "Numero di qubit" indicato non è un limite assoluto, ma rappresenta soglie approssimative entro le quali ci si può aspettare un'accuratezza della soluzione estremamente costante. Problemi di dimensioni maggiori sono stati risolti con successo e si incoraggia la sperimentazione oltre questi limiti.
La connettività arbitraria dei qubit è supportata per tutti i tipi di problema.
| Tipo di problema | Numero di qubit | Esempio | Accuratezza | Tempo totale (s) | Utilizzo runtime (s) | Numero di iterazioni |
|---|---|---|---|---|---|---|
| Problemi quadratici con connettività sparsa | 156 | Max-Cut 3-regolare | 100% | 1764 | 293 | 16 |
| Ottimizzazione binaria di ordine superiore | 156 | Modello di spin-glass di Ising | 100% | 1461 | 272 | 16 |
| Problemi quadratici con connettività densa | 50 | Max-Cut completamente connesso | 100% | 1758 | 268 | 12 |
| Problema vincolato con termini di penalità | 50 | Minimum Vertex Cover pesato con densità di archi 8% | 100% | 1074 | 215 | 10 |
Inizia a usarlo​
Prima di tutto, autenticati usando la tua chiave API di IBM Quantum. Poi seleziona la Qiskit Function come segue. (Questo snippet presuppone che tu abbia già salvato il tuo account nell'ambiente locale.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")
Esempio: ottimizzazione non vincolata​
Esegui il problema del taglio massimo (Max-Cut). Il seguente esempio dimostra le capacità del Solver su un problema Max-Cut su un grafo 3-regolare non pesato a 156 nodi, ma puoi risolvere anche problemi su grafi pesati.
Oltre a qiskit-ibm-catalog, per eseguire questo esempio avrai bisogno anche dei seguenti pacchetti: networkx e numpy. Puoi installare questi pacchetti decommentando la cella seguente se stai eseguendo questo esempio in un notebook con il kernel IPython.
# %pip install networkx numpy
1. Definisci il problema​
Puoi eseguire un problema Max-Cut definendo un problema su grafo e specificando problem_type='maxcut'.
import networkx as nx
import numpy as np
# Generate a random graph with 156 nodes
maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)
# Optionally, visualize the graph
nx.draw_networkx(
maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100
)

Il Solver accetta una stringa come input per la definizione del problema.
# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)
2. Esegui il problema​
Quando si utilizza il metodo di input basato su grafo, è necessario specificare il tipo di problema.
# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend_name = service.least_busy(n_qubits=156).name
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)
Controlla lo stato del workload della tua Qiskit Function o recupera i risultati come segue:
# Get job status
print(maxcut_job.status())
QUEUED
3. Recupera il risultato​
Recupera il valore del taglio ottimale dal dizionario dei risultati.
variables_to_bitstring_index_map che aiuta a verificare l'ordinamento.# Poll for results
maxcut_result = maxcut_job.result()
# Take the absolute value of the solution since the cost function is minimized
qctrl_maxcut = abs(maxcut_result["solution_bitstring_cost"])
# Print the optimal cut value found by the Optimization Solver
print(f"Optimal cut value: {qctrl_maxcut}")
Optimal cut value: 209.0
Puoi verificare l'accuratezza del risultato risolvendo il problema classicamente con solver open-source come PuLP se il grafo non è densamente connesso. I problemi ad alta densità potrebbero richiedere solver classici avanzati per validare la soluzione.
Esempio: ottimizzazione vincolata​
Il precedente esempio Max-Cut è un classico problema di ottimizzazione binaria quadratica non vincolata. L'Optimization Solver di Q-CTRL può essere utilizzato per vari tipi di problemi, inclusa l'ottimizzazione vincolata. Puoi risolvere tipi di problemi arbitrari inserendo la definizione del problema rappresentata come polinomio, dove i vincoli sono modellati come termini di penalità .
Il seguente esempio mostra come costruire una funzione di costo per un problema di ottimizzazione vincolata, il minimum vertex cover (MVC).
Oltre ai pacchetti qiskit-ibm-catalog e qiskit, per eseguire questo esempio avrai bisogno anche dei seguenti pacchetti: numpy, networkx e sympy. Puoi installare questi pacchetti decommentando la cella seguente se stai eseguendo questo esempio in un notebook con il kernel IPython.
# %pip install numpy networkx sympy
1. Definisci il problema​
Definisci un problema MVC casuale generando un grafo con nodi pesati casualmente.
import networkx as nx
from sympy import symbols, Poly, srepr
# To change the weights, change the seed to any integer.
rng_seed = 18
_rng = np.random.default_rng(rng_seed)
node_count = 50
edge_probability = 0.08
mvc_graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)
# add node weights
for i in mvc_graph.nodes:
mvc_graph.add_node(i, weight=_rng.random())
# Optionally, visualize the graph
nx.draw_networkx(mvc_graph, nx.kamada_kawai_layout(mvc_graph), node_size=200)

Un modello di ottimizzazione standard per il MVC pesato può essere formulato come segue. Innanzitutto, è necessario aggiungere una penalità per ogni caso in cui un arco non sia connesso a un vertice nel sottoinsieme. Quindi, sia se il vertice è nel cover (cioè nel sottoinsieme) e altrimenti. In secondo luogo, l'obiettivo è minimizzare il numero totale di vertici nel sottoinsieme, che può essere rappresentato dalla seguente funzione:
# Construct the cost function.
variables = symbols([f"n[{i}]" for i in range(node_count)])
cost_function = Poly(0, variables)
for i in mvc_graph.nodes():
weight = mvc_graph.nodes[i].get("weight", 0)
cost_function += variables[i] * weight
Ora ogni arco nel grafo deve includere almeno un estremo del cover, il che può essere espresso con la disuguaglianza:
Ogni caso in cui un arco non è connesso al vertice del cover deve essere penalizzato. Questo può essere rappresentato nella funzione di costo aggiungendo una penalità della forma dove è una costante di penalità positiva. Quindi, un'alternativa non vincolata alla disuguaglianza vincolata per il MVC pesato è:
# Add penalty term.
penalty_constant = 2
for i, j in mvc_graph.edges():
cost_function += penalty_constant * (
1 - variables[i] - variables[j] + variables[i] * variables[j]
)
2. Esegui il problema​
# Solve the problem
mvc_job = solver.run(
problem=srepr(cost_function),
backend_name=backend_name, # E.g. "ibm_fez"
)
Controlla lo stato del workload della tua Qiskit Function o recupera i risultati come segue:
print(mvc_job.status())
3. Ottieni il risultato​
Recupera la soluzione e analizza i risultati. Poiché questo problema ha nodi pesati, la soluzione non è semplicemente il numero minimo di nodi coperti. Il costo della soluzione rappresenta invece la somma dei pesi dei vertici inclusi nel vertex cover, ovvero il "costo" o "peso" totale necessario per coprire tutti gli archi del grafo usando i vertici selezionati.
mvc_result = mvc_job.result()
qctrl_cost = mvc_result["solution_bitstring_cost"]
# Print results
print(f"Solution cost: {qctrl_cost}")
Solution cost: 10.248198273708624
Supporto​
Per qualsiasi domanda o problema, contatta Q-CTRL.
Passi successivi​
- Richiedi l'accesso all'Q-CTRL Optimization Solver.
- Prova il tutorial Risolvi problemi di ottimizzazione binaria di ordine superiore con l'Optimization Solver di Q-CTRL.
- Consulta Sachdeva, N., et al. (2024). Quantum optimization using a 127-qubit gate-model IBM quantum computer can outperform quantum annealers for nontrivial binary optimization problems. arXiv preprint arXiv:2406.01743.
- Consulta Loco, D., et al. (2026). Practical protein-pocket hydration-site prediction for drug discovery on a quantum computer. arXiv preprint arXiv:2512.08390.
- Consulta il case study Mazda.
- Consulta il case study Network Rail.
- Consulta il case study Australian Army.
- Consulta il case study Transport for New South Wales.