Skip to main content
IBM Quantum Platform

Ottimizzatore quantistico vincolato - Una funzione Qiskit realizzata da Aqarios

Consulta la guida di riferimento all'API

Note

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


Panoramica

Con il Constrained Quantum Optimizer di Aqarios è possibile risolvere problemi di ottimizzazione binaria con vincoli su hardware quant IBM® e senza dover ricorrere alla riformulazione manuale e alla mappatura dei circuiti. È sufficiente fornire il problema sotto forma di file LP, file MPS o modello Luna. La funzione gestisce automaticamente la riformulazione, la sintesi dei circuiti, la transpilazione e l'esecuzione hardware. Il risolutore utilizza il warm-starting iterativo [1] dell'algoritmo FlexQAOA, un'estensione nativa per i vincoli dell'algoritmo di ottimizzazione approssimativa quantistica (QAOA).

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

Tipi di variabili supportati

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


Descrizione

Dichiarazione di non responsabilità

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

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

Utilizzando i mixer XY per applicare vincoli “one-hot”, è possibile migliorare notevolmente la qualità della soluzione per i problemi con vincoli, poiché lo spazio di ricerca viene praticamente ridotto a un sottospazio fattibile più piccolo — senza che venga introdotta alcuna complessità aggiuntiva, dovuta a termini di penalità, nel panorama di ottimizzazione.

Flusso di lavoro

Di seguito viene descritto il flusso di lavoro della funzione:

  1. Pre-elaborazione : prima della sintesi del circuito, l'ottimizzatore analizza la struttura dei vincoli del problema in ingresso:
    • Pre-elaborazione dei grafi: quando il problema lo consente, viene analizzato come un grafo, in cui gli archi determinano la relazione tra le variabili. L'analisi di questo grafico consente di apportare due miglioramenti al problema della pre-elaborazione. In primo luogo, i singoli vincoli possono essere aggregati in un unico grande vincolo mediante la fusione dei clique, in modo da essere rappresentati da un unico XY-mixer. In secondo luogo, le variabili con un solo vicino possono essere fissate in modo deterministico, riducendo così la dimensione e la complessità del problema.
    • Risoluzione dei vincoli: il preprocessore gestisce ogni vincolo separatamente: se il vincolo è di tipo “one-hot”, può essere applicato tramite XY-mixer. Se si tratta di un vincolo di inserto di insiemi, verrà 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 ricava automaticamente l'intensità della penalità dalla struttura del problema.
  2. Avvio a caldo iterativo : l'ottimizzatore esegue più catene di avvio a caldo in parallelo. Ogni catena contiene una distribuzione di probabilità sulle assegnazioni delle variabili, che viene utilizzata come probabilità iniziale del circuito QAOA (stato di avvio a caldo). Ad ogni iterazione, una catena preleva campioni dal circuito quantistico, applica una post-elaborazione classica e aggiorna la propria distribuzione in base alla frequenza di occorrenza dei campioni. Nel corso delle iterazioni, questo approccio concentra la ricerca su soluzioni di alta qualità senza ricorrere a 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 parametri principali che consentono di bilanciare la qualità della soluzione e il tempo di esecuzione.

Diagramma del flusso di lavoro dell'ottimizzatore quantistico vincolato

Benchmark

I benchmark riportati di seguito dimostrano che la funzione Qiskit "Constrained Quantum Optimizer" è in grado di trovare soluzioni fattibili e di alta qualità a problemi di ottimizzazione vincolata con ben oltre 100 variabili binarie. L'accuratezza è il rapporto tra il valore oggettivo rilevato e il valore ottimale noto. Il tempo di esecuzione corrisponde al tempo effettivo trascorso dall'invocazione della funzione alla restituzione del risultato. L'utilizzo della QPU corrisponde al tempo di calcolo quantistico fatturabile.

