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

חיתוך מעגל להפחתת עומק

אומדן שימוש: שמונה דקות על מעבד Eagle (הערה: זהו אומדן בלבד. זמן הריצה שלך עשוי להשתנות.)

רקע​

מדריך זה מדגים כיצד לבנות Qiskit pattern לחיתוך שערים במעגל קוונטי כדי להפחית את עומק המעגל. לדיון מעמיק יותר בחיתוך מעגלים, בקרו בתיעוד ה-Qiskit addon לחיתוך מעגלים.

דרישות​

לפני תחילת מדריך זה, ודאו שהמוצרים הבאים מותקנים אצלכם:

  • Qiskit SDK גרסה 2.0 ומעלה, עם תמיכת ויזואליזציה
  • Qiskit Runtime גרסה 0.22 ומעלה (pip install qiskit-ibm-runtime)
  • התוסף Qiskit לחיתוך מעגלים גרסה 0.9.0 ומעלה (pip install qiskit-addon-cutting)

הכנה​

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_cutting": "qiskit-addon-cutting", "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")
import numpy as np

from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית​

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

  • קלט: פרמטרים קלאסיים להגדרת מעגל

  • פלט: מעגל מופשט ומשתני מדידה

circuit = EfficientSU2(num_qubits=4, entanglement="circular").decompose()
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
observables = PauliList(["ZZII", "IZZI", "IIZZ", "XIXI", "ZIZZ", "IXIX"])
circuit.draw("mpl", scale=0.8, style="iqp")

Output of the previous code cell

שלב 2: אופטימיזציה של הבעיה להרצה על חומרה קוונטית​

  • קלט: מעגל מופשט ומשתני מדידה

  • פלט: מעגל יעד ומשתני מדידה שהופקו על ידי חיתוך שערים מרוחקים להפחתת עומק המעגל המתורגם

אנחנו בוחרים פריסה התחלתית הדורשת שתי החלפות כדי לבצע את השערים בין qubit 3 ו-0 ושתי החלפות נוספות כדי להחזיר את ה-qubits למיקומם ההתחלתי. אנחנו בוחרים optimization_level=3, שהיא רמת האופטימיזציה הגבוהה ביותר הזמינה עם מנהל מעברים קבוע מראש.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=circuit.num_qubits, simulator=False
)

pm = generate_preset_pass_manager(
optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend
)
transpiled_qc = pm.run(circuit)

Coupling map showing the qubits that will need to be swapped

print(f"Transpiled circuit depth: {transpiled_qc.depth()}")
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, style="iqp", fold=-1)
Transpiled circuit depth: 103

Output of the previous code cell

מציאה וחיתוך של השערים המרוחקים: נחליף את השערים המרוחקים (שערים המחברים qubits לא מקומיים, 0 ו-3) באובייקטים מסוג TwoQubitQPDGate על ידי ציון האינדקסים שלהם. cut_gates יחליף את השערים באינדקסים שצוינו באובייקטים מסוג TwoQubitQPDGate ויחזיר גם רשימה של מופעי QPDBasis - אחד לכל פירוק שער. האובייקט QPDBasis מכיל מידע על איך לפרק את השערים החתוכים לפעולות של qubit בודד.

# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(circuit.data)
if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]

# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)

qpd_circuit.draw("mpl", scale=0.8)

Output of the previous code cell

יצירת ניסויי המשנה להרצה על ה-backend: generate_cutting_experiments מקבל מעגל המכיל מופעי TwoQubitQPDGate ומשתני מדידה כ-PauliList.

כדי לדמות את ערך התוחלת של המעגל בגודל המלא, ניסויי משנה רבים מיוצרים מההתפלגות הקוואזי-הסתברותית המשולבת של השערים המפורקים ולאחר מכן מבוצעים על backend אחד או יותר. מספר הדגימות שנלקחות מההתפלגות נשלט על ידי num_samples, ומקדם משולב אחד ניתן לכל דגימה ייחודית. למידע נוסף על אופן חישוב המקדמים, ראו בחומר ההסבר.

# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observables, num_samples=np.inf
)

לצורך השוואה, אנחנו רואים שניסויי המשנה QPD יהיו רדודים יותר לאחר חיתוך השערים המרוחקים: זו דוגמה לניסוי משנה שנבחר באופן שרירותי שנוצר מהמעגל QPD. העומק שלו הופחת ביותר מחצי. יש ליצור ולהעריך ניסויי משנה הסתברותיים רבים כאלה על מנת לשחזר ערך תוחלת של המעגל העמוק יותר.

# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pm.run(subexperiments[100])

print(f"Original circuit depth after transpile: {transpiled_qc.depth()}")
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}"
)
transpiled_qpd_circuit.draw(
"mpl", scale=0.6, style="iqp", idle_wires=False, fold=-1
)
Original circuit depth after transpile: 103
QPD subexperiment depth after transpile: 46

Output of the previous code cell

מצד שני, החיתוך גורם לצורך בדגימה נוספת. כאן חתכנו שלושה שערי CNOT, מה שמביא לעומס דגימה של 939^3. למידע נוסף על עומס הדגימה שנגרם על ידי חיתוך מעגלים, ראו בתיעוד ערכת הכלים Circuit Knitting.

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0

שלב 3: הרצה באמצעות פרימיטיבים של Qiskit​

הרצת מעגלי היעד ("ניסויי המשנה") עם הפרימיטיב Sampler.

  • קלט: מעגלי יעד

  • פלט: התפלגויות קוואזי-הסתברות

# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)

# Set up the IBM Quantum Sampler primitive. For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)

# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()
print(job.job_id())
czypg1r6rr3g008mgp6g

שלב 4: עיבוד לאחר ההרצה והחזרת התוצאה בפורמט קלאסי הרצוי​

שימוש בתוצאות ניסויי המשנה, משתני מדידת המשנה ומקדמי הדגימה לשחזור ערך התוחלת של המעגל המקורי.

קלט: התפלגויות קוואזי-הסתברות פלט: ערכי תוחלת משוחזרים

reconstructed_expvals = reconstruct_expectation_values(
results,
coefficients,
observables,
)
# Reconstruct final expectation value
final_expval = np.dot(reconstructed_expvals, [1] * len(observables))
print("Final reconstructed expectation value")
print(final_expval)
Final reconstructed expectation value
1.0751342773437473
ideal_expvals = [
Statevector(circuit).expectation_value(SparsePauliOp(observable))
for observable in observables
]
print("Ideal expectation value")
print(np.dot(ideal_expvals, [1] * len(observables)).real)
Ideal expectation value
1.2283177520039992

סקר מדריך​

מלאו את הסקר הקצר הזה כדי לספק משוב על המדריך. התובנות שלכם יעזרו לנו לשפר את הצעות התוכן וחוויית המשתמש שלנו.

הערה: סקר זה נערך על ידי IBM Quantum ומכסה את תוכן המדריך (שנכתב על ידי IBM). doQumentation מספקת את האתר, התרגומים, והרצת הקוד — למשוב על אלה, אנא פתחו issue ב-GitHub.

קישור לסקר