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

Квантові алгоритми: Оцінка фази

примітка

Kento Ueda (15 травня 2024)

Цей ноутбук містить основні концепції та реалізацію Квантового перетворення Фур'є (QFT) і Квантової оцінки фази (QPE).

Завантажити PDF оригінальної лекції. Зверни увагу, що деякі фрагменти коду можуть застаріти, оскільки це статичні зображення.

Приблизний час QPU для виконання цього експерименту становить 7 секунд.

1. Вступ

Квантове перетворення Фур'є (QFT)

Квантове перетворення Фур'є є квантовим аналогом класичного дискретного перетворення Фур'є. Це лінійне перетворення, застосоване до квантових станів, яке відображає обчислювальні базиси в їхні представлення у базисі Фур'є. QFT відіграє ключову роль у багатьох квантових алгоритмах, пропонуючи ефективний метод отримання інформації про periodичність із квантових станів. QFT можна реалізувати за допомогою O(N2)O(N^2) операцій з квантовими Gate, такими як Gate Адамара та Gate керованої фази для NN кубітів, що забезпечує експоненційне прискорення порівняно з класичним перетворенням Фур'є.

  • Застосування: Він є фундаментальною складовою квантових алгоритмів, таких як алгоритм Шора для факторизації великих цілих чисел і дискретного логарифму.

Квантова оцінка фази (QPE)

Квантова оцінка фази — це квантовий алгоритм, що використовується для оцінки фази, пов'язаної з власним вектором унітарного оператора. Цей алгоритм є містком між абстрактними математичними властивостями квантових станів та їхніми обчислювальними застосуваннями.

  • Застосування: Він може розв'язувати задачі на кшталт пошуку власних значень унітарних матриць та симуляції квантових систем.

Разом QFT і QPE формують основу багатьох квантових алгоритмів, що вирішують задачі, непосильні для класичних комп'ютерів. Після завершення цього ноутбука ти отримаєш розуміння того, як реалізуються ці техніки.

2. Основи Квантового перетворення Фур'є (QFT)

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from qiskit.quantum_info import Statevector
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler

from numpy import pi

За аналогією з дискретним перетворенням Фур'є, QFT діє на квантовий стан X=j=0N1xjj\vert X\rangle = \sum_{j=0}^{N-1} x_j \vert j \rangle для NN кубітів і відображає його в квантовий стан Y=k=0N1ykk\vert Y\rangle = \sum_{k=0}^{N-1} y_k \vert k \rangle.

QFTN:j1Nk=0N1ωNjkkQFT_{N}: \vert j \rangle \mapsto \frac{1}{\sqrt{N}}\sum_{k=0}^{N-1}\omega_N^{jk} \vert k \rangle

де ωNjk=e2πijkN\omega_N^{jk} = e^{2\pi i \frac{jk}{N}}.

Або у вигляді унітарної матриці:

UQFT=1Nj=0N1k=0N1ωNjkkjU_{QFT} = \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} \sum_{k=0}^{N-1} \omega_N^{jk} \vert k \rangle \langle j \vert

2.1. Інтуїція

Квантове перетворення Фур'є (QFT) здійснює перехід між двома базисами: обчислювальним (Z) базисом і базисом Фур'є. Але що означає базис Фур'є в цьому контексті? Ти, мабуть, пам'ятаєш, що перетворення Фур'є F(ω)F(\omega) функції f(x)f(x) описує згортку f(x)f(x) із синусоїдальною функцією частоти ω\omega. Простіше кажучи: перетворення Фур'є — це функція, яка описує, скільки кожної частоти ω\omega потрібно, щоб побудувати функцію f(x)f(x) із синусоїд (або косинусоїд). Щоб краще зрозуміти, що означає QFT у цьому контексті, розглянь покрокові зображення нижче, які показують число, закодоване у двійковому вигляді з використанням чотирьох кубітів:

В обчислювальному базисі ми зберігаємо числа у двійковому вигляді, використовуючи стани 0|0\rangle та 1|1\rangle.

