Zum Hauptinhalt springen
Dieses Projekt befindet sich in der Beta-Phase. Einen Fehler gefunden? GitHub Issue erstellen. Frage oder Idee? In Discussions fragen — wir freuen uns über dein Feedback!
Wie ist die Übersetzungsqualität auf dieser Seite?

Constrained Quantum Optimizer – Eine Qiskit Function von Aqarios

Siehe die API-Referenz

hinweis

Qiskit Functions sind ein experimentelles Feature, das nur Nutzern des IBM Quantum® Premium Plan, Flex Plan und On-Prem Plan (über die IBM Quantum Platform API) zur Verfügung steht. Sie befinden sich im Preview-Release-Status und können sich ändern.

Überblick​

Mit dem Constrained Quantum Optimizer von Aqarios kannst du binäre Optimierungsprobleme mit Nebenbedingungen auf IBM®-Quantenhardware lösen, ohne manuelle Neuformulierung und Circuit-Mapping. Gib das Problem einfach als LP-Datei, MPS-Datei oder als Luna Model an. Die Funktion übernimmt automatisch die Neuformulierung, die Circuit-Synthese, die Transpilation und die Hardwareausführung. Der Solver nutzt iteratives Warm-Starting [1] des FlexQAOA-Algorithmus, einer constraint-nativen Erweiterung des Quantum Approximate Optimization Algorithm (QAOA).

Der Optimierer zielt auf binäre Probleme mit Constraints ab. Insbesondere können One-Hot-Constraints und Set-Packing-Constraints nativ mithilfe von XY-Mixern durchgesetzt werden. Andere Constraint-Typen werden automatisch als Penalty-Terme umformuliert.

Unterstützte Variablentypen

Die Funktion löst nur diskrete Probleme. Jede Entscheidungsvariable muss binär sein oder ganzzahlig mit expliziter unterer und oberer Schranke, damit sie in Qubits kodiert werden kann. Kontinuierliche (reellwertige) Variablen werden nicht unterstützt: Ein Modell, das sie enthält, wird mit dem Fehlercode 4710 abgelehnt.

Beschreibung​

Haftungsausschluss

Die Performance kann sowohl von der Probleminstanz als auch von den nachfolgenden Verarbeitungsschritten abhängen. In manchen Fällen können klassische Samples und quantengenerierte Samples nach äquivalenter Nachbearbeitung eine ähnliche finale Lösungsqualität erreichen. Die Bewertung sollte daher den gesamten Optimierungsworkflow berücksichtigen.

Der Constrained Quantum Optimizer implementiert den Iterative Warm-Start XY-Mixer QAOA (IWS-QAOA) [1]. Der Algorithmus führt Fixed-Angle-QAOA-Circuits auf Quantenhardware aus und aktualisiert die anfänglichen Warm-Start-Wahrscheinlichkeiten jedes Circuits anhand der Messergebnisse der vorherigen Iteration. Es ist kein variationelles Parametertraining erforderlich; stattdessen wird eine feste lineare Ramp-Parametrisierung gewählt.

Durch die Verwendung von XY-Mixern zur Durchsetzung von One-Hot-Constraints kannst du die Lösungsqualität für Probleme mit Nebenbedingungen drastisch verbessern, da der Suchraum praktisch auf einen kleineren zulässigen Teilraum reduziert wird — und keine zusätzliche Komplexität durch Penalty-Terme in die Optimierungslandschaft eingeführt wird.

Workflow​

Der Workflow der Funktion wird im Folgenden beschrieben:

  1. Preprocessing: Vor der Circuit-Synthese analysiert der Optimierer die Constraint-Struktur des Eingabeproblems:

    • Graph-Preprocessing: Sofern es sich anbietet, wird das Problem als Graph analysiert, wobei Kanten die Beziehung zwischen Variablen bestimmen. Diese Graphanalyse ermöglicht zwei Preprocessing-Verbesserungen des Problems. Erstens können einzelne Constraints durch Clique-Merging zu einem großen Constraint zusammengefasst werden, sodass sie durch einen einzigen XY-Mixer dargestellt werden. Zweitens können Variablen mit nur einem einzigen Nachbarn deterministisch fixiert werden, was die Problemgröße und -komplexität reduziert.

    • Constraint-Auflösung: Der Preprocessor behandelt jeden Constraint separat: Handelt es sich um einen One-Hot-Constraint, kann er durch XY-Mixer durchgesetzt werden. Handelt es sich um einen Set-Packing-Constraint, wird er zunächst in einen One-Hot-Constraint umgewandelt. Gleichheits-Constraints werden durch quadratische Penalties behandelt und Ungleichheits-Constraints durch unausgewogene Penalisierung.

    • Penalty-Ableitung: Constraints, die nicht als One-Hot-Gleichheits-Constraints ausgedrückt werden können, werden in Penalty-Terme umgewandelt. Der Optimierer leitet die Penalty-Stärke automatisch aus der Problemstruktur ab.

  2. Iteratives Warm-Starting: Der Optimierer führt mehrere parallele Warm-Start-Ketten aus. Jede Kette verwaltet eine Wahrscheinlichkeitsverteilung über Variablenbelegungen, die als Startwahrscheinlichkeit des QAOA-Circuits (Warm-Start-Zustand) verwendet wird. Bei jeder Iteration sampelt eine Kette aus dem Quanten-Circuit, wendet klassische Nachbearbeitung an und aktualisiert ihre Verteilung basierend auf dem Auftreten von Samples. Über die Iterationen hinweg fokussiert dies die Suche auf hochwertige Lösungen ohne eine variationelle Optimierungsschleife.

