Skip to content

Latest commit

 

History

History
206 lines (151 loc) · 11 KB

introduction.md

File metadata and controls

206 lines (151 loc) · 11 KB

2 семинар IT школы

Введение в Qiskit. Применение квантовых операций на одном и множестве кубитов. Построение квантовой схемы. Измерение кубита. Визуализация полученных значений.

Структура занятия

Вступление

Для работы используется Qiskit внутри https://quantum-computing.ibm.com/

Qiskit

В квантовой схеме мы даём что-то на вход, делаем вычисления и извлекаем результат (measurement).

QuantumCircuit в Qiskit создаётся с указанием количества кубитов и классических битов. Когда происходит измерение, то квантовый бит записывает результат в классический бит.

from qiskit import QuantumCircuit, assemble, Aer
from qiskit.visualization import plot_histogram

n = 8
n_q = n
n_b = n
qc_output = QuantumCircuit(n_q,n_b)

for j in range(n):
    qc_output.measure(j,j)

Для визуализации квантовой схемы:

qc_output.draw()

Кубиты всегда инициализируются 0, т.к. над ними не производится никаких операций, то результат будет 0.

# симулятор из Qiskit
sim = Aer.get_backend('qasm_simulator')
# https://qiskit.org/documentation/stubs/qiskit.compiler.assemble.html сериализирует payload, принимает на вход квантовую схему, возвращает Qobj, который может быть запущен на любом бэкенде (симулятор или системе)
qobj = assemble(qc_output)
counts = sim.run(qobj).result().get_counts()
plot_histogram(counts)

Квантовые компьютеры не возвращают точные данные, они возвращают вероятность (когда мы измеряем кубиты). В данном случае нет никаких квантовых операций, поэтому результат всегда будет одинаковый. Более того qasm_simulator сам по себе не содержит шумов, поэтому при повторных запусках результат отличаться не будет.

NOT gate - меняет кубит на обратное значение (0 - 1, 1 - 0). В Qiskit - x, допустим, для применения на конкретный кубит, можно использовать как в примере ниже

qc_encode = QuantumCircuit(n)
qc_encode.x(7)
qc_encode.draw()

Квантовые схемы можно соединять:

qc = qc_encode + qc_output
qc.draw()

Теперь выводится 10000000. Это потому, что на 7 кубит был применён NOT гейт. В Qiskit справа налево, поэтому NOT на 7 кубит превратил 00000000 в 10000000. Такой подход у них выбран потому это удобно когда биты используются для представления чисел, т.е. кубит 7 говорит сколько 2^7 в числе, т.е. 128 в 8 битном компьютере.

Попробуйте, закодировать какое-нибудь число, допустим, свой возраст. Не используемые биты слева оставьте 0.

qc_encode = QuantumCircuit(n)
qc_encode.x(0)
qc_encode.x(1)
qc_encode.x(4)

qc_encode.draw()

Для многих задач также полезен XOR gate

A B XOR
0 0 0
0 1 1
1 0 1
1 1 0

Этот гейт полезен для определяния правого бита при сложении. Допустим, 1+1 = 10, 0 - как раз то, что вернёт и XOR.

В квантовых компьютерах XOR выполняется при помощи controlled-NOT gate. Сокращенно CNOT, в Qiskit - cx. Он используется на 2-х кубитах. Первый кубит - это управляющий кубит, он не меняется. А во на второй применяется XOR и он изменяется.

qc_cnot = QuantumCircuit(2)
qc_cnot.cx(0,1)
qc_cnot.draw()

Его названия связано с тем, что поведение можно объяснить как: на целевой кубит применяется NOT если управляющий 1 и ничего не делает в остальных случаях.

Чему будет равен ответ?

qc = QuantumCircuit(2,2)
qc.x(0)
qc.cx(0,1)
qc.measure(0,0)
qc.measure(1,1)
qc.draw()

Попробуйте выполнить на симуляторе и проверить.

Результат получился 11. На 1-ый кубит применяется NOT, который делает его 1. Затем на 2-ой кубит применяется CNOT (1 XOR 0 = 1), соответственно 2-ой кубит становится 1. Результат 11.

Давайте подумаем над задачей, мы хотим сделать алгоритм, который складывает 2 кубита. Т.е.

