דלג לתוכן הראשי

הניסוי הקוונטי הראשון שלך

מבוא

בסרטון הבא, אוליביה ליינס מדריכה אותך דרך התוכן של השיעור הזה. לחלופין, אפשר לפתוח את סרטון YouTube של השיעור בחלון נפרד.

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

נחקור את הנוף הרחב יותר של בעיות מתאימות לקוונטום בשיעור מאוחר יותר. לעת עתה, נתמקד בבעיה בתחום סימולציית הטבע: שימוש במחשב קוונטי כתחליף נקי יותר וניתן לשליטה טובה יותר למערכת קוונטית טבעית. למעשה, זו הייתה היישום הראשון שריצ'רד פיינמן דמיין למחשבים קוונטיים בשנות השמונים. כפי שאמר בצורה ידועה: "הטבע אינו קלאסי, לעזאזל, ואם אתה רוצה לעשות סימולציה של הטבע, עדיף שתעשה אותה קוונטית מכנית..."

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

שני מגנטים מתקשרים

לבעיה זו, נשתמש בשני Qubit-ים, אחד לכל ספין במודל שלנו. כל ספין יכול להצביע למעלה (מצב Qubit 0|0\rangle), למטה (מצב Qubit 1|1\rangle), או בסופרפוזיציה של שני המצבים.

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

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

ניתן לתאר את כל האפקטים האלה באמצעות אובייקט מתמטי הנקרא המילטוניאן. ה-Hamiltonian אומר לנו את האנרגיה של המערכת עבור סידור נתון של ספינים:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

כאשר JJ הוא מקדם השולט בעוצמת האינטראקציה בין הספינים ו-hxh_x הוא מקדם לעוצמת השדה המגנטי החיצוני. Z1Z0Z_1 Z_0 מתגמל או מעניש את הספינים בהתאם לשאלה האם הם מיושרים או מנוגדים, ו-X0X_0 ו-X1X_1 מייצגים את אפקט הפיית הספין של השדה המגנטי.

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

במקום זאת, נשאל שאלה פשוטה יותר: אם נכין את הספינים במצב מסוים, מהי האנרגיה של אותו מצב?

כדי לענות על כך, נעשה את הבאות:

  1. נכין את הספינים במצב שבחרנו
  2. נמדוד את האנרגיה של אותו מצב באמצעות ה-Hamiltonian לעיל

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

מימוש ב-Qiskit

לפני שנצלול לכתיבת קוד, אנחנו צריכים קצת הקשר. כשמריצים Circuit קוונטי, תמיד מסיימים במדידת ה-Qubit-ים. אבל יש שני סוגים שונים של שאלות שאולי נרצה לשאול על תוצאת המדידה: לפעמים, אנחנו פשוט רוצים לדעת מה מצב ה-Qubit. בפעמים אחרות, אנחנו רוצים לדעת, בהינתן המצב הקוונטי, מהו ערכה של כמות פיזית, כמו אנרגיה?

ב-Qiskit, שני סוגי השאלות האלה מטופלים על ידי שני כלים שונים, הנקראים primitives.

Sampler עונה על סוג השאלה הראשון. הוא מריץ את ה-Circuit פעמים רבות ואומר לנו כמה פעמים אנחנו מודדים כל תוצאה אפשרית, כמו 00, 01, 10, או 11. התוצאה היא היסטוגרמה המציגה את ההסתברות של כל תוצאת מדידה.

Estimator עונה על סוג השאלה השני. במקום לתת לנו היסטוגרמה, הוא משלב הרבה מדידות מאחורי הקלעים כדי לחשב מספר אחד, כמו האנרגיה של המצב לפי Hamiltonian שאנחנו מספקים.

כדי לעזור לך להבין מתי ולמה נשתמש בכל אחד מהכלים האלה, נעבור דרך שני תהליכי עבודה שלמים (הנקראים "Qiskit patterns") המוחלים על אותה מערכת שני Qubit-ים.

תהליך עבודה של Qiskit patterns

