Optimization Solver: una funzione Qiskit di Q-CTRL Fire Opal
Consulta la documentazione dell'API
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.
Il codice presente in questa pagina è stato sviluppato sulla base dei seguenti requisiti. Si consiglia di utilizzare queste versioni o quelle più recenti.
qiskit-ibm-runtime~=0.47.0 sympy~=1.14.0
Panoramica
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. Il Solver fornisce costantemente soluzioni accurate a problemi classici, anche su scala full-device sulle più grandi QPU IBM®.
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.
Per accedere al risolutore di ottimizzazione, contattare Q-CTRL.
Descrizione funzione
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.
Per risolvere un problema generico con il Risolutore di ottimizzazione:
- Definire il problema come una funzione obiettivo, un grafico o una catena di spin
SparsePauliOp. - Collegarsi alla funzione attraverso il Catalogo delle funzioni di Qiskit.
- Eseguire il problema con il Risolutore e recuperare i risultati.
Formati di problema accettati
- 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.
- 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. - Rappresentazione a catena di spin di un problema specifico. La catena di spin deve essere rappresentata come un oggetto
SparsePauliOp; si veda la documentazione per maggiori dettagli.
Se desideri utilizzare un backend che questa funzione non supporta al momento, contatta Q-CTRL per richiedere l'aggiunta del supporto.
Benchmark
I risultati dei benchmark pubblicati 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.
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.
La connettività arbitraria dei qubit è supportata in tutti i tipi di problemi.
Tipo di problema | Numero di qubit | Esempio | Accuratezza | Tempo totale | Utilizzo del tempo di esecuzione (s) | Numero di iterazioni |
|---|---|---|---|---|---|---|
| Problemi quadratici scarsamente connessi | 156 | 3-regolare max-cut | 100% | 1764 | 293 | 16 |
| Ottimizzazione binaria di ordine superiore | 156 | Modello Ising spin-glass | 100% | 1461 | 272 | 16 |
| Problemi quadratici densamente connessi | 50 | Taglio massimo con connessione completa | 100% | 1758 | 268 | 12 |
| Problema con vincoli rigidi | 50 | Partizionamento di grafi ponderati con una densità di archi dell'8% | 100% | 1074 | 215 | 10 |
Introduzione
Innanzitutto, autenticati utilizzando la tua chiave API IBM Quantum. Quindi, selezionare la funzione Qiskit come segue. (Questo frammento di codice presuppone che tu abbia già salvato il tuo account nell'ambiente locale.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Verify that you have access to the function
catalog.list()Output:
[QiskitFunction(qunova/hivqe-chemistry),
QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
QiskitFunction(algorithmiq/tem),
QiskitFunction(qedma/qesem),
QiskitFunction(multiverse/singularity),
QiskitFunction(ibm/circuit-function),
QiskitFunction(q-ctrl/optimization-solver),
QiskitFunction(colibritd/quick-pde),
QiskitFunction(q-ctrl/performance-management),
QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")Esempio: Ottimizzazione senza vincoli
Esegui il problema del taglio massimo (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.
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.
# %pip install networkx numpy1. Definire il problema
È possibile risolvere un problema di taglio massimo definendo un problema grafico 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
)Output:
Il risolutore 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 sul grafico, specificare il tipo di problema.
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)Per verificare lo stato del carico di lavoro della funzione Qiskit o per ottenere i risultati, procedere come segue:
# Print the ID so you can use it later, if necessary
print(maxcut_job.job_id)
# Get job status
print(maxcut_job.status())Output:
34b53970-d95a-4e24-8763-fc6f3d112843
QUEUED
3. Recupera il risultato
Recupera il valore di taglio ottimale dal dizionario dei risultati.
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.
# 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}")Output:
Optimal cut value: 210.0
È possibile verificare l'accuratezza del risultato risolvendo il problema in modo classico con solutori open-source come PuLP se il grafo non è densamente connesso. I problemi ad alta densità possono richiedere solutori classici avanzati per convalidare la soluzione.
Esempio: Ottimizzazione vincolata
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.
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, 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.
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.
# %pip install numpy networkx sympy1. Definire il problema
Definire un problema di partizionamento casuale di un grafo generando un grafo con nodi ponderati in modo casuale.
import networkx as nx
from sympy import Symbol, 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
graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)
# add node weights
min_weight = -1.0
max_weight = 1.0
for i in graph.nodes:
weight = (max_weight - min_weight) * _rng.random() + min_weight
graph.add_node(i, weight=weight)
# Optionally, visualize the graph
nx.draw_networkx(graph, nx.kamada_kawai_layout(graph), node_size=200)Output:
Un modello standard di ottimizzazione per la partizionamento dei grafi ponderati può essere formulato come segue. Suddividiamo i nodi del grafo in tre gruppi e poniamo che se il nodo è assegnato al gruppo , e 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 è dato dalla somma dei pesi dei suoi due estremi, :
# Construct the cost function.
group_count = 3
variables = [
Symbol(f"n[{i},{g}]")
for i in range(node_count)
for g in range(group_count)
]
node_group_var = {
(i, g): variables[i * group_count + g]
for i in range(node_count)
for g in range(group_count)
}
cost_function = Poly(0, *variables)
for i, j in graph.edges():
edge_weight = graph.nodes[i]["weight"] + graph.nodes[j]["weight"]
for g in range(group_count):
cost_function += (
edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]
)Ogni nodo deve essere assegnato esattamente a uno dei tre gruppi. Si tratta di un vincolo di tipo “ Hamming-weight-1 ”: per ogni nodo , esattamente uno tra deve essere pari a 1, mentre gli altri devono essere pari a 0:
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 .
# Build the hard constraint: exactly one group per node.
constraint_dict = {
str(tuple(f"n[{i},{g}]" for g in range(group_count))): 1
for i in range(node_count)
}
print(f"Problem constraints: {constraint_dict}")Output:
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}
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.
2. Esegui il problema
# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)Per verificare lo stato del carico di lavoro della funzione Qiskit o per ottenere i risultati, procedere come segue:
# Print the ID so you can use it later, if necessary
print(partition_job.job_id)
# Get job status
print(partition_job.status())Output:
b8085944-f313-444e-be39-ea61b1b47ebd
QUEUED
3. Ottieni il risultato
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.
partition_result = partition_job.result()
qctrl_cost = partition_result["solution_bitstring_cost"]
solution_bitstring = partition_result["solution_bitstring"]
# Print results
print(f"Total weight of same-group edges: {qctrl_cost}")
print(f"Solution bitstring: {solution_bitstring}")Output:
Total weight of same-group edges: -36.5539
Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100
Ottenere supporto
Per qualsiasi domanda o problema, contattate Q-CTRL.
Log di modifica
- 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. - 11 febbraio 2026: ora supportiamo
ibm_miami
Passi successivi
- Richiedi l'accesso al Q-CTRL Optimization Solver.
- Consulta la documentazione di riferimento dell'API relativa a questa funzione di Qiskit.
- Provate l'esercitazione Risolvere problemi di ottimizzazione binaria di ordine superiore con il risolutore di ottimizzazione di Q-CTRL.
- 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.
- 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.
- Esamina il caso di studio Mazda.
- Esamina il caso di studio relativo a Network Rail.
- Esamina il caso di studio dell'esercito australiano.
- Esamina il caso di studio relativo ai trasporti nel Nuovo Galles del Sud.