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

Класичний зворотний зв'язок і керування потоком (динамічні схеми)

Версії пакетів

Код на цій сторінці розроблено з використанням наведених нижче залежностей. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.4.0

Динамічні схеми — потужний інструмент, який дозволяє вимірювати кубіти посередині виконання квантової схеми і на основі результатів цих проміжних вимірювань виконувати класичні логічні операції всередині самої схеми. Цей процес також відомий як класичний зворотний зв'язок (classical feedforward). Хоча ми ще на початку шляху розуміння того, як найкраще використовувати динамічні схеми, квантова дослідницька спільнота вже визначила низку сценаріїв застосування, зокрема:

Оператор if

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

У наведеному нижче прикладі застосовується вентиль Адамара до кубіта і виконується його вимірювання. Якщо результат дорівнює 1, то до кубіта застосовується вентиль X, який перевертає його назад у стан 0. Потім кубіт вимірюється знову. Результат повторного вимірювання має дорівнювати 0 з імовірністю 100%.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

Output of the previous code cell

Оператору with можна передати ціль присвоєння, яка сама є контекстним менеджером — її можна зберегти та згодом використати для створення блоку else, що виконується тоді, коли вміст блоку if не виконується.

У наведеному нижче прикладі ініціалізуються регістри з двома кубітами та двома класичними бітами. До першого кубіта застосовується вентиль Адамара і виконується його вимірювання. Якщо результат дорівнює 1, до другого кубіта застосовується вентиль Адамара; інакше — вентиль X. Наприкінці вимірюється й другий кубіт.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

Output of the previous code cell

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

У наведеному нижче прикладі до двох кубітів застосовуються вентилі Адамара і виконується їх вимірювання. Якщо результат дорівнює 01 — тобто перший кубіт дорівнює 1, а другий — 0 — до третього кубіта застосовується вентиль X. Наприкінці вимірюється третій кубіт. Зауваж, що для наочності ми явно вказали стан третього класичного біта (0) в умові if. На схематичному зображенні схеми умова позначається кружечками на класичних бітах, на яких задається умова. Зафарбований кружечок означає умову на 1, а незафарбований — умову на 0.

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

Output of the previous code cell

Класичні вирази

Модуль класичних виразів Qiskit qiskit.circuit.classical містить експериментальне представлення операцій виконання над класичними значеннями під час виконання схеми. Через апаратні обмеження наразі підтримуються лише умови QuantumCircuit.if_test().

Наступний приклад демонструє, що за допомогою обчислення парності можна створити n-кубітний GHZ-стан, використовуючи динамічні схеми. Спочатку генеруються n/2n/2 пар Белла на сусідніх кубітах. Потім ці пари з'єднуються між собою шаром вентилів CNOT між парами. Далі вимірюється цільовий кубіт усіх попередніх вентилів CNOT, і кожен виміряний кубіт скидається до стану 0\vert 0 \rangle. До кожного невиміряного вузла, для якого парність усіх попередніх бітів непарна, застосовується XX. Нарешті, до виміряних кубітів застосовуються вентилі CNOT для відновлення заплутаності, втраченої під час вимірювання.

При обчисленні парності перший елемент побудованого виразу передбачає підняття (lift) об'єкта Python mr[0] до вузла Value (lift використовується для перетворення довільних об'єктів на класичні вирази). Для mr[1] та можливих наступних класичних регістрів це не потрібно, оскільки вони є вхідними аргументами expr.bit_xor, а необхідне підняття виконується автоматично. Такі вирази можна будувати в циклах та інших конструкціях.

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue

# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

Output of the previous code cell

Наступні кроки

Рекомендації