Зверни увагу на частоту, з якою змінюються різні кубіти: крайній лівий кубіт перевертається при кожному збільшенні числа, наступний — кожні 2 кроки, третій — кожні 4 кроки, і так далі.

Якщо застосувати квантове перетворення Фур'є до цих станів, ми отримуємо відображення:

Стан в обчислювальному базисіQFTСтан у базисі Фур’є|\text{Стан в обчислювальному базисі}\rangle \quad \xrightarrow[]{\text{QFT}} \quad |\text{Стан у базисі Фур'є}\rangle QFTx=x~\text{QFT}|x\rangle = |\widetilde{x}\rangle

(Стани у базисі Фур'є ми часто позначаємо тильдою (~)).

У базисі Фур'є ми зберігаємо числа за допомогою різних обертань навколо осі Z:

IFRAME

Число, яке ми хочемо зберегти, визначає кут повороту кожного кубіта навколо осі Z. У стані 0~|\widetilde{0}\rangle всі кубіти перебувають у стані +|{+}\rangle. Як видно з прикладу вище, щоб закодувати стан 5~|\widetilde{5}\rangle на 4 кубітах, ми повернули крайній лівий кубіт на 52n=516\tfrac{5}{2^n} = \tfrac{5}{16} повних оборотів (516×2π\tfrac{5}{16}\times 2\pi радіан). Наступний кубіт повертається вдвічі більше (1016×2π\tfrac{10}{16}\times 2\pi радіан, або 10/1610/16 повних оборотів), цей кут подвоюється для наступного кубіта, і так далі.

Знову зверни увагу на частоту зміни кожного кубіта. Крайній лівий кубіт (qubit 0) у цьому випадку має найнижчу частоту, а крайній правий — найвищу.

2.2. Приклад: QFT для 1 кубіта

Розглянемо випадок N=21=2N = 2^1 = 2.

Унітарну матрицю можна записати:

UQFT=12j=01k=01ω2jkkj=12(ω2000+ω2001+ω2010+ω2111)=12(00+01+1011)=H\begin{aligned} U_{QFT} & = \frac{1}{\sqrt{2}} \sum_{j=0}^{1} \sum_{k=0}^{1} \omega_2^{jk} \vert k \rangle \langle j \vert \\ & = \frac{1}{\sqrt{2}} (\omega_2^{0} \vert 0 \rangle \langle 0 \vert + \omega_2^{0} \vert 0 \rangle \langle 1 \vert + \omega_2^{0} \vert 1 \rangle \langle 0 \vert + \omega_2^{1} \vert 1 \rangle \langle 1 \vert) \\ & = \frac{1}{\sqrt{2}} (\vert 0 \rangle \langle 0 \vert + \vert 0 \rangle \langle 1 \vert + \vert 1 \rangle \langle 0 \vert - \vert 1 \rangle \langle 1 \vert) \\ & = H \end{aligned}

Ця операція є результатом застосування Gate Адамара (HH).

2.3. Добутне представлення QFT

Узагальнимо перетворення для N=2nN = 2^n, QFTNQFT_{N}, що діє на стан x=x1xn\vert x \rangle = \vert x_1\ldots x_n \rangle.

QFTNx=1Ny=0N1ωNxyy=1Ny=0N1e2πixy/2ny оскількиωNxy=e2πixyNіN=2n=1Ny=0N1e2πi(k=1nyk/2k)xy1ynзаписуємо в дробовому двійковому записіy=y1yn,y/2n=k=1nyk/2k=1Ny=0N1k=1ne2πixyk/2ky1ynпісля розкладання експоненти суми в добуток експонент,=1Nk=1n(0+e2πix/2k1)після перестановки суми та добутків та розкладанняy=0N1=y1=01y2=01yn=01=1N(0+e2πi2x1)(0+e2πi22x1)(0+e2πi2n1x1)(0+e2πi2nx1)\begin{aligned} QFT_N\vert x \rangle & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1}\omega_N^{xy} \vert y \rangle \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} e^{2 \pi i xy / 2^n} \vert y \rangle ~\text{оскільки}\: \omega_N^{xy} = e^{2\pi i \frac{xy}{N}} \:\text{і}\: N = 2^n \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} e^{2 \pi i \left(\sum_{k=1}^n y_k/2^k\right) x} \vert y_1 \ldots y_n \rangle \:\text{записуємо в дробовому двійковому записі}\: y = y_1\ldots y_n, y/2^n = \sum_{k=1}^n y_k/2^k \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} \prod_{k=1}^n e^{2 \pi i x y_k/2^k } \vert y_1 \ldots y_n \rangle \:\text{після розкладання експоненти суми в добуток експонент,} \\ & = \frac{1}{\sqrt{N}} \bigotimes_{k=1}^n \left(\vert0\rangle + e^{2 \pi i x /2^k } \vert1\rangle \right) \:\text{після перестановки суми та добутків та розкладання} \sum_{y=0}^{N-1} = \sum_{y_1=0}^{1}\sum_{y_2=0}^{1}\ldots\sum_{y_n=0}^{1} \\ & = \frac{1}{\sqrt{N}} \left(\vert0\rangle + e^{\frac{2\pi i}{2}x} \vert1\rangle\right) \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^2}x} \vert1\rangle\right) \otimes \ldots \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^{n-1}}x} \vert1\rangle\right) \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^n}x} \vert1\rangle\right) \end{aligned}

