Руководство. Реализация преобразования Quantum Fourier в Q#
В этом учебнике показано, как создать и имитировать базовую квантовую программу, которая выполняет операции с отдельными кубитами.
Несмотря на то, что Q# был создан в первую очередь как язык программирования высокого уровня для крупномасштабных квантовых программ, его можно также использовать для изучения квантовых операций более низкого уровня, в том числе с непосредственным обращением к конкретным кубитам. Этот учебник, в частности, рассматривает подпрограмму квантового преобразования Фурье, которая является неотъемлемой частью многих крупных квантовых алгоритмов.
Из этого руководства вы узнаете, как выполнять следующие задачи:
- Определение квантовых операций в Q#.
- Запись канала преобразования Quantum Fourier
- Имитация квантовой операции из выделения кубитов в выходные данные измерения.
- Узнайте, как смоделированная волновая функция квантовой системы развивается во время операции.
Примечание.
Это низкоуровневое представление обработки квантовой информации часто рассматривается в терминах квантовых цепей, которые представляют последовательное применение вентилей или операций к конкретным кубитам системы. Таким образом, последовательно применяемые одно- и многокубитовые операции можно легко представить в виде схем цепи. Например, полное преобразование квантовых кубитов, используемое в этом руководстве, имеет следующее представление в качестве канала:
Совет
Если вы хотите ускорить путешествие квантовых вычислений, ознакомьтесь с кодом с помощью Azure Quantum, уникальной функцией веб-сайта Azure Quantum. Здесь можно запустить встроенные Q# примеры или Q# собственные программы, создать новый Q# код из запросов, открыть и запустить код в VS Code для Интернета с помощью одного щелчка мыши и задать Copilot любые вопросы о квантовых вычислениях.
Необходимые компоненты
Последняя версия Visual Studio Code или откройте VS Code в Интернете.
Последняя версия расширения AzureQuantum Development Kit. Дополнительные сведения об установке см. в разделе "Установка QDK" в VS Code.
Если вы хотите использовать Jupyter Notebook, необходимо также установить расширения Python и Jupyter , а также последний
qsharp
пакет Python. Для этого откройте терминал и выполните следующую команду:$ pip install --upgrade qsharp
Создание файла Q#
- В VS Code выберите файл > "Создать текстовый файл"
- Сохраните файл как QFTcircuit.qs. Этот файл будет содержать Q# код для программы.
- Откройте QFTcircuit.qs.
Запись канала QFT в Q#
В первой части этого руководства определяется операция Q# Main
, которая выполняет квантовое преобразование Фурье с тремя кубитами. Функция DumpMachine
используется для наблюдения за развитием имитируемой волновой функции для системы из трех кубитов в ходе выполнения операции. Во второй части этого учебника вы добавите возможности измерения и сравните состояния до и после измерения кубитов.
Вы создадите эту операцию в несколько этапов. Скопируйте и вставьте код в приведенные ниже разделы в файл QFTcircuit.qs .
Полный Q# код этого раздела можно просмотреть в качестве ссылки.
Импорт обязательных Q# библиотек
Q# В файле импортируйте соответствующие Microsoft.Quantum.*
пространства имен.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
// operations go here
Определение операций с аргументами и возвращаемыми значениями
Теперь определите операцию Main
:
operation Main() : Unit {
// do stuff
}
Операция Main()
никогда не принимает аргументы, и в настоящее время возвращает Unit
объект, который аналогичен возвращению void
в C# или пустой кортеж, Tuple[()]
в Python.
Теперь измените операцию так, чтобы она возвращала массив результатов измерения.
Выделение кубитов
Q# В рамках операции выделите регистр из трех кубитов с ключевым словомuse
. При использовании ключевого слова use
кубиты автоматически выделяются в состоянии $\ket{0}$.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Как и в реальном квантовом вычислении, Q# не предоставляет прямого доступа к состояниям кубитов. Однако операция DumpMachine
печатает target текущее состояние компьютера, поэтому она может предоставить ценные сведения об отладке и обучении при использовании в сочетании с полным симулятором состояния.
Применение однокубитных и управляемых операций
Затем вы применяете операции, составляющие Main
саму операцию. Q# уже содержит многие из них и другие основные квантовые операции в Microsoft.Quantum.Intrinsic
пространстве имен.
Примечание.
Обратите внимание, что Microsoft.Quantum.Intrinsic
не был импортирован в предыдущем фрагменте кода с другими пространствами имен, так как он загружается автоматически компилятором для всех Q# программ.
Прежде всего при применим операцию H
(операция Адамара) к первому кубиту:
Для применения операции к определенному кубиту из реестра (например, к одному Qubit
из массива Qubit[]
) используется стандартная нотация индекса.
Таким образом, применение операции H
к первому кубиту из реестра qs
записывается так:
H(qs[0]);
Помимо применения операции H
к отдельным кубитам, цепь квантового преобразования Фурье состоит в основном из контролируемых вращений R1
. Операция R1(θ, <qubit>)
в целом оставляет компонент $\ket{0}$ кубита без изменений при применении смены $e^{i\theta}$ к компоненту $\ket{1}$.
Q# значительно упрощает выполнение операции на основе состояния одного или нескольких управляющих кубитов. По сути, достаточно указать перед вызовом префикс Controlled
, и аргументы операции изменяются следующим образом:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
Обратите внимание, что аргумент управляющих кубитов должен иметь формат массива, даже если он содержит всего один кубит.
Контролируемые операции в QFT — это R1
операции, которые действуют на первом кубите (и управляются вторыми и третьими кубитами):
Вызовите эти операции в файле Q# с помощью следующих инструкций:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
Функция PI()
позволяет определять вращения значениями углов в радианах.
Применение операции SWAP
После применения соответствующих H
операций и контролируемых поворотов ко второму и третьему кубитам канал выглядит следующим образом:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Наконец, вы применяете SWAP
операцию к первым и третьим кубитам, чтобы завершить канал. Это необходимо потому, что из-за специфики квантового преобразования Фурье кубиты выводятся в обратном порядке, поэтому замена позволяет легко интегрировать подпрограмму в более крупные алгоритмы.
SWAP(qs[2], qs[0]);
Итак, вы завершили создание операций на уровне кубитов для выполнения квантового преобразовании Фурье в операции Q#:
Отмена выделения кубитов
Последним шагом будет повторный вызов DumpMachine()
для проверки состояния после операции и освобождения кубитов. При выделении все кубиты находились в состоянии $\ket{0}$, и теперь их нужно вернуть в исходное состояние с помощью операции ResetAll
.
Требование явного сброса всех кубитов на $\ket{0}$ — это базовая функция Q#, так как она позволяет другим операциям точно знать свое состояние, когда они начинают использовать те же кубиты (дефицитный ресурс). Кроме того, это позволяет гарантировать отсутствие запутывания с другими кубитами в системе. Если в конце блока выделения use
не выполнен сброс, может возникнуть ошибка времени выполнения.
Добавьте в файл Q# следующие строки:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
Полная операция QFT
Программа Q# завершена. Теперь файл QFTcircuit.qs должен выглядеть следующим образом:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
Запуск канала QFT
В настоящее время Main
операция не возвращает никакое значение. Операция возвращает Unit
значение. Позже вы измените операцию, чтобы вернуть массив результатов измерения (Result[]
).
- Перед запуском программы убедитесь в строке состояния в нижней части VS Code, что target для профиля задано значение Q#: Неограничен. Чтобы изменить target профиль, выберите target профиль в строке состояния и выберите "Неограниченный " в раскрывающемся меню. target Если для профиля не задано значение "Неограниченный", при запуске программы появится ошибка.
- Чтобы запустить программу, выберите "Запустить Q# файл" в раскрывающемся списке значка воспроизведения в правом верхнем углу или нажмите клавиши CTRL+F5. Программа выполняет
Main()
операцию на симуляторе по умолчанию. DumpMachine
ВыходныеMessage
данные отображаются в консоли отладки.
Если вы хотите узнать, как оно влияет на другие входные состояния, предлагаем поэкспериментировать с операциями на уровне кубитов, прежде чем применять преобразование.
Добавление измерений в канал QFT
При выполнении функции DumpMachine
отобразились ее результаты, но принципы квантовой механики, к сожалению, запрещают использовать подобную функцию DumpMachine
в настоящей квантовой системе.
Вместо этого информация извлекается через измерения, которые в большинстве случаев не только не позволяют выяснить полное квантовое состояние системы, но и могут радикально изменять его.
Существует множество видов квантовых измерений, из которых мы рассмотрим здесь только самые основные: проективные измерения отдельных кубитов. При измерении в указанном базисе (например, в вычислительном базисе $ { \ket{0}, \ket{1} } $), состояние кубита проецируется на измеренное базисное состояние, уничтожая любую существующую между кубитами суперпозицию.
Изменение операции QFT
Чтобы реализовать измерения в программе Q#, используйте операцию M
, которая возвращает данные типа Result
.
Сначала измените операцию Main
так, чтобы вместо Unit
возвращался массив результатов измерения Result[]
.
operation Main() : Result[] {
Определение и инициализация массива Result[]
Перед выделением кубитов объявите и привязать массив трех элементов (по одному Result
для каждого кубита):
mutable resultArray = [Zero, size = 3];
Ключевое слово mutable
перед resultArray
позволяет позже изменять переменную в коде, например, при добавлении результатов измерения.
Выполнение измерений в цикле for
и добавление результатов в массив
После операций преобразования QFT вставьте следующий код:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Вызываемая для массива (например, для массива кубитов qs
) функция IndexRange
возвращает диапазон индексов массива.
Здесь функция используется в цикле for
, чтобы последовательно измерять каждый кубит с помощью инструкции M(qs[i])
.
Затем каждый измеренный тип Result
(Zero
или One
) добавляется в соответствующую позицию индекса в resultArray
с помощью инструкции обновления и повторного назначения.
Примечание.
Синтаксис этой инструкции уникален в Q#, но соответствует аналогичному повторному назначению переменной resultArray[i] <- M(qs[i])
, имеющемуся в других языках, таких как F# и R.
Ключевое слово set
всегда используется для повторного присвоения переменных, привязанных с помощью mutable
.
Возвращение resultArray
Если все три кубита измерены, а результаты добавлены в resultArray
, теперь вы можете сбросить кубиты и отменить их выделение, чтобы восстановить прежнее состояние. Чтобы вернуть измерения, вставьте:
return resultArray;
Запуск канала QFT с измерениями
Давайте изменим размещение функций DumpMachine
так, чтобы выводить состояние до и после измерений.
Итоговый код Q# должен выглядеть следующим образом:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
Совет
Не забудьте сохранить файл каждый раз, когда вы вводите изменения в код перед его повторной запуском.
- Перед запуском программы убедитесь в строке состояния в нижней части VS Code, что target для профиля задано значение Q#: Неограничен. Чтобы изменить target профиль, выберите target профиль в строке состояния и выберите "Неограниченный " в раскрывающемся меню. target Если для профиля не задано значение "Неограниченный", при запуске программы появится ошибка.
- Чтобы запустить программу, выберите "Запустить Q# файл " в раскрывающемся списке значка воспроизведения в правом верхнем углу или нажмите клавиши CTRL+5. Программа выполняет
Main()
операцию на симуляторе по умолчанию. DumpMachine
ВыходныеMessage
данные отображаются в консоли отладки.
Выходные данные должны выглядеть следующим образом:
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
В этих выходных данных показан ряд отличительных моментов.
- Если сравнить возвращенный результат с предварительным измерением
DumpMachine
, он не иллюстрирует суперпозицию после выполнения операции QFT для базисных состояний. Измерение возвращает только одно базисное состояние с вероятностью, определяемой амплитудой этого состояния в волновой функции системы. - В результатах измерения
DumpMachine
после операции вы видите, что измерение изменяет само состояние, проецируя его из начальной суперпозиции базисных состояний в одно базисное состояние, соответствующее измеренному значению.
Если вы повторите эту операцию несколько раз, то вы увидите, что статистика результатов подтверждает равновесную суперпозицию состояний после выполнения операции квантового преобразования Фурье, которая позволяет получать случайный результат при каждом измерении. Однако помимо того, что этот способ является неэффективным и несовершенным, он, тем не менее, будет воспроизводить только относительные амплитуды базисных состояний, а не относительные фазы между ними. Последнее не является проблемой в этом примере, но если предоставить для операции QFT входные данные, которые являются более сложными, чем $\ket{000}$, вы увидите, что появятся относительные фазы.
Использование операций Q# для упрощения канала QFT
Как упоминалось во введении, существенное преимущество Q# заключается в том, что использование этого языка позволяет избежать трудностей, связанных с работой с отдельными кубитами.
Если вы хотите разрабатывать полномасштабные и действенные квантовые программы, озабоченность по поводу того, когда выполняется операция H
— до или после конкретного поворота, — только замедлит выполнение задач. Azure Quantum предоставляет ApplyQFT
операцию, которую можно использовать и применять для любого количества кубитов.
Замените все операции от первой
H
операции доSWAP
операции включительно:ApplyQFT(qs);
Теперь код должен выглядеть следующим образом.
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { set resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }
Q# Запустите программу еще раз и обратите внимание, что выходные данные совпадают с данными.
Чтобы увидеть реальную выгоду использования Q# операций, измените количество кубитов на что-то другое, кроме
3
:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
Таким образом, вы можете применить правильный QFT для любого заданного количества кубитов, не беспокоясь о добавлении новых H
операций и поворотов на каждом кубите.
Связанный контент
Ознакомьтесь с другими учебниками по Q#:
- Генератор квантовых случайных чисел показывает, как написать Q# программу, которая создает случайные числа из кубитов в суперпозиции.
- Алгоритм поиска Гровера показывает, как написать Q# программу, использующую алгоритм поиска Гровера.
- Квантовое запутание показывает, как писать Q# программу, которая управляет кубитами и измеряет кубиты и демонстрирует эффекты суперпозиции и запутанности.
- Квантовые Катас — это самоуправляемые учебники и упражнения по программированию, направленные на обучение элементам квантовых вычислений и Q# программирования одновременно.