자습서: Q#으로 양자 얽힘 살펴보기

이 자습서에서는 큐비트를 조작 및 측정하고 중첩과 얽힘의 효과를 시연하는 Q# 프로그램을 작성하는 방법을 보여 줍니다. 특정 양자 상태에서 두 개의 큐비트를 준비하고, 를 사용하여 큐비트 Q# 에서 작동하여 상태를 변경하는 방법을 알아보고, 중첩 및 얽힘의 영향을 보여 줍니다. 큐비트 상태, 작업 및 측정값을 소개하는 프로그램을 하나씩 빌드 Q# 합니다.

참고

Microsoft Quantum Development Kit(클래식 QDK)는 2024년 6월 30일 이후에 더 이상 지원되지 않습니다. 기존 QDK 개발자인 경우 새로운 Azure Quantum Development Kit(최신 QDK) 로 전환하여 양자 솔루션을 계속 개발하는 것이 좋습니다. 자세한 내용은 최신 QDK로 코드 마이그레이션을 참조하세요Q#.

시작하기 전에 이해해야 할 몇 가지 주요 개념은 다음과 같습니다.

  • 클래식 비트는 0 또는 1 같은 단일 이진 값을 보유하는 반면, 큐비트 상태는 두 양자 상태 0과 1의 중첩에 있을 수 있습니다. 가능한 각 양자 상태에는 연결된 확률 진폭이 있습니다.
  • 큐비트를 측정하는 행위는 특정 확률로 이진 결과를 생성하고 큐비트의 상태를 중첩에서 바꿉니다.
  • 서로 독립적으로 설명할 수 없도록 여러 큐비트를 얽을 수 있습니다. 즉, 얽힌 쌍의 한 큐비트에 어떤 일이 발생하든 다른 큐비트에도 발생합니다.

이 자습서에서 학습할 방법은 다음과 같습니다.

  • 큐비트를 원하는 상태로 초기화하는 작업을 만듭니 Q# 다.
  • 큐비트를 중첩에 넣습니다.
  • 큐비트 쌍을 얽습니다.
  • 큐비트를 측정하고 결과를 관찰합니다.

양자 컴퓨팅 과정을 가속화하려면 Azure Quantum 웹 사이트의 고유한 기능인 Azure Quantum을 사용하여 코드를 검사. 여기서는 기본 제공 샘플 또는 사용자 고유 Q# 의 Q# 프로그램을 실행하고, 프롬프트에서 새 Q# 코드를 생성하고, 한 번의 클릭으로 웹용 VS Code에서 코드를 열고 실행하고, Copilot에게 양자 컴퓨팅에 대한 질문을 할 수 있습니다.

사전 요구 사항

Azure Quantum용 Copilot에서 코드 샘플을 실행하려면 다음이 필요합니다.

  • MICROSOFT(MSA) 전자 메일 계정.

Copilot에 대한 자세한 내용은 Azure Quantum 탐색을 참조하세요.

알려진 상태로 큐비트 초기화

첫 번째 단계는 큐비트를 알려진 상태로 초기화하는 Q# 연산을 정의하는 것입니다. 이는 큐비트를 클래식 상태로 설정하기 위해 호출할 수 있습니다. 즉, 측정될 때 시간의 100%를 반환 Zero 하거나 시간의 100%를 반환 One 합니다. 큐비트 측정은 또는 OneZero 만 가질 수 있는 형식Result을 반환 Q# 합니다.

Azure Quantum용 Copilot를 열고 다음 코드를 코드 편집기 창에 복사합니다. 아직 실행을 클릭하지 마세요 . 자습서의 뒷부분에서 코드를 실행합니다.

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

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

이 코드 예제는 큐비트 상태를 변환하는 두 가지 연산인 MX를 소개합니다.

SetQubitState 연산:

  1. 두 개의 매개 변수를 사용합니다. 즉Result, 큐비트가 (Zero 또는 One)에 있을 때 원하는 상태를 나타내는 라는 desired형식과 형식Qubit을 사용합니다.
  2. 큐비트(Zero 또는 One)의 상태를 측정하고 결과를 desired에 지정된 값과 비교하는 측정 연산 M을 수행합니다.
  3. 측정값이 비교된 값과 일치하지 않는 경우 X 연산을 실행합니다. 이 연산은 측정이 ZeroOne을 반환할 확률이 역전되도록 큐비트의 상태를 플리핑합니다. 이러한 방식으로 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 에서 및 변수는 각각 및 initialOne1000 설정됩니다. 그러면 첫 번째 큐비트를 One로 초기화하고 각 큐비트를 1,000번 측정합니다.