תהליך עבודה של Qiskit patterns הוא מסגרת כללית שבה אנחנו משתמשים לפתרון בעיות קוונטיות עם Qiskit. הוא מחלק משימת חישוב קוונטי לארבעה שלבים:

  1. מיפוי הבעיה למודל שניתן לייצגו על ידי Circuits קוונטיים
  2. אופטימיזציה של ה-Circuit להרצה על Backend ספציפי
  3. הרצת ה-Circuit המאופטם על ה-Backend שנבחר
  4. עיבוד לאחר של נתוני המדידה הגולמיים

ניסוי 1: שימוש ב-Sampler למדידת המצב

מיפוי

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

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

כאן, נכין מצב Bell שזור, דומה לזה שיצרנו בשיעור הראשון של הקורס:

Ψ=12(1001)\vert\Psi\rangle = \frac{1}{\sqrt{2}}(\vert 10\rangle - \vert 01\rangle)
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Import Qiskit primitives
from qiskit import QuantumCircuit

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)

# Measure state
qc.measure_all()

# Draw circuit
qc.draw("mpl")

Output of the previous code cell

אופטימיזציה

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

במהלך האופטימיזציה:

  1. בוחרים את ה-Backend — מחשב קוונטי אמיתי או סימולטור.
  2. מקצים את ה-Qubit-ים של ה-Circuit שלנו ל-Qubit-ים פיזיים על ההתקן.
  3. מכתיבים מחדש את ה-Circuit רק באמצעות Gate-ים שהמחשב הקוונטי יכול לבצע בפועל.
  4. אפשרות לממש טכניקות הפחתת ודיכוי שגיאות כדי להפחית את ההשפעות של רעש.

ב-Qiskit, זה מטופל אוטומטית על ידי ה-Transpiler. ברגע שבוחרים את ה-Backend, ה-Transpiler עושה את כל העבודה להכנת ה-Circuit להרצה, כך שלא צריך לכוונן ידנית את ה-Gate-ים או הקצאות ה-Qubit. ה-Transpiler מציע גם רמות אופטימיזציה שונות, שיכולות לעזור בהפחתת שגיאות במידת הצורך. האופטימיזציה נעשית בשלבים הנקראים 'passes'. אז האופטימיזציה הזו תטופל על ידי ה-pass_manager בקוד למטה. למידע נוסף על שגיאות והפחתת שגיאות, ראה את הקורס Quantum Computing in Practice של אוליביה ליינס.

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

## Load the Qiskit Runtime service
# QiskitRuntimeService.save_account(
# channel="ibm_quantum_platform",
# token="YOUR_TOKEN_HERE",
# overwrite=True,
# set_as_default=True,
# )
# service = QiskitRuntimeService(channel="ibm_quantum_platform")

# Or load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez
# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output of the previous code cell

הרצה

עכשיו אנחנו מוכנים להריץ! נטען את Sampler, ואז נשלח את ה-Job ל-Backend.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

או, אם אתה משתמש בסימולטור, אפשר לבטל הערה ולהריץ את התא הזה במקום:

## Load the backend sampler
# from qiskit.primitives import BackendSamplerV2

## Load the Aer simulator and generate a noise model based on the currently-selected backend.
# from qiskit_aer import AerSimulator
# from qiskit_aer.noise import NoiseModel

# noise_model = NoiseModel.from_backend(backend)

## Define a simulator using Aer, and use it in Sampler.
# backend_sim = AerSimulator(noise_model=noise_model)
# sampler_sim = BackendSamplerV2(backend=backend_sim)

## Alternatively, load a fake backend with generic properties and define a simulator.
## backend_gen = GenericBackendV2(num_qubits=18)
## sampler_gen = BackendSamplerV2(backend=backend_gen)
job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

עיבוד לאחר

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'10': 49, '01': 50, '11': 1}

Output of the previous code cell

אנחנו רואים שרוב הספירות נמצאות ב-01 או 10, כלומר כשה-Qubit האחד נמדד כ-0, השני היה 1, ולהיפך. זה עקבי עם מצב Bell Ψ\vert \Psi^- \rangle שהכנו.

ניסוי 2: שימוש ב-Estimator למדידת האנרגיה

עכשיו שראינו כיצד לדגום מצב קוונטי, בואו נשתמש ב-Estimator כדי לחשב את האנרגיה של מצב Bell Ψ=12(0110)\vert \Psi^- \rangle = \frac{1}{\sqrt{2}}(\vert 01 \rangle - \vert 10 \rangle).

