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

מימוש ב-Qiskit

בחלק זה נסתכל על כמה מימושים של Qiskit לרעיונות שהוצגו בשיעור זה. אם ברצונך להריץ את המימושים האלה בעצמך — דבר שמומלץ בחום — עיין בדף התקנת Qiskit בתוך IBM Quantum Documentation לפרטים על אופן הגדרת Qiskit.

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

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import __version__

print(__version__)
2.1.1

אם אתה מריץ את זה בסביבת Python מבוססת ענן, ייתכן שתצטרך להתקין חלק מהחבילות הבאות:

#!pip install qiskit
#!pip install jupyter
#!pip install sympy
#!pip install matplotlib
#!pip install pylatexenc

וקטורים ומטריצות ב-Python

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

ב-Python, ניתן לבצע חישובי מטריצות ווקטורים באמצעות המחלקה array מספריית NumPy, המספקת פונקציונליות לחישובים נומריים ומדעיים רבים. הקוד הבא טוען את הספרייה הזו, מגדיר שני וקטורי עמודה, ket0 ו-ket1, המתאימים לוקטורי מצב ה-Qubit 0\vert 0\rangle ו-1,\vert 1\rangle, ולאחר מכן מדפיס את הממוצע שלהם.

import numpy as np

ket0 = np.array([[1], [0]])
ket1 = np.array([[0], [1]])

print(ket0 / 2 + ket1 / 2)
[[0.5]
[0.5]]

אנחנו יכולים גם להשתמש ב-array כדי ליצור מטריצות שיכולות לייצג פעולות.

M1 = np.array([[1, 1], [0, 0]])
M2 = np.array([[1, 0], [0, 1]])
M = M1 / 2 + M2 / 2
print(M)
[[1.  0.5]
[0. 0.5]]

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

כפל מטריצות, כולל כפל מטריצה-וקטור כמקרה פרטי, ניתן לבצע באמצעות הפונקציה matmul מ-NumPy.

print(np.matmul(M1, ket1))
print(np.matmul(M1, M2))
print(np.matmul(M, M))
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

עיצוב הפלט הזה מניח מה לרצות מבחינה ויזואלית. פתרון אחד, במצבים שדורשים משהו יפה יותר, הוא להשתמש בפונקציה array_to_latex ב-Qiskit, ממודול qiskit.visualization. שים לב שבקוד הבא, אנחנו משתמשים בפונקציה הגנרית display של Python. לעומת זאת, ההתנהגות הספציפית של print עשויה להשתנות בהתאם למה שמדפיסים, כפי שקורה עם מערכים המוגדרים על ידי NumPy.

from qiskit.visualization import array_to_latex

display(array_to_latex(np.matmul(M1, ket1)))
display(array_to_latex(np.matmul(M1, M2)))
display(array_to_latex(np.matmul(M, M)))
[10] \begin{bmatrix} 1 \\ 0 \\ \end{bmatrix} [1100] \begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix} [134014] \begin{bmatrix} 1 & \frac{3}{4} \\ 0 & \frac{1}{4} \\ \end{bmatrix}

מצבים, מדידות ופעולות

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

הגדרה והצגה של וקטורי מצב

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

from qiskit.quantum_info import Statevector
from numpy import sqrt

u = Statevector([1 / sqrt(2), 1 / sqrt(2)])
v = Statevector([(1 + 2.0j) / 3, -2 / 3])
w = Statevector([1 / 3, 2 / 3])

מחלקת Statevector כוללת מתודת draw להצגת וקטורי מצב במגוון דרכים, כולל text לטקסט רגיל, latex ל-LaTeX מעובד, ו-latex_source לקוד LaTeX, שיכול להיות שימושי לגזור ולהדביק למסמכים. (השתמש ב-print ולא ב-display כדי להציג קוד LaTeX בצורה הטובה ביותר.)

display(u.draw("text"))
display(u.draw("latex"))
print(u.draw("latex_source"))
[0.70710678+0.j,0.70710678+0.j]

220+221\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

מחלקת Statevector כוללת גם את המתודה is_valid, הבודקת אם וקטור נתון הוא וקטור מצב קוונטי חוקי (כלומר, שהנורמה האוקלידית שלו שווה ל-1):

display(u.is_valid())
display(w.is_valid())
True
False

סימולציה של מדידות באמצעות Statevector