Die Anzahl der Ketten (num_parallel), das gesamte Shot-Budget pro Kette (total_shots) und die Anzahl der QAOA-Wiederholungen (reps) sind die wichtigsten Stellschrauben, um Lösungsqualität gegen Laufzeit abzuwägen.

Workflow diagram of the Constrained Quantum Optimizer

Benchmarks​

Die folgenden Benchmarks zeigen, dass die Constrained Quantum Optimizer Qiskit Function in der Lage ist, hochwertige, zulässige Lösungen für Optimierungsprobleme mit Nebenbedingungen mit deutlich über 100 binären Variablen zu finden. Die Genauigkeit ist das Verhältnis des gefundenen Zielfunktionswerts zum bekannten Optimum. Die Laufzeit ist die reale Zeit (Wall-Clock-Zeit) vom Funktionsaufruf bis zur Ergebnislieferung. Die QPU-Nutzung ist die abrechenbare Quantum-Compute-Zeit.

Es wurden zwei Probleme untersucht: das Maximum Independent Set (MIS)-Problem und der Max-kk-Cut, eine Erweiterung des bekannten MaxCut-Problems auf mehr als zwei Kategorien. Max-kk-Cut ist auch das zum Graph-Coloring-Entscheidungsproblem äquivalente Optimierungsproblem (wenn alle Kanten geschnitten werden, ist der Graph kk-färbbar).

ProblemVariablenConstraintsQubitsGenauigkeitZulässigkeitGesamtlaufzeit (s)QPU-Nutzung (s)Gesamt-Shots
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

Benchmark-Hinweise​

  • Die obigen Ergebnisse wurden auf dem ibm_marrakesh Heron r2-Prozessor mit Standardoptionen und aktiviertem use_session erzielt. Für die Instanz sloane_2dc_128 wurde total_shots=12500 verwendet.

  • Die reduzierte Qubit-Anzahl für MIS-Instanzen (74 und 124 gegenüber 128 und 186 Variablen) spiegelt die Variablenfixierung im Preprocessing wider.

  • Die Probleminstanzen für MIS stammen aus der Quantum Optimization Benchmarking Library. - Die Instanz für das Max-kk-Cut-Problem ist der Zachary Karate Club-Graph, der eine chromatische Zahl von 5 hat.

Erste Schritte​

Dieser Leitfaden zeigt die grundlegende Verwendung des Constrained Quantum Optimizer, indem er zeigt, wie du die Funktion lädst und einfache Probleme ausführst. Für ein ausführlicheres Tutorial siehe Finde das Maximum Independent Set mit dem Aqarios Constrained Quantum Optimizer.

Authentifiziere dich mit deinem IBM Quantum API-Schlüssel und deiner Instance CRN und lade dann die Funktion aus dem Katalog.

hinweis

Der folgende Code setzt voraus, dass du deine Anmeldedaten gespeichert hast. Folge zum Einrichten den Anweisungen unter Speichere deine IBM Cloud-Kontoanmeldedaten.

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

Beispiel 1: Ein einfaches Problem mit Nebenbedingungen​

Das erste Beispiel zeigt, wie du die Funktion mit LP-Dateien verwendest, einer gängigen Optimierungsproblem-Spezifikation aus dem Operations Research. Du wirst das folgende beliebige Optimierungsproblem mit fünf binären Variablen xi∈{0,1}x_i \in \{0,1\} unter zwei Constraints formulieren:

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. Formuliere das Problem​

Gib den mathematischen Ausdruck direkt im folgenden LP-String-Format an:

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

Beachte, dass [ ... ] / 2 eine erforderliche Notation für quadratische Zielfunktionen aus dem LP-Standard ist.

2. Führe den Optimierer aus​

Löse das Problem mit dem Optimierer.

# 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. Rufe das Ergebnis ab​

Das Ergebnis enthält die Lösung, die die Variablennamen den zugehörigen Zuweisungen zuordnet.

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

Das Feld solutions im zurückgegebenen Dictionary enthält alle gefundenen entarteten besten Lösungen als Dictionaries, die jeden Variablennamen auf seine Zuweisung abbilden. Dieselben Lösungen werden auch als Bitstrings in solution_bitstrings zurückgegeben; metadata.solver_info.variable_mapping sagt dir, welche Zeichenposition eines Bitstrings zu welcher Variable gehört. Neben den Lösungsdaten werden der objective_value, ein feasible-Flag und zusätzliche metadata bereitgestellt.

