양자컴퓨팅 알고리듬

양자 알고리듬 두번째 관문 Grover Algorithm 2큐비트 |00>, |01>, |10>, |11> 예제

coding art 2026. 2. 2. 03:21
728x90

 

 

 

 

2개의 큐비트로부터 얻어지는 4개의 양자 상태 |00>, |01>, |10>, |11> 을 대상으로 그 중 하나를 찾아내기 위한 문제를 고려하자. 양자 상태의 순서는 임의로 섞어 순서가 바뀌어도 된다. 예로서 |01>을 검색하여 찾기로 하자.

 

4개의 객체를 대상으로 하는 아주 간단한 문제이지만 아직 양자 컴퓨터의 큐비트 수가 최근 100 개 단위로 증강된 상태이지만 2026년 초 현재 큐비트 3개 이상의 양자 컴퓨터는 오차 교정의 신뢰성 문제가 기술적으로 완전히 해결되지 못한 상태이기 때문에 과거 20년에 걸쳐 기본적으로 잘 연구 개발된 Transmon 2개를 사용한 예제를 검토하기로 gkau, 하며, 여기에서 기본적인 양자 게이트로 볼 수 있는 파울리 X 게이트, 아다마르 게이트 H, 제어 Z 게이트가 집중적으로 사용된다.

 

일단 인공지는 Gemini 에게 다음과 같이 Prompt를 입력하여 샘플 코드를 작성시키자.

 

Prompt: "Grover algorithm code 를 설명해주세요

 

작성된 코드는 Colab 에서 잘 작동하지만 각각의 명령 한줄마다 실행 전 과 후 큐비트 상태를 일일히 확인할 필요가 있어서 그 내용을 오른쪽 쏠림 내용으,로 채웠다. 과거 1980녀대 초 8080 one board computer 어셈블리 코드 작성 입력 만큼 까다로웠다. 독자분이 이 코드를 잘 이해하셨다면 이미 양자 컴퓨팅 프로그래밍 1/3 정도는 이해하셨다고 자부심을 가져도 될 것 같습니다.

 

 

리눅스 기반 Colab에서 qiskit, qiskit-aer, matplotlib 라이브러리 패키지를 설치 후 코드를 실행시키자. Colab 에서 코드를 작성하는 이유는 IBM 에서 제공하는 오픈소스 Qiskit  라이브러리가 자주 업데이트 되므로 Anaconda 3 의 Spyder 나 PyCharm을 사용할 경우 라입ㅡ러리 제거 및 설치를 반복해야 하는 번거로움을 피하기 위함이다.

 

!pip install qiskit

!pip install qiskit-aer matplotlib

 

단계 1: 초기화

from qiskit import QuantumCircuit, Aer, execute

qc = QuantumCircuit(2) # 1. 회로 초기화 (2 큐비트)

qc.h([0, 1]) # range(2) [0,1] -> 0번과 1번 큐비트 모두에 Hadamard 연산 실행

 

일반적으로 Transmon2개 사용하여 양자 회로를 처음 생성하면

즉 어떤 양자 게이트 명령 실행 전 모든 큐비트는 |0> 상태로 초기화된다.

 

 

 

qc.h([0, 1]) 명령처럼 큐비트 0 과 큐비트 1에 각각 텐서 곱 형태에 대해서

아다마르 연산을 실행하면 다음과 같이 중첩된 큐비트 상태가 된다.

 

# -- 단계 2: 오라클 (Oracle) -- 정답을 |01>로 만들기 위해 qc.x(1)을 사용한다.

qc.x(1)  # 1번 큐비트가 0일 때 작동하도록 반전

qc.x(1) 실행 이전 즉 아다마르 명령을 실행한 직후의 중첩 상태는 다음과 같다.

 

 

 

 

qc.x(1) 실행 이후에는 1번 큐비트 자리의 값이 0->1 또는 1->0 으로 반대로 바뀐다.

 

 

 

 

 

qc.cz(0, 1)    # Controlled-Z로 부호 반전

qc.cz(0, 1) 명령 실행 후에는 cz gate 2개의 큐비트로

구성된 Transmon에서 큐비트 모두가 1이면

큐비트 상태의 부호 반전(Phase flip)이 일어난다.

 

 

 

 

