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


Учебник. Изучение квантовой запутанности с помощью 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 {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set 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 переменные задаются и One имеют значение 1000 соответственно. Это инициализирует первый кубит в состоянии 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 {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set 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% и 50%, One 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 {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set 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 {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set 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 переменные задаются и One имеют значение 1000 соответственно. Этот шаг инициализирует первый кубит и измеряет каждый кубит 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 {
                set numOnesQ1 += 1;
            }
            if resultQ2 == One {
                set 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. Перед запуском программы убедитесь, что целевой профиль имеет значение "Неограниченный". Выберите представление —> палитра команд, найдите QIR, выберите Q#: задайте целевой профиль QIR Azure Quantum, а затем выберите Q#: неограниченный.

    Примечание.

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

  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 Вспомните операцию от инициализации кубита к известной процедуре состояния ранее, которая перевернула кубитов из One Zero (или наоборот), 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% и 50%, One 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, которая расшифровывается как Controlled-NOT (Контролируемое НЕ). В результате выполнения этой операции второй кубит инвертируется, только если первый кубит имеет значение 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 {
                set numOnesQ1 += 1;
            }
            if resultQ2 == One {
                set 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 различных уникальных результатов. Обратите внимание, что для каждого результата результаты измерения для первого и второго кубита всегда одинаковы.

    Снимок экрана: Q# окно гистограммы в Visual Studio Code.

    Совет

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

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

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

    Снимок экрана: Q# окно гистограммы в Visual Studio Code с отображением параметров.

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

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