Vai al contenuto principale
Questo progetto è in fase beta. Hai trovato un bug? Apri una issue su GitHub. Hai una domanda o un'idea? Chiedi su GitHub Discussions — apprezziamo il tuo feedback!
Com'è la qualità della traduzione su questa pagina?

Constrained Quantum Optimizer - Una funzione Qiskit di Aqarios

Consulta il riferimento API

nota

Le Qiskit Functions sono una funzionalità sperimentale disponibile solo per gli utenti dei piani IBM Quantum® Premium Plan, Flex Plan e On-Prem (tramite IBM Quantum Platform API). Sono in stato di rilascio in anteprima e soggette a modifiche.

Panoramica​

Con il Constrained Quantum Optimizer di Aqarios, puoi risolvere problemi di ottimizzazione binaria vincolata su hardware quantistico IBM® senza dover riformulare manualmente il problema o mappare il circuito. Basta fornire un problema come file LP, file MPS o Luna Model. La funzione gestisce automaticamente la riformulazione, la sintesi del circuito, la transpilazione e l'esecuzione sull'hardware. Il solver utilizza il warm-starting iterativo [1] dell'algoritmo FlexQAOA, un'estensione del Quantum Approximate Optimization Algorithm (QAOA) che gestisce nativamente i vincoli.

L'ottimizzatore è pensato per problemi binari con vincoli. In particolare, i vincoli one-hot e i vincoli di set-packing possono essere applicati in modo nativo utilizzando gli XY-mixer. Altri tipi di vincoli vengono automaticamente riformulati come termini di penalità.

Tipi di variabile supportati

La funzione risolve solo problemi discreti. Ogni variabile decisionale deve essere binaria, oppure intera con un limite inferiore e uno superiore espliciti, in modo che possa essere codificata in qubit. Le variabili continue (a valori reali) non sono supportate: un modello che le contiene viene rifiutato con il codice di errore 4710.

Descrizione​

Avvertenza

Le prestazioni possono dipendere sia dall'istanza del problema sia dalle fasi di elaborazione successive. In alcuni casi, i campioni classici e quelli generati quantisticamente potrebbero raggiungere una qualità finale della soluzione simile dopo un post-processing equivalente. La valutazione dovrebbe pertanto considerare l'intero flusso di lavoro di ottimizzazione.

Il Constrained Quantum Optimizer implementa l'Iterative warm-start XY-mixer QAOA (IWS-QAOA) [1]. L'algoritmo esegue circuiti QAOA ad angolo fisso sull'hardware quantistico e aggiorna le probabilità iniziali di warm-start di ciascun circuito utilizzando i risultati delle misurazioni dell'iterazione precedente. Non è richiesto alcun addestramento dei parametri variazionali; viene invece scelta una parametrizzazione a rampa lineare fissa.

Utilizzando gli XY-mixer per applicare i vincoli one-hot, puoi migliorare drasticamente la qualità della soluzione per i problemi vincolati, perché lo spazio di ricerca viene praticamente ridotto a un sottospazio ammissibile più piccolo — e non viene introdotta alcuna complessità aggiuntiva, dovuta a termini di penalità, nel paesaggio di ottimizzazione.

Flusso di lavoro​

Di seguito è descritto il flusso di lavoro della funzione:

  1. Preprocessing: Prima della sintesi del circuito, l'ottimizzatore analizza la struttura dei vincoli del problema di input:

    • Preprocessing del grafo: Quando applicabile al problema, questo viene analizzato come un grafo, in cui gli archi determinano la relazione tra le variabili. Questa analisi del grafo consente due miglioramenti nel preprocessing del problema. Innanzitutto, i singoli vincoli possono essere aggregati in un unico vincolo più grande utilizzando la clique merging, in modo da essere rappresentati da un singolo XY-mixer. In secondo luogo, le variabili con un solo vicino possono essere fissate in modo deterministico, riducendo le dimensioni e la complessità del problema.

    • Risoluzione dei vincoli: Il preprocessore gestisce ciascun vincolo separatamente: se il vincolo è un vincolo one-hot, può essere applicato tramite gli XY-mixer. Se è un vincolo di set-packing, viene prima trasformato in un vincolo one-hot. I vincoli di uguaglianza vengono gestiti tramite penalità quadratiche, mentre i vincoli di disuguaglianza tramite penalizzazione sbilanciata.

    • Derivazione della penalità: I vincoli che non possono essere espressi come vincoli di uguaglianza one-hot vengono convertiti in termini di penalità. L'ottimizzatore deriva automaticamente l'intensità della penalità dalla struttura del problema.

  2. Warm-starting iterativo: L'ottimizzatore esegue più catene di warm-start parallele. Ogni catena mantiene una distribuzione di probabilità sulle assegnazioni delle variabili, utilizzata come probabilità di partenza del circuito QAOA (stato di warm-start). A ogni iterazione, una catena campiona dal circuito quantistico, applica un post-processing classico e aggiorna la propria distribuzione in base all'occorrenza dei campioni. Attraverso le iterazioni, questo concentra la ricerca su soluzioni di alta qualità senza un ciclo di ottimizzazione variazionale.

