איך לפרש את הוויזואליזציות של shaded lightcone
החבילה מספקת שתי דרכים להסתכל על shaded lightcone:
-
draw_shaded_lightconeמציב את החסמים מעל דיאגרמת המעגל. זה הכי טוב למעגלים על שרשרת חד-ממדית של Qubit, שבהם דיאגרמת המעגל והפריסה הפיזית מתאימות. -
animate_shaded_lightconeמקרין את החסמים על מפת הקישוריות של ה-QPU ועובר על המעגל שכבה אחר שכבה. זה הכי טוב כשהקישוריות של ה-Qubit היא יותר מקו חד-ממדי, ודיאגרמת מעגל מפסיקה להעביר מי שכני מי.
שתי הוויזואליזציות מציגות את אותם מספרים. המדריך הזה מסביר מה המספרים האלה אומרים, ומסביר את הפרטים שקל לקרוא לא נכון.
הגדרה
השתמשו באותו מעגל Ising מסוג Trotter ובאותו אובזרבבל כמו במדריך ההתחלה המהירה.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_slc": "qiskit-addon-slc", "samplomatic": "samplomatic"}
_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")
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli, PauliLindbladMap, QubitSparsePauliList
from qiskit_addon_slc.bounds import (
compute_backward_bounds,
compute_forward_bounds,
merge_bounds,
)
from qiskit_addon_slc.utils import generate_noise_model_paulis
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions
def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):
"""Trotterized transverse-field Ising evolution on a 1D chain."""
circuit = QuantumCircuit(num_qubits)
for _ in range(num_steps):
circuit.rx(rx_angle, range(num_qubits))
circuit.barrier()
for start in (0, 1): # even then odd bonds
for i in range(start, num_qubits - 1, 2):
circuit.rzz(rzz_angle, i, i + 1)
circuit.barrier()
return circuit
num_qubits = 10
circuit = trotter_ising_circuit(
num_qubits, num_steps=4, rx_angle=np.pi / 16, rzz_angle=-np.pi / 3
)
observable = Pauli("I" * num_qubits).compose("Z", [num_qubits // 2])
boxing_pass = generate_boxing_pass_manager(
inject_noise_targets="all",
inject_noise_strategy="individual_modification",
inject_noise_site="after",
twirling_strategy="active",
remove_barriers="never",
)
boxed_circuit = boxing_pass.run(circuit)
noise_model_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_circuit)
)
forward_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable
)
backward_bounds = compute_backward_bounds(boxed_circuit, noise_model_paulis)
rng = np.random.default_rng(42)
noise_rates = {
layer: PauliLindbladMap.from_components(
rng.random(len(terms)) * 5e-3,
QubitSparsePauliList.from_sparse_list(
terms.to_sparse_list(), terms.num_qubits
),
)
for layer, terms in noise_model_paulis.items()
}
merged_bounds = merge_bounds(
boxed_circuit, forward_bounds, backward_bounds, noise_rates
)
print(f"observable: Z on qubit {num_qubits // 2}")
Optimal spacetime partitioning not implemented! Just partitioning list of noisy boxes.
observable: Z on qubit 5
draw_shaded_lightcone
כל תיבה רועשת מוצללת לפי החסם על השגיאות שמתרחשות בה: בהיר אומר חסם גדול, ותיבה שנעלמת לתוך הרקע אומרת שלשגיאות שלה יש השפעה קטנה על האובזרבבל. התיבות הדהויות האלה הן המועמדות להיזרק ממודל הרעש המוקל.
from qiskit_addon_slc.visualization import draw_shaded_lightcone
draw_shaded_lightcone(
boxed_circuit, merged_bounds, noise_model_paulis, fold=-1, scale=0.5
)