A B SUM
0 0 00
0 1 01
1 0 01
1 1 10
# результат вычисления мы хотим положить в другие кубиты, поэтому 2 кубита - для входа, 2 будем использовать в вычислениях, итого 4. И для результата понадобятся только 2 классических бита
qc_ha = QuantumCircuit(4,2)
# кодируем входящие кубиты
qc_ha.x(0) # Закомментируйте, чтобы A = 0
qc_ha.x(1) # Закомментируйте, чтобы B = 0
qc_ha.barrier() # для визуального более удобного разделения этапов на схеме
# CNOTs для кубита 2. Кубит 2 - это то, что будет записано в конце (т.е. второй классический бит). Мы можем использовать 2 CNOT гейта на 0 и 2, а затем на 1 и 2 кубиты, что вычислить, чему будет равен кубит 2
qc_ha.cx(0,2)
qc_ha.cx(1,2)
qc_ha.barrier()
# вычисляем
qc_ha.measure(2,0)
qc_ha.measure(3,1)

qc_ha.draw()

С кубитом 3 пока ничего не делали (с 1-ым классическим битом). Мы можем увидеть, что он равен 1 только в том случае, когда A и B - 1. И, конечно, есть такой гейт :)

Который смотрит на 2 управляющих кубита, а не на 1. Это гейт Тоффоли (Toffoli). Он применяет NOT на целевой кубито только когда оба управляющих 1. В Qiskit это ccx. В булевой логике это аналог AND.

# результат вычисления мы хотим положить в другие кубиты, поэтому 2 кубита - для входа, 2 будем использовать в вычислениях, итого 4. И для результата понадобятся только 2 классических бита
qc_ha = QuantumCircuit(4,2)
# кодируем входящие кубиты
qc_ha.x(0) # Закомментируйте, чтобы A = 0
qc_ha.x(1) # Закомментируйте, чтобы B = 0
qc_ha.barrier() # для визуального более удобного разделения этапов на схеме
# CNOTs для кубита 2. Кубит 2 - это то, что будет записано в конце (т.е. второй классический бит). Мы можем использовать 2 CNOT гейта на 0 и 2, а затем на 1 и 2 кубиты, что вычислить, чему будет равен кубит 2
qc_ha.cx(0,2)
qc_ha.cx(1,2)
# Toffoli, чтобы вычислить 3-ий кубит (1-ый классический бит)
qc_ha.ccx(0,1,3)
qc_ha.barrier()
# вычисляем
qc_ha.measure(2,0)
qc_ha.measure(3,1)

qc_ha.draw()

Это самые простейщие элементы, которые постоянно используются в квантовых вычислениях.

Кубиты

В квантовой физике используются векторы состояния. Допустим, для стандартного вектора это 0, 1, 2, 3 и т.д. Мы можем сказать, что какая-то точка находится в 3. Вектор состояния это 0 0 0 1 0 0 0, т.е. обозначить вероятность, с которой мы можем найти эту точку в данной позиции.

В отличии от бита, который может принимать 0 или 1. Кубит принимает это значение только во время измерения. В другое время его значение - это нечто более сложное, чем просто бинарное значение. |0> = [1 0], |1> = [0 1]

from qiskit import QuantumCircuit, assemble, Aer
from qiskit.visualization import plot_histogram, plot_bloch_vector
from math import sqrt, pi

qc = QuantumCircuit(1) # Создание квантовой схемы с одним кубитом
initial_state = [0,1]   # Определяем начальное состояние как |1>
qc.initialize(initial_state, 0) # Применяем к 0 кубиту
qc.draw()  # Рисуем схему

svsim = Aer.get_backend('statevector_simulator') # Берём симулятор

qobj = assemble(qc)     # Создаём Qobj
result = svsim.run(qobj).result() # Симулируем и возвращаем результат симуляции

out_state = result.get_statevector()
print(out_state) # Отобразим вектор состояния

Python j - это i для обозначения комплексных чисел. Т.е. тут это [0.+0.j 1.+0.j], где 0.+0.j - 0, 1.+0.j - 1

Измерим кубиты:

qc.measure_all()
qc.draw()

Получим вероятность, а не вектор состояния и нарисуем:

counts = result.get_counts()
plot_histogram(counts)