Sono stati esaminati due problemi: il problema dell’insieme indipendente massimo (MIS) e il problema del taglio “Max- kk ”, che rappresenta un’estensione del ben noto problema del taglio “ MaxCut ” a più di due categorie. Il problema "Max- kk -Cut" è anche l'equivalente, in termini di ottimizzazione, del problema di colorazione dei grafi (quando tutti gli archi vengono tagliati, il grafo è " kk -colorable").

Problema
Variabili
Vincoli
Qubit
Accuratezza
Fattibilità
Durata totale (s)
Utilizzo della QPU (s)
Tiri totali
MIS es60fst02186280124100.0%Vero940.2147.025.0k
MIS sloane_2dc_128128517374100.0%Vero347.6106.012.5k
Max-3-Cut Club di karate99339998.7%Vero279.5166.025.0k
Max-4-Cut Club di karate13233132100.0%Vero258.3200.025.0k

Note sul benchmark

  • I risultati sopra riportati sono stati ottenuti sul processore ibm_marrakesh Heron r2 con le opzioni predefinite e la funzione abilitata use_session . Ad esempio sloane_2dc_128 , total_shots=12500 è stato utilizzato.
  • Il numero ridotto di qubit nelle istanze MIS (74 e 124 rispetto a 128 e 186 variabili) riflette la fissazione delle variabili in fase di pre-elaborazione.
  • Le istanze di problema per il MIS sono tratte dalla Quantum Optimization Benchmarking Library. - L'istanza per il problema del taglio Max- kk -Cut è il grafo dello Zachary Karate Club, che ha un numero cromatico pari a 5.

Introduzione

Questa guida illustra le nozioni di base sull'utilizzo del Constrained Quantum Optimizer, spiegando come caricare la funzione ed eseguire semplici problemi. Per un tutorial più approfondito, consulta " Come trovare l'insieme indipendente massimo con l'ottimizzatore quantistico con vincoli di Aqarios ".

Effettua l'autenticazione utilizzando la tua chiave API di IBM Quantum e l'istanza CRN, quindi carica la funzione dal catalogo.

Note

Il codice riportato di seguito presuppone che tu abbia salvato le tue credenziali. Per configurarle, segui le istruzioni riportate nella sezione “Salva le credenziali del tuo account IBM Cloud ”.

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()

Output:

