Перейти до основного вмісту

Ансатц

Подивись, як Вікторія Ліпінська пояснює, що таке ансатц і чому він важливий у контексті варіаційного власного квантового розв'язувача (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 — 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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘

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

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(N1)/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-вимірному простору станів (ще одна причина не включати зайві гейти). Тут і вступають у гру класичні алгоритми оптимізації — це тема наступного уроку.