Винтик и Шпунтик осваивают квантовые вычисления

в 10:59, , рубрики: квантовые вычисления

Привет! Сегодня, в первый день нового учебного года мы будем решать интересную задачку про Винтика и Шпунтика, которую не так давно запостил vvvphoenix. Да решать не на бумажке, не на калькуляторе, и даже не на питоне, а на новейшем облачном фотонном квантовом компьютере!

Винтик и Шпунтик осваивают квантовые вычисления - 1
Credit: Bernard Marr

Задача

В оригинальном посте все началось с олимпиадной задачки для 7-8 классов:

Незнайка записывает 10-значное десятичное число, оставляя пропуск вместо одной из цифр. Он предлагает Винтику заполнить пропуск любой цифрой, а затем показать полученное 10-значное число Шпунтику. Как могут Винтик и Шпунтик договориться, чтобы Шпунтик угадал, на каком именно месте стоял пропуск?

Если вы не видели задачу раньше, подумайте немного над ее решением, оно не очень сложное.

Ответ

Ключ к решению — чексумма: Винтик заполняет пропуск такой цифрой, чтобы чексумма 10-значного числа совпадала с номером пропущенного разряда.

Чексумму можно выбрать любую, например, посчитать сумму всех цифр и взять ее последнюю цифру. Скажем, если Незнайка загадал 12345_7890, то Винтик заполняет его цифрой 7. Шпунтик считает сумму всех цифр: 1+2+3+4+5+7+7+8+9 = 46. Последняя цифра суммы — 6, значит пропуск стоял в шестом разряде.

Очевидно, что Винтик и Шпунтик могут договориться и использовать любую чексумму — а значит, у задачи есть больше одного решения. И тут vvvphoenix задается по-настоящему интересным вопросом: а сколько же всего существует решений? Иными словами, сколько существует взаимооднозначных соответствий между 10^10 строками с пропуском, которые придумывает Незнайка, и 10^10 десятизначными числами, которые записывает Винтик?

Вообще 10^10 — это немножко дофига. К счастью, смысл задачи не меняется для N-значных чисел в N-значной системе счисления: например, для N=2 Незнайка придумывает двухначное число, в котором один из разрядов — 0 или 1, а на месте второго стоит пропуск. Теперь Винтику и Шпунтику нужно найти отображение множества {0..., 1..., ...0, ...1} на {00, 01, 10, 11}. Нюанс в том, что не все варианты доступны: "0_" не может соответствовать "11", потому что первые разряды не совпадают. Давайте нарисуем возможные варианты в виде таблицы, где 1 — разрешенные соответствия, а 0 — запрещенные:

Винтик и Шпунтик осваивают квантовые вычисления - 2

Несложно заметить, что у этой задачи ровно два решения:

Винтик и Шпунтик осваивают квантовые вычисления - 3

Для N=3 таблица вырастает до N^N x N^N = 27x27 и выглядит так:

Винтик и Шпунтик осваивают квантовые вычисления - 4
Картинка из телеграма автора задачи

Таблицы — это здорово: глядя на них, легко заметить что ответ на задачу — это количество способов расставить небьющих друг друга ладей на разрешенные клетки. Любой математик, услышав про ладей, сразу же даст абсолютно точный, но совершенно бесполезный ответ: количество расстановок равно перманенту матрицы с картинки выше. К этому же ответу можно прийти и через теорию графов.

Перманент — это буквально задачка про ладей, заплывших в мир матриц. Считается он так: берем набор небьющих друг друга ладей, перемножаем числа под ними, повторяем для всех возможных наборов ладей, и, наконец, суммируем все получившиеся произведения. Этим перманент очень похож на детерминант, в котором все минусы заменены на плюсы:

Винтик и Шпунтик осваивают квантовые вычисления - 5

На этом сходства заканчиваются. В отличие от детерминанта, вычисление перманента имеет экспоненциальную сложность, и быстрого алгоритма для него не существует. Возвращаясь к задаче про Винтика и Шпунтика, для случая N=2 (матрица 4х4) перманент считается на бумажке и равен 2, расчеты для N=3 (матрица 27х27) на питоне занимают около часа и дают 10 752, а дальше вычисления становятся неприлично долгими. Случай N=10 так и вообще остается в области фантастики.

