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


Учебник. Изучение квантовой запутанности с помощью Q#

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

Ниже приведены некоторые основные понятия, которые необходимо понять перед началом работы:

  • В классическом бите хранится одно двоичное значение (0 или 1), а кубит может находиться в состоянии суперпозиции двух квантовых значений (0 и 1). Каждое возможное квантовое состояние связано с амплитудой вероятности.
  • Действие измерения кубита создает двоичный результат с определенной вероятностью и изменяет состояние кубита вне суперпозиции.
  • Несколько кубитов могут быть запутаны таким образом, что их нельзя описать независимо друг от друга. Это означает, что все, что происходит с одним из кубитов в паре запутанных, также происходит и с другим кубитом.

Из этого руководства вы узнаете, как выполнять следующие задачи:

  • Создайте Q# операции для инициализации кубита в требуемое состояние.
  • поместить кубит в суперпозицию.
  • Спутайте пару кубитов.
  • Измерение кубита и наблюдение за результатами.

Совет

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

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

Чтобы запустить пример кода в Copilot для Azure Quantum, вам потребуется:

  • Учетная запись электронной почты Майкрософт (MSA).

Дополнительные сведения о Copilot см. в статье "Обзор Azure Quantum".

Инициализировать кубит в известное состояние

Первым делом мы определим операцию Q#, которая инициализирует кубит в известном состоянии. Эту операцию можно вызвать для задания кубита классическому состоянию, что означает, что при измерении он возвращает Zero 100% времени или возвращает One 100% времени. Измерение кубита возвращает Q# тип Result, который может иметь только значение Zero или One.

Откройте Copilot для Azure Quantum и скопируйте следующий код в окно редактора кода. Еще не выбирайте запуск; позже вы запустите код в ходе руководства.

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

Пример кода содержит две стандартны операции, M и X, которые преобразуют состояние кубита.

Операция SetQubitState:

  1. Принимает два параметра: тип Result с именем desired, который представляет желаемое состояние кубита (Zero или One), а также тип Qubit.
  2. Выполняет операцию измерения, M, которая измеряет состояние кубита (Zero или One) и сравнивает результат со значением, указанным в desired.
  3. Если измерение не соответствует сравниваемому значению, выполняется операция X, которая изменяет состояние кубита так, что вероятности того, что измерение вернет Zero и One, меняются местами. Таким образом, SetQubitState всегда устанавливает целевой кубит в нужное состояние.

Написать тестовую операцию для проверки состояния Белла

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

Скопируйте следующий код в окно редактора кода под операцией SetQubitState .

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}

В коде переменные count и initial задаются значениями 1000 и One соответственно. Это инициализирует первый кубит в состоянии One и измеряет каждый кубит 1000 раз.

Операция Main:

  1. Задает переменные для счетчика и начального состояния кубита.
  2. Вызывает инструкцию use для инициализации двух кубитов.
  3. Выполняет цикл в течение count итераций. Для каждого цикла он
    1. Вызывает SetQubitState, чтобы задать указанное значение initial для первого кубита.
    2. Снова вызывает SetQubitState, чтобы установить для второго кубита состояние Zero.
    3. Использует операцию M для измерения каждого кубита.
    4. Сохраняет количество измерений для каждого кубита, возвращающего значение One.
  4. После завершения цикла снова вызывается метод SetQubitState, чтобы сбросить кубиты до известного состояния (Zero) и разрешить другим пользователям выделить кубиты в известном состоянии. Сброс требуется оператором use .
  5. Наконец, используется функция Message для вывода результатов в окна вывода Copilot, прежде чем вернуть результаты.

Запустите код в Copilot для Azure Quantum

Перед переходом к процедурам по суперпозиции и запутыванию можно проверить код до этой точки, чтобы увидеть инициализацию кубитов и их измерение.

Чтобы запустить код как автономную программу, Q# компилятор в Copilot должен знать , где начать программу. Так как пространство имен не указано, компилятор распознает точку входа по умолчанию как Main операцию. Дополнительные сведения см. в разделе "Проекты" и неявные пространства имен.

К этому моменту ваша программа Q# должна выглядеть следующим образом:

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
        
    
    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

}

Скопируйте и вставьте полный пример кода в окно Copilot для Azure Quantum, установите ползунок для количества снимков на "1" и выберите Запустить. Результаты отображаются в гистограмме и в полях результатов .

Q1 - Zeros: 0
Q1 - Ones: 1000
Q2 - Zeros: 1000
Q2 - Ones: 0

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

Если изменить значение initial на Zero и снова запустить программу, вы заметите, что первый кубит возвращает Zero каждый раз.

