דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

הגדרת רמת אופטימיזציה של ה-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

ללא אופטימיזציה: בדרך כלל משמש לאפיון חומרה

  • תרגום בסיסי
  • Layout/Routing: TrivialLayout, שבו נבחרים אותם מספרי קיוביט פיזיים כמו הווירטואליים ומוכנסים SWAPs לפי הצורך כדי שהמעגל יתאים לחומרה (באמצעות SabreSwap)
1

אופטימיזציה קלה:

  • Layout/Routing: תחילה מתבצע ניסיון ל-layout באמצעות TrivialLayout. אם נדרשים SWAPs נוספים, נמצא layout עם מספר מינימלי של SWAPs באמצעות SabreSwap, ולאחר מכן נעשה שימוש ב-VF2LayoutPostLayout כדי לנסות לבחור את הקיוביטים הטובים ביותר בגרף.
  • InverseCancellation
  • אופטימיזציית Gate חד-קיוביטית
2

אופטימיזציה בינונית:

  • Layout/Routing: רמת אופטימיזציה 1 (ללא trivial) + היוריסטיקה משופרת עם עומק חיפוש גדול יותר ויותר ניסיונות של פונקציית האופטימיזציה. מכיוון ש-TrivialLayout אינו בשימוש, אין ניסיון להשתמש באותם מספרי קיוביט פיזיים ווירטואליים.
  • CommutativeCancellation
3

אופטימיזציה גבוהה:

  • רמת אופטימיזציה 2 + היוריסטיקה משופרת על layout/routing עם מאמץ/ניסיונות נוספים
  • סינתזה מחדש של בלוקי Gateים דו-קיוביטיים באמצעות פירוק KAK של Cartan.
  • passes המפרים unitarity:
    • OptimizeSwapBeforeMeasure: מזיז את המדידות כדי להימנע מ-SWAPs
    • RemoveDiagonalGatesBeforeMeasure: מסיר Gateים לפני מדידות שלא ישפיעו על המדידות

רמת אופטימיזציה בפעולה​

מכיוון ש-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")

Output of the previous code cell

הערה

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

Output of the previous code cell

למעגל שעבר 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)

Output of the previous code cell

למעגל שעבר 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)

Output of the previous code cell

פעולה זו מניבה את אותן תוצאות כמו רמת אופטימיזציה 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)

Output of the previous code cell

כעת, יש רק שלושה 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)

Output of the previous code cell

למעגל זה יש רק שני 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, ראה ייצוג מחשבים קוונטיים.

הצעדים הבאים​

המלצות