Il numero di catene (num_parallel), il budget totale di shot per catena (total_shots) e il numero di ripetizioni QAOA (reps) sono i controlli principali per bilanciare la qualità della soluzione rispetto al tempo di esecuzione.

Workflow diagram of the Constrained Quantum Optimizer

Benchmark​

I benchmark riportati di seguito mostrano che la funzione Qiskit Constrained Quantum Optimizer è in grado di trovare soluzioni ammissibili di alta qualità per problemi di ottimizzazione vincolata con ben oltre 100 variabili binarie. L'accuratezza è il rapporto tra il valore obiettivo trovato e l'ottimo noto. Il runtime è il tempo reale trascorso dall'invocazione della funzione alla consegna del risultato. L'utilizzo della QPU è il tempo di calcolo quantistico fatturabile.

Sono stati esaminati due problemi: il problema del Maximum Independent Set (MIS) e il Max-kk-Cut, che è un'estensione del noto problema MaxCut a più di due categorie. Il Max-kk-Cut è anche l'equivalente, come problema di ottimizzazione, del problema decisionale di colorazione dei grafi (quando tutti gli archi sono tagliati, il grafo è kk-colorabile).

ProblemaVariabiliVincoliQubitAccuratezzaFattibilitàRuntime totale (s)Utilizzo QPU (s)Shot totali
MIS es60fst02186280124100.0%True940.2147.025.0k
MIS sloane_2dc_128128517374100.0%True347.6106.012.5k
Max-3-Cut Karate Club99339998.7%True279.5166.025.0k
Max-4-Cut Karate Club13233132100.0%True258.3200.025.0k

Note sui benchmark​

  • I risultati sopra riportati sono stati ottenuti sul processore Heron r2 ibm_marrakesh con le opzioni predefinite e use_session abilitato. Per l'istanza sloane_2dc_128 è stato utilizzato total_shots=12500.

  • Il numero ridotto di qubit per le istanze MIS (74 e 124 rispetto a 128 e 186 variabili) riflette il fissaggio delle variabili durante il preprocessing.

  • Le istanze del problema per il MIS sono tratte dalla Quantum Optimization Benchmarking Library. - L'istanza per il problema Max-kk-Cut è il grafo Zachary Karate Club, che ha un numero cromatico pari a 5.

Per iniziare​

Questa guida illustra l'utilizzo di base del Constrained Quantum Optimizer, mostrando come caricare la funzione ed eseguire problemi semplici. Per un tutorial più approfondito, consulta Find the Maximum Independent Set with the Aqarios Constrained Quantum Optimizer.

Autenticati con la tua chiave API IBM Quantum e il CRN dell'istanza, quindi carica la funzione dal catalogo.

nota

Il codice seguente presuppone che tu abbia salvato le tue credenziali. Per configurarle, segui le istruzioni in Save your IBM Cloud account credentials.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"luna_model": "luna-model", "networkx": "networkx", "qiskit_addon_opt_mapper": "qiskit-addon-opt-mapper", "qiskit_ibm_catalog": "qiskit-ibm-catalog"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()
[QiskitFunction(aqarios/constrained-quantum-optimizer)]
# Load the function
optimizer = catalog.load("aqarios/constrained-quantum-optimizer")
# Check the list of backends you have access to
catalog.backends()
[<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_boston')>,
<IBMBackend('ibm_phoenix')>,
<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_miami')>,
<IBMBackend('ibm_marrakesh')>,
<IBMBackend('ibm_kingston')>]
# Select the backend you want to use
backend = catalog.backend("ibm_phoenix")

Esempio 1: un semplice problema vincolato​

Il primo esempio mostra come utilizzare la funzione con file LP, una specifica comune per problemi di ottimizzazione utilizzata nella ricerca operativa. Formulerai il seguente problema di ottimizzazione arbitrario con cinque variabili binarie xi∈{0,1}x_i \in \{0,1\} soggette a due vincoli:

min⁡x1x2+2x2x3−x3x4−2x4x5−2x1x3−4x2x5s.a.x1+x2+x3≤1x3−x4+x5=0 \min x_1 x_2 + 2 x_2 x_3 - x_3 x_4 - 2 x_4 x_5 - 2 x_1 x_3 - 4 x_2 x_5 \\ \text{s.a.}\quad x_1 + x_2 + x_3 \leq 1 \\ \quad x_3 - x_4 + x_5 = 0

1. Formula il problema​

Esprimi direttamente l'espressione matematica nel seguente formato di stringa LP:

lp_str = """\\Problem name: Example
Minimize
obj: [ 2 x_1 x_2 + 4 x_2 x_3 - 2 x_3 x_4 - 4 x_4 x_5 - 4 x_1 x_3 - 8 x_2 x_5 ] / 2
Subject To
c1: x_1 + x_2 + x_3 <= 1
c2: x_3 - x_4 + x_5 = 0
Binaries
x_1 x_2 x_3 x_4 x_5
End
"""

Nota che [ ... ] / 2 è una notazione richiesta per gli obiettivi quadratici secondo lo standard LP.

2. Esegui l'ottimizzatore​

Risolvi il problema con l'ottimizzatore.

# Set up low-resource options for the simple problem
options = {"total_shots": 2000, "num_parallel": 5, "use_session": True}

# Dispatch the job
job_1 = optimizer.run(
problem=lp_str, backend_name=backend.name, options=options
)

print(f"Job ID: {job_1.job_id}")
Job ID: 2e06a3a8-7c29-4c76-94d7-194293363f4a
# Monitor the job status
job_1.status()
'RUNNING: WAITING_FOR_QPU'

3. Recupera il risultato​

La soluzione, che associa i nomi delle variabili alle relative assegnazioni, viene fornita dal risultato.

result = job_1.result()

result
{'solutions': [{'x_1': 0, 'x_2': 1, 'x_3': 0, 'x_4': 1, 'x_5': 1}],
'solution_bitstrings': ['01011'],
'raw_energy': -6.0,
'objective_value': -6.0,
'feasible': True,
'metadata': {'resource_usage': {'RUNNING: MAPPING': {'CPU': 5.405},
'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU': 9.902},
'RUNNING: WAITING_FOR_QPU': {'CPU': 25.456},
'RUNNING: EXECUTING_QPU': {'CPU': 25.202},
'RUNNING: POST_PROCESSING': {'CPU': 6.481}},
'circuit_metrics': {'before_transpilation': {'num_qubits': 6,
'gate_count': 42,
'depth': 15,
'two_qubit_gate_count': 12,
'two_qubit_gate_depth': 7,
'operations': {'rz': 12,
'p': 8,
'ry': 7,
'rzz': 4,
'cx': 3,
'xx_plus_yy': 3,
'global_phase': 2,
'cry': 2,
'x': 1}},
'after_transpilation': {'num_qubits': 6,
'gate_count': 254,
'depth': 100,
'two_qubit_gate_count': 48,
'two_qubit_gate_depth': 33,
'operations': {'rz': 78, 'sx': 122, 'cz': 48, 'measure': 6, 'x': 0}}},
'solver_info': {'variable_mapping': {'x_1': 0,
'x_2': 1,
'x_3': 2,
'x_4': 3,
'x_5': 4}}}}

Il campo solutions nel dizionario restituito contiene tutte le migliori soluzioni degeneri trovate, sotto forma di dizionari che associano ogni nome di variabile al suo valore assegnato. Le stesse soluzioni vengono restituite anche come stringhe di bit in solution_bitstrings; metadata.solver_info.variable_mapping indica quale posizione di carattere di una stringa di bit appartiene a quale variabile. Insieme ai dati della soluzione, vengono forniti objective_value, un flag feasible e ulteriori metadata.

Esempio 2: Maximum Independent Set​

Il problema del Maximum Independent Set (MIS) richiede il sottoinsieme più grande di vertici di un grafo tale che nessuna coppia di vertici del sottoinsieme condivida un arco. Questo esempio risolve il MIS su un grafo regolare a 12 nodi. Per maggiori informazioni sul problema e un esempio più ampio, consulta il tutorial.

Il problema viene costruito con qiskit-addon-opt-mapper. Installa i pacchetti richiesti, se necessario:

# %pip install networkx qiskit-addon-opt-mapper

Fase 1: definisci il problema​

Costruisci un grafo casuale 3-regolare e crea il problema di ottimizzazione MIS. Ogni nodo del grafo diventa una variabile binaria e ogni arco introduce un vincolo che garantisce che venga selezionato al massimo un estremo.

import networkx as nx
from qiskit_addon_opt_mapper.applications import IndependentSet
from qiskit_addon_opt_mapper.translators import to_docplex_mp

# 12-node random 3-regular graph
G = nx.random_regular_graph(3, 12, seed=7)

# Build MIS problem and export as LP string
mis = IndependentSet(G)
mis_problem = mis.to_optimization_problem()
lp_str = to_docplex_mp(mis_problem).export_as_lp_string()

print(lp_str)
\ This file has been generated by DOcplex
\ ENCODING=ISO-8859-1
\Problem name: Independent set

Maximize
obj: x_0 + x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 + x_8 + x_9 + x_10 + x_11
Subject To
c0: x_0 + x_4 <= 1
c1: x_0 + x_9 <= 1
c2: x_0 + x_8 <= 1
c3: x_1 + x_2 <= 1
c4: x_1 + x_7 <= 1
c5: x_1 + x_3 <= 1
c6: x_2 + x_4 <= 1
c7: x_2 + x_11 <= 1
c8: x_3 + x_10 <= 1
c9: x_3 + x_11 <= 1
c10: x_4 + x_5 <= 1
c11: x_5 + x_10 <= 1
c12: x_5 + x_6 <= 1
c13: x_6 + x_8 <= 1
c14: x_6 + x_7 <= 1
c15: x_7 + x_9 <= 1
c16: x_8 + x_11 <= 1
c17: x_9 + x_10 <= 1

Bounds
0 <= x_0 <= 1
0 <= x_1 <= 1
0 <= x_2 <= 1
0 <= x_3 <= 1
0 <= x_4 <= 1
0 <= x_5 <= 1
0 <= x_6 <= 1
0 <= x_7 <= 1
0 <= x_8 <= 1
0 <= x_9 <= 1
0 <= x_10 <= 1
0 <= x_11 <= 1

Binaries
x_0 x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9 x_10 x_11
End

Fase 2: esegui l'ottimizzatore​

Risolvi il problema con l'ottimizzatore.

options = {"total_shots": 2000, "use_session": True}

# Dispatch the job
job_2 = optimizer.run(
problem=lp_str, backend_name=backend.name, options=options
)

print(f"Job ID: {job_2.job_id}")
Job ID: f041431c-b42e-4627-afe5-522fea61faf6
# Monitor the job status
job_2.status()
'QUEUED'

Fase 3: recupera e interpreta i risultati​

Infine, recuperiamo la soluzione e ne verifichiamo la correttezza.

result = job_2.result()

print(f"Feasible: {result['feasible']}")
print(f"Independent set size: {int(result['objective_value'])}")

# Extract the selected nodes
solution = result["solutions"][0]
independent_set = [
int(k.split("_")[1]) for k, v in solution.items() if v == 1
]
print(f"Selected nodes: {sorted(independent_set)}")

# Verify no two selected nodes share an edge
conflicts = [
(u, v)
for u, v in G.edges()
if u in independent_set and v in independent_set
]
print(f"Edge conflicts: {conflicts}")
Feasible: True
Independent set size: 5
Selected nodes: [1, 4, 6, 9, 11]
Edge conflicts: []

Esempio 3: colorazione di un grafo a 3 colori (Max-3-Cut)​

Il problema Max-kk-Cut suddivide i vertici di un grafo in kk gruppi per massimizzare il numero di archi i cui estremi appartengono a gruppi diversi. Per k=3k = 3 questo equivale a trovare una colorazione a 3 colori che massimizzi gli archi tra colori diversi.

Formalmente, assegna ciascun nodo i∈Vi \in V a uno dei kk gruppi tramite variabili binarie xi,cx_{i,c} per il colore c∈{1,…,k}c \in \{1, \ldots, k\}. Il vincolo one-hot ∑cxi,c=1\sum_c x_{i,c} = 1 per ciascun nodo impone un'assegnazione univoca. L'obiettivo massimizza gli archi tra gruppi diversi:

min⁡∑(u,v)∈E∑cxu,c xv,cs.t.∑cxi,c=1∀i∈V\min \sum_{(u,v) \in E} \sum_c x_{u,c} \, x_{v,c}\\ \text{s.t.} \sum_{c} x_{i,c} = 1 \quad \forall i \in V

