הגדרת רמת אופטימיזציה של ה-Transpiler
Package versions
הקוד בדף זה פותח באמצעות הדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלו או בגרסאות חדשות יותר.
qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
מכשירים קוונטיים אמיתיים כפופים לרעש ושגיאות 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 — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_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 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 מ-qiskit_ibm_runtime משמש בדוגמאות אלו, אך ניתן לנסות זאת על כל backend אמיתי או מדומה התואם ל-Qiskit. התוצאות שלך עשויות להיות שונות.
ראשית, נבצע 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.