2.4. Приклад: Побудова Circuit для QFT з 3 кубітів

З наведеного вище опису може бути незрозуміло, як побудувати Circuit для QFT. Поки що просто зверни увагу на те, що ми очікуємо, що три кубіти матимуть фази, які еволюціонують із різною «швидкістю». Розуміння того, як саме це перетворюється на Circuit, залишається вправою для читача. У PDF лекції є кілька діаграм і прикладів. Додаткові ресурси включають цей урок із курсу «Основи квантових алгоритмів».

Ми продемонструємо QFT лише з використанням симуляторів, тому не будемо використовувати фреймворк шаблонів Qiskit, поки не перейдемо до квантової оцінки фази.

# Prepare for 3 qubits circuit
qr = QuantumRegister(3)
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr, cr)
qc.h(2)
qc.cp(pi / 2, 1, 2) # Controlled-phase gate from qubit 1 to qubit 2
qc.cp(pi / 4, 0, 2) # Controlled-phase gate from qubit 0 to qubit 2
qc.draw(output="mpl")

Output of the previous code cell

qc.h(1)
qc.cp(pi / 2, 0, 1) # Controlled-phase gate from qubit 0 to qubit 1

qc.draw(output="mpl")

Output of the previous code cell

qc.h(0)

qc.draw(output="mpl")

Output of the previous code cell

qc.swap(0, 2)

qc.draw(output="mpl")

Output of the previous code cell

Спробуємо застосувати QFT до 5|5\rangle як приклад.

Спочатку підтвердимо двійковий запис числа 5 і створимо Circuit, що кодує стан 5:

bin(5)
'0b101'
qc = QuantumCircuit(3)

qc.x(0)
qc.x(2)
qc.draw(output="mpl")

Output of the previous code cell

Перевіримо квантові стани за допомогою симулятора Aer:

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

Нарешті, додамо QFT і подивимося на кінцевий стан наших кубітів:

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)

qc.h(0)

qc.swap(0, 2)

qc.draw(output="mpl")

Output of the previous code cell

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

Ми бачимо, що функція QFT спрацювала правильно. Qubit 0 повернувся на 58\tfrac{5}{8} повного оберту, Qubit 1 — на 108\tfrac{10}{8} повних оберту (що еквівалентно 14\tfrac{1}{4} повного оберту), а Qubit 2 — на 208\tfrac{20}{8} повних оберту (що еквівалентно 12\tfrac{1}{2} повного оберту).

2.5. Вправа: QFT

(1) Реалізуй QFT для 4 кубітів.

##your code goes here##

(2) Застосуй QFT до 14|14\rangle, виконай симуляцію та побудуй вектор стану на сфері Блоха.

##your code goes here##

Розв'язок вправи: QFT

(1)

qr = QuantumRegister(4)
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr, cr)

qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)

qc.h(0)

qc.swap(0, 3)
qc.swap(1, 2)

qc.draw(output="mpl")

Output of the previous code cell

(2)

bin(14)
'0b1110'
qc = QuantumCircuit(4)

qc.x(1)
qc.x(2)
qc.x(3)
qc.draw("mpl")

Output of the previous code cell

qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)

qc.swap(0, 3)
qc.swap(1, 2)

qc.draw(output="mpl")

Output of the previous code cell

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

3. Основи Квантової оцінки фази (QPE)

За наявності унітарної операції UU, QPE оцінює θ\theta у рівнянні Uψ=e2πiθψU\vert\psi \rangle =e^{\boldsymbol{2\pi i} \theta }|\psi \rangle. Оскільки UU є унітарним, усі його власні значення мають норму 1.

3.1. Процедура

1. Налаштування

ψ\vert\psi\rangle знаходиться в одному наборі реєстрів кубітів. Додатковий набір із nn кубітів утворює лічильний регістр, у якому ми зберігатимемо значення 2nθ2^n\theta:

ψ0=0nψ|\psi_0\rangle = \lvert 0 \rangle^{\otimes n} \lvert \psi \rangle

2. Суперпозиція

Застосуй nn-бітну операцію Gate Адамара HnH^{\otimes n} до лічильного регістру:

ψ1=12n2(0+1)nψ|\psi_1\rangle = {\frac {1}{2^{\frac {n}{2}}}}\left(|0\rangle +|1\rangle \right)^{\otimes n} \lvert \psi \rangle

3. Операції керованого унітарного оператора

Нам потрібно ввести керований унітарний оператор CUCU, який застосовує унітарний оператор UU до цільового регістру лише тоді, коли відповідний керуючий біт дорівнює 1|1\rangle. Оскільки UU є унітарним оператором із власним вектором ψ|\psi\rangle таким, що Uψ=e2πiθψU|\psi \rangle =e^{\boldsymbol{2\pi i} \theta }|\psi \rangle, це означає:

U2jψ=U2j1Uψ=U2j1e2πiθψ==e2πi2jθψU^{2^{j}}|\psi \rangle =U^{2^{j}-1}U|\psi \rangle =U^{2^{j}-1}e^{2\pi i\theta }|\psi \rangle =\cdots =e^{2\pi i2^{j}\theta }|\psi \rangle

3.2. Приклад: QPE для T-gate

Використаємо Gate TT як приклад QPE та оцінимо її фазу θ\theta.

T1=(100eiπ4)(01)=eiπ41T|1\rangle = \begin{pmatrix} 1 & 0\\ 0 & e^\frac{i\pi}{4}\\ \end{pmatrix} \begin{pmatrix} 0\\ 1\\ \end{pmatrix} = e^\frac{i\pi}{4}|1\rangle

Очікуємо отримати:

θ=18\theta = \frac{1}{8}

де

T1=e2iπθ1T|1\rangle = e^{2i\pi\theta}|1\rangle

Ініціалізуємо ψ=1\vert\psi\rangle = \vert1\rangle власного вектора Gate TT, застосовуючи Gate XX:

qpe = QuantumCircuit(4, 3)
qpe.x(3)
qpe.draw(output="mpl")

Output of the previous code cell

Далі застосовуємо Gate Адамара до лічильних кубітів:

for qubit in range(3):
qpe.h(qubit)
qpe.draw(output="mpl")

Output of the previous code cell

Виконуємо операції керованого унітарного оператора:

repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.draw(output="mpl")

Output of the previous code cell

Застосовуємо обернене квантове перетворення Фур'є для конвертації стану лічильного регістру, після чого вимірюємо лічильний регістр:

from qiskit.circuit.library import QFT
# Apply inverse QFT
qpe.append(QFT(3, inverse=True), [0, 1, 2])
qpe.draw(output="mpl")

Output of the previous code cell

for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")

Output of the previous code cell

Можемо виконати симуляцію за допомогою симулятора Aer:

aer_sim = AerSimulator()
shots = 2048

pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)

sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()

plot_histogram(answer)

Output of the previous code cell

Ми бачимо, що з певністю отримуємо один результат (001), який у десятковому записі дорівнює: 1. Тепер потрібно поділити наш результат (1) на 2n2^n, щоб отримати θ\theta:

θ=123=18\theta = \frac{1}{2^3} = \frac{1}{8}

Алгоритм Шора дозволяє нам факторизувати число, будуючи Circuit із невідомим θ\theta та отримуючи θ\theta.

3.3 Вправа

Оціни θ=1/3\theta = 1/3, використовуючи 3 кубіти для підрахунку і один кубіт для власного вектора.

P1=eiλ1P|1\rangle = e^{i\lambda}|1\rangle. Оскільки ми хочемо реалізувати U1=e2πi131U|1\rangle = e^{2\pi i \tfrac{1}{3}}|1\rangle, потрібно встановити λ=2π3\lambda = \tfrac{2 \pi}{3}.

##your code goes here##

Розв'язання вправи: θ=1/3\theta = 1/3

# Create and set up circuit
qpe = QuantumCircuit(4, 3)

# Apply H-Gates to counting qubits:
for qubit in range(3):
qpe.h(qubit)

# Prepare our eigenstate |psi>:
qpe.x(3)

# Do the controlled-U operations:
angle = 2 * pi / 3
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(angle, counting_qubit, 3)
repetitions *= 2

# Do the inverse QFT:
qpe.append(QFT(3, inverse=True), [0, 1, 2])

for n in range(3):
qpe.measure(n, n)

qpe.draw(output="mpl")

Output of the previous code cell

aer_sim = AerSimulator()
shots = 4096

pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)

sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()

plot_histogram(answer)

Output of the previous code cell

4. Виконання за допомогою примітиву Sampler із Qiskit Runtime

Ми виконаємо QPE на реальному квантовому пристрої та дотримаємось 4 кроків шаблонів Qiskit.

  1. Відобразити задачу у квантово-нативний формат
  2. Оптимізувати схеми
  3. Виконати цільову схему
  4. Постобробити результати
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler
# Loading your IBM Quantum® account(s)

service = QiskitRuntimeService()

4.1 Крок 1: Відображення задачі на квантові схеми та оператори

qpe = QuantumCircuit(4, 3)
qpe.x(3)
for qubit in range(3):
qpe.h(qubit)
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")

Output of the previous code cell

backend = service.least_busy(simulator=False, operational=True, min_num_qubits=4)

print(backend)
<IBMBackend('ibm_strasbourg')>

4.2 Крок 2: Оптимізація для цільового обладнання

# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qpe)

qc_compiled.draw("mpl", idle_wires=False)

Output of the previous code cell

4.3 Крок 3: Виконання на цільовому обладнанні

real_sampler = Sampler(mode=backend)
job = real_sampler.run([qc_compiled], shots=1024)
job_id = job.job_id()
print("job id:", job_id)
job id: d13p4zb5z6q00087ag00
job = service.job(job_id)  # Input your job-id between the quotations
job.status()
'DONE'
result_real = job.result()
print(result_real)
PrimitiveResult([SamplerPubResult(data=DataBin(c=BitArray(<shape=(), num_shots=1024, num_bits=3>)), metadata={'circuit_metadata': {}})], metadata={'execution': {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2025-06-09 22:39:00', stop='2025-06-09 22:39:00', size=1024>)])}, 'version': 2})

4.4 Крок 4: Постобробка результатів

from qiskit.visualization import plot_histogram

plot_histogram(result_real[0].data.c.get_counts())

Output of the previous code cell

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'