자습서: 다음에서 Quantum Fourier 변환 구현 Q#
이 자습서에서는 개별 큐비트에서 작동하는 기본 양자 프로그램을 작성하고 시뮬레이션하는 방법을 보여 줍니다.
Q#은 주로 대규모 양자 프로그램을 위한 상위 수준의 프로그래밍 언어로 만들어졌지만, 하위 수준의 양자 프로그램을 탐색하는 데도 사용할 수 있습니다(특정 큐비트를 직접 지정). 특히, 이 자습서에서는 여러 대규모 양자 알고리즘에 필수적인 서브루틴인 QFT(양자 푸리에 변환)를 더 상세히 살펴봅니다.
이 자습서에서는 다음 방법을 알아봅니다.
- 에서 Q#양자 연산을 정의합니다.
- Quantum 푸리에 변환 회로 작성
- 큐비트 할당에서 측정 출력까지 양자 연산을 시뮬레이션합니다.
- 양자 시스템의 시뮬레이션된 파동이 작업 전체에서 어떻게 진화하는지 관찰합니다.
참고 항목
양자 정보 처리에 대한 이 하위 수준 정보 처리는 시스템의 특정 큐비트에 대한 게이트의 순차적 적용 또는 연산을 나타내는 양자 회로 측면에서 설명하는 경우가 많습니다. 따라서 순차적으로 적용되는 단일 및 다중 큐비트 연산을 회로 다이어그램으로 쉽게 나타낼 수 있습니다. 예를 들어 이 자습서에서 사용되는 전체 3큐비트 양자 푸리에 변환에는 회로로 다음과 같은 표현이 있습니다.
팁
양자 컴퓨팅 과정을 가속화하려면 Azure Quantum 웹 사이트의 고유한 기능인 Azure Quantum을 사용하여 코드를 확인하세요. 여기서는 기본 제공 Q# 샘플 또는 사용자 고유 Q# 의 프로그램을 실행하고, 프롬프트에서 새 Q# 코드를 생성하고, 한 번의 클릭으로 웹용 VS Code에서 코드를 열고 실행하고, Copilot에게 양자 컴퓨팅에 대한 질문을 할 수 있습니다.
필수 조건
최신 버전의 Visual Studio Code 또는 웹에서 VS Code를 엽니다.
최신 버전의 Azure Quantum Development Kit(QDK) 확장은. 설치 세부 정보는 QDK 확장설정을 참조하세요.
Jupyter Notebook을 사용하려면 Python해야 합니다. 이렇게 하려면 터미널을 열고 다음 명령을 실행합니다.
$ pip install --upgrade qsharp
새 Q# 파일 만들기
- VS Code에서 새 텍스트 파일 선택 >
- 파일을 QFTcircuit.qs로 저장합니다. 이 파일에는 프로그램에 대한 Q# 코드가 포함되어 있습니다.
- QFTcircuit.qs를 엽니다.
에서 QFT 회로 작성 Q#
이 자습서의 첫 번째 부분은 세 개의 큐비트에서 양자 푸리에 변환을 수행하는 연산Q#을 정의하는 Main
것으로 구성됩니다.
DumpMachine
함수를 사용하여 3큐비트 시스템의 시뮬레이트된 파형 함수가 연산 전체에서 어떻게 진화하는지 관찰합니다. 자습서의 두 번째 부분에서는 측정 기능을 추가하고 큐비트의 사전 및 사후 측정 상태를 비교합니다.
작업을 단계별로 빌드합니다. 다음 섹션의 코드를 복사하여 QFTcircuit.qs 파일에 붙여넣 습니다 .
이 섹션의 전체 Q# 코드를 참조로 볼 수 있습니다.
필요한 Q# 라이브러리 가져오기
Q# 파일 내에서 관련 Microsoft.Quantum.*
네임스페이스를 가져옵니다.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
// operations go here
인수 및 반환을 사용하여 연산 정의
다음으로 작업을 정의합니다.Main
operation Main() : Unit {
// do stuff
}
이 Main()
작업은 인수를 받지 않으며, 지금은 Python에서 C# 또는 빈 튜플Unit
로 반환하는 void
것과 유사한 개체를 반환 Tuple[()]
합니다.
나중에 측정 결과 배열을 반환하도록 작업을 수정합니다.
큐비트 할당
작업 내에서 Q# 키워드를 사용하여 세 개의 큐비트의 레지스터를 use
할당합니다. 이 use
경우 큐비트는 $\ket{0}$ 상태로 자동으로 할당됩니다.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
실제 양자 계산과 마찬가지로 Q# 큐비트 상태에 직접 액세스할 수 없습니다. 그러나 DumpMachine
작업은 target 컴퓨터의 현재 상태를 인쇄하므로 전체 상태 시뮬레이터와 함께 사용할 때 디버깅 및 학습에 대한 유용한 인사이트를 제공할 수 있습니다.
단일 큐비트 및 제어된 작업 적용
다음으로 작업 자체를 구성하는 Main
작업을 적용합니다.
Q# 이미 네임스페이스에 이러한 많은 양자 작업과 기타 기본 양자 연산이 Microsoft.Quantum.Intrinsic
포함되어 있습니다.
참고 항목
이전 코드 조각에서는 다른 네임스페이 Microsoft.Quantum.Intrinsic
스를 사용하여 가져오지 않았으므로 모든 Q# 프로그램에 대해 컴파일러에서 자동으로 로드됩니다.
적용된 첫 번째 연산은 첫 번째 큐비트에 대한 H
(Hadamard) 연산입니다.
레지스터의 특정 큐비트(예: Qubit
배열의 단일 Qubit[]
)에 연산을 적용할 때는 표준 인덱스 표기법을 사용합니다.
따라서 H
연산을 qs
레지스터의 첫 번째 큐비트에 적용할 때는 다음과 같은 형식을 사용합니다.
H(qs[0]);
H
연산을 개별 큐비트에 적용하는 것 외에도 QFT 회로는 주로 제어되는 R1
회전으로 구성됩니다.
R1(θ, <qubit>)
일반적으로 작업은 $\ket{0}$ 구성 요소에 $e^{i\theta}$의 회전을 적용하는 동안 큐비트의 $\ket{1}$ 구성 요소를 변경하지 않습니다.
Q#을 사용하면 1개 또는 여러 개의 컨트롤 큐비트에 대한 연산 실행 조건을 쉽게 지정할 수 있습니다. 일반적으로 호출 앞에 Controlled
가 오며 연산 인수는 다음과 같이 변경됩니다.
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
컨트롤 큐비트는 단일 큐비트일 경우에도 배열로 제공되어야 합니다.
QFT R1
에서 제어되는 작업은 첫 번째 큐비트에서 작동하고 두 번째 및 세 번째 큐비트로 제어되는 작업입니다.
Q# 파일에서 다음 문을 사용하여 이러한 연산을 호출합니다.
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
PI()
함수를 사용하여 pi 라디안을 기준으로 회전을 정의합니다.
SWAP 작업 적용
관련 H
작업 및 제어된 회전을 두 번째 및 세 번째 큐비트에 적용하면 회로는 다음과 같습니다.
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
마지막으로 첫 번째 및 세 번째 큐비트에 작업을 적용 SWAP
하여 회로를 완료합니다. 양자 푸리에 변환은 큐비트를 역순으로 출력하므로 스왑을 통해 서브루틴을 더 큰 알고리즘에 원활하게 통합할 수 있기 때문에 이 작업이 필요합니다.
SWAP(qs[2], qs[0]);
이제 양자 푸리에 변환의 큐비트 수준 연산을 Q# 연산에 작성하는 작업이 완료되었습니다.
큐비트 할당 취소
마지막 단계는 연산 후 상태를 확인하고 큐비트의 할당을 취소하기 위해 다시 DumpMachine()
을 호출하는 것입니다. 큐비트는 할당했을 때 $\ket{0}$ 상태였으며 ResetAll
연산을 사용하여 초기 상태로 다시 설정해야 합니다.
모든 큐비트를 $\ket$로 명시적으로 재설정하도록 요구하는 것은 다른 작업에서 동일한 큐비트{0}(부족한 리소스)를 사용하기 시작할 때 상태를 정확하게 알 수 있도록 하는 기본 기능 Q#입니다. 또한 다시 설정하면 시스템의 다른 큐비트와 얽혀 있지 않습니다.
use
할당 블록의 끝에서 리셋이 수행되지 않으면 런타임 오류가 발생할 수 있습니다.
Q# 파일에 다음 줄을 추가합니다.
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
전체 QFT 작업
Q# 프로그램이 완료되었습니다. 이제 QFTcircuit.qs 파일이 다음과 같이 표시됩니다.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
QFT 회로 실행
지금은 Main
작업이 값을 반환하지 않습니다. 이 작업은 값을 반환합니다 Unit
. 나중에 측정 결과 배열(Result[]
)을 반환하도록 작업을 수정합니다.
- 프로그램을 실행하기 전에 VS Code 아래쪽의 상태 표시줄에서 프로필이 다음과 같이 제한되지 않음targetQ#설정되어 있는지 확인합니다. 프로필을 변경 target 하려면 상태 표시줄에서 프로필을 선택하고 target 드롭다운 메뉴에서 제한 없음을 선택합니다. target 프로필이 무제한로 설정되지 않은 경우, 프로그램 실행 시 오류가 발생합니다.
- 프로그램을 실행하려면 오른쪽 위에 있는 재생 아이콘 드롭다운에서 파일 실행을 Q# 선택하거나 Ctrl+F5를 누릅니다. 프로그램은 기본 시뮬레이터에서 작업을 실행
Main()
합니다. -
Message
디버그 콘솔에 출력 및DumpMachine
출력이 표시됩니다.
다른 입력 상태가 어떻게 영향을 받는지 궁금한 경우 변환 전에 다른 큐비트 작업을 적용하는 실험을 하는 것이 좋습니다.
QFT 회로에 측정값 추가
DumpMachine
함수의 디스플레이는 연산의 결과를 보여 주었지만, 양자 역학의 초석에 따르면 실제 양자 시스템은 그러한 DumpMachine
함수를 가질 수 없습니다.
대신 측정값에서 정보가 추출됩니다. 이는 일반적으로 전체 양자 상태를 제공하지 못할 뿐만 아니라 시스템 자체를 획기적으로 바꿀 수도 있습니다.
다양한 종류의 양자 측정값이 있지만 이 예제에서는 단일 큐비트의 가장 기본적인 투사 측정값을 중점적으로 살펴보겠습니다. 특정 기저(예: 계산 기저 $ { \ket{0}, \ket{1} } $)에서 측정할 때는 기저 상태가 측정된 쪽에 큐비트 상태가 투사되므로 둘 사이의 중첩이 소멸됩니다.
QFT 작업 수정
Q# 프로그램 내에서 측정값을 구현하려면 M
형식을 반환하는 Result
연산을 사용합니다.
먼저 , 대신 측정 결과 Main
배열을 반환하도록 작업을 수정 Result[]
합니다Unit
.
operation Main() : Result[] {
배열 정의 및 초기화 Result[]
큐비트를 할당하기 전에 3개 요소 배열(각 큐비트에 대해 하나씩 Result
)을 선언하고 바인딩합니다.
mutable resultArray = [Zero, size = 3];
mutable
앞에 resultArray
키워드를 사용하면 나중에 변수를 코드에서 수정할 수 있습니다(예: 측정 결과를 추가할 때).
for
루프에서 측정을 수행하고 배열에 결과 추가
QFT 변환 작업 후에 다음 코드를 삽입합니다.
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
배열(예: 큐비트 배열 IndexRange
)에서 호출된 qs
함수는 배열의 인덱스에 대한 범위를 반환합니다.
여기서는 for
루프에서 M(qs[i])
문을 사용하여 각 큐비트를 순차적으로 측정합니다.
Result
측정된 각 형식(또는Zero
One
)은 update-and-reassign 문을 사용하여 해당 인덱스 위치에 resultArray
추가됩니다.
참고 항목
이 문의 구문은 고유 Q#하지만 F# 및 R과 같은 다른 언어에서 볼 수 있는 유사한 변수 재할당 resultArray[i] <- M(qs[i])
에 해당합니다.
키워드 set
는 항상 .를 사용하여 mutable
바인딩된 변수를 다시 할당하는 데 사용됩니다.
돌아오다 resultArray
세 개의 큐비트를 모두 측정하고 결과를 resultArray
에 추가하여 이전처럼 큐비트를 재설정하고 할당 해제하는 것이 안전합니다. 측정값을 반환하려면 다음을 삽입합니다.
return resultArray;
측정값을 사용하여 QFT 회로 실행
이제 측정 전후의 상태를 출력하도록 DumpMachine
함수의 배치를 변경해 보겠습니다.
최종 Q# 코드는 다음과 유사할 것입니다.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
팁
코드를 다시 실행하기 전에 코드를 변경할 때마다 파일을 저장해야 합니다.
- 프로그램을 실행하기 전에 VS Code 아래쪽의 상태 표시줄에서 프로필이 다음과 같이 제한되지 않음targetQ#설정되어 있는지 확인합니다. 프로필을 변경 target 하려면 상태 표시줄에서 프로필을 선택하고 target 드롭다운 메뉴에서 제한 없음을 선택합니다. target 프로필이 무제한로 설정되지 않은 경우, 프로그램 실행 시 오류가 발생합니다.
- 프로그램을 실행하려면 오른쪽 위에 있는 재생 아이콘 드롭다운에서 파일 실행을 Q# 선택하거나 Ctrl+5를 누릅니다. 프로그램은 기본 시뮬레이터에서 작업을 실행
Main()
합니다. -
Message
디버그 콘솔에 출력 및DumpMachine
출력이 표시됩니다.
출력은 다음과 유사합니다.
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
이 출력은 다음과 같은 몇 가지 다른 사항을 보여 줍니다.
- 반환된 결과를 사전 측정
비교할 때는 QFT 이후 중첩을 기본 상태에 대해 설명할 없습니다. 측정값은 시스템의 파동에서 해당 상태의 진폭에 의해 결정되는 확률과 함께 단일 기본 상태만 반환합니다. - 사후 측정값
DumpMachine
에서는 측정값이 상태 자체를 변경하여 기저 상태에 대한 초기 중첩에서 측정된 값에 해당하는 단일 기저 상태로 투사하는 것을 볼 수 있습니다.
이 작업을 여러 번 반복하면 결과 통계가 각 샷에서 임의의 결과를 발생시키는 QFT 이후 상태의 가중치 중첩을 동일하게 설명하기 시작하는 것을 볼 수 있습니다. 그러나 비효율적이고 여전히 불완전한 것 외에도, 이것은 그럼에도 불구하고 그들 사이의 상대적 단계가 아니라 기본 상태의 상대적 진폭만 재현할 것입니다. 이 예제에서는 후자가 문제가 되지 않지만 $\ket{000}$보다 QFT에 더 복잡한 입력이 제공되면 상대적인 위상이 나타날 것입니다.
작업을 Q# 사용하여 QFT 회로 간소화
소개에서 언급했듯이, '의 Q#힘의 대부분은 개별 큐비트를 다루는 걱정을 추상화 할 수 있다는 사실에 달려 있습니다.
실제로 본격적인 적용 가능한 양자 프로그램을 개발하려는 경우 특정 회전 전후에 작업이 진행되는지 여부에 H
대해 걱정하면 속도가 느려집니다. Azure Quantum은 ApplyQFT
여러 큐비트에 사용하고 적용할 수 있는 작업을 제공합니다.
첫 번째
H
작업에서 작업까지 모든 항목을SWAP
다음으로 바꿉다.ApplyQFT(qs);
이제 코드가 다음과 같이 표시됩니다.
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }
Q# 프로그램을 다시 실행하고 출력이 이전과 동일한지 확인합니다.
작업 사용 Q# 의 실제 이점을 확인하려면 큐비트 수를 다음 이외의
3
값으로 변경합니다.
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
따라서 각 큐비트에 새 H
작업 및 회전을 추가하는 것에 대해 걱정할 필요 없이 지정된 수의 큐비트에 적절한 QFT를 적용할 수 있습니다.
관련 콘텐츠
다른 Q# 자습서 살펴보기:
- 양자 난수 생성기는 중첩에서 큐비트에서 난수를 생성하는 프로그램을 작성하는 Q# 방법을 보여 줍니다.
- Grover의 검색 알고리즘 은 Grover의 검색 알고리즘을 Q# 사용하는 프로그램을 작성하는 방법을 보여줍니다.
- 양자 얽 힘은 큐비트를 조작 및 측정하고 중첩 및 얽힘의 효과를 보여 주는 프로그램을 작성하는 Q# 방법을 보여 줍니다.
- Quantum Katas는 양자 컴퓨팅 및 프로그래밍 요소를 동시에 교육하기 위한 자가 진행 자습서 및 Q# 프로그래밍 연습입니다.