הגדרת רמת אופטימיזציה של ה-Transpiler
גרסאות חבילות
הקוד בדף זה פותח באמצעות הדרישות הבאות. אנו ממליצים להשתמש בגרסאות אלו או בגרסאות חדשות יותר.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
מכשירים קוונטיים אמיתיים כפופים לרעש ושגיאות Gate, ולכן אופטימיזציה של המעגלים כדי להפחית את עומקם ומספר ה-Gate שלהם יכולה לשפר משמעותית את התוצאות המתקבלות מהרצת אותם מעגלים.
לפונקציה generate_preset_pass_manager יש ארגומנט מיקום חובה אחד, optimization_level, השולט בכמה מאמץ ה-Transpiler משקיע באופטימיזציה של מעגלים. ארגומנט זה יכול להיות מספר שלם המקבל אחד מהערכים 0, 1, 2 או 3.
רמות אופטימיזציה גבוהות יות ר מייצרות מעגלים מאופטמים יותר על חשבון זמני קומפילציה ארוכים יותר.
הטבלה הבאה מסבירה את האופטימיזציות המבוצעות עם כל הגדרה.
| רמת אופטימיזציה | תיאור |
|---|---|
| 0 | ללא אופטימיזציה: בדרך כלל משמש לאפיון חומרה
|
| 1 | אופטימיזציה קלה:
|
| 2 | אופטימיזציה בינונית:
|
| 3 | אופטימיזציה גבוהה:
|
רמת אופטימיזציה בפעולה
מכיוון ש-Gateים דו-קיוביטיים הם בדרך כלל המקור המשמעותי ביותר לשגיאות, נוכל לכמת בקירוב את "יעילות החומרה" של ה-transpilation על ידי ספירת מספר ה-Gateים הדו-קיוביטיים במעגל המתקבל. כאן, ננסה את רמות האופטימיזציה השונות על מעגל קלט המורכב מ-unitary אקראי אחריו Gate SWAP.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Operator, random_unitary
UU = random_unitary(4, seed=12345)
rand_U = UnitaryGate(UU)
qc = QuantumCircuit(2)
qc.append(rand_U, range(2))
qc.swap(0, 1)
qc.draw("mpl", style="iqp")
נשתמש ב-backend המדומה FakeSherbrooke בדוגמאות שלנו. ראשית, נבצע transpile באמצעות רמת אופטימיזציה 0.
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
backend = FakeSherbrooke()
pass_manager = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)
למעגל שעבר transpile יש שישה Gate ECR דו-קיוביטיים.
חזור על הפעולה עבור רמת אופטימיזציה 1:
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
backend = FakeSherbrooke()
pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)
למעגל שעבר transpile עדיין יש שישה Gate ECR, אך מספר ה-Gateים החד-קיוביטיים הצטמצם.
חזור על הפעולה עבור רמת אופטימיזציה 2:
pass_manager = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=12345
)
qc_t2_exact = pass_manager.run(qc)
qc_t2_exact.draw("mpl", idle_wires=False)
פעולה זו מניבה את אותן תוצאות כמו רמת אופטימיזציה 1. שים לב שהגדלת רמת האופטימיזציה לא תמיד עושה הבדל.
חזור שוב, עם רמת אופטימיזציה 3:
pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=12345
)
qc_t3_exact = pass_manager.run(qc)
qc_t3_exact.draw("mpl", idle_wires=False)
כעת, יש רק שלושה Gate ECR. אנו מקבלים תוצאה זו מכיוון שברמת אופטימיזציה 3, Qiskit מנסה לסנתז מחדש בלוקי Gate דו-קיוביטיים, וכל Gate דו-קיוביטי ניתן לממש באמצעות לכל היותר שלושה Gate ECR. נוכל לקבל אפילו פחות Gate ECR אם נגדיר את approximation_degree לערך קטן מ-1, ומאפשרים ל-Transpiler לבצע קירובים שעשויים להכניס שגיאה מסוימת בפירוק ה-Gate (ראה פרמטרים נפוצים לשימוש ב-transpilation):
pass_manager = generate_preset_pass_manager(
optimization_level=3,
approximation_degree=0.99,
backend=backend,
seed_transpiler=12345,
)
qc_t3_approx = pass_manager.run(qc)
qc_t3_approx.draw("mpl", idle_wires=False)
למעגל זה יש רק שני Gate ECR, אך הוא מעגל משוער. כדי להבין כיצד ההשפעה שלו שונה מהמעגל המדויק, נוכל לחשב את ה-fidelity בין האופרטור האוניטרי שמעגל זה מממש, לבין האוניטרי המדויק. לפני ביצוע החישוב, נצמצם תחילה את המעגל שעבר transpile, המכיל 127 קיוביטים, למעגל המכיל רק את הקיוביטים הפעילים, שמספרם שניים.
import numpy as np
def trace_to_fidelity_2q(trace: float) -> float:
return (4.0 + trace * trace.conjugate()) / 20.0
# Reduce circuits down to 2 qubits so they are easy to simulate
qc_t3_exact_small = QuantumCircuit.from_instructions(qc_t3_exact)
qc_t3_approx_small = QuantumCircuit.from_instructions(qc_t3_approx)
# Compute the fidelity
exact_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_exact_small).adjoint().data, UU))
)
approx_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_approx_small).adjoint().data, UU))
)
print(
f"Synthesis fidelity\nExact: {exact_fid:.3f}\nApproximate: {approx_fid:.3f}"
)
Synthesis fidelity
Exact: 1.000+0.000j
Approximate: 0.992+0.000j
כוונון רמת האופטימיזציה יכול לשנות היבטים אחרים של המעגל גם כן, לא רק את מספר ה-Gate ECR. לדוגמאות של כיצד הגדרת רמת האופטימיזציה משנה את ה-layout, ראה ייצוג מחשבים קוונטיים.
צעדים הבאים
- כדי ללמוד עוד על הפונקציה
generate_preset_passmanager, התחל עם הנושא הגדרות ברירת מחדל ואפשרויות תצורה של Transpilation. - המשך ללמוד על transpilation עם הנושא שלבי Transpiler.
- נסה את המדריך השוואת הגדרות Transpiler.
- נסה את המדר יך בניית קודי חזרה.
- ראה את תיעוד API של Transpile.