הערות חשובות
1. כל מספר הוא סכום על איברי השגיאה החולקים תומך. בלי מסנן, החסמים של כל איבר שגיאה הפועל על אותו Qubit, או על אותו זוג Qubit, מתווספים לערך אחד: כל שלושת סוגי ה-Pauli על Qubit, וכל התשעה על זוג. לכך יש שתי השלכות. ערך יכול לחרוג מ-2.0, אף על פי שהחסם של אף איבר בודד לא יכול (תיבה עם התווית 4.5 היא כמה איברים שמסתכמים, לא חסם שהופר). והאיור לא יכול לומר לכם אם ערך גדול בא מסוג Pauli אחד או מכמה סוגים דומים בגודלם; הסעיף הבא מראה איך להפריד ביניהם.
2. סקאלת הצבעים מתחילה ב-2.0 וגדלה עם הנתונים. הסקאלה רצה מ-0 עד הערך "max_bound" במטא-נתונים של המעגל המונח מעליו, שהוא הגדול מבין 2.0 והערך הגדול ביותר המוצג. סכומים לא מסוננים חורגים לעיתים קרובות מ-2.0, ולכן הסקאלה נמתחת כדי להכיל אותם, ואותו צבע יכול לייצג ערכים שונים באיורים שונים. כשכל הערכים קטנים, הסקאלה נשארת ב-2.0 והכול נראה כהה באופן אחיד (זו הסקאלה שמשקפת את הגודל המוחלט, לא תקלת רינדור).
הפרדה בין סוגי ה-Pauli
השתמשו ב-pauli_filter כדי לראות סוג אחד בכל פעם. מספר שלם בוחר משקל Pauli; מחרוזת בוחרת Pauli ספציפי, ומותאמת מול כל איבר רעש מצומצם לתומך הלא-זהותי שלו. כך ש-"X" בוחר כל שגיאת חד-Qubitית, בלי קשר לאיזה Qubit היא פועלת, ו-"XX" בוחר את אלה במשקל 2 (תווית ברוחב מלא כמו "XII" לא מתאימה לכלום).
from qiskit_addon_slc.visualization import accumulate_filtered_bounds
box_id = next(iter(merged_bounds))
print(f"single-qubit supports of box {box_id!r}:\n")
print(f"{'filter':>8} bounds per qubit")
for pauli_filter in (None, 1, "X", "Y", "Z"):
filtered = accumulate_filtered_bounds(
boxed_circuit, merged_bounds, noise_model_paulis, pauli_filter
)
per_qubit = {
support[0]: round(float(bound), 3)
for support, bound in sorted(filtered[box_id].items())
if len(support) == 1
}
print(f"{pauli_filter!s:>8} {per_qubit}")
single-qubit supports of box 'm0':
filter bounds per qubit
None {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.312, 4: 1.355, 5: 5.111, 6: 1.506, 7: 0.124, 8: 0.019, 9: 0.0}
1 {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.312, 4: 1.355, 5: 5.111, 6: 1.506, 7: 0.124, 8: 0.019, 9: 0.0}
X {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.153, 4: 0.577, 5: 2.0, 6: 0.702, 7: 0.056, 8: 0.01, 9: 0.0}
Y {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.148, 4: 0.607, 5: 2.0, 6: 0.631, 7: 0.057, 8: 0.01, 9: 0.0}
Z {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.011, 4: 0.172, 5: 1.111, 6: 0.172, 7: 0.011, 8: 0.0, 9: 0.0}
השורה הלא מסוננת היא הסכום של שלושת סוגי ה-Pauli שמתחתיה. סינון הוא הדרך היחידה לדעת אם ערך לא מסונן גדול בא מסוג שגיאה דומיננטי אחד או מכמה סוגים דומים בגודלם. זה חשוב, כי ההפחתה פועלת לכל איבר, לא לכל אתר.
העברת אותו מסנן ל-draw_shaded_lightcone רושמת אותו בכותרת האיור, כך שאי אפשר לטעות ולחשוב שאיור מסונן הוא לא מסונן:
draw_shaded_lightcone(
boxed_circuit,
merged_bounds,
noise_model_paulis,
pauli_filter="X",
fold=-1,
scale=0.5,
)

