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

ה-Ansatz

צפו בהסבר של ויקטוריה ליפינסקה על מה זה ansatz ומדוע הוא חשוב בהקשר של פותר ערכים עצמיים קוונטי וריאציוני (VQE).

מקורות

המאמרים הבאים מצוינים בסרטון לעיל.

קוד ה-Ansatz

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

  • את מגוון ה-ansaetze המוכנים מראש הזמינים בספריית המעגלים
  • כיצד לציין או לשנות את מאפייני ה-ansatz
  • כיצד לבנות ansatz משלכם
  • דוגמאות ל-ansaetze טובים ורעים

לספריית המעגלים של Qiskit יש קטגוריות רבות של מעגלים שניתן להשתמש בהם כ-ansatz. כאן, נגביל את הדיון למעגלים דו-מקומיים (מעגלים המורכבים מ-Gate-ים הפועלים על לכל היותר שני Qubit-ים בכל פעם). Efficient SU2 הוא ansatz נפוץ במיוחד.

מעגל efficient_su_2 מורכב משכבות של פעולות על Qubit בודד הנפרשות על ידי SU(2) (קבוצת יחידה מיוחדת מדרגה 2, כמו Gate-י סיבוב פאולי) וקשרי CX. זהו תבנית היוריסטית שיכולה להיות שימושית באלגוריתמים קוונטיים וריאציוניים כמו VQE ומעגלי סיווג בלמידת מכונה קוונטית (QML).

נתחיל עם דוגמה של מעגל efficient_su2 בן ארבעה Qubit-ים עם שני סוגי Gate-י SU(2), נניח rx ו-y. כמו כן נציין סכמת שזירה ומספר חזרות. אם פשוט תריצו .draw() על המעגלים, תקבלו ייצוג מופשט למדי. דיאגרמת מעגל מובנת יותר מתקבלת על ידי שימוש ב-.decompose().draw(), וכאן נשתמש ב-output = "mpl".

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit.circuit.library import efficient_su2

SU2_ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
print(SU2_ansatz.draw())
SU2_ansatz.decompose().draw(output="mpl")
┌──────────┐┌───┐     ┌──────────┐   ┌───┐
q_0: ┤ Rx(θ[0]) ├┤ Y ├──■──┤ Rx(θ[4]) ├───┤ Y ├─────────────────────
├──────────┤├───┤┌─┴─┐└──────────┘┌──┴───┴───┐ ┌───┐
q_1: ┤ Rx(θ[1]) ├┤ Y ├┤ X ├─────■──────┤ Rx(θ[5]) ├───┤ Y ├─────────
├──────────┤├───┤└───┘ ┌─┴─┐ └──────────┘┌──┴───┴───┐┌───┐
q_2: ┤ Rx(θ[2]) ├┤ Y ├────────┤ X ├─────────■──────┤ Rx(θ[6]) ├┤ Y ├
├──────────┤├───┤ └───┘ ┌─┴─┐ ├──────────┤├───┤
q_3: ┤ Rx(θ[3]) ├┤ Y ├────────────────────┤ X ├────┤ Rx(θ[7]) ├┤ Y ├
└──────────┘└───┘ └───┘ └──────────┘└───┘

Output of the previous code cell

ה-Gate-ים של SU(2) מופיעים בהתחלה ובסוף לפי הסדר והאלמנטים שצוינו ב-su2_gates = [...]. סכמת השזירה linear פירושה שה-Gate-ים CX עוברים דרך ה-Qubit-ים הממוספרים, משזרים את 0 ו-1, ואז 1 ו-2, וכן הלאה, לאורך קו אלכסוני במעגל. כפי שניתן לצפות, הגדרת reps = 2 פשוט מוסיפה שכבת שזירה ושכבת SU(2) סיום. הגדרת reps = n מקבילה ל-n שכבות שזירה, עם שכבות SU(2) ביניהן ובכל קצה.

SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")

Output of the previous code cell