מיפוי

כתזכורת, האנרגיה של המערכת נקבעת על ידי האינטראקציה בין הספינים (JJ) והשדה המגנטי החיצוני (hxh_x) כפי שמלוכד ב-Hamiltonian:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

כל איבר ב-Hamiltonian אומר לנו כיצד שילוב מסוים של ספינים תורם לאנרגיה. ב-Qiskit, ניתן לייצג את האיברים האלה כאופרטורי Pauli, שהם פשוט תוויות לפעולות פשוטות על Qubit-ים:

  • Z1Z0Z_1 Z_0 פועל עם ZZ על שני ה-Qubit-ים.
  • X0X_0 פועל עם XX על Qubit 0.
  • X1X_1 פועל עם XX על Qubit 1.

SparsePauliOp ב-Qiskit הוא דרך לאחסן רשימה של אופרטורי Pauli אלה יחד עם המקדמים המספריים שלהם. אופרטורי Pauli האלה הם ה-observables שאנחנו רוצים שהמחשב הקוונטי ימדוד — הכמויות שאומרות לנו על המערכת. באמצעות Estimator, ניתן לחשב את הערך הממוצע של כל observable על המצב שלנו ולשלב אותם לפי המקדמים ב-Hamiltonian כדי לקבל את האנרגיה הכוללת.

# Import Qiskit primitives
from qiskit.quantum_info import SparsePauliOp

# Parameters
J = 1.0 # antiferromagnetic coupling (J<0)
hx = -0.5 # transverse field strength

# 1. Define the Hamiltonian H = J Z1 Z2 + hx (X1 + X2)
obs = SparsePauliOp.from_list([("ZZ", J), ("XI", hx), ("IX", hx)])

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)
<qiskit.circuit.instructionset.InstructionSet at 0x1387ed630>

שים לב שהשמטנו את שורת qc.measure_all() בקוד שלנו. הסיבה לכך היא שעם Estimator, לא צריך לציין היכן למדוד ב-Circuit. פשוט נאמר לו אילו observables אנחנו רוצים להעריך, ו-Qiskit מטפל במדידות מאחורי הקלעים.

אופטימיזציה

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

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)

qc_isa.draw("mpl")

Output of the previous code cell

הרצה

בשלב ההרצה, נטען את Estimator, ואז נשלח את ה-Circuit בצירוף רשימת ה-observables שאנחנו רוצים שיעריך למחשב הקוונטי.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
# Load the backend sampler

# noise_model = NoiseModel.from_backend(backend)

# Use Aer simulator in Estimator
# estimator_sim = BackendEstimatorV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# estimator_gen = BackendEstimatorV2(backend=backend_gen)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()

# Uncomment lines below to run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()

עיבוד לאחר

לבסוף, בשלב העיבוד לאחר, פשוט מדפיסים את האנרגיה שחושבה מאחורי הקלעים על ידי Estimator.

print(res[0].data.evs)
-0.9934112021453058

זוהי האנרגיה של המצב שלנו!

סיכום

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

הרחבה למספר רב של ספינים

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

הרחבה טבעית של הניסוי הזה תהיה להגדיל את מספר ה-Qubit-ים לייצוג של יותר ספינים ולהתאים את אופן הכנת הספינים כדי לנסות למצוא את מצב האנרגיה הנמוכה ביותר. גישה זו היא עיקר השיטות הווריאציוניות, שניתן ללמוד עליהן בקורס Variational Quantum Algorithms.

ישנן גם גישות קוונטיות אחרות לחקר אנרגיות מצב הבסיס שחורגות מטכניקות ווריאציוניות. שיטות אלה אינן מכוסות כאן, אבל מוצגות בקורס Quantum Diagonalization Algorithms אם אתה מעוניין ללמוד עוד.

מטרת הלמידה

חזור לתחילת ניסוי 2 ונסה שוב עם מצב סופרפוזיציה שונה. האם תוכל למצוא מצב עם אנרגיה נמוכה עוד יותר מזו שהשתמשנו בה?

This translation based on the English version of 7 במאי 2026