I vincoli one-hot sono ideali per gli XY-mixer, che l'ottimizzatore gestisce in modo nativo.

Questo esempio utilizza il grafo Zachary Karate Club (34 nodi, 78 archi) e costruisce il modello con il Luna Model. Installa i pacchetti richiesti, se necessario:

# %pip install networkx matplotlib luna-model

Fase 1: definisci il problema​

Innanzitutto, genera le variabili binarie con il pacchetto luna-model. Quindi, definisci l'obiettivo e aggiungi i vincoli al modello.

import networkx as nx
from luna_model import Model, quicksum

k = 3
G = nx.karate_club_graph()

# Instantiate the Model object
model = Model(name="Max-3-Cut")

# Add binary variables
x = model.add_variables("x", shape=(G.number_of_nodes(), k))

# Add the objective by using the numpy dot abstraction
model.objective = quicksum(x[i].dot(x[j]) for i, j in G.edges)

# Bulk-add the one-hot constraints
model.add_constraints(x.sum(axis=1) == 1)

# Encode the model to send via API
model_enc = model.encode_b64()

Fase 2: esegui l'ottimizzatore​

Risolvi il problema con l'ottimizzatore.

options = {"use_session": True}

# Dispatch the job
job_3 = optimizer.run(
problem=model_enc, backend_name=backend.name, options=options
)

print(f"Job ID: {job_3.job_id}")
Job ID: df911d83-9df7-44a1-bfad-5d66e08676c3
# Monitor the job status
job_3.status()
'RUNNING: WAITING_FOR_QPU'

Fase 3: recupera e visualizza i risultati​

Recupera e visualizza i risultati.

result = job_3.result()
solution = result["solutions"][0]

# Map each node to its assigned color group
node_group = {}
for i in G.nodes:
for c in range(k):
if solution[f"x{i},{c}"] == 1:
node_group[i] = c

print(f"Feasible: {result['feasible']}")
print(f"Violations: {int(result['objective_value'])}")

# Visualize the partition
colors = ["tab:blue", "tab:orange", "tab:green"]
node_colors = [colors[node_group[i]] for i in G.nodes()]
nx.draw(G, nx.kamada_kawai_layout(G), node_color=node_colors, node_size=200)
Feasible: True
Violations: 4

Output of the previous code cell

Registro delle modifiche​

[0.4.0] - 2026-09-22​

Modifiche non retrocompatibili

  • Il parametro di input model è stato rinominato in problem.
  • Il campo di output obj_value è stato rinominato in objective_value.
  • L'opzione postprocessing è stata sostituita da postprocessing_level, che accetta un intero nell'intervallo [0, 3] invece di una stringa. I valori precedenti corrispondono a "off" -> 0, "weak" -> 1, "medium" -> 2 e "strong" -> 3, e il valore predefinito resta invariato a 3.
  • metadata.circuit_metrics è stato ristrutturato. Ora riporta num_qubits, gate_count, depth, two_qubit_gate_count, two_qubit_gate_depth e operations separatamente per before_transpilation e after_transpilation, invece di un unico dizionario piatto di conteggi medi di gate. Il campo è None quando il problema è stato risolto durante il preprocessing e non è stato eseguito alcun circuito (codice di errore 4712).

Funzionalità

  • Aggiunta l'opzione seed_transpiler per rendere la sintesi e il routing dei circuiti riproducibili tra le esecuzioni.
  • Aggiunta l'opzione job_tags per associare i tuoi tag a ogni job Qiskit Runtime inviato dalla funzione.
  • Aggiunto il campo di output solution_bitstrings, che restituisce le stesse soluzioni di solutions in forma di stringhe di bit.
  • Aggiunto metadata.solver_info.variable_mapping, che associa ogni nome di variabile alla sua posizione di carattere nelle voci di solution_bitstrings.

Documentazione

  • Documentato che le variabili continue (a valori reali) non sono supportate e che le variabili intere richiedono limiti espliciti. Tali modelli falliscono con il codice di errore 4710.

Ottieni assistenza​

Per domande o problemi, contatta support@aqarios.com indicando l'ID del tuo job.

Prossimi passi​

Consigli

Riferimenti​

  1. Bucher et al. (2026), Constrained Quantum Optimization via Iterative Warm-Start XY-Mixers 10.1088/1367-2630/ae8ea2