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


Руководство. Реализация преобразования 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 операцию к первым и третьим кубитам, чтобы завершить канал. Это необходимо потому, что из-за специфики квантового преобразования Фурье кубиты выводятся в обратном порядке, поэтому замена позволяет легко интегрировать подпрограмму в более крупные алгоритмы.

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. 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;

}

Совет

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

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

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

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

Если вы повторите эту операцию несколько раз, то вы увидите, что статистика результатов подтверждает равновесную суперпозицию состояний после выполнения операции квантового преобразования Фурье, которая позволяет получать случайный результат при каждом измерении. Однако помимо того, что этот способ является неэффективным и несовершенным, он, тем не менее, будет воспроизводить только относительные амплитуды базисных состояний, а не относительные фазы между ними. Последнее не является проблемой в этом примере, но если предоставить для операции 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) {
            set 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# программирования одновременно.