האיור הזה נראה שונה למדי מהאיור הלא מסונן שלמעלה, ולא רק כי הוא מציג פחות ערכים. כל מציין מקום באיורים האלה מייצג תומך אחד: Qubit בודד, או זוג Qubit. בלי מסנן, תיבה מציגה מציין מקום על כל Qubit ועל כל זוג Qubit שכנים, ולכן היא נפרשת על כמה עמודות. עם "X" נשארים רק התומכים החד-Qubitיים, וכל תיבה מתכווצת לעמודה אחת.
אבל אף אחת מהתמונות לא מראה מה PEC באמת מפעיל. ה-placeholders הם דרך להציג חסמים, ולא שערים נגד רעש. במהלך הניסוי, כל קופסה שאיברי השגיאה שלה מקוּזזים נחשבת לשכבה אחת של שערים דו-קיוביטיים, ופאולי-ים נגד רעש שנדגמו עבור איברי השגיאה שלה מורכבים לפאולי אחד שמופעל על השכבה כולה.
animate_shaded_lightcone
במכשיר שהקישוריות שלו אינה קו חד-ממדי, דיאגרמת מעגל כבר לא מראה אילו קיוביטים שכנים, ולכן אי אפשר לקרוא ממנה את ההתפשטות המרחבית של חרוט האור.
animate_shaded_lightcone במקום זה מצייר את מפת הצימוד של ה-QPU ומאניין את החסמים עליה, קופסה רועשת אחת בכל פריים.
הפונקציה צריכה CouplingMap, שהיא מפרסת על גבי רשת על ידי הטמעה של גרף המכשיר, ולכן היא מטפלת גם בקישוריות heavy-hex וגם בקישוריות ריבועית.
הערה: בתיעוד המרונדר הזה האנימציה סטטית. המחוון וכפתור ההפעלה עובדים רק כשה-notebook רץ באופן אינטראקטיבי, למשל ב-Jupyter.
from qiskit import transpile
from qiskit.transpiler import CouplingMap
from qiskit_addon_slc.visualization import animate_shaded_lightcone
# A small square-lattice device stands in for a real backend here.
coupling_map = CouplingMap.from_grid(3, 4)
# Lay the chain onto a connected path of physical qubits, snaking through the grid, so the
# transpiler inserts no SWAPs: a SWAP-laden circuit is far more expensive to bound and harder to
# read.
initial_layout = [0, 1, 2, 3, 7, 6, 5, 4, 8, 9]
device_circuit = transpile(
circuit,
coupling_map=coupling_map,
initial_layout=initial_layout,
optimization_level=0,
)
boxed_device_circuit = boxing_pass.run(device_circuit)
device_noise_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_device_circuit),
coupling_map=coupling_map,
circuit=boxed_device_circuit,
)
# The same observable, on the physical qubit the middle of the chain was laid onto.
device_observable = Pauli("I" * device_circuit.num_qubits).compose(
"Z", [initial_layout[num_qubits // 2]]
)
device_bounds = compute_forward_bounds(
boxed_device_circuit, device_noise_paulis, device_observable
)
animate_shaded_lightcone(boxed_device_circuit, device_bounds, coupling_map)

קריאת האנימציה
כל קיוביט הוא תרשים עוגה, פרוסה אחת לכל סוג פאולי. הסמן של קיוביט מחולק לשלוש פרוסות עבור שגיאות , ו- שלו, וכל אחת מוצללת לפי החסם שלה. זה הסינון מהסעיף הקודם, מובנה בתוך האיור (אין צורך ב-pauli_filter כדי להבדיל בין הסוגים).
הצלעות נושאות את החסמים של השגיאות הדו-קיוביטיות. הקו בין שני קיוביטים מצומדים מוצלל לפי החסם על שגיאות ממשקל 2 שפועלות על הזוג הזה.
אפור בהיר אומר שאין נתונים, לא חסם קטן. פרוסה או צלע מצוירות באפור בהיר כשלמודל הרעש של הקופסה אין איבר שגיאה עבורן, למשל בקיוביטים שהקופסה לא פועלת עליהם. הן מצוירות באפור ולא בכהה כדי שאי אפשר יהיה לבלבל אותן עם חסם אפס. קופסאות שמעבר למגבלת max_num_boxes שונות: הן נושאות את החסם הטריוויאלי 2.0, ולכן הן מצוירות בקצה הבהיר של הסקאלה.
המחוון עובר על קופסאות, לא על הזמן באופן אחיד. יש פריים אחד לכל קופסה רועשת, לפי סדר המעגל (או בסדר הפוך עם reverse=True, שעוקב אחרי חרוט האור אחורה מה-observable). בדרך כלל הכיוון ההפוך אינטואיטיבי יותר: החרוט גדל החוצה מהקיוביטים הנמדדים. כל פריים מסומן במזהה של הקופסה שלו, אותו מפתח שבו מילון החסמים שומר את הקופסה, כך שאפשר גם להשתמש במחוון כדי לקפוץ לקופסה מסוימת ולראות את המספרים שלה.
הערה: פריסת הקיוביטים משוחזרת מהגרף של מפת הצימוד על ידי הטמעה שלו ברשת ריבועית. מפת צימוד שלא ניתן להטמיע (כי היא לא קשירה, או כי הקישוריות שלה צפופה מדי לרשת) מעלה ValueError במקום לצייר תמונה מטעה.
במה להשתמש
draw_shaded_lightcone | animate_shaded_lightcone | |
|---|---|---|
| גיאומטריה | דיאגרמת המעגל | מפת הצימוד של ה-QPU |
| מתאים במיוחד ל | שרשראות חד-ממדיות | כל קישוריות |
| סוגי פאולי | מסוכמים, אלא אם מסננים | תמיד מפוצלים, כפרוסות עוגה |
| זמן | כל השכבות בבת אחת | שכבה אחת בכל פריים |
| פלט | איור של matplotlib | איור של plotly |
עבור שרשרת חד-ממדית, האיור הסטטי קל יותר לקליטה במבט אחד וניתן לשחזור במאמר. מעבר לחד-ממד, האנימציה היא היחידה מבין השתיים שמראה נאמנה את המבנה המרחבי.