ישנן עוד כמה סכמות שזירה. שתיים שכדאי להזכיר הן circular ו-full. שזירה מעגלית זהה לשזירה ליניארית, אך עם Gate CX נוסף המשזר את ה-Qubit הראשון והאחרון. סכמת השזירה המלאה כוללת Gate CX בין כל זוג Qubit-ים. שימו לב שעבור Circuit בן N Qubit-ים, מדובר ב-N(N1)/2N(N-1)/2 Gate-י CXCX, שיכול להפוך ליקר מבחינה חישובית.

SU2_ansatz3 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="circular", reps=1
)
SU2_ansatz3.decompose().draw(output="mpl")

Output of the previous code cell

SU2_ansatz4 = efficient_su2(4, su2_gates=["rx", "y", "z"], entanglement="full", reps=1)
SU2_ansatz4.decompose().draw(output="mpl")

Output of the previous code cell

ניתן לנטר את עומקי המעגל שלכם על ידי שימוש ב-.depth(), או לעיתים ב-.decompose().depth().

print(SU2_ansatz4.decompose().depth())
11

הכללה של ה-efficient_su2 היא המעגל הדו-מקומי, שהוא עצמו מקרה פרטי של מעגלי n-מקומי. המעגלים הדו-מקומיים מכילים גם בלוקי SU(2) (או בלוקי סיבוב) ובלוקי שזירה. כאן, אנחנו חופשיים לציין את סוג Gate-י השזירה שרוצים להשתמש בהם, למשל Gate-י CRX. בדוגמה זו, כל ה-Gate-ים מקבלים פרמטר, אך זה לא חייב להיות כך. אפשר להשתמש ב-Gate-י סיבוב Y וב-Gate-י שזירה CX, למשל.

from qiskit.circuit.library import n_local

rotation_blocks = ["ry"]
entanglement_blocks = ["crx"]
two_ansatz = n_local(
4, rotation_blocks, entanglement_blocks, "linear", insert_barriers=True, reps=2
)
two_ansatz.decompose().draw(output="mpl")

Output of the previous code cell

ה-ansatz האחרון שנדון בו בשמו הוא עיצוב פאולי-דו (Pauli-two-design). מעגל זה מכיל סיבוב ראשוני של RY(pi/4)RY(pi/4), ושכבות הסיבוב מכילות סיבובי פאולי של Qubit בודד, כאשר הציר נבחר באקראי אחיד מבין X, Y או Z. שכבות השזירה מורכבות מ-Gate-י CZ בצמדים עם עומק כולל של שניים. שימו לב להבדל בעומק השזירה (והמעגל הכולל) בין ה-pauli_two_design הזה לבין, למשל, ה-efficient_su2.

from qiskit.circuit.library import pauli_two_design

PtwoD_ansatz = pauli_two_design(5, reps=1, seed=10599, insert_barriers=True)
PtwoD_ansatz.decompose().draw(output="mpl")

Output of the previous code cell

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

כדי לבנות ansatz משלכם, פשוט בנו Circuit קוונטי כאשר חלק מה-Gate-ים הם פונקציות של אלמנטים של וקטור פרמטרים ("theta" בדוגמה בת שלושת ה-Qubit-ים למטה).

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector

n = 3

theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 1):
qc.cx(i, i + 1)
qc.cz(0, n - 1)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 1)
for i in reversed(range(n - 1)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz = qc
print(own_ansatz.depth())
qc.draw("mpl")
9

Output of the previous code cell

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

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

n = 4

theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 2):
qc.cx(i, i + 1)
qc.cz(0, n - 2)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 2)
for i in reversed(range(n - 2)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz2 = qc
print(own_ansatz2.depth())
qc.draw("mpl")
9

Output of the previous code cell

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

su2_ansatz_long = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=4
)
print(su2_ansatz_long.decompose().depth())
su2_ansatz_long.decompose().draw(output="mpl")
24

Output of the previous code cell

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