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

גרעינים קוונטיים עם שערים שבריים

הערכת שימוש: פחות מ-30 שניות על מעבד Heron r2 (הערה: זוהי הערכה בלבד. זמן הריצה שלך עשוי להשתנות.)

תוצאות למידה​

  • מהם שערים שבריים וכיצד הם מפחיתים את עומק המעגל ומשך הזמן על QPU-ים של IBM®

  • האילוצים הקשורים לשימוש בשערים שבריים (בפרט, טווח הזווית RZZ)

  • כיצד לבנות זרימת עבודה של גרעין קוונטי המשתמשת בשערים שבריים עם IBM Quantum Compute Service

  • כיצד להשוות מדדי הרצה על חומרה (עומק, משך זמן, מספר שערים לא-מקומיים, נאמנות) עם ובלי שערים שבריים

  • כיצד להשתמש רק בשערי RX שבריים תוך שמירה על זרימת העבודה הסטנדרטית של דפוסי Qiskit

דרישות קדם​

רקע​

שערים שבריים ב-QPU של IBM​

שערים שבריים הם שערים קוונטיים מפרמטרים המאפשרים ביצוע ישיר של סיבובים בזוויות שרירותיות (בתוך גבולות ספציפיים), ומבטלים את הצורך לפרק אותם למספר שערי בסיס. על ידי ניצול האינטראקציות הטבעיות בין qubits פיזיים, ניתן לממש יוניטריים מסוימים ביעילות רבה יותר בחומרה.

QPU של IBM Quantum® Heron תומכים בשערים השבריים הבאים:

  • RZZ(θ)R_{ZZ}(\theta) עבור 0<θ<π/20 < \theta < \pi / 2
  • RX(θ)R_X(\theta) עבור כל ערך ממשי θ\theta

שערים אלה יכולים להפחית באופן משמעותי הן את העומק והן את משך הזמן של מעגלים קוונטיים. הם מועילים במיוחד ביישומים המסתמכים במידה רבה על RZZR_{ZZ} ו-RXR_X, כגון סימולציה המילטונית, אלגוריתם האופטימיזציה הקוונטי המקורב (QAOA), ושיטות גרעין קוונטי. במדריך זה, אנחנו מתמקדים בגרעין הקוונטי כדוגמה מעשית.

מגבלות​

שערים שבריים הם כרגע תכונה ניסיונית ומגיעים עם כמה אילוצים:

שערים שבריים דורשים זרימת עבודה שונה בהשוואה לגישה הסטנדרטית. מדריך זה מסביר כיצד לעבוד עם שערים שבריים באמצעות יישום מעשי.

ראה את הדברים הבאים לפרטים נוספים על שערים שבריים.

גישות זרימת עבודה עבור אילוץ הזווית RZZ​

זרימת העבודה לשימוש בשערים שבריים עוקבת בדרך כלל אחר זרימת העבודה של דפוסי Qiskit. ההבדל המרכזי הוא שכל זוויות RZZ חייבות לעמוד באילוץ 0<θ≤π/20 < \theta \leq \pi/2. ישנן שתי גישות כדי להבטיח שתנאי זה מתקיים, כפי שנדון להלן. אנחנו ממליצים על הגישה השנייה, ובמדריך זה, אנחנו מדגימים אותה באמצעות דוגמה בהשראת שיטת הגרעין הקוונטי. כדי להבין טוב יותר היכן גרעינים קוונטיים סביר להיות שימושיים, אנחנו ממליצים לקרוא את 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 עם חפיפה יוניטרית. פונקציית הגרעין לוקחת וקטורים במרחב הממופה לתכונות ומחזירה את המכפלה הפנימית שלהם כערך של מטריצת הגרעין: K(x,y)=⟨Φ(x)∣Φ(y)⟩,K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, כאשר ∣Φ(x)⟩|\Phi(x)\rangle מייצג את המצב הקוונטי הממופה לתכונות.

אנחנו בונים ידנית מעגל מפת תכונות ZZ באמצעות שערי RZZ. למרות ש-Qiskit מספק zz_feature_map מובנה, הוא אינו תומך כרגע בשערי RZZ נכון ל-Qiskit v2.4.1 (ראה בעיה).

לאחר מכן, אנחנו מחשבים את פונקציית הגרעין עבור קלטים זהים - לדוגמה, K(x,x)=1K(x, x) = 1. במחשבים קוונטיים רועשים, ערך זה עשוי להיות פחות מ-1 עקב רעש. תוצאה קרובה יותר ל-1 מצביעה על רעש נמוך יותר בביצוע. במדריך זה, אנחנו מתייחסים לערך זה כ-נאמנות (fidelity), המוגדרת כ- fidelity=K(x,x).\text{fidelity} = K(x, x).

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)

Output of the previous code cell

בזרימת העבודה הסטנדרטית של דפוסי 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)

Output of the previous code cell

שלב 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})

Output of the previous code cell

כדי להעריך את ההשפעה של שערים שבריים, אנחנו מעריכים את מספר השערים הלא-מקומיים (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: עיבוד בתר והחזרת תוצאה בפורמט קלאסי רצוי​

אתה יכול לקבל את ערך פונקציית הגרעין K(x,x)K(x, x) על ידי מדידת ההסתברות של מחרוזת הביטים שכולה אפסים 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})

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

דוגמת חומרה בקנה מידה גדול​

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

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

אנחנו משווים את זמן השימוש ב-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")

Output of the previous code cell

אנחנו מבצעים תחילה טרנספילציה של המעגל עבור 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})

Output of the previous code cell

לאחר מכן, אנחנו מבצעים טרנספילציה של אותו מעגל באמצעות שערי 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})

Output of the previous code cell

אופטימיזציה של שערי 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})

Output of the previous code cell

צעדים הבאים​

המלצות

אם מצאתם את העבודה הזו מעניינת, ייתכן שתתעניינו בחומר הבא: