אלגוריתם קירוב אופטימיזציה קוונטי
הערכת שימוש: 22 דקות על מעבד Heron r3 (הערה: זוהי הערכה בלבד. זמן הריצה שלך עשוי להשתנות.)
תוצרי למידה
לאחר שתסיים את המדריך הזה, תוכל לצפות להבין את המידע הבא:
- כיצד למפות בעיית אופטימיזציה קומבינטורית קלאסית (max-cut) להמילטוניאן קוונטי
- כיצד לממש ולהריץ את אלגוריתם קירוב האופטימיזציה הקוונטי (QAOA) באמצעות סשנים של Qiskit Runtime
- כיצד להרחיב זרימת עבודה של QAOA מדוגמת סימולטור קטנה להרצה בקנה מידה שימושי על חומרה
דרישות מקדימות
מומלץ שתכיר את הנושאים הבאים:
- יסודות המעגלים הקוונטיים
- אלגוריתמים וריאציוניים
- QAOA לעומק — לטיפול מקיף באלגוריתם QAOA וביישומו בקנה מידה שימוש י
רקע
אלגוריתם קירוב האופטימיזציה הקוונטי (QAOA) הוא שיטה איטרטיבית היברידית קוונטית-קלאסית לפתרון בעיות אופטימיזציה קומבינטורית. במדריך הזה תשתמש ב-QAOA כדי לפתור את בעיית החיתוך המקסימלי (max-cut) — בעיית אופטימיזציה מסוג NP-hard עם יישומים באשכולות, מדעי הרשת ופיזיקה סטטיסטית. בהינתן גרף של קודקודים המחוברים בקשתות, המטרה היא לחלק את הקודקודים לשתי קבוצות כך שמספר הקשתות החוצות את החלוקה יהיה מקסימלי.