Q1 - Zeros: 1000
Q1 - Ones: 0
Q2 - Zeros: 1000
Q2 - Ones: 0

Совет

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

Поместите кубит в состояние суперпозиции

В настоящее время кубиты в программе находятся в классическом состоянии, то есть они либо 1 или 0. Это известно, потому что программа инициализирует кубиты в известном состоянии, и вы не добавили процессы для управления ими. Прежде чем запутать кубиты, вы помещаете первый кубит в состояние суперпозиции, где при измерении кубита результат равен Zero примерно в 50% случаев и One примерно в 50% случаев. Концептуально кубит можно представить как имеющий равную вероятность измерения либо Zero, либо One.

Чтобы помещать кубиты в состояние суперпозиции, Q# предоставляет операцию H (операцию Адамара). X Вспомните операцию из процедуры инициализации кубита в известное состояние ранее, которая перевернула кубит с 0 на 1 (или наоборот); операция H переворачивает кубит на полпути, приводя его в состояние с равной вероятностью Zero и One. При измерении кубита в суперпозиции он должен возвращать примерно равное количество результатов Zero и One.

Измените код в операции Main, установив начальное значение в One и добавив строку для операции H.

for test in 1..count {
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        H(q1);                // Add the H operation after initialization and before measurement

        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2); 
        ...

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

Q1 - Zeros: 523            // results vary
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0

Каждый раз, когда вы запускаете программу, результаты первого кубита немного различаются, но близко к 50% One и 50% Zero, а результаты для второго кубита остаются Zero все время.

Q1 - Zeros: 510           
Q1 - Ones: 490
Q2 - Zeros: 1000
Q2 - Ones: 0

Инициализация первого кубита в состоянии Zero дает аналогичные результаты.

Q1 - Zeros: 504           
Q1 - Ones: 496
Q2 - Zeros: 1000
Q2 - Ones: 0

Примечание.

Переместив ползунок в Copilot для Azure Quantum и увеличив количество запусков, вы можете увидеть, как результаты суперпозиции немного варьируются в зависимости от распределения запусков.

Запутайте два кубита

Как упоминалось ранее, запутанные кубиты находятся в такой зависимости, что их невозможно описать независимо друг от друга. Проще говоря, какая операция происходит с одним кубитом, то же происходит и с запутанным кубитом. Это позволяет вам выяснить состояние одного из этих кубитов, без измерения его состояния, просто путем измерения состояния другого кубита. (В нашем примере используется всего два кубита, но вы можете запутать три кубита и даже больше.)

Для обеспечения запутанности Q# предоставляет операцию CNOT, которая означает Controlled-NOT (Контролируемое НЕ). В результате выполнения этой операции второй кубит переворачивается, если первый кубит равен One.

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

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

    operation SetQubitState(desired : Result, target : Qubit) : Unit {
        if desired != M(target) {
            X(target);
        }
    }

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = Zero;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
    
        H(q1);            
        CNOT(q1, q2);      // Add the CNOT operation after the H operation

        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

    }

Теперь при запуске программы вы увидите примерно следующее:

Q1 - Zeros: 502           // results will vary
Q1 - Ones: 498
Q2 - Zeros: 502
Q2 - Ones: 498

Обратите внимание, что статистика для первого кубита не изменилась (по-прежнему существует ~50/50 вероятность Zero измерения или One после измерения), но результаты измерения для второго кубита всегда совпадают с измерением первого кубита, независимо от того, сколько раз вы запускаете программу. Операция CNOT запутала два кубита, так что любые изменения с одним из них теперь происходят и с другим.

Требования

Чтобы разработать и запустить пример кода в локальной среде разработки:

Создайте новый файл Q#

  1. Откройте Visual Studio Code и выберите "Создать > текстовый файл ", чтобы создать новый файл.
  2. Сохраните файл как CreateBellStates.qs. Этот файл будет содержать Q# код для программы.

Инициализация кубита в известном состоянии

Первым делом мы определим операцию Q#, которая инициализирует кубит в известном состоянии. Эту операцию можно вызвать, чтобы задать кубит классическому состоянию, что означает, что она возвращает Zero 100% времени или возвращает One 100% времени. Значения Zero и One имеют тип Q# и представляют единственные возможные результаты измерения кубита.

Откройте CreateBellStates.qs и скопируйте следующий код:

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

Пример кода содержит две стандартны операции, M и X, которые преобразуют состояние кубита.

