Учебник. Реализация квантового генератора случайных чисел на языке Q#

Примечание

Microsoft Quantum Development Kit (классический QDK) больше не будет поддерживаться после 30 июня 2024 г. Если вы уже являетесь разработчиком QDK, мы рекомендуем перейти на новую версию Azure Quantum Development Kit (современный QDK), чтобы продолжить разработку квантовых решений. Дополнительные сведения см. в статье Перенос Q# кода в современный QDK.

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

Выполняя данное руководство, вы сделаете следующее:

  • Q# Create программы.
  • Просмотрите main компоненты Q# программы.
  • Определите логику задачи.
  • Сочетание классических и квантовых операций для решения задачи.
  • Работа с кубитами и суперпозицией для создания квантового генератора случайных чисел.

Совет

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

Предварительные требования

  • Чтобы запустить пример кода в Copilot в Azure Quantum, выполните следующие действия.

    • Учетная запись электронной почты Майкрософт (MSA).
  • Чтобы разработать и запустить пример кода в Visual Studio Code:

Определение проблемы

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

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

Кубит — это единица квантовой информации, которая может находиться в суперпозиции. При измерении кубит может принимать только два состояния: 0 или 1. Однако до измерения состояние кубита представляет вероятность получить значение 0 или 1 при измерении.

Для начала возьмите кубит в базовом состоянии, например 0. Первым шагом генератора случайных чисел является использование операции Hadamard, чтобы поместить кубит в равную суперпозицию. Измерение этого состояния приводит к нулю или единице с вероятностью 50 % каждого результата, действительно случайным битом.

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

Предположим, что вы повторяете этот процесс четыре раза, создав следующую последовательность двоичных цифр:

$${0, 1, 1, 0}$$

Если сцепить (или объединить) эти биты в битовую строку, можно получить большее число. В этом примере битовая последовательность ${0110}$ эквивалентна шести в десятичном формате.

$${0110_{\ binary} \equiv 6_{\ decimal}}$$

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

Определение логики генератора случайных чисел

Давайте рассмотрим логику генератора случайных чисел при условии, что у нас есть генератор случайных битов:

  1. Определите max как максимальное число, которое требуется создать.
  2. Определите количество случайных битов, которые требуется создать. Для этого вычисляется количество битов (nBits), необходимых для выражения целых чисел вплоть до max.
  3. Создание случайной битовой строки, длина которой равна nBits.
  4. Если битовая строка представляет число больше max, выполняется возврат к шагу 3.
  5. В противном случае процесс завершается. Возврат созданного числа в виде целого числа.

В качестве примера зададим для max значение 12. То есть 12 — это максимальное число, которое требуется использовать в качестве надежного пароля.

Для представления числа от 0 до 12 требуется ${\lfloor ln(12) / ln(2) + 1 \rfloor}$ или 4 бита. (Для краткости мы опустим вывод этого уравнения.)

Предположим, что вы создаете битовую строку $${1101_{\ binary}}$, которая эквивалентна ${13_{\ decimal}}$. Так как 13 больше 12, вы повторяете процесс.

Затем создается битовая строка ${0110_{\ binary}}$, которая эквивалентна ${6_{\ decimal}}$. Так как 6 меньше 12, процесс завершен.

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

Создание генератора случайных битов

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

operation GenerateRandomBit() : Result {
    // Allocate a qubit.
    use q = Qubit();

    // Set the qubit into superposition of 0 and 1 using the Hadamard 
    H(q);

    // At this point the qubit `q` has 50% chance of being measured in the
    // |0〉 state and 50% chance of being measured in the |1〉 state.
    // Measure the qubit value using the `M` operation, and store the
    // measurement value in the `result` variable.
    let result = M(q);

    // Reset qubit to the |0〉 state.
    // Qubits must be in the |0〉 state by the time they are released.
    Reset(q);

    // Return the result of the measurement.
    return result;
}

