Optimierungssolver: Eine Qiskit-Funktion von Q-CTRL Fire Opal
Siehe API-Referenz
Qiskit-Funktionen sind eine experimentelle Funktion, die nur für Benutzer des IBM Quantum® Premium Plan, Flex Plan und On-Prem (über IBM Quantum Platform API) Plan verfügbar ist. Sie befinden sich im Status einer Vorabversion und können sich noch ändern.
Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.
qiskit-ibm-runtime~=0.47.0 sympy~=1.14.0
Übersicht
Mit dem Fire Opal Optimization Solver können Sie Optimierungsprobleme auf Quantenhardware lösen, ohne dass Sie über Quantenkenntnisse verfügen müssen. Geben Sie einfach die übergeordnete Problemdefinition ein, und der Solver kümmert sich um den Rest. Der gesamte Arbeitsablauf ist geräuschbewusst und nutzt das Performance Management von Fire Opal unter der Haube. Der Solver liefert durchgängig genaue Lösungen für klassisch anspruchsvolle Probleme, selbst bei vollem Geräteumfang auf den größten IBM® QPUs.
Der Solver ist flexibel und kann zur Lösung kombinatorischer Optimierungsprobleme eingesetzt werden, die als Zielfunktionen oder beliebige Graphen definiert sind. Probleme müssen nicht der Gerätetopologie zugeordnet werden. Sowohl Probleme ohne als auch mit Nebenbedingungen lassen sich lösen, wobei die Nebenbedingungen als harte „ Hamming-weight-1 “-Nebenbedingungen und nicht als Strafterme durchgesetzt werden. Die in diesem Leitfaden enthaltenen Beispiele veranschaulichen, wie ein Optimierungsproblem im großen Maßstab – sowohl mit als auch ohne Nebenbedingungen – unter Verwendung verschiedener Solver-Eingabetypen gelöst wird. Das erste Beispiel betrifft ein Max-Cut-Problem, das auf einem 3-regulären Graphen mit 156 Knoten definiert ist, während sich das zweite Beispiel mit einem Graphenpartitionierungsproblem mit 50 Knoten befasst, das durch eine Kostenfunktion definiert ist.
Um Zugang zum Optimierungslöser zu erhalten, wenden Sie sich an Q-CTRL.
Funktionsbeschreibung
Der Solver optimiert und automatisiert den gesamten Algorithmus, von der Fehlerunterdrückung auf der Hardwareebene bis hin zur effizienten Problemabbildung und der klassischen Closed-Loop-Optimierung. Hinter den Kulissen reduziert die Pipeline des Solvers Fehler in jeder Phase und ermöglicht so die verbesserte Leistung, die für eine sinnvolle Skalierung erforderlich ist. Der zugrundeliegende Arbeitsablauf ist inspiriert vom Quantum Approximate Optimization Algorithm (QAOA), einem hybriden quantenklassischen Algorithmus. Eine detaillierte Zusammenfassung des gesamten Optimization Solver-Arbeitsablaufs finden Sie im veröffentlichten Manuskript.
So lösen Sie ein allgemeines Problem mit dem Optimierungslöser:
- Definieren Sie Ihr Problem in Form einer Zielfunktion, eines Graphen oder einer
SparsePauliOpSpin-Kette. - Verbinden Sie sich mit der Funktion über den Qiskit-Funktionskatalog.
- Führen Sie das Problem mit dem Solver aus und rufen Sie die Ergebnisse ab.
Zulässige Problemformate
- Polynomielle Darstellung einer Zielfunktion. Idealerweise erstellt in Python mit einem bestehenden SymPy Poly-Objekt und formatiert in eine Zeichenkette mit sympy.srepr.
- Grafische Darstellung einer bestimmten Problemart. Das Diagramm sollte mithilfe der networkx-Bibliothek unter Python erstellt werden. Anschließend sollte es mithilfe der networkx-Funktion in eine Zeichenfolge umgewandelt werden
nx.readwrite.json_graph.adjacency_data. - Darstellung einer Spin-Kette für ein bestimmtes Problem. Die Spin-Kette sollte als
SparsePauliOpObjekt dargestellt werden; weitere Einzelheiten finden Sie in der Dokumentation.
Wenn Sie ein Backend verwenden möchten, das diese Funktion derzeit nicht unterstützt, wenden Sie sich bitte an Q-CTRL, um die Unterstützung hinzuzufügen.
Benchmarks
Die Leistung kann sowohl von der jeweiligen Problemstellung als auch von den nachfolgenden Verarbeitungsschritten abhängen. In manchen Fällen können klassische Stichproben und quantengenerierte Stichproben nach einer gleichwertigen Nachbearbeitung eine ähnliche Qualität der Endlösung erzielen. Bei der Bewertung sollte daher der gesamte Optimierungsablauf berücksichtigt werden.
Veröffentlichte Benchmarking-Ergebnisse zeigen, dass der Solver erfolgreich Probleme mit mehr als 120 Qubits löst und dabei sogar früher veröffentlichte Ergebnisse zu Quanten-Annealing und Trapped-Ion-Geräten übertrifft. Die folgenden Benchmark-Metriken geben einen groben Hinweis auf die Genauigkeit und Skalierung von Problemtypen anhand einiger Beispiele. Die tatsächlichen Metriken können sich je nach den verschiedenen Problemmerkmalen unterscheiden, wie z. B. der Anzahl der Terme in der Zielfunktion (Dichte) und ihrer Lokalisierung, der Anzahl der Variablen und der Polynomordnung.
Die angegebene "Anzahl der Qubits" ist keine harte Begrenzung, sondern stellt einen groben Schwellenwert dar, bei dem man eine extrem gleichmäßige Lösungsgenauigkeit erwarten kann. Größere Probleme wurden bereits erfolgreich gelöst, und es wird empfohlen, über diese Grenzen hinaus zu testen.
Beliebige Qubit-Konnektivität wird für alle Problemtypen unterstützt.
Problemtyp | Anzahl der Qubits | Beispiel | Genauigkeit | Gesamtzeit (S) | Laufzeitverbrauch (s) | Anzahl der Iterationen |
|---|---|---|---|---|---|---|
| Quadratische Probleme mit spärlichen Verbindungen | 156 | 3-facher Max-Cut | 100 % | 1764 | 293 | 16 |
| Binäre Optimierung höherer Ordnung | 156 | Ising-Spin-Glas-Modell | 100 % | 1461 | 272 | 16 |
| Quadratische Probleme mit dichtem Zusammenhang | 50 | Vollständig verbundener Max-Cut | 100 % | 1758 | 268 | 12. |
| Beschränktes Problem mit harten Nebenbedingungen | 50 | Gewichtete Graphpartitionierung bei einer Kantendichte von 8 % | 100 % | 1074 | 215 | 10 |
Erste Schritte
Melden Sie sich zunächst mit Ihrem API-Schlüssel für „ IBM Quantum “ an. Wählen Sie anschließend die Qiskit-Funktion wie folgt aus. (In diesem Codeausschnitt wird davon ausgegangen, dass Sie Ihr Konto bereits in Ihrer lokalen Umgebung gespeichert haben.)
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")Beispiel: Uneingeschränkte Optimierung
Führen Sie das Maximum-Cut -Problem (Max-Cut) aus. Das folgende Beispiel veranschaulicht die Leistungsfähigkeit des Solvers anhand eines Max-Cut-Problems in einem ungewichteten, regulären Graphen mit 156 Knoten und 3 Kanten, aber Sie können auch Probleme in gewichteten Graphen lösen.
Zusätzlich zu qiskit-ibm-catalog werden Sie auch die folgenden Pakete verwenden, um dieses Beispiel auszuführen: networkx und numpy. Sie können diese Pakete installieren, indem Sie die folgende Zelle auskommentieren, wenn Sie dieses Beispiel in einem Notebook ausführen, das den IPython-Kernel verwendet.
# %pip install networkx numpy1. Definieren Sie das Problem
problem_type='maxcut'Sie können ein Max-Cut-Problem ausführen, indem Sie ein Graphenproblem definieren und angeben.
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:
Der Solver akzeptiert eine Zeichenkette als Eingabe für die Problemdefinition.
# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)2. Führen Sie das Problem aus
Wenn Sie die grafikbasierte Eingabemethode verwenden, geben Sie den Problemtyp an.
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)Überprüfen Sie den Status Ihrer Qiskit-Funktions-Workload oder rufen Sie die Ergebnisse wie folgt ab:
# 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. Das Ergebnis abrufen
Rufen Sie den optimalen Schnittwert aus dem Ergebniswörterbuch ab.
Die Zuordnung der Variablen zur Bitfolge hat sich möglicherweise geändert. Das Ausgabewörterbuch enthält ein variables_to_bitstring_index_map Unterwörterbuch, das bei der Überprüfung der Reihenfolge hilft.
# 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
Sie können die Genauigkeit des Ergebnisses überprüfen, indem Sie das Problem klassisch mit Open-Source-Lösern wie PuLP wenn der Graph nicht dicht verbunden ist. Probleme mit hoher Dichte können fortgeschrittene klassische Löser erfordern, um die Lösung zu validieren.
Beispiel: Optimierung unter Nebenbedingungen
Das vorangegangene „Max-Cut“-Beispiel ist ein gängiges quadratisches, uneingeschränktes binäres Optimierungsproblem. Der Optimierungslöser von Q-CTRL kann auch Optimierungsaufgaben mit Nebenbedingungen lösen, indem harte Nebenbedingungen direkt über die constraint Eingabe an den Optimierungslöser übergeben werden, anstatt sie als Strafterme in der Zielfunktion zu kodieren. Der Solver unterstützt derzeit „ Hamming-weight-1 “-Beschränkungen: Jede Beschränkung legt eine Gruppe von Variablen fest, bei der genau eine Variable den Wert 1 und alle übrigen den Wert 0 annehmen müssen.
Das folgende Beispiel veranschaulicht, wie eine Kostenfunktion und eine Reihe von harten Nebenbedingungen für ein Optimierungsproblem mit Nebenbedingungen – die Graphpartitionierung – erstellt werden, bei dem jeder Knoten eines Graphen genau einer von mehreren Gruppen zugeordnet wird, während gleichzeitig das Gesamtgewicht der Kanten minimiert wird, deren Endpunkte in derselben Gruppe liegen.
Zusätzlich zu den Paketen qiskit-ibm-catalog und qiskit werden Sie auch die folgenden Pakete verwenden, um dieses Beispiel auszuführen: numpy, networkx, und sympy. Sie können diese Pakete installieren, indem Sie die folgende Zelle auskommentieren, wenn Sie dieses Beispiel in einem Notebook ausführen, das den IPython-Kernel verwendet.
# %pip install numpy networkx sympy1. Definieren Sie das Problem
Definieren Sie ein Problem der zufälligen Graphpartitionierung, indem Sie einen Graphen mit zufällig gewichteten Knoten erzeugen.
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:
Ein Standard-Optimierungsmodell für die Partitionierung gewichteter Graphen lässt sich wie folgt formulieren. Teile die Knoten des Graphen in drei Gruppen auf: „ “, und setze „ “, wenn der Knoten „ “ der Gruppe „ “ zugeordnet ist, und andernfalls „ “. Das Ziel besteht darin, das Gesamtgewicht der Kanten zu minimieren, deren Endpunkte derselben Gruppe zugeordnet sind, wobei das Gewicht einer Kante das kombinierte Gewicht ihrer beiden Endpunkte ist:
# 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)]
)Jeder Knoten muss genau einer der drei Gruppen zugeordnet sein. Dies ist eine „ Hamming-weight-1 “-Bedingung: Für jeden Knoten muss genau eine der Werte gleich 1 sein, während die übrigen Werte gleich 0 sein müssen:
Anstatt diese Anforderung als Strafterm in der Kostenfunktion zu verankern, übergeben Sie sie mithilfe der Eingabe constraint direkt als harte Nebenbedingung an den Solver.
# 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}
Sie müssen nicht jede Variable zu constraint. hinzufügen. Jede Variable, die nicht im Wörterbuch aufgeführt ist, bleibt unbeschränkt, sodass Sie Gruppen von Variablen mit festen Beschränkungen und freie Variablen in derselben Aufgabe miteinander kombinieren können.
2. Führen Sie das Problem aus
# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)Überprüfen Sie den Status Ihrer Qiskit-Funktions-Workload oder rufen Sie die Ergebnisse wie folgt ab:
# 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. Das Ergebnis erhalten
Rufen Sie die Lösung ab und analysieren Sie die Ergebnisse. Die Lösungskosten entsprechen dem Gesamtgewicht der Kanten, deren Endpunkte in derselben Gruppe gelandet sind; daher weisen niedrigere Kosten auf eine bessere Partitionierung des Graphen hin.
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
Unterstützung anfordern
Bei Fragen oder Problemen wenden Sie sich an Q-CTRL.
Änderungsprotokoll
- 10.08.2026: Unterstützung für harte (Hamming-Gewicht 1) Nebenbedingungen über die
constraintEingabe hinzugefügt und das Beispiel zur Optimierung mit Nebenbedingungen entsprechend angepasst. - 11.02.2026: Wir bieten nun Unterstützung für
ibm_miami
Nächste Schritte
- Zugang zum Q-CTRL Optimization Solver beantragen.
- Rufen Sie die API-Referenz für diese Qiskit-Funktion auf.
- Versuchen Sie das Tutorial Lösen Sie binäre Optimierungsprobleme höherer Ordnung mit Q-CTRL's Optimization Solver.
- Rezension Sachdeva, N., et al. (2024). Die Quantenoptimierung mit einem 127-Qubit-Gate-Modell- IBM -Quantencomputer kann Quanten-Annealer bei nicht trivialen binären Optimierungsproblemen übertreffen. arXiv Vorabdruck arXiv:2406.01743.
- Rezension Loco, D., et al. (2026). Praktische Vorhersage von Hydratationsstellen in Proteintaschen für die Arzneimittelforschung auf einem Quantencomputer. arXiv Vorabdruck arXiv:2512.08390.
- Lesen Sie die Fallstudie zu Mazda.
- Lesen Sie die Fallstudie zu Network Rail.
- Sehen Sie sich die Fallstudie zur australischen Armee an.
- Lesen Sie die Fallstudie zu Transport for New South Wales.