Операция SetQubitState:

  1. Принимает два параметра: тип Result, именуемый desired, который представляет требуемое состояние для кубита (Zero или One), и тип Qubit.
  2. Выполняет операцию измерения, M, которая измеряет состояние кубита (Zero или One) и сравнивает результат со значением, указанным в desired.
  3. Если результат измерения не соответствует сравниваемому значению, выполняется операция X, которая инвертирует состояние кубита так, что вероятности измерений, возвращающих Zero и One, меняются местами. Таким образом, SetQubitState всегда устанавливает целевой кубит в нужное состояние.

Написать тестовую операцию для проверки состояния Белла

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

Добавьте следующую операцию в файл CreateBellStates.qs после операции SetQubitState.

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}

В коде переменные count и initial установлены на 1000 и One соответственно. Этот шаг инициализирует первый кубит до One и измеряет каждый кубит по 1000 раз.

Операция Main:

  1. Принимает два параметра: count — количество запусков измерения, initial — требуемое состояние для инициализации кубита.
  2. Вызывает инструкцию use для инициализации двух кубитов.
  3. Выполняет цикл на протяжении count итераций. Для каждого цикла он
    1. Вызывает SetQubitState, чтобы задать указанное значение initial для первого кубита.
    2. Снова вызывает SetQubitState, чтобы установить для второго кубита состояние Zero.
    3. Использует операцию M для измерения каждого кубита.
    4. Сохраняет количество измерений для каждого кубита, возвращающего значение One.
  4. После завершения цикла снова вызывается метод SetQubitState, чтобы сбросить кубиты до известного состояния (Zero) и разрешить другим пользователям выделить кубиты в известном состоянии. Сброс кубита требуется инструкцией use .
  5. Наконец, функция Message используется для вывода сообщения в консоль перед возвратом результатов.

Выполнение кода

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

Чтобы запустить код как автономную программу, Q# компилятор должен знать , где запустить программу. Так как пространство имен не указано, компилятор распознает точку входа по умолчанию как Main операцию. Дополнительные сведения см. в разделе "Проекты" и неявные пространства имен.

  1. Файл CreateBellStates.qs до этого момента должен выглядеть следующим образом:

    import Microsoft.Quantum.Intrinsic.*;
    import Microsoft.Quantum.Canon.*;
    
    operation SetQubitState(desired : Result, target : Qubit) : Unit {
        if desired != M(target) {
            X(target);
        }
    }
    
    operation Main() : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;
        let count = 1000;
        let initial = One;
    
        // allocate the qubits
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2);           
    
            // Count the number of 'Ones' returned:
            if resultQ1 == One {
                numOnesQ1 += 1;
            }
            if resultQ2 == One {
                numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
    
    
        // Display the times that |0> is returned, and times that |1> is returned
        Message($"Q1 - Zeros: {count - numOnesQ1}");
        Message($"Q1 - Ones: {numOnesQ1}");
        Message($"Q2 - Zeros: {count - numOnesQ2}");
        Message($"Q2 - Ones: {numOnesQ2}");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
    }
    
  2. Перед запуском программы убедитесь, что целевой профиль имеет значение "Неограниченный". Выберите Палитра команд<с0 />, найдите QIR, выберите <с2 />: Установите целевой профиль QIR Azure Quantum, и выберите : неограниченный.

    Примечание.

    Если целевой профиль не задан как Неограниченный, при запуске программы возникает ошибка.

  3. Чтобы запустить программу, выберите Запустить Q# Файл в раскрывающемся списке значка воспроизведения в правом верхнем углу, выберите Запустить из списка команд перед Main операцией, или нажмите клавиши Ctrl+F5. Программа выполняет Main операцию на симуляторе по умолчанию.

  4. Выходные данные отображаются в консоли отладки.

    Q1 - Zeros: 0
    Q1 - Ones: 1000
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

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

  5. Если изменить значение initial на Zero и снова запустить программу, вы должны заметить, что первый кубит также каждый раз возвращается к Zero.

    Q1 - Zeros: 1000
    Q1 - Ones: 0
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

Совет

Нажмите Ctrl+Z или Редактировать > Отменить и сохраните файл каждый раз, когда вы вносите тестовое изменение в код перед повторным запуском.

Поместить кубит в состояние суперпозиции

В настоящее время кубиты в нашей программе находятся в классическом состоянии, то есть всегда имеют значение 1 или 0. Это известно, потому что программа инициализирует кубиты в известном состоянии, и вы не добавили процессы для управления ими. Прежде чем запутать кубиты, вы помещаете первый кубит в состояние суперпозиции, в котором измерение кубита дает результат Zero в 50% случаев и One в 50% случаев. В теории можно рассматривать состояние кубита как промежуточное между Zero и One.