Теперь давайте изучим этот новый код.

  • Вы определяете операцию GenerateRandomBit, которая не принимает никаких входных данных и формирует значение типа Result. Тип Result представляет результат измерения и может иметь два возможных значения: Zero или One.
  • Вы выделяете один кубит с use помощью ключевое слово. Только что выделенный кубит всегда находится в состоянии Zero.
  • Операция используется H для размещения кубита в равной суперпозиции.
  • Операция используется для M измерения кубита, возврата измеряемого значения (Zero или One).
  • Операция используется Reset для сброса кубита в состояние |0〉.

Если мы переводим кубит в состояние суперпозиции с помощью операции H и измеряем его с помощью операции M, при каждом вызове кода возвращаются разные результаты.

Визуализация Q# кода с помощью сферы Блоха

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

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

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

  1. Для начала инициализируйте кубит в состоянии 0 и примените H, чтобы создать состояние суперпозиции с равной вероятностью получить значения 0 и 1.

    Схема, показывающая подготовку кубита в суперпозиции путем применения шлюза hadamard.
  2. Затем измерьте состояние кубита и сохраните выходное значение:

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

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

Создание полного генератора случайных чисел

  1. Сначала необходимо добавить необходимые Q# пространства имен в программу. Для полного генератора случайных чисел необходимо включить три Q# пространства имен: Microsoft.Quantum.Math, Microsoft.Quantum.Intrinsicи Microsoft.Quantum.Convert.

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    
  2. Затем определите GenerateRandomNumberInRange операцию. Эта операция многократно вызывает операцию GenerateRandomBit для создания строки битов.

        /// Generates a random number between 0 and `max`.
        operation GenerateRandomNumberInRange(max : Int) : Int {
            // Determine the number of bits needed to represent `max` and store it
            // in the `nBits` variable. Then generate `nBits` random bits which will
            // represent the generated random number.
            mutable bits = [];
            let nBits = BitSizeI(max);
            for idxBit in 1..nBits {
                set bits += [GenerateRandomBit()];
            }
            let sample = ResultArrayAsInt(bits);
    
            // Return random number if it is within the requested range.
            // Generate it again if it is outside the range.
            return sample > max ? GenerateRandomNumberInRange(max) | sample;
        }
    
    

    Рассмотрим этот новый код.

    • Необходимо вычислить количество битов, необходимых для выражения целых чисел, вплоть до max. Функция BitSizeI из Microsoft.Quantum.Math пространства имен преобразует целое число в число битов, необходимых для его представления.
    • Операция SampleRandomNumberInRange использует цикл for для создания случайных чисел до тех пор, пока не будет получено значение, которое меньше или равно max. Цикл for работает точно так же, как for цикл в других языках программирования.
    • Переменная bits является изменяемой переменной. Изменяемая переменная может изменяться во время вычисления. Для изменения значения изменяемой переменной используется директива set.
    • Функция ResultArrayAsInt поступает из Microsoft.Quantum.Convert пространства имен . Эта функция преобразует битовую строку в положительное целое число.
  3. Наконец, вы добавляете точку входа. В этом примере Main операция является точкой входа программы. Он вызывает GenerateRandomNumberInRange операцию для создания случайного числа в диапазоне от 0 до 100.

        @EntryPoint()
        operation Main() : Int {
            let max = 100;
            Message($"Sampling a random number between 0 and {max}: ");
    
            // Generate random number in the 0..max range.
            return GenerateRandomNumberInRange(max);
        }
    

    Директива let объявляет переменные, которые не меняются во время вычисления. Здесь мы определяем максимальное значение как 100.

  4. Полный код генератора случайных чисел выглядит следующим образом:

namespace QuantumRandomNumberGenerator {
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;

    @EntryPoint()
    operation Main() : Int {
        let max = 100;
        Message($"Sampling a random number between 0 and {max}: ");

        // Generate random number in the 0..max range.
        return GenerateRandomNumberInRange(max);
    }