[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()

Output:

[<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 con vincoli

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

min⁡x1x2+2x2x3−x3x4−2x4x5−2x1x3−4x2x5s.t.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.t.}\quad x_1 + x_2 + x_3 \leq 1 \\ \quad x_3 - x_4 + x_5 = 0

1. Formulare il problema

Esprimere direttamente l'espressione matematica nel seguente formato LP-string:

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
"""

Si noti che [...] / 2 è una notazione obbligatoria per gli obiettivi quadratici secondo lo standard LP.

2. Eseguire 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}")

Output:

Job ID: 2e06a3a8-7c29-4c76-94d7-194293363f4a
# Monitor the job status
job_1.status()

Output:

'RUNNING: WAITING_FOR_QPU'

3. Recuperare il risultato

La soluzione, che mette in corrispondenza i nomi delle variabili con le assegnazioni associate, è fornita dal risultato.

result = job_1.result()

result

Output:

{'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 solutions campo nel dizionario di ritorno contiene tutte le soluzioni ottimali degenerate individuate, sotto forma di dizionari che associano ciascun nome di variabile al suo valore. Le stesse soluzioni vengono restituite anche come stringhe di bit in solution_bitstrings; metadata.solver_info.variable_mapping indica a quale posizione di carattere di una stringa di bit corrisponde ciascuna variabile. Oltre ai dati relativi alla soluzione, metadata vengono forniti il objective_value, un flag feasible e ulteriori.


Esempio 2: Insieme indipendente massimo

Il problema dell'insieme indipendente massimo (MIS) consiste nell'individuare il più grande sottoinsieme di vertici in un grafo tale che nessun paio di vertici all'interno del sottoinsieme sia collegato da un arco. Questo esempio risolve il problema MIS su un grafo regolare a 12 nodi. Per ulteriori informazioni sul problema e per un esempio più dettagliato, consulta il tutorial.

Il problema è stato creato con qiskit-addon-opt-mapper. Se necessario, installa i pacchetti richiesti:

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

Fase 1: Definire il problema

Costruire un grafo regolare a 3 spigoli scelto a caso e definire 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)

Output:

\ 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: Eseguire 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}")

Output:

Job ID: f041431c-b42e-4627-afe5-522fea61faf6
# Monitor the job status
job_2.status()

Output:

'QUEUED'

Fase 3: Acquisizione e interpretazione dei 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}")

Output:

Feasible: True
Independent set size: 5
Selected nodes: [1, 4, 6, 9, 11]
Edge conflicts: []

Esempio 3: Colorazione di grafi a 3 colori ( Max-3-Cut )

Il problema “Max- kk -Cut” suddivide i vertici di un grafo in gruppi “ kk ” in modo da massimizzare il numero di archi i cui estremi appartengono a gruppi diversi. Per k=3k = 3 ciò equivale a trovare una colorazione a 3 colori che massimizzi il numero di spigoli di colore incrociato.

Formalmente, si assegna a ciascun nodo i∈Vi \in V uno dei gruppi kk con 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 garantisce un’assegnazione univoca. L'obiettivo massimizza i bordi tra i gruppi:

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 i mixer XY, che l'ottimizzatore gestisce in modo nativo.

In questo esempio viene utilizzato il grafico dello Zachary Karate Club (34 nodi, 78 archi) e il modello viene costruito con Luna Model. Se necessario, installa i pacchetti richiesti:

# %pip install networkx matplotlib luna-model

Fase 1: Definire il problema

Per prima cosa, generare le variabili binarie con il pacchetto luna-model . Quindi, definire l'obiettivo e aggiungere 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: Eseguire 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}")

Output:

Job ID: df911d83-9df7-44a1-bfad-5d66e08676c3
# Monitor the job status
job_3.status()

Output:

'RUNNING: WAITING_FOR_QPU'

Fase 3: Recupero e visualizzazione dei risultati

Recuperare e visualizzare 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)

Output:

Feasible: True
Violations: 4
Output of the previous code cell

Changelog

[0.4.0] - 22 settembre 2026

Modifiche sostanziali

  • Il parametro di input è stato rinominato model in problem.
  • Ho rinominato il campo di output obj_value in objective_value.
  • L'opzione postprocessing è stata sostituita con postprocessing_level, che accetta un numero intero compreso nell'intervallo [0, 3] anziché una stringa. I valori precedenti corrispondono a "off" -> 0, "weak" -> 1, "medium" -> e "strong" -> 2``3, mentre il valore predefinito rimane invariato a 3.
  • Ristrutturato metadata.circuit_metrics. 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 contenente i conteggi medi dei gate. Il campo viene compilato quando None il problema è stato risolto durante la pre-elaborazione e non è stato eseguito alcun circuito (codice di errore 4712).

Funzioni

  • È stata aggiunta l'opzione seed_transpiler per rendere la sintesi e il routing dei circuiti riproducibili tra un'esecuzione e l'altra.
  • È stata aggiunta l'opzione job_tags che consente di associare tag personalizzati a ogni processo Qiskit Runtime inviato dalla funzione.
  • È stato aggiunto il campo di output solution_bitstrings, che restituisce le stesse soluzioni in solutions formato stringa di bit.
  • Aggiunto metadata.solver_info.variable_mapping, che associa ogni nome di variabile alla sua posizione in termini di caratteri nelle voci di solution_bitstrings.

Documentazione

  • È stato documentato che le variabili continue (a valori reali) non sono supportate e che le variabili intere richiedono limiti espliciti. Tali modelli generano un errore con il codice 4710.

Ottenere supporto

Per domande o problemi, contatta [email protected] indicando il numero di riferimento del lavoro.


Passi successivi

Suggerimenti

Riferimenti

  1. Bucher et al. (2026), *Ottimizzazione quantistica vincolata tramite XY-Mixer iterativi con Warm-Start *10.1088/1367-2630/ae8ea2
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.