А еще у перманента не очень много практических применений, поэтому я не удивлюсь, если вы услышали о нем впервые. Мне он тоже попался на глаза совсем недавно. И совсем по другой причине.

Boson sampling

Задача о бозонном сэмплинге появилась почти пятнадцать лет назад, но прогремела заметно позже. Именно на ее разновидностях сначала Google, а потом и группа профессора Пэна в Китае продемонстрировали превосходство квантового компьютера над классическим. Бозонный сэмплинг — задача довольно синтетическая: для нее не существует эффективных классических алгоритмов, при этом она идеально подходит для реализации на квантовых компьютерах. А теперь пристегните ремни, потому что звучит она еще более необычно:

У нас есть n волноводов для света (например, оптических волокон). Некоторые из волноводов попарно пересекаются на сплиттерах: на каждом сплиттере свет из каждого волновода делится на две части, половина остается в том же волноводе, половина уходит в другой. Расположение сплиттеров известно. Мы выбираем m из n волноводов и запускаем на вход каждого из них ровно по одному фотону. Какова вероятность того, что на выходе каждого из этих m волноводов все еще останется по одному фотону?

Винтик и Шпунтик осваивают квантовые вычисления - 6
Волноводы на фотонном чипе. Credit: PhotonQ

Вопрос из зала: эээ… и это все еще имеет отношение к квантовому превосходству?

И еще какое! Секрет в том, что одиночные фотоны — это квантовые объекты: проходя через сплиттер, фотон не выбирает один выход, а остается в обоих, прямо как полуживой-полумертвый кот сами-знаете кого. А еще фотоны интерферируют между собой, добавляя щепотку квантовой запутанности в эту и без того запутанную задачу.

Вопрос из зала: Так мы же знаем расположение сплиттеров? Вычисление должно быть элементарным, разве нет?

Давайте разбираться. Вот, например, мы послали на первые два волновода по фотону и хотим увидеть на выходе один фотон во втором волноводе, а один — в третьем. Какова вероятность такого события?

Винтик и Шпунтик осваивают квантовые вычисления - 7
Отсюда

Ну понятно, тут два (на самом деле 2!) варианта: либо первый фотон пойдет во второй волновод, а второй — в третий, либо наоборот. Можно записать это как произведение вероятностей, сумма которых подозрительно напоминает перманент матрицы 2х2:

Винтик и Шпунтик осваивают квантовые вычисления - 8

А если фотонов три? Тогда у нас 3! = 6 вариантов:

Винтик и Шпунтик осваивают квантовые вычисления - 9

и идеальное совпадение с перманентом матрицы 3х3. Именно этот перманент и делает бозонный сэмплинг экспоненциально сложным для классических алгоритмов.

Linear optics quantum computing

Разумеется, необычная формулировка задачи про бозонный сэмплинг появилась не на пустом месте. Все железо в ней — фотонный чип с волноводами и сплиттерами, источники одиночных фотонов и их детекторы — это части одной большой парадигмы квантовых вычислений на линейной оптике. Удивительно, но такого простого (да нифига) набора оптики с лихвой хватает и для реализации основных квантовых гейтов, и для гораздо более интересных приемов.

Впрочем, в 2019 году Google демонстрировала квантовое превосходство на сверхпроводниковых кубитах. На тот момент это была единственная масштабируемая платформа для квантовых компьютеров. Зато уже через год научная группа Цзянвей Пэна в Китае запустила бозонный сэмплинг на фотонном компьютере, да каком — вместо фотонного чипа там была настоящая стеклянная оптика, занимавшая целый стол!

Винтик и Шпунтик осваивают квантовые вычисления - 10
Фотонный компьютер Jiuzhang собственной персоной. Credit: University of Science and Technology of China

Вскоре после этого фотонные квантовые компьютеры уверенно ворвались на рынок. Сейчас в мире даже есть два сервера облачных фотонных вычислений, и если канадская Xanadu ориентирована только на корпоративных клиентов, то французская Quandela дает всем желающим возможность бесплатно прикоснуться к фотонным вычислениям. Черт, вы только представьте: 2010-й год, Ааронсон и Архипов — авторы идеи бозонного сэмплинга — выглядят буквально как персонаж мема:

Винтик и Шпунтик осваивают квантовые вычисления - 11

а сегодня кто угодно может запустить их идею в несколько строчек на питоне. Ну что, попробуем и мы?

Дорога в облака

После регистрации на cloud.quandela.com мы попадаем на главную страницу, которая встречает нас статусом доступных платформ. Среди них несколько симуляторов и квантовый процессор (QPU) под названием Altair:

Винтик и Шпунтик осваивают квантовые вычисления - 12

Сердце Altair — это универсальный программируемый фотонный чип (universal interferometer). Специальный источник (SPS) генерирует поток одиночных фотонов, который разводится демультиплексором на заданные входы в чипе. На выходе чипа стоят сверхпроводящие однофотонные детекторы (SNSPD), которые измеряют распределение фотонов после прохождения чипа.

Винтик и Шпунтик осваивают квантовые вычисления - 13
Отсюда. Картинке больше года, с тех пор чип немного вырос.

В 20-модовый чип Altair (это значит, что в нем 20 волноводов) можно запустить до 10 фотонов. Это очень условно соответствует 10+ кубитам; условно — потому что фотонные компьютеры кодируют информацию более эффективно, и 10 кубитов — это оценка снизу. На странице QPU можно найти полную схему чипа, она выглядит примерно так:

Винтик и Шпунтик осваивают квантовые вычисления - 14

и содержит несколько сотен сплиттеров и фазовращателей. Сплиттеры фиксированы, программируется чип именно фазовращателями, которые меняют фазу проходящего через них света, влияя на интерференцию фотонов в разных модах.

Теперь давайте разберемся с тем, как все это запустить.

Квантовый Hello, world!

Для работы с облаком нужно сгенерировать токен и установить фреймворк под названием Perceval (pip install perceval-quandela). В Perceval нас интересует несколько сущностей.

Processor

Первым делом мы инициализируем квантовый процессор или его симулятор. Если нас интересует процессор в облаке, то мы инициализируем класс RemoteProcessor именем процессора с Quandela Cloud и токеном:

import perceval as pcvl

proc = pcvl.RemoteProcessor("qpu:altair", token)    # QPU Altair
proc = pcvl.RemoteProcessor("sim:altair", token)    # симулятор Altair на GPU

Если же мы не хотим в облако, то можно запустить симулятор на локальной машине, указав архитектуру процессора:

proc = pcvl.Processor("CliffordClifford2017")

На симуляторе можно запускать любое число задач, а для работы на настоящем QPU понадобятся кредиты. Юзеру дается 200 бесплатных кредитов каждый месяц.

Circuit

Следующим шагом мы создаем схему, которую хотим запрограммировать в фотонный чип. Она инициализируется количеством мод, которые мы хотим использовать:

c = pcvl.Circuit(2)
pcvl.pdisplay(c)

Последняя строчка рисует схему: сейчас в ней два волновода, которые идут от входа к выходу без каких-либо пересечений:

Винтик и Шпунтик осваивают квантовые вычисления - 15

Давайте добавим в схему два сплиттера с фазовращателем — это называется интерферометр Маха-Цендера:

phase = 0.5*np.pi
c.add(0, pcvl.BS())
c.add(0, pcvl.PS(phi=phase))
c.add(0, pcvl.BS())
pcvl.pdisplay(c)

Винтик и Шпунтик осваивают квантовые вычисления - 16

Осталось записать эту схему в чип:

proc.set_circuit(c)

Source

Теперь мы должны прописать входы чипа, на которые мы подаем одиночные фотоны. Это делается при помощи класса BasicState. Мы подадим один фотон в первую моду, а вторую оставим пустой:

input_state = pcvl.BasicState([1,0])
proc.with_input(input_state)

Sampler

Остается запустить вычисления, а потом повторить их несколько тысяч раз. Зачем? Все потому, что любые квантовые вычисления на любых платформах — вероятностые: после открывании коробки любой кот окажется либо живым, либо мертвым. Каким он был до открывания — квантовым, немножко квантовым, обычным дворовым — черт его знает. Чтобы узнать это, придется приготовить кота:

Винтик и Шпунтик осваивают квантовые вычисления - 17