כעת נראה דרך אחת לסימולציה של מדידות של מצבים קוונטיים ב-Qiskit, תוך שימוש במתודת measure ממחלקת Statevector. נשתמש באותו וקטור מצב Qubit v שהוגדר קודם.

display(v.draw("latex"))

(13+2i3)0231(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

הרצת המתודה measure מסמלצת מדידת בסיס סטנדרטי. היא מחזירה את תוצאת המדידה, בתוספת וקטור המצב הקוונטי החדש של המערכת לאחר המדידה. (כאן אנחנו משתמשים בפונקציה print של Python עם קידומת f להדפסה מעוצבת עם ביטויים מוטבעים.)

outcome, state = v.measure()
print(f"Measured: {outcome}\nPost-measurement state:")
display(state.draw("latex"))
Measured: 1
Post-measurement state:

1- |1\rangle

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

(1+2i5)0\biggl(\frac{1 + 2i}{\sqrt{5}}\biggr) \vert 0\rangle

(ולא 0\vert 0\rangle) או

1- \vert 1\rangle

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

Statevector יזרוק שגיאה אם המתודה measure תוחל על וקטור מצב קוונטי לא חוקי.

ל-Statevector יש גם מתודת sample_counts המאפשרת סימולציה של כל מספר מדידות על המערכת, בכל פעם מתחילה עם עותק טרי של המצב. לדוגמה, הקוד הבא מציג את תוצאת מדידת הוקטור v 10001000 פעמים, שמניבה (בהסתברות גבוהה) את התוצאה 00 בערך 55 מתוך כל 99 פעמים (או כ-556556 מתוך 10001000 הניסיונות) ואת התוצאה 11 בערך 44 מתוך כל 99 פעמים (או כ-444444 מתוך 10001000 הניסיונות). הקוד הבא מדגים גם את הפונקציה plot_histogram ממודול qiskit.visualization לצורך ויזואליזציה של התוצאות.

from qiskit.visualization import plot_histogram

statistics = v.sample_counts(1000)
plot_histogram(statistics)

Output of the previous code cell

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

ביצוע פעולות עם Operator ו-Statevector

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

from qiskit.quantum_info import Operator

Y = Operator([[0, -1.0j], [1.0j, 0]])
H = Operator([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]])
S = Operator([[1, 0], [0, 1.0j]])
T = Operator([[1, 0], [0, (1 + 1.0j) / sqrt(2)]])

display(T.draw("latex"))
[10022+2i2] \begin{bmatrix} 1 & 0 \\ 0 & \frac{\sqrt{2}}{2} + \frac{\sqrt{2} i}{2} \\ \end{bmatrix}

ניתן להחיל פעולה אוניטרית על וקטור מצב באמצעות המתודה evolve.

v = Statevector([1, 0])

v = v.evolve(H)
v = v.evolve(T)
v = v.evolve(H)
v = v.evolve(S)
v = v.evolve(Y)

display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

תצוגה מקדימה של מעגלים קוונטיים

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

from qiskit import QuantumCircuit

circuit = QuantumCircuit(1)

circuit.h(0)
circuit.t(0)
circuit.h(0)
circuit.s(0)
circuit.y(0)

display(circuit.draw(output="mpl"))

Output of the previous code cell

כאן אנחנו משתמשים במתודת draw ממחלקת QuantumCircuit עם הרנדרר mpl (קיצור של Matplotlib, ספריית ויזואליזציה ב-Python). זהו הרנדרר היחיד שנשתמש בו עבור מעגלים קוונטיים בקורס זה, אך קיימות אפשרויות נוספות, כולל רנדרר מבוסס טקסט ורנדרר מבוסס LaTeX.

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

display(Operator.from_circuit(circuit).draw("latex"))
[0.14644660940.3535533906i0.8535533906+0.3535533906i0.3535533906+0.8535533906i0.3535533906+0.1464466094i] \begin{bmatrix} 0.1464466094 - 0.3535533906 i & 0.8535533906 + 0.3535533906 i \\ -0.3535533906 + 0.8535533906 i & 0.3535533906 + 0.1464466094 i \\ \end{bmatrix}

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

ket0 = Statevector([1, 0])
v = ket0.evolve(circuit)
display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

הקוד הבא מסמלץ ניסוי שבו המצב המתקבל מה-Circuit לעיל נמדד עם מדידת בסיס סטנדרטי 4000 פעמים (תוך שימוש בעותק טרי של המצב בכל פעם).

statistics = v.sample_counts(4000)
display(plot_histogram(statistics))

Output of the previous code cell