מאופטימיזציה קלאסית למעגלים קוונטיים
ניתן לבטא את max-cut כבעיית אופטימיזציה בינארית קלאסית. לכל קודקוד מוקצה משתנה בינארי המציין לאיזו קבוצה הוא שייך. המטרה היא למקסם את מספר הקשתות שבהן הקצוות נמצאים בקבוצות שונות:
זוהי באופן שקול בעיית אופטימיזציה בינארית בלתי מוגבלת ריבועית (QUBO) מהצורה . דרך החלפת משתנים סטנדרטית (), ניתן לכתוב מחדש את ה-QUBO כהמילטוניאן עלות שמצב היסוד שלו מקודד את הפתרון האופטימלי. באופן כללי, להמילטוניאן הזה יש איברים ריבועיים וגם ליניאריים:
עבור בעיית max-cut הלא-משוקללת הנדונה כאן, המקדמים הליניאריים מתאפסים () ו- עבור כל קשת, מה שמותיר את הצורה הפשוטה יותר שתבנה בקוד בהמשך. הצורה הכללית יותר לעיל היא מה שתצטרך כדי להתאים את זרימת העבודה הזו לגרפים משוקללים או לבעיות אחרות שניתן לבטא כ-QUBO.
כיצד QAOA עובד
QAOA מכין פתרונות מועמדים על ידי החלת שכבות מתחלפות של שני אופרטורים על מצב סופרפוזיציה התחלתי : אופרטור העלות ואופרטור הערבוב (mixer) . הזוויות ו- עוברות אופטימיזציה בלולאת משוב קלאסית; המחשב הקוונטי מעריך את פונקציית העלות, ואופטימייזר קלאסי מעדכן את הפרמטרים עד להתכנסות. הלולאה האיטרטיבית הזו רצה בתוך session של Qiskit Runtime, השומר על ההתקן הקוונטי שמור לאורך האיטרציות לקבלת זמן השהיה נמוך יותר.
לטיפול מעמיק יותר בתאוריה של QAOA, כולל הגזירה המלאה מ-QUBO להמילטוניאן, ראה את מודול הקורס של QAOA.
במדריך הזה תפתור תחילה max-cut על גרף קטן בן חמישה קודקודים, ולאחר מכן תרחיב את אותה זרימת עבודה לבעיה בקנה מידה שימושי בת 100 קודקודים על חומרה אמיתית. הערה לגבי גישה לתוכנית: מדריך זה משתמש בסשנים של Qiskit Runtime, הזמינים רק בתוכנית Premium. אם אתה בתוכנית Open, אינך יכול להריץ את המדריך כפי שהוא כתוב; במקום זאת, תצטרך להח ליף את Session במצב job (כלומר, להגיש כל איטרציה כ-job עצמאי במקום לעטוף את לולאת האופטימיזציה ב-with Session(...)). זרימת העבודה עדיין רצה, אך כל איטרציה סופגת את זמן ההמתנה המלא בתור במקום לעשות שימוש חוזר בהתקן שמור. ראה סקירה כללית של התוכניות הזמינות למידע נוסף.
דרישות
לפני תחילת מדריך זה, ודא שיש לך את הדברים הבאים מותקנים:
- Qiskit SDK גרסה 2.0 ומעלה, עם תמיכה ב-ויזואליזציה
- Qiskit Runtime גרסה 0.22 ומעלה (
pip install qiskit-ibm-runtime)
בנוסף, תזדקק לגישה לאינסטנס ב-IBM Quantum® Platform.
הגדרה
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime rustworkx scipy
import matplotlib.pyplot as plt
import rustworkx as rx
from rustworkx.visualization import mpl_draw as draw_graph
import numpy as np
from scipy.optimize import minimize
from collections import defaultdict
from typing import Sequence
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import QAOAAnsatz
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session, EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler
דוגמה בקנה מידה קטן
החלק הזה עובר על כל שלב בזרימת העבודה של QAOA על מופע max-cut קטן בן חמישה קודקודים. למרות התווית "קנה מידה קטן", הדוגמה הזו עדיין רצה על חומרת IBM Quantum אמיתית — הקוד בוחר Backend עם 127 qubits או יותר ומריץ שם את ה-Circuit. אתחל את הבעיה שלך על ידי יצירת גרף עם קודקודים.
n_small = 5
graph = rx.PyGraph()
graph.add_nodes_from(np.arange(0, n_small, 1))
edge_list = [
(0, 1, 1.0),
(0, 2, 1.0),
(0, 4, 1.0),
(1, 2, 1.0),
(2, 3, 1.0),
(3, 4, 1.0),
]
graph.add_edges_from(edge_list)
draw_graph(graph, node_size=600, with_labels=True)
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
מפה את הגרף הקלאסי לCircuits ואופרטורים קוונטיים. כפי שתואר ברקע, עבור max-cut לא-משוקלל המילטוניאן העלות מצטמצם ל-, ו-QAOA משתמש ב-Circuit ansatz מפורמט כדי להכין מצבי יסוד מועמדים של .
בניית המילטוניאן העלות
המר את קשתות הגרף לאיברי Pauli כדי לבנות את (ראה רקע לגזירה).
def build_max_cut_paulis(
graph: rx.PyGraph,
) -> list[tuple[str, list[int], float]]:
"""Convert graph edges to a list of ZZ Pauli terms.
The returned list is in the sparse format expected by
``SparsePauliOp.from_sparse_list``: each element is
``(pauli_string, qubit_indices, coefficient)``.
"""
pauli_list = []
for edge in list(graph.edge_list()):
weight = graph.get_edge_data(edge[0], edge[1])
pauli_list.append(("ZZ", [edge[0], edge[1]], weight))
return pauli_list
max_cut_paulis = build_max_cut_paulis(graph)
cost_hamiltonian = SparsePauliOp.from_sparse_list(max_cut_paulis, n_small)
print("Cost Function Hamiltonian:", cost_hamiltonian)
Cost Function Hamiltonian: SparsePauliOp(['IIIZZ', 'IIZIZ', 'ZIIIZ', 'IIZZI', 'IZZII', 'ZZIII'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])
בניית מעגל ה-QAOA ansatz
השתמש ב-QAOAAnsatz כדי לבנות את ה-Circuit המפורמט של QAOA מתוך המילטוניאן העלות. כאן אנו משתמשים ב-reps=2 (שתי שכבות QAOA, ארבעה פרמטרים: ).
circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2)
circuit.measure_all()
circuit.draw("mpl")
circuit.parameters
ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(β[1]), ParameterVectorElement(γ[0]), ParameterVectorElement(γ[1])])
שלב 2: אופטימיזציה של הבעיה לביצוע על חומרה קוונטית
בצע Transpile של ה-Circuit המופשט להוראות מקוריות לחומרה. השלב הזה מטפל במיפוי qubits, בפירוק שערים, בניתוב ובדיכוי שגיאות. ראה את תיעוד הטרנספילציה למידע נוסף.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(backend)
# Create pass manager for transpilation. Level 3 is the most aggressive
# preset: slower to transpile, but produces shorter circuits that are
# more robust to hardware noise.
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
candidate_circuit = pm.run(circuit)
candidate_circuit.draw("mpl", fold=False, idle_wires=False)
<IBMBackend('ibm_pittsburgh')>
