Изучите основные строительные блоки квантовой науки о данных

Хотите начать работу с квантовым машинным обучением? Взгляните на Практическое обучение квантовому машинному обучению с помощью Python.

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

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

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

Разбиваем все значения на равные блоки. В простейшем случае размер блока представляет наклон и точку пересечения (когда наклон = точка пересечения).

Тогда первое состояние (00) имеет один блок (поскольку мы интерпретируем это состояние как значение 0), а два последующих состояния отличаются на один блок. Мы добавляем еще один блок с каждым шагом. На следующем рисунке показан этот подход.

Конечно, мы не можем считать, что наклон и точка пересечения равны. Поэтому нам может понадобиться использовать блоки меньшего размера. Тогда наклон (то есть a) и точка пересечения (то есть b) могут состоять из нескольких блоков. Но общий подход остается неизменным. На следующем рисунке мы используем один блок в качестве точки пересечения (b) и несколько блоков в качестве наклона (a). Состояние 00 имеет только блок перехвата. Затем мы добавляем блоки в количестве, соответствующем уклону.

Итак, наша задача состоит из двух основных пунктов:

  • рассчитать количество блоков, которое должно быть в состоянии
  • подготовьте кубиты так, чтобы вероятности измерения состояний представляли соответствующее число

Первая задача достаточно проста. Если мы знаем позицию состояния, то количество блоков соответствует этой позиции, умноженной на блоки, представляющие наклон, плюс блоки, представляющие точку пересечения. В коде Python функция является однострочной.

Вторая задача немного сложнее. Но это не так уж и сложно.

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

Итак, мы начинаем со старшего кубита и делим состояния на основе общего количества блоков, как показано на следующем рисунке.

Мы достигаем этого, используя вентиль RY — вращение кубита вокруг оси Y.

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

Далее мы смотрим только на те состояния, в которых верхний кубит равен 1. Затем мы разделяем эти состояния и подсчитываем блоки.

Мы достигаем этого с помощью управляемого RY-гейта. Это применимо только в том случае, если управляющий кубит равен 1 — то есть только для верхней половины. Обратите внимание, что значения верхнего и нижнего теперь должны представлять только подмножества.

Мы делаем то же самое для этих состояний, где верхний кубит равен 0, инкапсулируя его в X вентили. Вентиль X переворачивает 0 и 1 для данного кубита.

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

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

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

На верхнем уровне нашей программы мы определяем основные константы, создаем QuantumCircuit и QuantumRegister, вызываем функцию split и выполняем схему.

Вся магия происходит внутри функции разделения, которая принимает три параметра:

  • кубит current — это позиция кубита в цепи,
  • offsets — это список всех родительских кубитов, равных 1,
  • controls — это список всех родительских кубитов.

Вызов функции с помощью split(QUBITS-1, [], []) позволяет нам начать с самого старшего кубита без родительских кубитов.

Итак, давайте рассмотрим функцию.

Во-первых, мы вычисляем несколько значений, которые нам понадобятся. middle обозначает положение, которое находится в центре интервала, который охватывает текущий кубит. Например, мы начинаем с третьего кубита (в позиции 2). Следовательно, middle равно 2^{2+1}/2=8/2=4. Это помогает нам решить, где разделить состояния.

offset — это сумма квадратов всех позиций в списке offsets. Поскольку в настоящее время он пуст, offset равен 0.

Теперь мы вычисляем блоки всех состояний, которые мы помещаем в нижнюю половину, и мы вычисляем те, которые мы помещаем в верхнюю половину. Как уже упоминалось, middle служит концом lower и началом upper части. Мы пока игнорируем смещение, потому что оно равно 0. Таким образом, lower содержит блоки состояний от 0 до 3, а upper включает состояния от 4 до 7.

На следующем шаге мы добавляем квантовые вентили. В этой первой итерации список controls пуст. Поэтому прыгаем прямо к воротам RY. Затем мы поворачиваем current кубит на угол, представляющий вероятность, определяемую отношением верхних блоков к общему количеству блоков.

Наконец, поскольку current больше 0, мы дважды вызываем функцию разделения. Мы уменьшаем current на единицу, потому что хотим работать со следующим младшим кубитом. В обоих вызовах мы добавляем current в список controls. Единственная разница между обоими вызовами заключается в том, что мы добавляем current в список offsets во втором вызове.

Итак, давайте посмотрим, что происходит, когда мы вызываем split во второй раз. Мы называем это как split(1, [2], [2]).

Теперь для middle установлено значение 2^{1+1}/2=2. Смещение теперь 2^2=4. Это наша отправная точка, потому что в этой итерации мы справляемся только с верхней половиной состояний (4-7). Когда мы вычисляем lower, мы начинаем считать блоки с позиции четыре и заканчиваем непосредственно перед позицией 4+2=6. Верх начинается с шестой позиции и заканчивается непосредственно перед восьмой позицией.

На этот раз у нас есть кубит в controls. Итак, прокручиваем этот список. Но так как этот элемент тоже есть в списке offsets, мы не применяем X-гейт.

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

Мы снова вызываем функции разделения, на этот раз для кубита в позиции 0. Первый вызов здесь — split(0,[2], [2,1]). Мы добавили текущий кубит (позиция 1) в элементы управления, но не в смещения.

Поэтому, когда мы рассмотрим эту итерацию, offset будет таким же, как и раньше (=4). Но поскольку позиция current теперь равна 0, middle отличается. Итак, теперь мы разделяем состояния 4 и 5.

Основное отличие теперь в том, что у нас есть одна запись в списке controls, которой нет в списке offsets. Это позиция 1. Следовательно, мы применяем вентиль X к этому кубиту до и после применения мультиуправляемого вентиля RY. Это означает, что мы хотим применить вращение только для тех случаев, когда кубит в позиции 1 равен 0.

Итак, давайте, наконец, посмотрим на результаты, когда мы запустим эту схему.

Заключение

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

Хотите начать работу с квантовым машинным обучением? Взгляните на Практическое обучение квантовому машинному обучению с помощью Python.

Получите первые три главы бесплатно здесь.