открыть коробку, записать результат, а потом повторить эксперимент еще пару тысяч раз. Если в половине случаев кот остался живым, то наш рецепт готовил идеального полуживого-полумертвого кота Шредингера. Если живыми остались 90% котов, то и кот из рецепта был скорее жив, чем мертв. Именно так и никак иначе мы и набираем итоговую статистику. В любых квантовых вычислениях мы измеряем вероятность тех или иных исходов, запуская один и тот же алгоритм много раз и считая количество результатов. Собственно, это и называется сэмплингом.

А так как квантовое измерение — процесс, подобный подбрасыванию монетки, то он подвержен дробовому шуму. Из-за него квантовые алгоритмы в принципе не могут дать нам точный результат. Цель квантовых вычислений — это дать быструю эффективную оценку, а не точность в несколько знаков после запятой. Поэтому отклонение от точного результата в десятки процентов, а то и несколько раз — это совершенно нормально.

Для запуска сэмплинга в Perceval используется класс Sampler, который инициализируется процессором с прописанной схемой и максимальным количеством шотов (shots):

sampler = Sampler(proc, max_shots_per_call=1000)

Здесь max_shots_per_call — это количество повторений, в которых на выходе чипа был задетектирован хотя бы один фотон. Дело в том, что реальный чип — он большой и сложный, и какой-то процент фотонов в нем теряется, поэтому в ряде попыток до детекторов не долетит ничего. Чтобы получить 1000 шотов, компьютер сделает больше, чем 1000 повторений. Впрочем, наш локальный симулятор работает без потерь.

Нам осталось поставить задачу в очередь задач:

job = sampler.sample_count.execute_async()

и время от времени проверять ее статус:

print(f"Job status = {job.status()}")

Job status = SUCCESS

Results

Когда задача будет готова, мы можем забрать ее результаты:

results = job.get_results()

Можно забрать и результат более старой задачи

Для этого нужно скопитьвать ее ID из списка задач на облаке:

Винтик и Шпунтик осваивают квантовые вычисления - 18

и обратиться к ней таким образом:

proc = pcvl.RemoteProcessor(proc_name, token)
job_id = 'e2692e28-9ccd-4f9d-b2fe-8c64ded10d66'
job = proc.resume_job(job_id)
results = job.get_results()

Результат квантового вычисления показывает, сколько раз алгоритм дал ту или иную комбинацию фотонов (outcomes) на выхоже из чипа. В нашем случае он выглядит так:

outcomes = results['results']
print(outcomes)

{
  |0,1>: 497
  |1,0>: 503
}

Симулятор произвел 1000 шотов, в 503 из них фотон оказался в первой моде, в 497 — во второй. Почти идеальное соотношение 50:50. А что будет, если мы поменяем фазу интерферометра Маха-Цендера?

phase = 0*np.pi

{
  |0,1>: 1000
}

phase = 1*np.pi

{
  |1,0>: 1000
}

Ого, поведение интерферометра меняется в зависимости от приложенной фазы! В принципе, это именно то, чего мы от него ожидаем: интерферометр Маха-Цендера часто используется именно для перераспределения света между двумя выводами. (Если присмотреться к схеме чипа Altair, то можно заметить, что он буквально набит такими интерферометрами.) Это не квантовый эффект, а классическая интерференция, но для Hello, world! нам её хватит.

Теперь повторим те же вычисления не на симуляторе, а на QPU. При этом с вашего счета в Quandela Cloud списываются кредиты: один кредит соответствует миллиону шотов. Нам хватит всего тысячи шотов:

phase = 0*np.pi

{
  |0,1>: 930
  |1,0>: 70
}

phase = 0.5*np.pi

{
  |0,1>: 480
  |1,0>: 520
}

phase = 1*np.pi

{
  |0,1>: 141
  |1,0>: 859
}

Не так идеально, но работает! Ничего удивительного: оптика QPU сложная, поэтому к выходу из чипа набегает какая-то ошибка — впрочем, она не сильно мешает реальным квантовым вычисленям.

Матрицы

Вместо того, чтобы набирать схему из сплиттеров и фазовращателей, можно пойти другим путем. По большому счету чип с N модами — это матрица размера NxN, которая превращает входной вектор длины N в выходной вектор такой же длины:

Винтик и Шпунтик осваивают квантовые вычисления - 19

Компоненты входного вектора — это количество фотонов, которое мы запускаем в каждую из мод, ну а выходной вектор показывает среднее число фотонов на выходе из чипа. А теперь самое прекрасное: оказывается, что любую унитарную матрицу можно записать в чип за полиномиальное время. Алгоритм для этого подбирает такие значения фазовращателей, при которых пропускание чипа хорошо аппроксимирует заданную матрицу. А вы же помните, что прохождение через чип тесно связано с перманентом?

Квантовый перманент

Чуть раньше мы узнали, что вероятность получить определенное распределение фотонов на выходе пропорциональна перманенту некой матрицы. Давайте начнем с самого простого случая: две моды, мы запускаем по фотону в каждую и хотим узнать, как часто мы увидим такое же распределение — по фотону в каждой моде — на выходе из чипа. Не вдаваясь в детали, посчитать вероятность такого исхода невероятно просто: она равна

Винтик и Шпунтик осваивают квантовые вычисления - 20

где U — это матрица, описывающая чип. А значит, перманент матрицы U можно вычислить как

Винтик и Шпунтик осваивают квантовые вычисления - 21

Здесь N_shots — общее число шотов, а N_events — количество интересующих нас событий на выходе, где в каждой моде оказалось по фотону.

На удивление простая формула, не так ли? Давайте проверим её на каком-нибудь примере. Возьмем, например, вот такую матрицу:

Винтик и Шпунтик осваивают квантовые вычисления - 22

перманент которой несложно посчитать: 0.6 + (-0.4) = 0.2. А теперь попробуем вычислить его на симуляторе. В Perceval матрица записывается в фотонный чип вот таким образом:

U = np.array([
    [np.sqrt(0.6), np.sqrt(0.4)],
    [-np.sqrt(0.4), np.sqrt(0.6)],
 ])
unitary_component = comp.Unitary(pcvl.Matrix(U))
pcvl.pdisplay(unitary_component)

и отображается не как набор сплиттеров-фазовращателей, а как один большой блок:

Винтик и Шпунтик осваивают квантовые вычисления - 23

Посылаем по фотону в каждую моду и запускаем вычисления:

input_state = pcvl.BasicState([1,1])
shots = 10_000

proc = pcvl.Processor("CliffordClifford2017")
proc.set_circuit(unitary_component)
proc.with_input(input_state)
proc.min_detected_photons_filter(1)

sampler = Sampler(proc, max_shots_per_call=shots)
remote_job = sampler.sample_count
remote_job.execute_async()

и смотрим на результаты:

results = remote_job.get_results()
outcomes = results['results']
print(outcomes)

{
  |2,0>: 4759
  |0,2>: 4829
  |1,1>: 412
}

412 событий из 10 000 шотов соответствуют перманенту:

perm = np.sqrt(412/10_000)
print(f'Calculated permanent: {perm:.2f}')

Calculated permanent: 0.20

Идеальное совпадение с ожидаемым результатом!

Унитарность

С матрицами связан один нюанс. Матрица U должна быть унитарной, другую физически не получится прописать в чип. Но это не проблема: если мы хотим посчитать перманент произвольной матрицы A размером N x N, то мы всегда можем дополнить ее до унитарной матрицы U размером 2N x 2N:

Винтик и Шпунтик осваивают квантовые вычисления - 24

Матрица U переводит входной вектор длины 2N в выходной вектор такой же длины, но подматрица А видит только первую половину входного вектора, переводя ее в первую половину выходного. А значит, чтобы посчитать перманент А, мы можем подать по одиночному фотону только в первые N мод и выбрать события с таким же распределением фотонов на выходе.

Алгоритм дополнения до унитарной матрицы

хорошо описан в этой работе:

Винтик и Шпунтик осваивают квантовые вычисления - 25

Если в двух словах, то мы:

  • генерируем матрицу A_s, нормируя A на ее спектральную норму s
  • составляем матрицу U с подматрицей A_s как в уравнении (6)
  • после вычисления перманента A_s не забываем скорректировать его на спектральную норму:

