Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Ансатц

Подивись, як Вікторія Ліпінська пояснює, що таке ансатц і чому він важливий у контексті варіаційного власного квантового розв'язувача (VQE).

Посилання​

У відео вище згадуються такі статті:

Код ансатцу​

У попередньому уроці ти створив гамільтоніан, що описує енергію молекули, яка тебе цікавить, і відобразив його у формат, зручний для квантового комп'ютера. VQE використовує варіаційну схему для підготовки квантових станів. Потім ці стани використовуються для визначення очікуваного значення гамільтоніана (енергії). Параметри варіаційної схеми варіюються до тих пір, поки обчислення не збіжиться до мінімального очікуваного значення. У контексті квантової хімії це має бути енергія основного стану. Цей урок присвячений варіаційній схемі, яку також називають ансатцом (від німецького слова, що означає «підхід» або «метод»). У цьому уроці ти навчишся:

  • які готові ансатці є в бібліотеці схем
  • Як задавати або змінювати властивості ансатцу
  • Як будувати власний ансатц
  • приклади гарних і поганих ансатців

Бібліотека схем Qiskit має багато категорій схем, що можна використовувати як ансатц. Тут ми обмежимося дволокальними схемами (схемами, що складаються з гейтів, які діють максимум на два кубіти). Efficient SU2 — широко використовуваний ансатц.

Схема efficient_su_2 складається з шарів однокубітних операцій, що охоплюють SU(2) (спеціальна унітарна група другого ступеня, як обертальні гейти Паулі), та заплутувальних CX-гейтів. Це евристичний шаблон, корисний у варіаційних квантових алгоритмах, як-от VQE, і в схемах класифікації у квантовому машинному навчанні (QML).

Ми почнемо з чотирикубітної прикладної схеми efficient_su2 з двома типами SU(2)-гейтів, скажімо rx та y. Ми також вкажемо схему заплутування та кількість повторень. Якщо ти просто виконаєш .draw() для схем, ти отримаєш досить абстрактне представлення. Більш зрозуміла діаграма схеми отримується за допомогою .decompose().draw(), і тут ми будемо використовувати `output = "mpl"'.

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

_needed = {"qiskit": "qiskit"}
_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")
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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘

Виведення попереднього блоку коду

SU(2)-вентилі з'являються на початку і в кінці з порядком та елементами, як зазначено у su2_gates = [...]. Схема заплутування linear означає, що CX-вентилі проходять через пронумеровані кубіти, заплутуючи 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")

Виведення попереднього блоку коду

Існують і інші схеми заплутування. Варто відзначити circular і full. Кільцеве заплутування ідентичне лінійному, але з додатковим CX-гейтом між першим і останнім кубітами. Повне заплутування включає CX-гейт між кожною парою кубітів. Зауваж: для N-кубітної схеми це N(N−1)/2N(N-1)/2 гейтів CX, що може бути обчислювально дорогим.

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

Виведення попереднього блоку коду

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

Виведення попереднього блоку коду

Глибину схеми можна перевірити за допомогою .depth() або .decompose().depth().

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

Узагальненням efficient_su2 є двокальна схема, що сама є окремим випадком n-кальних схем. Двокальні схеми також містять блоки SU(2) (блоки обертання) і блоки заплутування. Тут можна вільно задавати тип гейтів заплутування, наприклад CRX-гейти. У цьому прикладі всі гейти приймають параметр, але так буває не завжди. Можна, наприклад, використати гейти обертання Y і гейти заплутування 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")

Виведення попереднього блоку коду

Останній ансатц, про який ми згадаємо за назвою, — Pauli-two-design. Ця схема містить початкове обертання RY(pi/4)RY(pi/4), а шари обертання складаються з однокубітних обертань Паулі, де вісь рівномірно випадково обирається між X, Y або Z. Шари заплутування складаються з попарних 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")

Виведення попереднього блоку коду

Ці готові варіаційні схеми є корисними евристиками як для досягнення бажаного рівня заплутування, так і для обмеження глибини схеми. Але нічого «чарівного» в них немає. Ти можеш будувати власні варіаційні схеми — і це може бути вигідним, якщо знаєш щось про заплутування цільового стану системи.

Щоб побудувати власний ансатц, просто конструюєш квантову схему, де деякі гейти є функціями елементів вектора параметрів («theta» у трикубітному прикладі нижче).

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

Виведення попереднього блоку коду

Загалом, вибір найкращого ансатцу — це мистецтво; найкращий ансатц — той, що допомагає досягти цілі за найменшу кількість кроків оптимізації. Легше ідентифікувати ансатці, які, ймовірно, будуть поганими. Наприклад, більша глибина схеми зазвичай призводить до накопичення помилок. Пом'якшення помилок може допомогти, але гарна практика — тримати глибину схеми якомога меншою в розумних межах. Але не відкидай заплутування, що є необхідним. Може знадобитися цільовий стан, що вимагає повного заплутування. Нижче наведено два приклади, які з досить очевидних причин, імовірно, будуть поганим вибором. Вибір гарного ансатцу буде розглянуто детальніше в наступних розділах у контексті тестів збіжності.

Перша схема — ймовірно, поганий вибір, тому що останній кубіт взагалі не заплутаний з іншими. Немає обчислювально значущої дії на останній кубіт. Найімовірніше, його слід або заплутати з іншими, або виключити з обчислення.

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

Виведення попереднього блоку коду

Остання схема — ймовірно, поганий вибір через дуже велику глибину гейтів, а чотириразове повторення шару заплутування навряд чи дасть суттєво кращу відповідність цільовому стану порівняно з двома або трьома повтореннями.

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

Виведення попереднього блоку коду

Ці схеми «незавершені» в тому сенсі, що в багато гейтів ще потрібно вставити невідомі змінні параметри. Ці параметри підбираються послідовними здогадками, оновлюючись для зменшення очікуваного значення функції витрат (у хімічному контексті — енергії основного стану). В одному чи кількох вимірах це тривіально. Але схема вище має 20 варіаційних параметрів — пошук цільового стану з мінімальною енергією означає навігацію по 20-вимірному простору станів (ще одна причина не включати зайві гейти). Тут і вступають у гру класичні алгоритми оптимізації — це тема наступного уроку.