음의 부호를 가지는 양자 상태가 나타나도 확률 파동 함수의 진폭은

제곱하여 계산되므로 4개의 양자 상태는 결국 25% 확률값을 갖는다.

하지만 이 음()의 부호는 Diffusion 과정에서 중요한 역할을 한다.

 

qc.x(1)    # 다시 원상복구

다시 qc.x(1) 을 실행하게 되면

다시 1번 큐비트의 값을 반전시킨다. (0 1)

 

 

 

 

 

두 번째 |01> 의 부호가 반전된 qc.x(1) 을 다음과 같이 변형하자.

 

 

 

 

 

qc.h([0, 1])  qc.x(1)을 비교하면  4개의 양자 상태는 동일하게 각 25% 확률값을 갖는다.

qc.x(1)을 연산의 편의를 위해서 다음과 같이 조작한다.

qc.x(1)=(1/2)(|00>+|01>+|10>+|11>)-|01>=|s>-|01>

 

# --- 단계 3: 확산 연산자 (Diffusion) --- 평균에 대한 반전을 수행.

qc.h([0, 1])

qc.h([0,1]) 2개의 그룹으로 묶은 후 텐서 곱 형태들에 대해 아다마르 연산을 적용한다.

앞부분은 H의 제곱을 적용하면 연산 결과는 |s> =|00> 이 된다. |01> 은 별도 처리하자

부호가 반전된 qc.x(1)  qc.h([0,1])을 적용하면

두 번째 |01> 과 세 번째 |10> 의 부호가 반전됨을 알 수 있다.

 

qc.x([0, 1])    # 여기서도 x 게이트를 사용하여 |00> 상태를 타겟팅-> |11>

다시 qc.x([0, 1])를 적용하면

|00>  |11> 로 바뀌었다.

qc.cz(0, 1)

qc.cz(0, 1)을 적용하면 첫 번째 |11>항의 부호가 반전된다.

 

 

 

qc.x([0, 1])

qc.x([0, 1]) 명령을 적용하면 결국 qc.h([0, 1]) 의 항들에서

첫 번째 |00> 항과 세 번째 |10> 항 부호가 반전되었다.

qc.h([0, 1])

마지막으로 qc.h([0, 1]) 연산을 수행하자.

 

|00>, |10>, |11> 의 진폭은 모두 0 이 되어 사라지고 |01>1 이 된다.

 

# 마지막 단계: 측정

qc.measure_all()

# 실행 및 결과 출력

backend = Aer.get_backend('qasm_simulator')

result = execute(qc, backend, shots=1024).result()

print(result.get_counts())

 

실행 결과는 다음과 같다.

# 회로출력

 

마지막으로 회로 출력 결과가 상당히 길어 보이는데 각 명령 전후로 큐비트 상태를 모니터링 했듯이 중간에 qc.draw() 명령을 사용하면 해당 부분의 회로를 관찰할 수 있다.

 

이 양자 회로의 이해를 돕기 위해서 가로형으로 출력된 회로와 각 회로별 명령에 따른 양자 상태를 다음 블로그에서 관찰해보자.

2 Qubits Grover algorithm Circuits, States, Logic Gate Command Table

https://ejleep1.tistory.com/1695

 

grover algorithm circuit 으로 Yahoo.com 애서 이미지 검색을 해보면 다양한 회로들을 볼 수 있을 것이다. 엇 비슷하긴 하지만 각각의 명령들을 어떻게 조합하느냐에 달려 있다.

 

첨부된 파일을 다운 받은 후 Colab 에 업로드 시켜 실행시켜 보자.

grover2q.ipynb
0.01MB

 

 

추가로 |01> 만이 아니라 |00>, |10>, |11> 이 정답인 경우 코드가 어떻게 바뀌어야 하는지 살펴보자. |01>을 찾는 현재의 코드에서 CZ({0, 1}) 전 후에 X(1) logic gate 명령이 사용되었다. 만약 |11>을 찾으려면 X(1)을 전후로 제거하면 되고, |10>을 찾으려면 X(1) 전후를 X(0) 으로 대체하면 되며, |00>을 찾으려면 X(1) 전후를 X([0, 1])로 대체하면 된다.

 

Under Construction