Руководство. Реализация преобразования Quantum Fourier в Q#
В этом учебнике показано, как создать и имитировать базовую квантовую программу, которая выполняет операции с отдельными кубитами.
Несмотря на то, что Q# был создан в первую очередь как язык программирования высокого уровня для крупномасштабных квантовых программ, его можно также использовать для изучения квантовых операций более низкого уровня, в том числе с непосредственным обращением к конкретным кубитам. Этот учебник, в частности, рассматривает подпрограмму квантового преобразования Фурье, которая является неотъемлемой частью многих крупных квантовых алгоритмов.
В этом руководстве описано, как:
- Определение квантовых операций в Q#.
- Запись канала преобразования Quantum Fourier
- Имитация квантовой операции из выделения кубитов в выходные данные измерения.
- Узнайте, как смоделированная волновая функция квантовой системы развивается во время операции.
Примечание.
Это низкоуровневое представление обработки квантовой информации часто рассматривается в терминах квантовых цепей, которые представляют последовательное применение вентилей или операций к конкретным кубитам системы. Таким образом, последовательно применяемые одно- и многокубитовые операции можно легко представить в виде схем цепи. Например, полное преобразование квантовых кубитов, используемое в этом руководстве, имеет следующее представление в качестве канала:
Совет
Если вы хотите ускорить путешествие квантовых вычислений, ознакомьтесь с кодом с помощью Azure Quantum, уникальной функцией веб-сайта Azure Quantum. Здесь можно запустить встроенные Q# примеры или Q# собственные программы, создать новый Q# код из запросов, открыть и запустить код в VS Code для Интернета с помощью одного щелчка мыши и задать Copilot любые вопросы о квантовых вычислениях.
Необходимые компоненты
Последняя версия Visual Studio Code или откройте VS Code в Интернете.
Последняя версия расширения Azure Quantum Development Kit (QDK). Дополнительные сведения об установке см. в разделе Настройка расширения QDK.
Если вы хотите использовать 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
операцию к первым и третьим кубитам, чтобы завершить канал. Эта операция необходима, так как квантовое преобразование Fourier выводит кубиты в обратном порядке, поэтому переключения позволяют легко интегрировать подпрограмму в более крупные алгоритмы.
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()
операцию на симуляторе по умолчанию. -
Message
ВыходныеDumpMachine
данные отображаются в консоли отладки.
Если вам интересно, как влияют другие входные состояния, рекомендуется поэкспериментировать с применением других операций кубита перед преобразованием.
Добавление измерений в канал QFT
Отображение из функции DumpMachine
показало результаты операции, но, к сожалению, согласно основополагающему принципу квантовой механики, истинная квантовая система не может иметь такой функции DumpMachine
.
Вместо этого информация извлекается через измерения, которые в большинстве случаев не только не позволяют выяснить полное квантовое состояние системы, но и могут радикально изменять его.
Существует множество видов квантовых измерений, из которых мы рассмотрим здесь только самые основные: проективные измерения отдельных кубитов. При измерении в указанном базисе (например, в вычислительном базисе $ { \ket{0}, \ket{1} } $), состояние кубита проецируется на измеренное базисное состояние, уничтожая любую существующую между кубитами суперпозицию.
Изменение операции QFT
Чтобы реализовать измерения в программе Q#, используйте операцию M
, которая возвращает данные типа Result
.
Сначала измените операцию Main
так, чтобы вместо Result[]
возвращался массив результатов измерения Unit
.
operation Main() : Result[] {
Определение и инициализация массива Result[]
Перед выделением кубитов объявите и привязать массив трех элементов (по одному Result
для каждого кубита):
mutable resultArray = [Zero, size = 3];
Ключевое слово mutable
перед resultArray
позволяет позже изменять переменную в коде, например, при добавлении результатов измерения.
Выполнение измерений в цикле for
и добавление результатов в массив
После операций преобразования QFT вставьте следующий код:
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
Вызываемая для массива (например, для массива кубитов IndexRange
) функция qs
возвращает диапазон индексов массива.
Здесь он используется в цикле 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) {
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()
операцию на симуляторе по умолчанию. -
Message
ВыходныеDumpMachine
данные отображаются в консоли отладки.
Выходные данные должны выглядеть следующим образом:
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, которое приводит к случайному результату каждого измерения. Однако помимо того, что этот способ является неэффективным и несовершенным, он, тем не менее, будет воспроизводить только относительные амплитуды базисных состояний, а не относительные фазы между ними. Последний не является проблемой в этом примере, но вы увидите, что относительные фазы появятся, если дать более сложные входные данные для 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) { 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# программирования одновременно.