Поделиться через


Руководство. Реализация преобразования Quantum Fourier в Q#

В этом учебнике показано, как создать и имитировать базовую квантовую программу, которая выполняет операции с отдельными кубитами.

Несмотря на то, что Q# был создан в первую очередь как язык программирования высокого уровня для крупномасштабных квантовых программ, его можно также использовать для изучения квантовых операций более низкого уровня, в том числе с непосредственным обращением к конкретным кубитам. Этот учебник, в частности, рассматривает подпрограмму квантового преобразования Фурье, которая является неотъемлемой частью многих крупных квантовых алгоритмов.

В этом руководстве описано, как:

  • Определение квантовых операций в Q#.
  • Запись канала преобразования Quantum Fourier
  • Имитация квантовой операции из выделения кубитов в выходные данные измерения.
  • Узнайте, как смоделированная волновая функция квантовой системы развивается во время операции.

Примечание.

Это низкоуровневое представление обработки квантовой информации часто рассматривается в терминах квантовых цепей, которые представляют последовательное применение вентилей или операций к конкретным кубитам системы. Таким образом, последовательно применяемые одно- и многокубитовые операции можно легко представить в виде схем цепи. Например, полное преобразование квантовых кубитов, используемое в этом руководстве, имеет следующее представление в качестве канала: Схема канала преобразования Quantum Fourier.

Совет

Если вы хотите ускорить путешествие квантовых вычислений, ознакомьтесь с кодом с помощью Azure Quantum, уникальной функцией веб-сайта Azure Quantum. Здесь можно запустить встроенные Q# примеры или Q# собственные программы, создать новый Q# код из запросов, открыть и запустить код в VS Code для Интернета с помощью одного щелчка мыши и задать Copilot любые вопросы о квантовых вычислениях.

Необходимые компоненты

Создание файла Q#

  1. В VS Code выберите файл > "Создать текстовый файл"
  2. Сохраните файл как QFTcircuit.qs. Этот файл содержит код Q# для программы.
  3. Откройте 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 (операция Адамара) к первому кубиту:

Схема, показывающая канал для трех кубитов QFT через первый Hadamard.

Для применения операции к определенному кубиту из реестра (например, к одному 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#:

Схема, показывающая канал для трех кубитового преобразования Quantum Fourier.

Отмена выделения кубитов

Последним шагом будет повторный вызов 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[]).

  1. Перед запуском программы убедитесь в строке состояния в нижней части VS Code, что target для профиля задано значение Q#: Неограничен. Чтобы изменить target профиль, выберите target профиль в строке состояния и выберите "Неограниченный " в раскрывающемся меню. Если для профиля target не задано значение Неограниченная, при запуске программы возникает ошибка.
  2. Чтобы запустить программу, выберите "Запустить Q# файл" в раскрывающемся списке значка воспроизведения в правом верхнем углу или нажмите клавиши CTRL+F5. Программа выполняет Main() операцию на симуляторе по умолчанию.
  3. 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;

}

Совет

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

  1. Перед запуском программы убедитесь в строке состояния в нижней части VS Code, что target для профиля задано значение Q#: Неограничен. Чтобы изменить target профиль, выберите target профиль в строке состояния и выберите "Неограниченный " в раскрывающемся меню. Если для профиля target не задано значение Неограниченная, при запуске программы возникает ошибка.
  2. Чтобы запустить программу, выберите "Запустить Q# файл " в раскрывающемся списке значка воспроизведения в правом верхнем углу или нажмите клавиши CTRL+5. Программа выполняет Main() операцию на симуляторе по умолчанию.
  3. 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]

В этих выходных данных показан ряд отличительных моментов.

  1. При сравнении возвращаемого результата с DumpMachineпредварительным измерением, становится очевидно, что не иллюстрирует суперпозицию после выполнения QFT над базисными состояниями. Измерение возвращает только одно базисное состояние с вероятностью, определяемой амплитудой этого состояния в волновой функции системы.
  2. В результатах измерения DumpMachine после операции вы видите, что измерение изменяет само состояние, проецируя его из начальной суперпозиции базисных состояний в одно базисное состояние, соответствующее измеренному значению.

Если повторять эту операцию много раз, вы увидите, что статистика результатов начинает иллюстрировать одинаково взвешенную суперпозицию состояния после QFT, которое приводит к случайному результату каждого измерения. Однако помимо того, что этот способ является неэффективным и несовершенным, он, тем не менее, будет воспроизводить только относительные амплитуды базисных состояний, а не относительные фазы между ними. Последний не является проблемой в этом примере, но вы увидите, что относительные фазы появятся, если дать более сложные входные данные для QFT, чем $\ket{000}$.

Использование операций Q# для упрощения канала QFT

Как упоминалось во введении, существенное преимущество Q# заключается в том, что использование этого языка позволяет избежать трудностей, связанных с работой с отдельными кубитами. Если вы хотите разрабатывать полномасштабные и действенные квантовые программы, озабоченность по поводу того, когда выполняется операция H — до или после конкретного поворота, — только замедлит выполнение задач. Azure Quantum предоставляет ApplyQFT операцию, которую можно использовать и применять для любого количества кубитов.

  1. Замените все операции от первой H операции до SWAP операции включительно:

    ApplyQFT(qs);
    
  2. Теперь код должен выглядеть следующим образом.

    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;
    
    }
    
  3. Q# Запустите программу еще раз и обратите внимание, что выходные данные совпадают с данными.

  4. Чтобы увидеть реальную выгоду использования Q# операций, измените количество кубитов на что-то другое, кроме 3:

mutable resultArray = [Zero, size = 4];

use qs = Qubit[4];
//...

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

Ознакомьтесь с другими учебниками по Q#:

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