    /// Generates a random number between 0 and `max`.
    operation GenerateRandomNumberInRange(max : Int) : Int {
        // Determine the number of bits needed to represent `max` and store it
        // in the `nBits` variable. Then generate `nBits` random bits which will
        // represent the generated random number.
        mutable bits = [];
        let nBits = BitSizeI(max);
        for idxBit in 1..nBits {
            set bits += [GenerateRandomBit()];
        }
        let sample = ResultArrayAsInt(bits);

        // Return random number if it is within the requested range.
        // Generate it again if it is outside the range.
        return sample > max ? GenerateRandomNumberInRange(max) | sample;
    }

    operation GenerateRandomBit() : Result {
        // Allocate a qubit.
        use q = Qubit();

        // Set the qubit into superposition of 0 and 1 using the Hadamard 
        H(q);

        // At this point the qubit `q` has 50% chance of being measured in the
        // |0〉 state and 50% chance of being measured in the |1〉 state.
        // Measure the qubit value using the `M` operation, and store the
        // measurement value in the `result` variable.
        let result = M(q);

        // Reset qubit to the |0〉 state.
        // Qubits must be in the |0〉 state by the time they are released.
        Reset(q);

        // Return the result of the measurement.
        return result;
    }
}

Запуск программы генератора случайных чисел

Программу можно запустить в Copilot в Azure Quantum, а также в Visual Studio Code как автономное Q# приложение или с помощью хост-программы Python.

Вы можете бесплатно протестировать Q# код с помощью Copilot в Azure Quantum. Вам потребуется только учетная запись электронной почты Майкрософт (MSA). Дополнительные сведения о Copilot в Azure Quantum см. в статье Изучение Azure Quantum.

  1. Откройте Copilot в Azure Quantum в браузере.

  2. Скопируйте и вставьте следующий код в редактор кода.

    namespace Tutorial {
        open Microsoft.Quantum.Convert;
        open Microsoft.Quantum.Intrinsic;
        open Microsoft.Quantum.Math;
    
        @EntryPoint()
        operation Main() : Int {
            let max = 100;
            Message($"Sampling a random number between 0 and {max}: ");
    
            // Generate random number in the 0..max range.
            return GenerateRandomNumberInRange(max);
        }
    
        /// # Summary
        /// Generates a random number between 0 and `max`.
        operation GenerateRandomNumberInRange(max : Int) : Int {
            // Determine the number of bits needed to represent `max` and store it
            // in the `nBits` variable. Then generate `nBits` random bits which will
            // represent the generated random number.
            mutable bits = [];
            let nBits = BitSizeI(max);
            for idxBit in 1..nBits {
                set bits += [GenerateRandomBit()];
            }
            let sample = ResultArrayAsInt(bits);
    
            // Return random number if it is within the requested range.
            // Generate it again if it is outside the range.
            return sample > max ? GenerateRandomNumberInRange(max) | sample;
        }
    
        /// # Summary
        /// Generates a random bit.
        operation GenerateRandomBit() : Result {
            // Allocate a qubit.
            use q = Qubit();
    
            // Set the qubit into superposition of 0 and 1 using the Hadamard 
            // operation `H`.
            H(q);
    
            // At this point the qubit `q` has 50% chance of being measured in the
            // |0〉 state and 50% chance of being measured in the |1〉 state.
            // Measure the qubit value using the `M` operation, and store the
            // measurement value in the `result` variable.
            let result = M(q);
    
            // Reset qubit to the |0〉 state.
            // Qubits must be in the |0〉 state by the time they are released.
            Reset(q);
    
            // Return the result of the measurement.
            return result;
    
            // Note that Qubit `q` is automatically released at the end of the block.
        }
    }
    
  3. Выберите количество снимков для выполнения и нажмите кнопку Выполнить.

  4. Результаты отображаются в гистограмме и в полях Результаты .

  5. Щелкните Объяснить код , чтобы предложить Copilot объяснить код.

Совет

В Copilot в Azure Quantum можно открыть программу в VS Code для Интернета , нажав кнопку с логотипом VS Code в правом углу редактора кода.

Примечание

Этот фрагмент кода в настоящее время не выполняется на доступном оборудовании targetsAzure Quantum, так как вызываемому объекту требуется ResultArrayAsInt QPU с полным профилем вычислений.

Дальнейшие действия

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

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