TestBellState 연산:

  1. 카운터 및 초기 큐비트 상태에 대한 변수를 설정합니다.
  2. use 문을 호출하여 두 큐비트를 초기화합니다.
  3. count 반복을 반복합니다. 각 루프에 대해
    1. SetQubitState를 호출하여 첫 번째 큐비트에 지정된 initial 값을 설정합니다.
    2. SetQubitState를 다시 호출하여 두 번째 큐비트를 Zero 상태로 설정합니다.
    3. M 연산을 사용하여 각 큐비트를 측정합니다.
    4. One을 반환하는 각 큐비트의 측정 수를 저장합니다.
  4. 루프가 완료되면 SetQubitState를 다시 호출하여 큐비를 알려진 상태(Zero)로 다시 설정합니다. 그러면 다른 사용자가 큐비트를 알려진 상태에서 할당할 수 있습니다. 이는 use 문에 필요합니다.
  5. 마지막으로 함수를 Message 사용하여 결과를 반환하기 전에 Copilot 출력 창에 결과를 출력합니다.

Azure Quantum용 Copilot에서 코드 실행

중첩 및 얽힘 프로시저로 이동하기 전에 이 시점까지 코드를 테스트하여 큐비트의 초기화 및 측정을 확인할 수 있습니다.

코드를 독립 실행형 프로그램으로 실행하려면 Copilot의 Q# 컴파일러에서 프로그램을 시작할 위치를 알아야 합니다. 이 작업은 먼저 실행하려는 작업 바로 앞에 를 추가하여 @EntryPoint() 파일에서 Q# 수행됩니다. 예를 들어 이 경우 작업은 입니다 TestBellState .

참고

@EntryPoint()는 독립 실행형 Q# 프로그램에만 필요합니다. Jupyter Notebooks에서 프로그램을 실행 Q# 하거나 Python 호스트 파일에서 프로그램을 호출 Q# 하는 경우 필요하지 않으며 포함된 경우 오류가 발생합니다.

@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 for Azure Quantum 코드 창에 붙여넣고, 샷 수에 대한 슬라이드를 "1"로 설정하고 실행을 클릭합니다. 결과는 히스토그램 및 결과 필드에 표시됩니다.

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

큐비트는 아직 조작되지 않았기 때문에 초기 값을 유지했습니다. 첫 번째 큐비트는 매번 One을 반환하고 두 번째 큐비트는 Zero을 반환합니다.

의 값을 initialZero 로 변경하고 프로그램을 다시 실행하는 경우 첫 번째 큐비트도 매번 반환 Zero 되는 것을 확인해야 합니다.

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

큐비트를 중첩에 넣기

현재 프로그램의 큐비트는 모두 클래식 상태입니다. 즉, 1 또는 0입니다. 프로그램에서 큐비트를 알려진 상태로 초기화하고 조작할 프로세스를 추가하지 않았기 때문에 이를 알 수 있습니다. 큐비트를 얽기 전에 첫 번째 큐비트를 중첩 상태로 전환합니다. 여기서 큐비트의 측정값은 시간의 ~50%와 One 시간의 ~50%를 반환 Zero 합니다. 개념적으로 큐비트는 또는 One를 측정 Zero 할 확률이 같다고 생각할 수 있습니다.

큐비트를 중첩에 배치하기 위해 Q#에서는 H 또는 Hadamard 연산을 제공합니다. X 큐비트를 0에서 1(또는 그 반대로 H 대칭 이동)한 이전에 알려진 상태 프로시저로 큐비트 초기화 작업을 기억합니다. 이 연산은 큐비트를 중간에 또는 OneZero 동일한 확률 상태로 대칭 이동합니다. 측정할 때 중첩의 큐비트는 대략 동일한 수의 ZeroOne 결과를 반환해야 합니다.

초기 값을 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

참고

Azure Quantum용 Copilot에서 슬라이더를 이동하고 샷 수를 늘리면 중첩 결과가 샷 분포에 따라 약간 달라지는 방식을 확인할 수 있습니다.

두 큐비트 얽기

앞에서 설명한 것처럼 얽힌 큐비트는 서로 독립적으로 설명할 수 없도록 연결됩니다. 즉, 하나의 큐비트에 어떤 연산이 일어나든 얽힌 큐비트에도 발생합니다. 이렇게 하면 다른 큐비트의 상태를 측정하여 측정하지 않고 한 큐비트의 결과 상태를 알 수 있습니다. 이 예제에서는 두 큐비트를 사용하지만 셋 이상의 큐비트를 얽을 수도 있습니다.

얽힘을 사용하도록 설정하기 위해 Q#에서는 CNOTControled-NOT을 나타내는 연산을 제공합니다. 두 큐비트에 대해 이 연산을 실행하면 그 결과로 첫 번째 큐비트가 One인 경우 두 번째 큐비트가 대칭 이동됩니다.

프로그램에서 H 연산 바로 다음에 CNOT 연산을 추가합니다. 전체 프로그램은 다음과 같습니다.

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# 연산을 정의하는 것입니다. 이를 호출하여 큐비트를 클래식 상태로 설정할 수 있습니다. 즉, 시간의 Zero 100%를 반환하거나 시간의 One 100%를 반환합니다. ZeroOne은 큐비트 측정에서 가능한 두 가지 결과를 나타내는 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);
           }
       }
   }

이 코드 예제는 큐비트 상태를 변환하는 두 가지 연산인 MX를 소개합니다.

SetQubitState 연산:

  1. 두 개의 매개 변수를 사용합니다. 즉 Result, desired큐비트가 (Zero 또는 One)에 있을 원하는 상태를 나타내는 라는 형식과 형식 Qubit을 사용합니다.
  2. 큐비트(Zero 또는 One)의 상태를 측정하고 결과를 desired에 지정된 값과 비교하는 측정 연산 M을 수행합니다.
  3. 측정값이 비교된 값과 일치하지 않는 경우 X 연산을 실행합니다. 이 연산은 측정이 ZeroOne을 반환할 확률이 역전되도록 큐비트의 상태를 플리핑합니다. 이러한 방식으로 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 변수는 각각 및 One1000 설정됩니다. 그러면 첫 번째 큐비트를 One로 초기화하고 각 큐비트를 1,000번 측정합니다.

TestBellState 연산:

  1. 두 매개 변수를 사용합니다. 하나는 측정을 실행할 횟수인 count이고 다른 하나는 큐비트를 초기화할 필요한 상태인 initial입니다.
  2. use 문을 호출하여 두 큐비트를 초기화합니다.
  3. count 반복을 반복합니다. 각 루프에 대해
    1. SetQubitState를 호출하여 첫 번째 큐비트에 지정된 initial 값을 설정합니다.
    2. SetQubitState를 다시 호출하여 두 번째 큐비트를 Zero 상태로 설정합니다.
    3. M 연산을 사용하여 각 큐비트를 측정합니다.
    4. One을 반환하는 각 큐비트의 측정 수를 저장합니다.
  4. 루프가 완료되면 SetQubitState를 다시 호출하여 큐비를 알려진 상태(Zero)로 다시 설정합니다. 그러면 다른 사용자가 큐비트를 알려진 상태에서 할당할 수 있습니다. 이는 use 문에 필요합니다.
  5. 마지막으로, 결과를 반환하기 전에 Message 함수를 사용하여 콘솔에 메시지를 출력합니다.

코드 실행

중첩 및 얽힘 프로시저로 이동하기 전에 이 지점까지 코드를 테스트하여 큐비트의 초기화 및 측정을 확인합니다.

이 작업은 실행하려는 작업 바로 앞에 를 추가하여 @EntryPoint() 파일에서 Q# 수행됩니다. 예를 들어 이 경우 작업은 입니다 TestBellState .

참고

@EntryPoint()는 독립 실행형 Q# 프로그램에만 필요합니다. Jupyter Notebook에서 프로그램을 실행 Q# 하거나 Python 호스트 파일에서 프로그램을 호출 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);
           }
       }

    @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을 검색하고, : Azure Quantum QIR 대상 프로필을 설정한 다음, : 무제한을 선택합니다Q#.Q#

프로그램을 실행하려면 오른쪽 위에 있는 재생 아이콘 드롭다운에서 파일 실행을 Q# 선택하거나 Ctrl+F5를 누릅니다. 프로그램은 기본 시뮬레이터에서 특성으로 @EntryPoint() 표시된 작업 또는 함수를 실행합니다.

참고

대상 프로필이 무제한으로 설정되지 않은 경우 프로그램을 실행할 때 오류가 발생합니다.

출력이 디버그 콘솔에 표시됩니다.

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

큐비트는 아직 조작되지 않았기 때문에 초기 값을 유지했습니다. 첫 번째 큐비트는 매번 One을 반환하고 두 번째 큐비트는 Zero을 반환합니다.

값을 initialZero 로 변경하고 프로그램을 다시 실행하는 경우 첫 번째 큐비트도 매번 반환 Zero 되는 것을 관찰해야 합니다.

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

코드를 다시 실행하기 전에 변경 내용을 적용할 때마다 파일을 저장해야 합니다.

큐비트를 중첩에 넣기

현재 프로그램의 큐비트는 모두 클래식 상태입니다. 즉, 1 또는 0입니다. 프로그램에서 큐비트를 알려진 상태로 초기화하고 조작할 프로세스를 추가하지 않았기 때문에 이를 알 수 있습니다. 큐비트를 얽기 전에 첫 번째 큐비트를 중첩 상태로 전환합니다. 여기서 큐비트의 측정값은 Zero(시간의 50%) 및 One(시간의 50%)을 반환합니다. 개념적으로 큐비트는 ZeroOne의 중간으로 생각할 수 있습니다.

큐비트를 중첩에 배치하기 위해 Q#에서는 H 또는 Hadamard 연산을 제공합니다. X큐비트를 이전의 알려진 상태 프로시저로 초기화하여 큐비트를 에서 ZeroOne (또는 그 반대로H) 대칭 이동한 작업을 회수합니다. 이 작업은 큐비트를 중간에 또는 의 ZeroOne동일한 확률 상태로 대칭 이동합니다. 측정할 때 중첩의 큐비트는 대략 동일한 수의 ZeroOne 결과를 반환해야 합니다.

H 연산을 포함하도록 TestBellState 연산의 코드를 수정합니다.

    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#에서는 CNOTControled-NOT을 나타내는 연산을 제공합니다. 두 큐비트에 대해 이 연산을 실행하면 그 결과로 첫 번째 큐비트가 One인 경우 두 번째 큐비트가 대칭 이동됩니다.

프로그램에서 H 연산 바로 다음에 CNOT 연산을 추가합니다. 전체 프로그램은 다음과 같습니다.

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발)을 입력하고 Enter 키를 누릅니 . 히스토그램이 히스토그램 창에 Q# 표시됩니다.

  3. 히스토그램의 각 막대는 가능한 결과에 해당하며, 그 높이는 결과가 관찰되는 횟수를 나타냅니다. 이 경우 50개의 고유한 결과가 있습니다. 각 결과에 대해 첫 번째 및 두 번째 큐비트의 측정 결과는 항상 동일합니다.

    Q# Visual Studio Code 히스토그램 창을 스크린샷합니다.

    마우스 스크롤 휠 또는 트랙 패드 제스처를 사용하여 히스토그램을 확대/축소할 수 있습니다. 확대하면 스크롤하는 동안 'Alt'를 눌러 차트를 이동하면 됩니다.

  4. 막대를 클릭하여 해당 결과의 백분율 을 표시합니다.

  5. 왼쪽 위 설정 아이콘 을 클릭하여 옵션을 표시합니다. 상위 10개 결과, 상위 25개 결과 또는 모든 결과를 표시할 수 있습니다. 결과를 높음에서 낮음 또는 낮음에서 높음으로 정렬할 수도 있습니다.

    Q# 설정을 표시하는 방법을 보여 주는 Visual Studio Code 히스토그램 창 스크린샷

다음 단계

다른 Q# 자습서 살펴보기:

  • 양자 난수 생성기는 중첩의 큐비트에서 난수를 생성하는 프로그램을 작성하는 Q# 방법을 보여 줍니다.
  • Grover의 검색 알고리즘 은 Grover의 검색 알고리즘을 Q# 사용하는 프로그램을 작성하는 방법을 보여줍니다.
  • Quantum 푸리에 변환 은 특정 큐비트를 직접 해결하는 프로그램을 작성하는 Q# 방법을 살펴봅니다.
  • Quantum Katas는 양자 컴퓨팅 및 프로그래밍 요소를 동시에 교육하기 위한 자기 주도적인 자습서 및 Q# 프로그래밍 연습입니다.