Beispiel 2: Maximum Independent Set​

Das Maximum Independent Set (MIS)-Problem fragt nach der größten Teilmenge von Knoten in einem Graphen, sodass keine zwei Knoten in der Teilmenge eine Kante teilen. Dieses Beispiel löst MIS auf einem regulären Graphen mit 12 Knoten. Weitere Informationen zum Problem und ein größeres Beispiel findest du im Tutorial.

Das Problem wird mit qiskit-addon-opt-mapper erstellt. Installiere bei Bedarf die erforderlichen Pakete:

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

Schritt 1: Definiere das Problem​

Erstelle einen zufälligen 3-regulären Graphen und erzeuge das MIS-Optimierungsproblem. Jeder Graphknoten wird zu einer binären Variable, und jede Kante führt einen Constraint ein, der sicherstellt, dass höchstens ein Endpunkt ausgewählt wird.

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

Schritt 2: Führe den Optimierer aus​

Löse das Problem mit dem Optimierer.

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'

Schritt 3: Rufe Ergebnisse ab und interpretiere sie​

Abschließend rufen wir die Lösung ab und überprüfen sie auf Korrektheit.

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: []

Beispiel 3: Graph-3-Färbung (Max-3-Cut)​

Das Max-kk-Cut-Problem partitioniert die Knoten eines Graphen in kk Gruppen, um die Anzahl der Kanten zu maximieren, deren Endpunkte zu unterschiedlichen Gruppen gehören. Für k=3k = 3 ist dies gleichbedeutend mit dem Finden einer 3-Färbung, die die Anzahl der farbübergreifenden Kanten maximiert.

Formal wird jeder Knoten i∈Vi \in V mithilfe binärer Variablen xi,cx_{i,c} für Farbe c∈{1,…,k}c \in \{1, \ldots, k\} einer von kk Gruppen zugewiesen. Der One-Hot-Constraint ∑cxi,c=1\sum_c x_{i,c} = 1 für jeden Knoten erzwingt eine eindeutige Zuweisung. Die Zielfunktion maximiert gruppenübergreifende Kanten:

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

Die One-Hot-Constraints sind ideal für XY-Mixer, die der Optimierer nativ verarbeitet.

Dieses Beispiel verwendet den Zachary Karate Club-Graphen (34 Knoten, 78 Kanten) und erstellt das Modell mit dem Luna Model. Installiere bei Bedarf die erforderlichen Pakete:

# %pip install networkx matplotlib luna-model

Schritt 1: Definiere das Problem​

Erzeuge zunächst die binären Variablen mit dem Paket luna-model. Definiere anschließend die Zielfunktion und füge die Constraints zum Modell hinzu.

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

Schritt 2: Führe den Optimierer aus​

Löse das Problem mit dem Optimierer.

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'

Schritt 3: Rufe Ergebnisse ab und visualisiere sie​

Rufe die Ergebnisse ab und visualisiere sie.

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

Änderungsprotokoll​

[0.4.0] - 2026-09-22​

Breaking Changes

  • Der Eingabeparameter model wurde in problem umbenannt.
  • Das Ausgabefeld obj_value wurde in objective_value umbenannt.
  • Die Option postprocessing wurde durch postprocessing_level ersetzt, die statt eines Strings eine ganze Zahl im Bereich [0, 3] erwartet. Die bisherigen Werte werden wie folgt abgebildet: "off" -> 0, "weak" -> 1, "medium" -> 2 und "strong" -> 3; der Standardwert bleibt unverändert bei 3.
  • metadata.circuit_metrics wurde umstrukturiert. Es meldet nun num_qubits, gate_count, depth, two_qubit_gate_count, two_qubit_gate_depth und operations getrennt für before_transpilation und after_transpilation, statt eines einzelnen flachen Dictionaries gemittelter Gate-Anzahlen. Das Feld ist None, wenn das Problem während des Preprocessings gelöst wurde und kein Circuit ausgeführt wurde (Fehlercode 4712).

Features

  • Die Option seed_transpiler wurde hinzugefügt, um Circuit-Synthese und Routing über mehrere Läufe hinweg reproduzierbar zu machen.
  • Die Option job_tags wurde hinzugefügt, um jedem Qiskit-Runtime-Job, den die Function einreicht, eigene Tags anzuhängen.
  • Das Ausgabefeld solution_bitstrings wurde hinzugefügt, das dieselben Lösungen wie solutions in Bitstring-Form zurückgibt.
  • metadata.solver_info.variable_mapping wurde hinzugefügt, das jeden Variablennamen auf seine Zeichenposition in den Einträgen von solution_bitstrings abbildet.

Dokumentation

  • Es wurde dokumentiert, dass kontinuierliche (reellwertige) Variablen nicht unterstützt werden und dass ganzzahlige Variablen explizite Schranken benötigen. Solche Modelle schlagen mit dem Fehlercode 4710 fehl.

Support erhalten​

Bei Fragen oder Problemen wende dich mit deiner Job-ID an support@aqarios.com.

Nächste Schritte​

Empfehlungen

Referenzen​

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