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

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

Примечание

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

Перед началом работы необходимо понять некоторые ключевые понятия.

  • В классическом бите хранится одно двоичное значение (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 и скопируйте следующий код в окно редактора кода. Пока не нажимайте кнопку Выполнить ; вы запустите код далее в этом руководстве.

   namespace Bell {
       open Microsoft.Quantum.Intrinsic;
       open 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 всегда устанавливает целевой кубит в нужное состояние.

Написание тестовой операции для проверки состояния Bell

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

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

operation TestBellState() : (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 присваиваются значения 1000 и One соответственно. Это действие инициализирует первый кубит в состоянии One и измеряет каждый кубит 1000 раз.

Операция TestBellState выполняет следующие действия.

  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

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

Чтобы запустить код как автономную программу, компилятор в Copilot должен знать, Q#с чего начать программу. Это делается в Q# файле путем добавления @EntryPoint() непосредственно перед операцией, которую вы хотите выполнить первым. Например, в этом случае является операцией TestBellState .

Примечание

@EntryPoint() требуется только для автономных программ Q#. При запуске Q# программы в Записных книжках Jupyter Notebook или вызове Q# программы из файла узла Python это не требуется и при ее включении выдаст ошибку.

@EntryPoint() Добавьте непосредственно перед TestBellState операцией, и программа Q# до этого момента должна выглядеть следующим образом:

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState() : (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

Состояние суперпозиции для кубита

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

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

Измените код в TestBellState операции, сбросив начальное значение 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 will 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. Теперь программа должна выглядеть так:

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState() : (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 после ZeroOne измерения), но результаты измерения для второго кубита всегда совпадают с измерением первого кубита, независимо от того, сколько раз вы запускаете программу. Операция CNOT добавила запутанность кубитов, и теперь любые изменения одного из них влияют и на другой.

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

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

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

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

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

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

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

   namespace Bell {
       open Microsoft.Quantum.Intrinsic;
       open 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 всегда устанавливает целевой кубит в нужное состояние.

Написание тестовой операции для проверки состояния Bell

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

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

operation TestBellState() : (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 присваиваются значения 1000 и One соответственно. Это действие инициализирует первый кубит в состоянии One и измеряет каждый кубит 1000 раз.

Операция TestBellState выполняет следующие действия.

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

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

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

Это делается в Q# файле путем добавления @EntryPoint() непосредственно перед операцией, которую требуется выполнить. Например, в этом случае является операцией TestBellState .

Примечание

@EntryPoint() требуется только для автономных программ Q#. При запуске Q# программы в Записных книжках Jupyter Notebook или вызове Q# программы из хост-файла Python это не требуется и в случае включения в нее будет выдаваться ошибка.

Теперь файл CreateBellStates.qs должен выглядеть так:

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState() : (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 );

    }
}

Перед запуском программы необходимо задать для целевого профиля значение Неограниченную. Выберите Вид —> палитра команд, найдите QIR, выберите Q#: Задайте целевой профиль Azure Quantum QIR, а затем выберите Q#: неограниченный.

Чтобы запустить программу, выберите Запустить Q# файл в раскрывающемся списке значок воспроизведения в правом верхнем углу или нажмите клавиши CTRL+F5. Программа запускает операцию или функцию, помеченную атрибутом @EntryPoint() в симуляторе по умолчанию.

Примечание

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

Выходные данные появятся в консоли отладки.

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

Совет

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

Состояние суперпозиции для кубита

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

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

Измените код в операции TestBellState, добавив в него операцию 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 will 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

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

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

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

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

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState() : (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);
        
            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

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

Построение гистограммы частоты

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

  1. Выберите Вид —> палитра команд или нажмите клавиши CTRL+SHIFT+P и введите "гистограмма", чтобы открыть Q#параметр : Запустить файл и показать гистограмму . Выберите этот параметр, чтобы открыть окно гистограммы Q# .

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

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

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

    Совет

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

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

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

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

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

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

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