Чтобы помещать кубиты в состояние суперпозиции, Q# предоставляет операцию H (операцию Адамара). X Вспомните операцию из процедуры инициализации кубита в известное состояние ранее, которая перевернула кубит из состояния Zero в состояние One (или наоборот); H операция переворачивает кубит на полпути в состояние с равной вероятностью быть в Zero или One. При измерении кубита в состоянии суперпозиции он должен возвращать примерно равное количество результатов Zero и One.

  1. Измените код в операции Main, добавив в него операцию H:

    for test in 1..count {
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            H(q1);                // Add the H operation after initialization and before measurement
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2); 
            ...
    
  2. Теперь при запуске программы вы можете наблюдать результаты применения суперпозиции к первому кубиту:

    Q1 - Zeros: 523            // results will vary
    Q1 - Ones: 477
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    
  3. Каждый раз, когда вы запускаете программу, результаты для первого кубита немного различаются, но будут близки к 50% One и 50% Zero, в то время как результаты для второго кубита остаются неизменными на всем Zero времени.

    Q1 - Zeros: 510           
    Q1 - Ones: 490
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    
  4. Инициализация первого кубита в состоянии Zero дает аналогичные результаты.

    Q1 - Zeros: 504           
    Q1 - Ones: 496
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

Запутывание двух кубитов

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

Для работы с состоянием запутанности Q# предоставляет операцию CNOT, которая расшифровывается как Контролируемое-НЕ. В результате выполнения этой операции инвертируется второй кубит, если первый кубит имеет значение One.

  1. Добавьте в программу операцию CNOT, поместив ее сразу после операции H. Теперь программа должна выглядеть так:

    import Microsoft.Quantum.Intrinsic.*;
    import Microsoft.Quantum.Canon.*;
    
        operation SetQubitState(desired : Result, target : Qubit) : Unit {
            if desired != M(target) {
                X(target);
            }
        }
    
    operation Main() : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;
        let count = 1000;
        let initial = Zero;
    
        // allocate the qubits
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            H(q1);            
            CNOT(q1, q2);      // Add the CNOT operation after the H operation
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2);           
    
            // Count the number of 'Ones' returned:
            if resultQ1 == One {
                numOnesQ1 += 1;
            }
            if resultQ2 == One {
                numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
    
    
        // Display the times that |0> is returned, and times that |1> is returned
        Message($"Q1 - Zeros: {count - numOnesQ1}");
        Message($"Q1 - Ones: {numOnesQ1}");
        Message($"Q2 - Zeros: {count - numOnesQ2}");
        Message($"Q2 - Ones: {numOnesQ2}");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
    
        }
    
    
    Q1 - Zeros: 502           
    Q1 - Ones: 498       // results will vary
    Q2 - Zeros: 502
    Q2 - Ones: 498
    Result: "(502, 498, 502, 498)"
    

Статистика первого кубита не изменилась (после измерения вероятность Zero или One составляет 50 на 50), но результаты измерения второго кубита всегда совпадают с измерением первого кубита. Операция CNOT запутала два кубита, так что все, что происходит с одним из них, происходит с другим.

График гистограммы частоты

Давайте визуализируем распределение результатов, полученных при выполнении квантовой программы несколько раз. Гистограмма частоты помогает визуализировать распределение вероятностей этих результатов.

  1. Выберите Вид —> Палитра команд или нажмите Ctrl+Shift+P, и введите «гистограмма», чтобы открыть Q#: Запустить файл и отобразить гистограмму. Вы также можете выбрать гистограмму из списка команд, описанных выше Main. Выберите этот параметр, чтобы открыть Q# окно гистограммы.

  2. Введите количество раз для выполнения программы, например, 100 итераций, и нажмите клавишу ВВОД. Гистограмма отображается в Q# окне гистограммы.

  3. Каждая полоса в гистограмме соответствует возможному результату, и его высота представляет количество наблюдаемых результатов. В этом случае существует 50 различных уникальных результатов. Обратите внимание, что для каждого результата результаты измерения для первого и второго кубита всегда одинаковы.

    Сделайте снимок экрана окна гистограммы в Visual Studio Code.

    Совет

    Вы можете увеличить гистограмму с помощью колесика прокрутки мыши или жеста трекпада. При увеличении масштаба диаграмму можно сдвигать, нажав клавиши ALT во время прокрутки.

  4. Выберите столбец, чтобы отобразить процент этого результата.

  5. Щелкните значок параметров вверху слева, чтобы отобразить параметры. Вы можете отобразить первые 10 результатов, 25 лучших результатов или все результаты. Вы также можете отсортировать результаты от высокого до низкого или от низкого до высокого.

    Сделайте снимок экрана окна гистограммы в Visual Studio Code, демонстрирующего, как отображать параметры.

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

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