גרעינים קוונטיים עם שערים שבריים
הערכת שימוש: פחות מ-30 שניות על מעבד Heron r2 (הערה: זוהי הערכה בלבד. זמן הריצה שלך עשוי להשתנות.)
תוצאות למידה
-
מהם שערים שבריים וכיצד הם מפחיתים את עומק המעגל ומשך הזמן על QPU-ים של IBM®
-
האילוצים הקשורים לשימוש בשערים שבריים (בפרט, טווח הזווית RZZ)
-
כיצד לבנות זרימת עבודה של גרעין קוונטי המשתמשת בשערים שבריים עם IBM Quantum Compute Service
-
כיצד להשוות מדדי הרצה על חומרה (עומק, משך זמן, מספר שערים לא-מקומיים, נאמנות) עם ובלי שערים שבריים
-
כיצד להשתמש רק בשערי RX שבריים תוך שמירה על זרימת העבודה הסטנדרטית של דפוסי Qiskit
דרישות קדם
-
זרימת העבודה של דפוסי Qiskit
-
מדריך שערים שבריים
-
המדריך אימון גרעין קוונטי והשיעור גרעינים קוונטיים בקורס למידת מכונה קוונטית
רקע
שערים שבריים ב-QPU של IBM
שערים שבריים הם שערים קוונטיים מפרמטרים המאפשרים ביצוע ישיר של סיבובים בזוויות שרירותיות (בתוך גבולות ספציפיים), ומבטלים את הצורך לפרק אותם למספר שערי בסיס. על ידי ניצול האינטראקציות הטבעיות בין qubits פיזיים, ניתן לממש יוניטריים מסוימים ביעילות רבה יותר בחומרה.
QPU של IBM Quantum® Heron תומכים בשערים השבריים הבאים:
- עבור
- עבור כל ערך ממשי
שערים אלה יכולים להפחית באופן משמעותי הן את העומק והן את משך הזמן של מעגלים קוונטיים. הם מועילים במיוחד ביישומים המסתמכים במידה רבה על ו-, כגון סימולציה המילטונית, אלגוריתם האופטימיזציה הקוונטי המקורב (QAOA), ושיטות גרעין קוונטי. במדריך זה, אנחנו מתמקדים בגרעין הקוונטי כדוגמה מעשית.
מגבלות
שערים שבריים הם כרגע תכונה ניסיונית ומגיעים עם כמה אילוצים:
- מוגבל לזוויות בטווח .
- שימוש בשערים שבריים אינו נתמך עבור מעגלים דינמיים, Pauli twirling, ביטול שגיאות הסתברותי (PEC), ו-אקסטרפולציה של אפס רעש (ZNE) (באמצעות הגברת שגיאות הסתברותית (PEA)).
שערים שבריים דורשים זרימת עבודה שונה בהשוואה לגישה הסטנדרטית. מדריך זה מסביר כיצד לעבוד עם שערים שבריים באמצעות יישום מעשי.
ראה את הדברים הבאים לפרטים נוספים על שערים שבריים.
גישות זרימת עבודה עבור אילוץ הזווית RZZ
זרימת העבודה לשימוש בשערים שבריים עוקבת בדרך כלל אחר זרימת העבודה של דפוסי Qiskit. ההבדל המרכזי הוא שכל זוויות RZZ חייבות לעמוד באילוץ . ישנן שתי גישות כדי להבטיח שתנאי זה מתקיים, כפי שנדון להלן. אנחנו ממליצים על הגישה השנייה, ובמדריך זה, אנחנו מדגימים אותה באמצעות דוגמה בהשראת שיטת הגרעין הקוונטי. כדי להבין טוב יותר היכן גרעינים קוונטיים סביר להיות שימושיים, אנחנו ממליצים לקרוא את Liu, Arunachalam & Temme (2021).
אתה יכול גם לעבוד דרך המדריך אימון גרעין קוונטי ואת השיעור גרעינים קוונטיים בקורס למידת מכונה קוונטית ב-IBM Quantum® Learning.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_basis_constructor": "qiskit-basis-constructor", "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")
1. יצירת ערכי פרמטרים העומדים באילוץ זווית RZZ
אם אתה בטוח שכל זוויות RZZ נמצאות בתוך הטווח התקף, תוכל לעקוב אחר זרימת העבודה הסטנדרטית של דפוסי Qiskit. במקרה זה, אתה פשוט מגיש את ערכי הפרמטרים כחלק מ-PUB. זרימת העבודה ממשיכה כדלקמן.
pm = generate_preset_pass_manager(backend=backend, ...)
t_circuit = pm.run(circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit, parameter_values)])
estimator.run([(t_circuit, t_observable, parameter_values)])
אם תנסה לשלוח PUB הכולל שער RZZ עם זווית מחוץ לטווח התקף, תיתקל בהודעת שגיאה כגון:
'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'
כדי להימנע משגיאה זו, השתמשו בגישה השנייה המתוארת להלן.
2. הקצאת ערכי פרמטרים למעגלים לפני טרנספילציה
חבילת qiskit-ibm-runtime מספקת מעבר טרנספיילר מיוחד בשם FoldRzzAngle.
מעבר זה הופך מעגלים קוונטיים כך שכל זוויות RZZ יעמדו באילוץ זווית RZZ.
אם אתה מספק את ה-backend ל-generate_preset_pass_manager או transpile, Qiskit מיישם אוטומטית את FoldRzzAngle למעגלים הקוונטיים.
זה דורש ממך להקצות ערכי פרמטרים למעגלים קוונטיים לפני הטרנספילציה.
זרימת העבודה ממשיכה כדלקמן.
pm = generate_preset_pass_manager(backend=backend, ...)
b_circuit = circuit.assign_parameters(parameter_values)
t_circuit = pm.run(b_circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit,)])
estimator.run([(t_circuit, t_observable)])
שימו לב שזרימת עבודה זו גוררת עלות חישובית גבוהה יותר מהגישה הראשונה, מכיוון שהיא כרוכה בהקצאת ערכי פרמטרים למעגלים קוונטיים ואחסון מקומי של המעגלים עם הפרמטרים הקשורים.
שימו לב לבעיה ידועה ב-qiskit-ibm-runtime v0.47.0 שבה שערי RZZ עם זוויות לא תקפות עלולים להישאר במעגלים גם לאחר טרנספילציה בתרחישים מסוימים.
ראו qiskit-ibm-runtime#2441 כדי לעקוב אחר ההתקדמות בבעיה זו. אנחנו ממליצים על הפתרון העוקף הבא עד שהיא תיפתר.
pm = generate_preset_pass_manager(backend=backend, ...)
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
... = pm.run(...)
דרישות
לפני שמתחילים מדריך זה, ודא שיש לך את הדברים הבאים מותקנים:
- Qiskit SDK v2.0 ומעלה, עם תמיכה ב-ויזואליזציה
- Qiskit Runtime v0.41 ומעלה (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 ומעלה (
pip install qiskit-aer) - Qiskit Basis Constructor (
pip install qiskit_basis_constructor)
הגדרה
import matplotlib.pyplot as plt
import numpy as np
from qiskit import QuantumCircuit, generate_preset_pass_manager
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import UGate, n_local, unitary_overlap
from qiskit.transpiler import Target, PassManager
from qiskit.transpiler.passes import (
Optimize1qGatesDecomposition,
RemoveIdentityEquivalent,
)
from qiskit_aer.primitives import SamplerV2 as AerSampler
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_runtime.transpiler.passes import FoldRzzAngle
הפעלת שערים שבריים ובדיקת שערי בסיס
כדי להשתמש בשערים שבריים, תוכל להשיג backend התומך בהם על ידי הגדרת אופציה use_fractional_gates=True.
אם ה-backend תומך בשערים שבריים, תראה את rzz ו-rx רשומים בין שערי הבסיס שלו.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
) # backend should be a heron device or later
backend_name = backend.name
backend_c = service.backend(backend_name) # w/o fractional gates
backend_f = service.backend(
backend_name, use_fractional_gates=True
) # w/ fractional gates
print(f"Backend: {backend_name}")
print(f"No fractional gates: {backend_c.basis_gates}")
print(f"With fractional gates: {backend_f.basis_gates}")
if "rzz" not in backend_f.basis_gates:
print(f"Backend {backend_name} does not support fractional gates")
Backend: ibm_marrakesh
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']
דוגמת סימולטור בקנה מידה קטן
בפרק זה, אנחנו עוברים על ארבעת השלבים של זרימת העבודה של דפוסי Qiskit על סימולטור, תוך שימוש במעגל הגרעין הקוונטי כדוגמה מעשית.
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
מעגל גרעין קוונטי
בחלק זה, אנחנו בוחנים את מעגל הגרעין הקוונטי באמצעות שערי RZZ כדי להציג את זרימת העבודה עבור שערים שבריים.
אנחנו מתחילים בבניית מעגל קוונטי לחישוב ערכים בודדים של מטריצת הגרעין. זה נעשה על ידי שילוב מעגלי מפת תכונות ZZ עם חפיפה יוניטרית. פונקציית הגרעין לוקחת וקטורים במרחב הממופה לתכונות ומחזירה את המכפלה הפנימית שלהם כערך של מטריצת הגרעין: כאשר מייצג את המצב הקוונטי הממופה לתכונות.
אנחנו בונים ידנית מעגל מפת תכונות ZZ באמצעות שערי RZZ.
למרות ש-Qiskit מספק zz_feature_map מובנה, הוא אינו תומך כרגע בשערי RZZ נכון ל-Qiskit v2.4.1 (ראה בעיה).
לאחר מכן, אנחנו מחשבים את פונקציית הגרעין עבור קלטים זהים - לדוגמה, . במחשבים קוונטיים רועשים, ערך זה עשוי להיות פחות מ-1 עקב רעש. תוצאה קרובה יותר ל-1 מצביעה על רעש נמוך יותר בביצוע. במדריך זה, אנחנו מתייחסים לערך זה כ-נאמנות (fidelity), המוגדרת כ-
optimization_level = 2
shots = 2000
reps = 3
rng = np.random.default_rng(seed=123)
def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:
x = ParameterVector("x", num_qubits * reps)
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for k in range(reps):
K = k * num_qubits
for i in range(num_qubits):
qc.rz(x[i + K], i)
pairs = [(i, i + 1) for i in range(num_qubits - 1)]
for i, j in pairs[0::2] + pairs[1::2]:
qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)
return qc
def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:
qc = my_zz_feature_map(num_qubits, reps=reps)
inner_product = unitary_overlap(qc, qc, "x", "y", insert_barrier=True)
inner_product.measure_all()
return inner_product
def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:
return np.tile(rng.random(inner_product.num_parameters // 2), 2)
def fidelity(result) -> float:
ba = result.data.meas
return ba.get_int_counts().get(0, 0) / ba.num_shots
מעגלי גרעין קוונטי וערכי הפרמטרים המתאימים שלהם נוצרים עבור מערכות עם 4 עד 40 qubits, והנאמנות שלהם מוערכת לאחר מכן.
qubits = list(range(4, 12, 2))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
מעגל ארבעת ה-qubits מוצג להלן.
circuits[0].draw("mpl", fold=-1)
בזרימת העבודה הסטנדרטית של דפוסי Qiskit, ערכי פרמטרים מועברים בדרך כלל ל-Sampler או Estimator primitive כחלק מ-PUB. עם זאת, בעת שימוש ב-backend התומך בשערים שבריים, ערכי פרמטרים אלה חייבים להיות מוקצים במפורש למעגל הקוונטי לפני הטרנספילציה.
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)
שלב 2: אופטימיזציה של בעיה לביצוע על חומרה קוונטית
לאחר מכן אנחנו מבצעים טרנספילציה של המעגל באמצעות מנהל המעברים בהתאם לדפוס Qiskit הסטנדרטי.
על ידי מתן backend התומך בשערים שבריים ל-generate_preset_pass_manager, מעבר מיוחד בשם FoldRzzAngle נכלל אוטומטית.
מעבר זה משנה את המעגל כדי לעמוד באילוצי זווית RZZ.
כתוצאה מכך, שערי RZZ עם ערכים שליליים באיור הקודם הופכים לערכים חיוביים, וכמה שערי X נוספים מתווספים.
backend_f = service.backend(name=backend_name, use_fractional_gates=True)
# pm_f includes `FoldRzzAngle` pass
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_f
)
pm_f.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend_f.target),
RemoveIdentityEquivalent(target=backend_f.target),
]
)
t_qc_f = pm_f.run(b_qc)
print(t_qc_f[0].count_ops())
t_qc_f[0].draw("mpl", fold=-1)
OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})
כדי להעריך את ההשפעה של שערים שבריים, אנחנו מעריכים את מספר השערים הלא-מקומיים (CZ ו-RZZ עבור backend זה), יחד עם עומקי המעגלים ומשכי הזמן, ומשווים מדדים אלה לאלה מזרימת עבודה סטנדרטית מאוחר יותר.
nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]
depth_f = [qc.depth() for qc in t_qc_f]
duration_f = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc_f
]
שלב 3: ביצוע באמצעות primitives של Qiskit
אנחנו מריצים את המעגל המטרנספל עם ה-backend התומך בשערים שבריים.
sampler_f = AerSampler.from_backend(backend_f)
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())
085ce928-767e-4200-93bf-3905e5411cfe
שלב 4: עיבוד בתר והחזרת תוצאה בפורמט קלאסי רצוי
אתה יכול לקבל את ערך פונקציית הגרעין על ידי מדידת ההסתברות של מחרוזת הביטים שכולה אפסים 00...00 בפלט.
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
[0.929, 0.882, 0.8645, 0.817]
השוואת זרימת עבודה ומעגל ללא שערים שבריים
בחלק זה, אנחנו מציגים את זרימת העבודה הסטנדרטית של דפוסי Qiskit באמצעות backend שאינו תומך בשערים שבריים. על ידי השוואת המעגלים המטרנספלים, תבחין שהגרסה המשתמשת בשערים שבריים (מהחלק הקודם) קומפקטית יותר מזו ללא שערים שבריים.
# step 1: map classical inputs to quantum problem
# `circuits` and `params` from the previous section are reused here
# step 2: optimize circuits
backend_c = service.backend(backend_name) # w/o fractional gates
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc_c = pm_c.run(circuits)
print(t_qc_c[0].count_ops())
t_qc_c[0].draw("mpl", fold=-1)
OrderedDict({'rz': 130, 'sx': 80, 'cz': 36, 'measure': 4, 'barrier': 2})
nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]
depth_c = [qc.depth() for qc in t_qc_c]
duration_c = [
qc.estimate_duration(backend_c.target, unit="u") for qc in t_qc_c
]
# step 3: execute
sampler_c = AerSampler.from_backend(backend_c)
job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())
f2cca29d-7263-4976-9e51-13a91b75c3ae
# step 4: post-processing
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)
[0.8625, 0.7605, 0.702, 0.671]
השוואת עומקים, משכי זמן, ונאמנות
בחלק זה, אנחנו משווים את מספר השערים הלא-מקומיים ואת הנאמנות בין מעגלים עם ובלי שערים שבריים. זה מדגיש את היתרונות הפוטנציאליים של שימוש בשערים שבריים מבחינת יעילות ביצוע ואיכות.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x116af3cb0>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11ea4f4d0>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x1247fc440>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x120b792b0>
דוגמת חומרה בקנה מידה גדול
בפרק זה, אנחנו בוחנים את זרימת העבודה של גרעין קוונטי עם ובלי שערים שבריים על חומרה קוונטית עם עד 40 qubits.
שלבים 1-4 משולבים
זרימת העבודה עוקבת אחר אותו מבנה כמו הדוגמה בקנה מידה קטן. אנחנו מבצעים טרנספילציה לכל המעגלים עם ובלי שערים שבריים, אוספים מדדים, ולאחר מכן שולחים את המעגלים לחומרה קוונטית אמיתית.
# -------------------------Step 1-------------------------
qubits = list(range(4, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
def benchmark(b_qc, backend):
# -------------------------Step 2-------------------------
pm = generate_preset_pass_manager(optimization_level, backend=backend)
if "rzz" in backend.target.operation_names:
# workaround until https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441 is resolved
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
t_qc = pm.run(b_qc)
nnl = [qc.num_nonlocal_gates() for qc in t_qc]
depth = [qc.depth() for qc in t_qc]
duration = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc
]
# -------------------------Step 3-------------------------
sampler = SamplerV2(mode=backend)
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.dynamical_decoupling.skip_reset_qubits = True
sampler.options.environment.job_tags = ["TUT_FG"]
job = sampler.run(t_qc, shots=shots)
job_id = job.job_id()
return nnl, depth, duration, job_id
def postprocessing(job_id: str):
# -------------------------Step 4-------------------------
job = service.job(job_id)
result = job.result()
fidelities = [fidelity(result=res) for res in result]
usage = job.usage()
return fidelities, usage
backend_f = service.backend(backend_name, use_fractional_gates=True)
nnl_f, depth_f, duration_f, job_id_f = benchmark(
b_qc, backend_f
) # step 2 & 3
print("job id (w/ fractional gates):", job_id_f)
fidelity_f, usage_f = postprocessing(job_id_f) # step 4
job id (w/ fractional gates): d8uasitbh0os73eqnpig
backend_c = service.backend(backend_name, use_fractional_gates=False)
nnl_c, depth_c, duration_c, job_id_c = benchmark(b_qc, backend_c)
print("job id (w/o fractional gates):", job_id_c)
fidelity_c, usage_c = postprocessing(job_id_c)
job id (w/o fractional gates): d8uav3lposuc738pruug
לאחר מכן אנחנו משווים מדדים.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12461e660>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11f2ac980>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x125c91be0>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11fcf6e40>
אנחנו משווים את זמן השימוש ב-QPU עם ובלי שערים שבריים. התוצאות בתא הבא מראות שזמני השימוש ב-QPU כמעט זהים.
print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")
no fractional gates: 8 seconds
fractional gates: 8 seconds
נושא מתקדם: שימוש רק בשערי RX שבריים
הצורך בזרימת עבודה מותאמת בעת שימוש בשערים שבריים נובע בעיקר מההגבלה על זוויות שער RZZ. עם זאת, אם אתה משתמש רק בשערי RX השבריים ולא כולל את שערי RZZ השבריים, אתה יכול להמשיך לעקוב אחר זרימת העבודה הסטנדרטית של דפוסי Qiskit. גישה זו עדיין יכולה להציע יתרונות משמעותיים, במיוחד במעגלים הכוללים מספר רב של שערי RX ושערי U, על ידי הפחתת ספירת השערים הכוללת ושיפור הביצועים באופן פוטנציאלי. בחלק זה, אנחנו מדגימים כיצד לבצע אופטימיזציה למעגלים שלך באמצעות רק שערי RX שבריים, תוך השמטת שערי RZZ.
כדי לתמוך בזה, אנחנו מספקים פונקציית עזר המאפשרת לך להשבית שער בסיס ספציפי באובייקט Target. כאן, אנחנו משתמשים בה כדי להשבית שערי RZZ.
def remove_instruction_from_target(target: Target, gate_name: str) -> Target:
new_target = Target(
description=target.description,
num_qubits=target.num_qubits,
dt=target.dt,
granularity=target.granularity,
min_length=target.min_length,
pulse_alignment=target.pulse_alignment,
acquire_alignment=target.acquire_alignment,
qubit_properties=target.qubit_properties,
concurrent_measurements=target.concurrent_measurements,
)
for name, qarg_map in target.items():
if name == gate_name:
continue
instruction = target.operation_from_name(name)
if qarg_map == {None: None}:
qarg_map = None
new_target.add_instruction(instruction, qarg_map, name=name)
return new_target
אנחנו משתמשים במעגל המורכב משערי U, CZ ו-RZZ כדוגמה.
qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")
אנחנו מבצעים תחילה טרנספילציה של המעגל עבור backend שאינו תומך בשערים שבריים.
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc = pm_c.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})
לאחר מכן, אנחנו מבצעים טרנספילציה של אותו מעגל באמצעות שערי RX שבריים, תוך השמטת שערי RZZ. זה מביא להפחתה קלה בספירת השערים הכוללת, הודות ליישום היעיל יותר של שערי RX.
backend_f = service.backend(backend_name, use_fractional_gates=True)
target = remove_instruction_from_target(backend_f.target, "rzz")
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})
אופטימיזציה של שערי U עם שערי RX שבריים
בפרק זה, אנחנו מדגימים כיצד לבצע אופטימיזציה לשערי U באמצעות שערי RX שבריים, על בסיס אותו מעגל שהוצג בפרק הקודם. אנחנו מבצעים טרנספילציה של המעגל באמצעות רק שערי RX שבריים, תוך השמטת שערי RZZ. על ידי הצגת כלל פירוק מותאם אישית, כפי שמוצג להלן, אנחנו יכולים להפחית את מספר שערי qubit-יחיד הנדרשים ליישום שער U.
תכונה זו נמצאת כרגע בדיון ב-בעיה GitHub זו.
# special decomposition rule for UGate
x = ParameterVector("x", 3)
zxz = QuantumCircuit(1)
zxz.rz(x[2] - np.pi / 2, 0)
zxz.rx(x[0], 0)
zxz.rz(x[1] + np.pi / 2, 0)
DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)
לאחר מכן, אנחנו מיישמים את הטרנספיילר באמצעות תרגום constructor-beta המסופק על ידי חבילת qiskit-basis-constructor.
כתוצאה מכך, מספר השערים הכולל מצטמצם בהשוואה לטרנספילציה הקודמת.
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
translation_method="constructor-beta",
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})
צעדים הבאים
אם מצאתם את העבודה הזו מעניינת, ייתכן שתתעניינו בחומר הבא:
- מדריך שערים שבריים
- מתי לא להשתמש בשערים שבריים
- הפניה ל-API של מעבר הטרנספיילר
FoldRzzAngle - המדריך אימון גרעין קוונטי
- השיעור גרעינים קוונטיים בקורס למידת מכונה קוונטית