Винтик и Шпунтик осваивают квантовые вычисления - 26

где n — число строк (или столбцов) матрицы.

А код для этого выглядит вот так:

import numpy as np
from numpy import linalg
from scipy.linalg import sqrtm

def prepare_unitary(A):
    n = len(A)
    _, D, _ = linalg.svd(A)
    s = np.max(D)
    As = A/s

    Q = sqrtm(np.identity(n) - np.dot(As, As.conj().T))
    R = sqrtm(np.identity(n) - np.dot(As.conj().T, As))

    Ubmat = np.bmat([
        [As, Q], 
        [R, -As.conj().T]
    ])
    Ubmat = Ubmat.real
    return np.copy(Ubmat), s

Заметьте, что кроме матрицы U функция возвращает ещё и спектральную норму s, на которое нужно будет отнормировать результат (n — это размер матрицы A):

Винтик и Шпунтик осваивают квантовые вычисления - 27

В задаче про Винтика и Шпунтика для N=2 мы считаем перманент матрицы 4х4, которую мы впишем в унитарную матрицу размером 8x8 и легко запишем в чип Altair. К сожалению, уже для N=3 нам потребуется матрица 54х54 — это заметно больше нынешних возможностей. Ну что ж, не все сразу, начнем с простого.

Считаем!

Задаем матрицу из задачки про Винтика и Шпутника для N=2:

A = np.array([
    [1, 0, 1, 0],
    [0, 1, 0, 1],
    [1, 1, 0, 0],
    [0, 0, 1, 1],
 ])

Дополняем ее до унитарной матрицы U и готовимся послать по одиночному фотону в первые четыре моды:

import perceval.components as comp

U, s = prepare_unitary(A)
print(f'Spectral norm = {s}')
unitary_component = comp.Unitary(pcvl.Matrix(U))
input_state = pcvl.BasicState([1,1,1,1,0,0,0,0])

Spectral norm = 2.0

Локальный симулятор

Для начала запускаем вычисления на нашем компьютере:

proc = pcvl.Processor("CliffordClifford2017")
proc.set_circuit(unitary_component)
proc.with_input(input_state)
proc.min_detected_photons_filter(1)

shots = 10_000
sampler = Sampler(proc, max_shots_per_call=shots)
sampler.default_job_name = 'Permanent'
remote_job = sampler.sample_count
remote_job.execute_async()

и получаем результат:

results = remote_job.get_results()
outcomes = results['results']
print(outcomes)

{
  |0,0,0,0,1,0,1,2>: 66
  |0,0,0,2,1,1,0,0>: 249
  |0,0,0,0,0,2,2,0>: 63
  |0,0,0,0,0,1,2,1>: 80
  ...
  |1,0,0,0,0,0,3,0>: 1
}

Нас интересует только события, когда выходное состояние совпало с входным. Переменная outcomes — это объект класса BSCount, который наследует от dict, поэтому к нему можно обратиться просто как

events = outcomes[input_state]
print(f'Detected events: {events}')

Detected events: 153

Восстанавливаем перманент по формуле выше, не забывая про нормировку на спектральную норму s:

n = 4 # размер матрицы
perm = (s**n) * np.sqrt(events/shots)
print(f'Calculated permanent: {perm:.2f}')

1.98

Вау, идеальное совпадение с ожидаемой двойкой!

Симулятор Altair

Теперь запустим то же самое на облачном симуляторе, предварительно увеличив число шотов до 100 миллионов:

shots = 100_000_000
proc = pcvl.RemoteProcessor('sim:altair', token)

После окончания посмотрим, сколько же совпадений мы увидели:

events = outcomes[input_state]
print(f'Detected events: {events}')

Detected events: 83

Всего 83 из 100 миллионов?! Минуту назад мы видели в два раза больше совпадений на 10 тысяч шотов, что произошло?

Секрет в том, что QPU очень большой и сложный, и фотону легко в нем потеряться. Суммарное пропускание (transmittance) QPU Altair составляет всего несколько процентов. По дефолту симулятор запускает расчет именно пропусканием в 6 % — то есть только 6 % фотонов оказываются задетектированы на выходе из чипа. Но это же симулятор, поэтому пропускание можно выставить обратно на 100 %:

proc = pcvl.RemoteProcessor('sim:altair', token)
proc.set_parameters({
    "transmittance": 1.00,
})

и запустить вычисления еще раз:

events = outcomes[input_state]
print(f'Detected events: {events}')

n = 4 # размер матрицы
perm = (s**n) * np.sqrt(events/shots)
print(f'Calculated permanent: {perm:.2f}')

Detected events:  1444826
Calculated permanent: 1.98

Совсем другое дело! Но что же делать с симуляцией рельного QPU, у которого пропускание t гораздо ниже? В принципе, эти потери несложно учесть:

  • Один фотон, запущенный в QPU, будет задетектирован с вероятностью t, а потерян — с веротяностью 1-t.
  • Для двух фотонов вероятность задетектировать оба равна t^2, а вероятность потерять оба — (1-t)^2.
  • И так далее. Для четырех картинка выглядит как-то так:

Винтик и Шпунтик осваивают квантовые вычисления - 28

Когда мы вычисляем перманент, мы делим число интересующих нас событий |1,1,1,1,0,0,0,0> на число всех событий. Из-за потерь события |1,1,1,1,0,0,0,0> приходят в t^4 раз реже, а шоты надо скорректировать на 1 — (1-t)^4 чтобы не забыть случаи со всеми нулями на выходе. В итоге получаем красивую формулу:

Винтик и Шпунтик осваивают квантовые вычисления - 29

И немедленно подставляем в нее наш результат с 83 событиями из 100 000 000 и t = 6 %:

events = outcomes[input_state]
print(f'Detected events: {events}')

n = 4 # размер матрицы
t = 0.06 # пропускание
perm = (s**n) * np.sqrt(events/shots * (1-(1-t)**n)/t**n)
print(f'Calculated permanent: {perm:.2f}')

Detected samples: 83
Calculated permanent: 1.90 

Бинго! Вот таким нехитрым способом можно учесть неидеальность квантового процессора.

QPU Altair

Осталось запустить то же самое на настоящем QPU. Его пропускание еще меньше, поэтому увеличим число шотов до 1 миллиарда:

shots = 1_000_000_000
proc = pcvl.RemoteProcessor('qpu:altair', token)

и заберем результаты:

events = outcomes[input_state]
print(f'Detected events: {events}')

Detected events: 69

Пропускание в тот день составило 2.3 %, поэтому после коррекции я получил

n = 4 # размер матрицы
t = 0.023 # пропускание
perm = (s**n) * np.sqrt(events/shots * (1-(1-t)**n)/t**n)
print(f'Calculated permanent: {perm:.2f}')

Calculated permanent: 2.22

И вновь перманент равен двум, как мы и ожидали. Напомню, что мы не ждем высокой точности от квантовых вычислений, ошибка всего в 10 % — это более, чем замечательно.

Ну что же, принимайте поздравления, мы решили задачу про Винтика и Шпунтика на квантовом компьютере!

Вместо заключения

Вы удивитесь, но перманент оказывается крепким орешком даже для квантовых компьютеров. Хоть каждый шот и вычисляется со скоростью света, количество шотов, необходимых для вычислений, быстро растет с размерностью задачи. Похоже, что задачи из класса #P, к которым относится и вычисление перманента, так и останутся экспоненциально сложными и для классических, и для квантовых алгоритмов — хотя последние и показывают на них приличное ускорение.

А теперь хорошие новости: фотонные квантовые компьютеры способны далеко не только на бозонный сэмплинг. Фотонные платформы принципиально отличаются от привычных сверхпроводниковых чипов IBM или Google и позволяет нативно реализовывать новые интересные алгоритмы. При этом на фотонные чипы на удивление хорошо портируются примитивы сверхпроводниковых платформ, поэтому на них можно будет успешно запускать и знаменитые алгоритмы Шора или Гровера. Разумеется, десятифотонного компьютера от Quandela пока еще недостаточно для факторизации простых чисел; впрочем, он уже успешно используется в простых юз-кейсах и учебных программах. Плюс ко всему все нынешние фотонные платформы показывают уверенный рост и на удивление хорошие перспективы для масштабирования. Так что все самое интересное только начинается.

Автор: qbertych

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js