Skip to main content
IBM Quantum Platform

교실에서 Qiskit 시작하기

이 교실 내 키스킷 모듈을 사용하려면 학생에게 다음 패키지가 설치된 Python 환경이 있어야 합니다:

  • qiskit v2.1.0 이상
  • qiskit-ibm-runtime v0.40.1 이상
  • qiskit-aer v0.17.0 이상
  • qiskit.visualization
  • numpy
  • pylatexenc

위의 패키지를 설정하고 설치하려면 키스킷 설치 가이드를 참조하세요. 실제 양자 컴퓨터에서 작업을 실행하려면 학생들은 IBM Cloud® 계정 설정 가이드의 단계에 따라 IBM Quantum® 계정을 설정해야 합니다.

이 모듈은 Heron v2 프로세서에서 2초의 QPU 시간을 사용하여 테스트되었습니다. 이는 추정치일 뿐입니다. 실제 사용량은 다를 수 있습니다.

# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'

소개

교실에서의 키스킷 모듈에서는 양자 컴퓨터를 사용하여 양자 역학, 컴퓨터 과학, 화학 등 양자 컴퓨팅과 인접한 분야의 다양한 개념을 탐구할 수 있는 기회를 갖게 됩니다. 이 모듈은 다른 모듈의 전제 조건이 되며, 양자 컴퓨팅의 기초와 키스킷을 사용하여 양자 회로를 실행하는 방법을 소개합니다.

먼저 클래식 컴퓨터의 작동 원리를 간략히 살펴본 다음, 이러한 개념이 양자 컴퓨팅 패러다임에 맞게 어떻게 적용되는지 보여드리겠습니다. 마지막으로 이러한 개념을 종합하여 첫 번째 양자 회로를 구축하고 실행하는 방법을 보여드리겠습니다.


고전적 컴퓨터

기존 컴퓨터의 작동 방식에 대한 기본 사항은 이미 잘 알고 계시겠지만, 여기서는 양자 컴퓨터와 비교할 수 있도록 몇 가지 주요 기능을 강조해 보겠습니다.

정보의 기본 단위: 비트

고전적인 컴퓨터는 고전적인 정보를 처리하며, 고전적인 정보의 기본 단위는 비트입니다. 하나의 비트에는 하나의 "예/아니오" 질문에 대한 답을 저장할 수 있습니다. 일반적으로 비트의 두 가지 이진 상태는 "0"과 "1"로 표현합니다.

이진수 복습

비트를 결합하면 더 많은 정보를 저장할 수 있습니다. 예를 들어 0에서 15까지의 숫자를 저장하려면 다음과 같은 방법으로 4비트로 저장할 수 있습니다:

0 = 00004 = 01008 = 100012 = 1100
1 = 00015 = 01019 = 100113 = 1101
2 = 00106 = 011010 = 101014 = 1110
3 = 00117 = 011111 = 101115 = 1111

일반적으로 2진수인 NN 비트에서 익숙한 base-10 숫자로 변환하려면 가장 중요도가 낮은(가장 오른쪽) 비트에 20=12^0 = 1 을 곱하고, 그 다음 왼쪽 비트에 21=22^1 = 2 을 곱하고, 그 다음 비트에 22=42^2 = 4 을 곱하는 식으로 가장 중요한(가장 왼쪽) 비트에 도달할 때까지 2N12^{N-1} 을 곱하면 됩니다.

즉, NN 비트는 2N2^N 가능한 여러 상태 중 하나에 있을 수 있습니다.

이해도 점검

숫자 86을 표현하려면 몇 비트가 필요할까요? 이 숫자를 2진수로 인코딩하는 비트 문자열을 작성합니다.

  • NN 비트는 숫자 00 ~ 2N12^N - 1 를 표현할 수 있으므로 6비트를 사용하면 261=632^6 - 1 = 63 까지 표현할 수 있지만 충분하지 않다는 것을 기억하세요. 비트를 하나 더 추가하면 271=1272^7 - 1 = 127 이 됩니다. 이제 86을 2의 거듭제곱으로 나눠 보겠습니다:

    86=64+16+4+2=26×1+25×0+24×1+23×0+22×1+21×1+20×0=1010110\begin{aligned} 86 &= 64 + 16 + 4 + 2 \\ &= 2^6 \times 1 + 2^5 \times 0 + 2^4 \times 1 + 2^3 \times 0 + 2^2 \times 1 + 2^1 \times 1 + 2^0 \times 0 \\ &= 1010110 \end{aligned}

기본 연산: 게이트

이제 컴퓨터가 계산을 하려면 비트로 무언가를 수 있어야 합니다. 이진 게이트는 더 복잡한 모든 알고리즘과 코드의 기본 구성 요소를 형성하는 연산입니다.

단일 비트 게이트:

아님

비트가 하나만 있는 경우 상태를 변환할 수 있는 방법은 0에서 1로 또는 1에서 0으로 뒤집는 한 가지 방법뿐입니다. 우리는 이것을 "NOT" 게이트라고 부릅니다. 이 게이트와 아래에서 설명할 나머지 게이트의 효과는 큐비트의 입력 및 출력 상태를 나타내는 열이 있는 이른바 '진실 테이블'로 나타낼 수 있습니다. NOT 게이트의 진실 테이블은 다음과 같습니다:

입력
출력
01
10

다중 비트 게이트:

AND는 두 개의 입력 비트를 받아 하나의 비트를 출력하는 2비트 게이트입니다. 두 입력 비트가 모두 1이면 1을 출력하고 그렇지 않으면 0을 출력합니다:

입력
출력
000
010
1,000만0
111

또는

OR은 단일 출력 비트가 있는 또 다른 2비트 게이트입니다. 두 비트 중 하나라도 1이면 1을 출력합니다:

입력
출력
000
011
1,000만1
111

XOR

XOR은 "배타적 OR"의 약자로 OR 게이트와 같지만 입력 비트 중 하나만 1이면 1을 출력합니다. 둘 다 1이거나 둘 다 0이면 0을 출력합니다:

입력
출력
000
011
1,000만1
110

측정:

일반적으로 고전 컴퓨팅에 대해 배울 때 비트의 상태를 읽어내는 과정에는 많은 주의를 기울이지 않습니다. 개념적인 관점에서 보면 그리 복잡하지 않기 때문입니다. 계산 전, 계산 중 또는 계산 후에 언제든지 비트를 측정할 수 있으며 결과에 영향을 미치지 않습니다. 아래에서 설명하겠지만 양자 컴퓨팅에서는 그렇지 않습니다.

회로:

위의 게이트를 조합하면 컴퓨터에서 원하는 모든 종류의 작업을 수행할 수 있습니다. 간단한 예를 들어보겠습니다: AND 및 XOR 게이트를 사용하여 두 비트의 합을 계산하는 하프 가산 회로를 구성할 수 있습니다. 이는 논리 회로도로 표현되며, 전선은 비트를 나타내고 비트에서 작동하는 게이트는 해당 전선에 기호로 표시됩니다:

하프 애더 회로의 고전적인 회로도. XOR 게이트는 합계 출력 비트를 생성하고 AND 게이트는 캐리 출력 비트를 생성합니다.

따라서 두 비트가 복사되어 AND 게이트와 XOR 게이트를 통해 공급됩니다. XOR 게이트의 결과는 2진수의 1 자리에 남아있는 '합계 비트(S)'이며, AND 게이트의 결과는 2진수에서 다음으로 큰 자리의 값인 '캐리 비트(C)'입니다. 다음은 진실 표입니다:

AA
BB
합계 ( ABA \oplus B )
캐리 ( ABA \wedge B )
0000
0110
1010
1101

이해도 점검

위의 진리표가 가산기 회로에 대한 올바른 해를 산출하는지 확인합니다. 즉, A와 B의 네 가지 옵션 각각에 대해 A+B=S+2×CA+B=S+2 \times C.

  • 0+0=0+0=0 0+1=1+0=1 1+0=1+0=1 1+1=0+2=2 \begin{aligned} 0+0 &= 0+0 = 0 ~\checkmark \\ 0+1 &= 1+0 = 1 ~\checkmark \\ 1+0 &= 1+0 = 1 ~\checkmark \\ 1+1 &= 0+2 = 2 ~\checkmark \\ \end{aligned}

양자 컴퓨터

비트 \rightarrow 큐비트

비트가 기존 정보의 기본 단위인 것처럼 양자 비트, 즉 '큐비트'는 양자 정보의 기본 단위입니다. 클래식 비트와 마찬가지로 큐비트의 상태는 0 또는 1이 될 수 있으며, 일반적으로 0\vert 0\rangle1\vert 1\rangle 로 표시됩니다. 그러나 클래식 비트와 달리 양자 비트는 0\vert 0\rangle 상태와 1\vert 1\rangle 상태가 동시에 중첩되어 있을 수도 있습니다. 일반적으로 큐비트는 ψ\vert \psi\rangle 형식의 모든 상태일 수 있습니다:

ψ=c00+c11\vert \psi\rangle = c_0 \vert 0\rangle + c_1 \vert 1\rangle

여기서 c0c_0c1c_1c02+c12=1\vert c_0 \vert ^2+\vert c_1\vert ^2=1 과의 복잡한 진폭입니다.

양자 위상

c0c_0c1c_1 는 복소수이므로, 각각 cj=cjeiϕjc_j = \vert c_j\vert e^{i\phi_j} 로 표기할 수 있으며, 이때 ϕj\phi_j위상 이라고 합니다. 전체 상태에 동일한 전체 위상 계수를 곱하더라도 물리적으로는 아무런 변화가 일어나지 않는다. 이를 ‘전역 위상’이라고 하며, 관측 가능한 결과는 나타나지 않는다.

따라서 일반적으로 eiϕ0e^{i\phi_0}, 기부하는 것이 일반적입니다:

ψ=c00+c1eiϕ1\vert \psi\rangle = \vert c_0\vert \vert 0\rangle + \vert c_1\vert e^{i\phi}\vert 1\rangle

여기서 ϕ=ϕ1ϕ0\phi = \phi_1-\phi_0 은 양자 상태의 상대적 위상이며 , 관찰 가능한 결과를 가져옵니다.

이 단계는 양자 컴퓨팅에서 매우 중요한 역할을 하며, 이후의 키스킷 인 더 클래스룸 모듈에서 다양한 결과를 살펴볼 것입니다.

다중 큐비트

다중 비트의 상태는 0s 와 1s 의 문자열로 간단히 표현할 수 있지만, 다중 큐비트의 상태는 중첩과 얽힘의 원리 때문에 조금 더 복잡해집니다.

NN 비트는 이진수 000...000에서 111...111에 이르는 2N2^N 가능한 상태 중 하나에 있을 수 있다는 것을 기억하세요. 하지만 이제 중첩의 원리로 인해 NN 큐비트는모든 상태가 한꺼번에 중첩될 수 있습니다!

이는 다음과 같이 표현할 수 있습니다

ψN=j=02N1cjj\psi_N = \sum_{j=0}^{2^N-1} c_j \vert j\rangle

여기서, 고전적인 경우와 마찬가지로, 상태 j\vert j\rangle 는 각 큐비트가 이진수 jj 를 산출하는 데 필요한 0s 와 1s 의 올바른 조합을 이루는 상태에 해당한다. 이를 양자 시스템의 “계산 기저 상태”라고 한다. 예를 들어, 3큐비트 상태는 8개의 계산 기저 상태의 중첩으로 표현될 수 있습니다:

ψ3=c0000+c1001+c2010+c3011+c4100+c5101+c6110+c7111\psi_3 = c_0 \vert 000\rangle + c_1 \vert 001\rangle + c_2 \vert 010\rangle + c_3 \vert 011\rangle + c_4 \vert 100\rangle + c_5 \vert 101\rangle + c_6 \vert 110\rangle + c_7 \vert 111\rangle

시스템의 각 큐비트는 00 ~ N1N-1 의 인덱스로 표시됩니다. 큐비트 상태를 오른쪽에서 왼쪽으로 읽는 것이 관례이므로 큐비트 00 의 상태가 가장 오른쪽 상태이고 큐비트 N1N-1 의 상태가 가장 왼쪽입니다. 이를 "리틀 엔디안" 표기법이라고 하는데, 왼쪽에서 오른쪽으로 읽는 데 익숙해져 있기 때문에 처음에는 직관적이지 않게 보일 수 있습니다.

이해도 점검

언뜻 보기에는 리틀 엔디안 표기법처럼 큐비트를 오른쪽에서 왼쪽으로 정렬하는 것이 반직관적으로 보일 수 있지만, 실제로는 매우 논리적인 방법입니다! 이유를 설명하세요. (위의 이진수를 base-10 숫자로 변환하는 방법에 대한 설명을 기억하세요.)

  • 큐비트를 오른쪽에서 왼쪽으로 정렬하여 큐비트 0이 가장 먼 오른쪽에 있고 큐비트 N-1 가 가장 먼 왼쪽에 있도록 한다면, 최하위 비트인 큐비트 00202^0 을 곱한 값과 최상위 비트인 큐비트 N1N-12N12^{N-1} 을 곱한 값을 연관시키는 것이 논리적으로 맞습니다.

얽힘

앞서 언급했듯이 큐비트의 또 다른 주요 특징은 큐비트가 서로 얽힐 수 있다는 것입니다. c0=c3=12c_0 = c_3 = \frac{1}{\sqrt{2}}c1=c2=0c_1 = c_2 = 0 이 있는 2큐비트 상태를 예로 들어 보겠습니다:

ψ=12(00+11)\vert \psi\rangle = \frac{1}{\sqrt{2}}(\vert 00\rangle + \vert 11\rangle)

따라서 큐비트 0의 상태는 동일한 확률로 0\vert 0\rangle 또는 1\vert 1\rangle 중 하나가 될 수 있으며, 큐비트 1의 상태도 마찬가지입니다. 하지만 이러한 확률은 더 이상 서로 독립적이지 않습니다. 큐비트 0의 상태가 0\vert 0\rangle 에 있다면, 큐비트 1도 0\vert 0\rangle 에 있을 것이라는 것을 수 있습니다. 이는 서로 아무리 멀리 떨어져 있어도 마찬가지이며, 이 때문에 얽힌 상태를 측정하는 행위를 "원거리에서의 스푸키 액션"이라고 부르기도 합니다

얽힘은 다른 형태로도 나타날 수 있습니다. 예를 들어, 상태

ψ=12(01+10)\vert \psi\rangle = \frac{1}{\sqrt{2}}(\vert 01\rangle + \vert 10\rangle)

는 매번 정반대의 결과를 생성합니다. 한 큐비트가 0\vert 0\rangle 로 측정되면 다른 큐비트는 1\vert 1\rangle 상태에서 발견될 수 있습니다.

이해도 점검

ψ=11\vert \psi\rangle = \vert 11\rangle 상태가 얽혀 있나요? 그 이유는?

  • 얽혀 있지 않습니다. 두 큐비트를 모두 측정할 때 결과는 항상 동일하지만, 이는 각 큐비트가 항상 1\vert 1\rangle 상태로 고정되어 있기 때문입니다. 한 큐비트를 측정한 결과는 실제로 다른 큐비트에 의존하지 않으며, 둘 다 항상 1\vert 1\rangle.

    일반적으로 각 큐비트의 상태를 개별적으로 설명한 다음 이렇게 함께 곱하면 됩니다:

    ψ=ψ1ψ0\vert \psi\rangle = \vert \psi_1\rangle \vert \psi_0\rangle

    이를 '제품 상태'라고 하며 얽히지 않습니다.

벡터 표기법

벡터와 행렬을 사용하면 다양한 연산에서 양자 상태가 어떻게 변환되는지 확인하는 데 도움이 되는 경우가 많습니다. 이 표현에서 양자 상태는 벡터가 되고, 양자 게이트(다음 섹션에서 설명)는 벡터를 변환하는 행렬이 됩니다.

단일 큐비트의 경우 상태의 벡터 형태가 선택됩니다: 0=(10)\vert 0\rangle = \begin{pmatrix}1 \\ 0\end{pmatrix} 1=(01)\vert 1\rangle = \begin{pmatrix}0 \\ 1\end{pmatrix} 이런 식으로 임의의 상태 ψ=a0+b1\vert \psi\rangle = a\vert 0\rangle+b\vert 1\rangle 는 다음과 같이 작성할 수 있습니다 ψ=(ab)\vert \psi\rangle =\begin{pmatrix}a \\ b\end{pmatrix}

일반적인 nn -쿼비트 상태의 경우, 2n2^n -차원 벡터가 필요하며, 기저 상태는 예상대로 오름차순 이진 값으로 정렬됩니다:

0000=(1000),0001=1110=(0010),1111=(0001)\vert 0 \dots 000\rangle = \begin{pmatrix}1 \\ 0 \\ 0 \\ \vdots \\ 0\end{pmatrix}, \vert 0 \dots 001 \rangle = \vert 1 \dots 110\rangle = \begin{pmatrix}0 \\ \vdots \\ 0 \\ 1 \\ 0\end{pmatrix}, \vert 1 \dots 111 \rangle = \begin{pmatrix}0 \\ \vdots \\ 0 \\ 0\\ 1\end{pmatrix}

이러한 벡터 표기법을 염두에 두고 필요한 양자 게이트와 양자 상태에 미치는 영향, 행렬 형태를 소개할 수 있습니다.

이해도 점검

2큐비트 시스템에는 네 가지 계산 기준 상태가 있습니다. 각각을 케트 표기법과 벡터 표기법으로 작성합니다.

  • 00=(1000),01=(0100),,10=(0010),11=(0001)\vert 00\rangle = \begin{pmatrix}1 \\ 0 \\ 0 \\ 0\end{pmatrix}, \vert 01 \rangle = \begin{pmatrix}0 \\ 1 \\ 0 \\ 0\end{pmatrix}, \dots, \vert 10\rangle = \begin{pmatrix}0 \\ 0 \\ 1 \\ 0\end{pmatrix}, \vert 11 \rangle = \begin{pmatrix}0 \\ 0 \\ 0\\ 1\end{pmatrix}

게이트 \rightarrow 양자 게이트

NOT, AND, OR, XOR과 같은 고전적인 게이트를 결합하여 임의의 고전적인 회로를 구축할 수 있는 것처럼, 양자 게이트는 양자 컴퓨팅에서도 동일한 역할을 합니다. 큐비트에는 추가적인 양자 역학적 특징이 있기 때문에 양자 게이트는 그에 따라 더 풍부해집니다. 여전히 0|0\rangle1|1\rangle 에서 진위 표를 통해 이들의 행동을 설명할 수 있지만, 이는 전체 상황을 파악할 수 있는 것은 아닙니다. 양자 게이트의 경우, 기저 상태의 중첩에도 작용하기 때문에 행렬 표현을 사용하는 것이 더 자연스러운 경우가 많습니다.

아래에서는 가장 일반적인 양자 게이트와 양자 게이트가 상호 작용하는 큐비트를 변환하는 방법을 소개합니다. 해당되는 경우 익숙한 클래식 게이트로 다시 연결합니다.

단일 큐비트 게이트

XX 게이트 : NOT 연산에 해당하는 양자 연산입니다. 진리 테이블은 고전적인 NOT 게이트와 똑같이 생겼습니다:

입력
출력
0\vert 0\rangle1\vert 1\rangle
1\vert 1\rangle0\vert 0\rangle

그리고 매트릭스 표현:

X=(0110)X=\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}

키스킷에서 XX 게이트가 있는 회로를 만드는 것은 다음과 같습니다:

from qiskit import QuantumCircuit

qc = QuantumCircuit(1)
qc.x(0)
qc.draw("mpl")

Output:

Output of the previous code cell

이 매우 간단한 회로도에서 큐비트는 검은색 수평선인 와이어로 표시되며, 게이트는 해당 와이어에 상자로 표시됩니다.

하다마드 게이트: 중첩 상태를 생성합니다. 진실 테이블:

입력
출력
0\vert 0\rangle12(0+1)\frac{1}{\sqrt{2}}\left(\vert 0\rangle+\vert 1\rangle\right)
1\vert 1\rangle12(01)\frac{1}{\sqrt{2}}\left(\vert 0\rangle-\vert 1\rangle\right)

매트릭스 표현: H=12(1111)H=\frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}

하다마드 게이트가 있는 회로는 다음과 같이 만들어집니다:

from qiskit import QuantumCircuit

qc = QuantumCircuit(1)
qc.h(0)
qc.draw("mpl")

Output:

Output of the previous code cell

ZZ 게이트 : 1|1\rangle 상태에 Δϕ=π\Delta \phi = \pi 위상 전환을 추가합니다:

입력
출력
0\vert 0\rangle0\vert 0\rangle
1\vert 1\rangle1-\vert 1\rangle

Z=(1001)Z=\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}

키스킷에서 ZZ 게이트가 있는 회로를 만드는 것은 다음과 같습니다:

qc = QuantumCircuit(1)
qc.z(0)
qc.draw("mpl")

Output:

Output of the previous code cell

TT 게이트 : 1|1\rangle 상태에 Δϕ=π/4\Delta \phi = \pi/4 위상 전환을 추가합니다:

입력
출력
0\vert 0\rangle0\vert 0\rangle
1\vert 1\rangleeiπ/41e^{i\pi/4}\vert 1\rangle

T=(100eiπ/4)T=\begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix}

키스킷에서 TT 게이트가 있는 회로를 만드는 것은 다음과 같습니다:

qc = QuantumCircuit(1)
qc.t(0)
qc.draw("mpl")

Output:

Output of the previous code cell

다중 큐비트 게이트

2큐비트 게이트는 기존 2비트 게이트와 비슷할 수 있지만, 한 가지 중요한 주의 사항이 있습니다. 모든 양자 게이트는 가역적이어야 한다는 것입니다. 선형 대수학 용어로 이는 일차 행렬로 표현된다는 의미입니다. 따라서 두 개의 입력 큐비트는 항상 두 개의 출력 큐비트에 매핑되며, 원칙적으로 연산을 취소할 수 있습니다. 이는 정보를 잃고 되돌릴 수 없는, 즉 출력이 주어지면 입력을 고유하게 결정할 수 없는 AND나 OR과 같은 고전적인 게이트와 대조적입니다.

CNOT(제어형 NOT) 게이트: 두 개의 입력 큐비트를 각각 “제어” 큐비트와 “대상” 큐비트라고 합니다. 제어 큐비트는 변하지 않지만, 그 상태에 따라 대상 큐비트에 어떤 일이 일어나는지가 결정됩니다. 제어 큐비트가 1\vert 1\rangle 상태에 있다면, 해당 대상에 XX 게이트가 적용되며, 제어 큐비트의 상태가 0\vert 0\rangle 라면 아무런 변화도 일어나지 않는다. 아래 표기법에서, 큐비트 AA (가장 오른쪽 큐비트)를 제어 큐비트로, 큐비트 BB (가장 왼쪽 큐비트)를 대상 큐비트로 가정한다. 이하에서는 CNOT(qcontrol,qtarget)BACNOT(q_{control},q_{target})\vert BA\rangle 표기법을 사용합니다.

CNOT(A,B)BAinput=BAoutputCNOT(A,B)\vert BA\rangle_{input} = \vert BA\rangle_{output}

입력
출력
00\vert 00\rangle00\vert 00\rangle
01\vert 01\rangle11\vert 11\rangle
10\vert 10\rangle10\vert 10\rangle
11\vert 11\rangle01\vert 01\rangle

따라서 이 작업을 나타내는 행렬은 다음과 같습니다:

CNOT=(1000000100100100)CNOT=\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix}

qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw("mpl")

Output:

Output of the previous code cell

이것은 두 개의 큐비트가 있는 첫 번째 회로도로, 두 개의 와이어로 표시됩니다. CNOT 게이트는 두 큐비트 사이에 구현되며, q0q_0 을 컨트롤로, q1q_1 을 타겟으로 합니다.

이해도 점검

대부분의 게이트는 Qiskit에서도 다른 곳과 마찬가지로 동일한 행렬 형태를 띱니다. 하지만 CNOT 게이트는 두 개의 큐비트에 작용하기 때문에, 갑자기 큐비트의 순서 지정 규칙이 문제가 됩니다. 큐비트를 순서대로 나열한 문헌 q0,q1,...\vert q_0,q_1,...\rangle 에서는 CNOT 게이트에 대해 서로 다른 행렬 형태를 제시하고 있다. 명시적인 행렬 곱셈을 통해 위의 CNOT 행렬이 상태 01\vert 01\rangle 에 대해 올바른 작용을 가지는지 확인하십시오.

  • CNOT01=(1000000100100100)(0100)=(0001)=11CNOT\vert 01\rangle =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix}\begin{pmatrix}0 \\ 1 \\ 0 \\0\end{pmatrix} = \begin{pmatrix}0 \\ 0 \\ 0 \\1\end{pmatrix} = \vert 11\rangle

스왑 게이트: 이 게이트는 두 큐비트의 상태를 교환합니다. 진실 테이블:

입력
출력
00\vert 00\rangle00\vert 00\rangle
01\vert 01\rangle10\vert 10\rangle
10\vert 10\rangle01\vert 01\rangle
11\vert 11\rangle11\vert 11\rangle

따라서 이 작업을 나타내는 행렬은 다음과 같습니다:

SWAP=(1000001001000001)SWAP=\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1\end{pmatrix}

qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.draw("mpl")

Output:

Output of the previous code cell

스왑 게이트는 실제로 세 개의 CNOT로 구성할 수 있습니다. 방법을 확인하려면 decompose() 에서 키스킷으로 게이트에 접속하세요:

qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")

Output:

Output of the previous code cell

여기서는 회로도에 여러 개의 게이트가 표시되는 방식을 처음으로 살펴봅니다. 왼쪽에서 오른쪽으로 읽으므로 가장 왼쪽의 게이트가 먼저 적용됩니다.

이해도 점검

위의 CNOT 조합이 스왑 게이트가 되는지 확인합니다. 행렬 곱셈이나 다른 방법을 사용하면 됩니다.

  • 행렬 곱셈을 사용합니다:

    (1000000100100100)(1000010000010010)(1000000100100100)=(1000001001000001)=SWAP \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix} \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0\end{pmatrix} \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0\end{pmatrix} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1\end{pmatrix} = SWAP ~\checkmark

    진리 테이블을 사용하여 각 CNOT에 따라 상태가 어떻게 변하는지 확인합니다. 마지막 열에서 상태는 SWAP 진실성 테이블의 '출력' 열과 동일해야 합니다:

    입력
    CNOT(A,B)
    CNOT(B,A)
    CNOT(A,B)
    00\vert 00\rangle00\vert 00\rangle00\vert 00\rangle00\vert 00\rangle \checkmark
    01\vert 01\rangle11\vert 11\rangle10\vert 10\rangle10\vert 10\rangle \checkmark
    10\vert 10\rangle10\vert 10\rangle11\vert 11\rangle01\vert 01\rangle \checkmark
    11\vert 11\rangle01\vert 01\rangle01\vert 01\rangle11\vert 11\rangle \checkmark

토폴리 게이트(또는 "제어-제어-NOT"(CCNOT)): 3큐비트 게이트입니다. 제어 큐비트 2개와 대상 큐비트 1개가 있고, 두 제어 큐비트가 모두 1\vert 1\rangle 상태에 있을 때만 대상 큐비트의 상태가 반전됩니다. CNOT에서 사용했던 순서 규칙을 그대로 유지합니다:

CCNOT(ControlA,ControlB,TargetC)CBACCNOT(Control A, Control B, Target C)\vert CBA\rangle

그래서 진실 테이블은 다음과 같습니다:

입력
출력
000\vert 000\rangle000\vert 000\rangle
001\vert 001\rangle001\vert 001\rangle
010\vert 010\rangle010\vert 010\rangle
011\vert 011\rangle111\vert 111\rangle
100\vert 100\rangle100\vert 100\rangle
101\vert 101\rangle101\vert 101\rangle
110\vert 110\rangle110\vert 110\rangle
111\vert 111\rangle011\vert 011\rangle

그리고 이 작업을 나타내는 행렬은 다음과 같습니다:

CCNOT=(1000000001000000001000000000000100001000000001000000001000010000)CCNOT=\begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\end{pmatrix}
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw("mpl")

Output:

Output of the previous code cell

토폴리 게이트 역시 다른 게이트와 함께 CNOT로 분해될 수 있습니다. 그러나 이는 스왑 게이트 분해보다 훨씬 더 복잡하므로 이 분해를 탐색하고 검증하는 것은 모듈 마지막에 선택 사항으로 남겨둘 것입니다.


수치

양자 컴퓨팅에서는 측정이 특별한 역할을 하는데, 이는 기존 컴퓨팅에는 아날로그가 없기 때문입니다. 클래식 컴퓨팅에서는 알고리즘을 실행하는 동안 원하는 시점에 비트를 확인할 수 있지만, 양자 컴퓨팅에서는 측정이 큐비트의 상태를 붕괴시키고 큐비트에 계산 복잡성을 부여하는 중첩을 파괴하기 때문에 큐비트를 확인할 시점을 매우 신중하게 선택해야 합니다.

특히 NN -비트 양자 상태 ψ=i=02N1cii\vert \psi\rangle = \sum_{i=0}^{2^N-1} c_i \vert i\rangle 가 주어졌을 때, 측정은 ci2\vert c_i\vert ^2 와 같은 확률로 기저 함수 i\vert i\rangle 중 하나로 상태를 붕괴시킵니다.

하지만 측정의 이러한 파괴적인 효과가 항상 방해가 되는 것은 아닙니다. 실제로 양자 텔레포트와 양자 키 분배와 같은 특정 알고리즘과 프로토콜의 핵심 리소스입니다.

키스킷에서는 측정이 이루어지면 클래식 레지스터로 전송되어 클래식 비트로 저장됩니다. 측정값으로 회로를 만드는 것은 다음과 같습니다:

qc = QuantumCircuit(
    1, 1
)  # the second number is the number of classical bits in the circuit
qc.measure(0, 0)
qc.draw("mpl")

Output:

Output of the previous code cell

회로

큐비트, 게이트, 측정이 어떻게 작동하는지 알았으니 이제 직접 양자 회로를 만들어 실행해 보겠습니다! 이를 위해 키스킷 패턴이라는 유용한 워크플로우를 소개해드리겠습니다.

Qiskit 패턴 프레임워크

키스킷 패턴 프레임워크는 양자 컴퓨터로 문제에 접근하고 해결하기 위한 일반적인 절차입니다. 4단계로 구성되어 있습니다:

  1. 양자 회로와 연산자에 문제 매핑하기
  2. 대상 하드웨어에 맞게 회로 최적화
  3. 대상 하드웨어에서 실행
  4. 결과 후 처리

이러한 단계를 설명하기 위해 위에서 설명한 하프 가산 회로의 양자 버전을 구현해 보겠습니다.

1. 매핑

고전적인 가산기 회로는 XOR과 AND 게이트를 사용하여 각각 합계와 캐리 비트를 계산합니다. 이러한 게이트를 양자 컨텍스트에 맞게 조정하여 양자 반 덧셈기를 만들 수 있습니다. 첫째, 양자 게이트는 가역적이기 때문에 단순히 입력을 덮어쓸 수 없다는 점을 기억하세요. 대신 합계를 저장하고 출력을 수행하기 위해 0\vert 0\rangle 로 초기화된 두 개의 헬퍼 큐비트를 도입합니다. 따라서 전체 양자 상태는 큐비트 AABB, 그리고 합과 캐리 큐비트로 구성되며, 이를 SSCC 라고 부릅니다:

ψ=CSBA\vert \psi\rangle = \vert C S B A\rangle

이제 고전적인 회로에서 XOR과 AND 게이트가 수행했던 작업을 수행할 수 있는 양자 게이트가 필요합니다.

합계:

XOR의 경우, 각각 제어 큐비트( AA )와 타겟 큐비트( BB ), 그리고 두 큐비트 모두에 대한 타겟 큐비트( SS ) 등 두 개의 CNOT를 적용합니다. AABB 가 다르면 CNOT 게이트 중 하나가 SS1\vert 1\rangle 상태로 전환합니다. AABB 이 모두 0\vert 0\rangle 인 경우 SS 에는 아무 일도 일어나지 않으며 0\vert 0\rangle 상태로 유지됩니다. AABB 이 모두 1\vert 1\rangle 인 경우 SS 의 상태가 두 번 반전되어 0\vert 0\rangle 상태로 돌아갑니다.

휴대:

캐리 비트의 경우, 고전적인 AND 게이트처럼 작동하는 것이 필요합니다.

이해도 점검

지금까지 살펴본 양자 게이트를 되돌아보며 기존 AND 게이트 대신 어떤 양자 게이트를 사용할지 짐작할 수 있는지 알아보세요:

  • 토폴리 게이트입니다! 토폴리 또는 제어-제어-NOT 게이트는 제어 큐비트 0과 제어 큐비트 1이 모두 1\vert 1\rangle 인 경우에만 목표 상태를 뒤집습니다. 따라서 대상 큐비트가 0\vert 0\rangle 상태에서 시작하면 AND 게이트와 동일한 동작을 합니다.

이제 양자 회로를 만드는 데 필요한 모든 재료가 준비되었습니다:

# qubits: a, b, sum, carry
qc = QuantumCircuit(4)

# Choose values for A and B:
a = 0
b = 0

# Prepare A and B qubits according to selected values:
if a:
    qc.x(0)
if b:
    qc.x(1)

# XOR (sum) into qubit 2
qc.cx(0, 2)
qc.cx(1, 2)

# AND (carry) into qubit 3
qc.ccx(0, 1, 3)  # a AND b

# measure
qc.measure_all()


qc.draw("mpl")

Output:

Output of the previous code cell

위는 양자 하프 가산기 회로의 회로도입니다. 앞서 언급했듯이 와이어는 위에서 아래로 순서대로 00 ~ 33 의 큐비트를 나타내며, 고전적인 비트 레지스터는 아래쪽 이중 줄 와이어입니다. 그런 다음 왼쪽에서 오른쪽으로 읽으면서 각 큐비트에 게이트가 어떻게 적용되는지 해당 와이어의 박스가 어디에 표시되는지 확인합니다. 마지막으로 측정값이 마지막에 표시됩니다. 측정은 큐비트 상태를 명확한 00 또는 11 값으로 축소하고 그 결과를 클래식 레지스터로 전송합니다.

한 가지 미묘한 점은 회로도가 왼쪽에서 오른쪽으로 그려져 있지만, 해당 행렬 식을 작성할 때는 오른쪽에서 왼쪽으로 읽어야 한다는 것입니다. 행렬 곱셈에서는 상태 벡터에 가장 가까운 연산자가 먼저 작동하기 때문입니다. 예를 들어 위의 회로(측정값은 무시)는 다음과 같이 작성할 수 있습니다:

CCNOT(q0,q1,q3)CNOT(q1,q2)CNOT(q0,q2)q3q2q1q0CCNOT(q_0,q_1,q_3)CNOT(q_1, q_2)CNOT(q_0,q_2)\vert q_3 q_2 q_1 q_0\rangle

2. 최적화:

다음으로 양자 하드웨어에서 실행할 회로를 최적화해야 합니다. 이 최적화는 위에 표시된 추상적인 회로를 양자 컴퓨터가 이해할 수 있는 명령어로 변환하는 트랜스파일러를 통해 이루어집니다. 위의 논리적 큐비트를 프로세서의 실제 물리적 큐비트에 할당하고 양자 컴퓨터에서 실행되도록 최적화된 자체 네이티브 게이트 세트의 관점에서 게이트를 다시 작성합니다. 마지막으로, 트랜스파일러는 '오류 억제 및 완화'라는 기능을 구현하여 오류가 결과에 미치는 영향을 최소화합니다. 아주 간단한 회로에서는 그다지 중요하지 않지만, 더 복잡한 회로를 실행하는 양자 컴퓨팅 여정을 계속 진행하다 보면 오류 억제 및 완화의 가치를 곧 알게 될 것입니다. 이에 대해 자세히 알아보려면 Olivia Lane의 강좌인 Quantum Computing in Practice를 참조하세요.

먼저 IBM® 양자 컴퓨터와 통신하는 데 필요한 패키지를 로드하고 실행할 백엔드를 선택합니다. 가장 사용량이 적은 백엔드를 선택하거나 속성을 알고 있는 특정 백엔드를 선택할 수 있습니다.

처음 사용할 때 자격 증명을 저장하는 코드는 아래에 있습니다. 노트북을 공유할 때 실수로 자격 증명이 공유되지 않도록 노트북을 환경에 저장한 후 이 정보를 노트북에서 삭제해야 합니다. 자세한 안내는 IBM Cloud 계정 설정하기신뢰할 수 없는 환경에서 서비스 초기화를 참조하세요.

# Load IBM Quantum Compute Service
from qiskit_ibm_runtime import QiskitRuntimeService

# Load IBM Quantum Compute Service

# Syntax for first saving your token.  Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform',
# instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR-API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')

# Load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)

Output:

ibm_fez

이제 트랜스파일러를 사용하여 회로를 최적화합니다. 최적화 수준은 0(최적화 없음)에서 3(최고 최적화)까지 선택할 수 있습니다. 각 레벨의 내용을 확인하려면 트랜스파일러 최적화 레벨 설정 가이드를 참조하세요. 결과 회로는 매핑 단계에서 만든 논리 회로와 크게 달라 보일 것입니다.

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output:

Output of the previous code cell

“샘플러(Sampler)”는 양자 회로에서 발생할 수 있는 상태들을 표본 추출하고, 어떤 상태가 어떤 확률로 측정될 수 있는지에 대한 통계를 수집하도록 설계된 기본 요소입니다. 여기서 IBM Quantum 샘플러를 가져옵니다:

# Load the IBM Quantum primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

실제 양자 컴퓨터에서 할당된 시간을 모두 소진했거나 인터넷에 연결할 수 없는 경우 시뮬레이터를 사용하는 것이 좋습니다. 이렇게 하려면 아래 셀을 실행하고 '실행' 단계에서 관련 줄의 주석 처리를 해제합니다.

# Load the backend sampler
from qiskit.primitives import BackendSamplerV2

# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel

noise_model = NoiseModel.from_backend(backend)

# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)
sampler_sim = BackendSamplerV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# sampler_gen = BackendSamplerV2(backend=backend_gen)

3. 실행

회로를 준비했으니 이제 양자 컴퓨터에서 실행할 수 있습니다!

job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

4. 후처리

이제 결과를 볼 준비가 되었습니다! 회로의 샘플 100개에 대한 히스토그램을 표시합니다.

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)

Output:

counts =  {'0000': 90, '0100': 4, '1100': 3, '0010': 3}
Output of the previous code cell

위의 히스토그램은 회로의 끝에서 네 큐비트 모두의 측정 결과를 보여줍니다. 노이즈가 없는 이상적인 양자 컴퓨터는 큐비트가 매번 동일한 값을 갖도록 측정하지만, 현실에서는 노이즈로 인해 일부 실행에서 오류가 발생할 수 있습니다.

이해도 점검

AA, BB, SS, CC 의 값으로 가장 많은 개수를 가진 비트 문자열을 사용하여 양자 가산기 회로가 작동하는지 확인합니다.

  • A+B=S+2×CA+B = S+2 \times C 을 확인해야 합니다. 비트 문자열 순서는 리틀 엔디안 표기법을 따르므로 CSBA 으로 읽어야 합니다.

    위의 히스토그램을 보면 0000 비트 문자열이 지배적인 것을 알 수 있습니다.

    0+0=0+0×2=0 0 + 0 = 0 + 0 \times 2 = 0 ~\checkmark

돌아가서 AABB 값을 A=1A=1B=1B=1 로 변경하고 키스킷 패턴 단계를 다시 진행하여 회로를 다시 실행합니다. 가산기 회로가 다시 작동하는지 확인합니다.

  • 지배적인 비트 문자열이 1011 인 히스토그램을 얻어야 합니다:

    1+1=0+1×2=2 1 + 1 = 0 + 1 \times 2 = 2 ~\checkmark

기존 하프 가산기에 비해 퀀텀 하프 가산기의 추가 기능 중 하나는 양자 입력으로 실행할 수 있다는 것입니다. 즉, 큐비트 AABB 가 중첩 상태에 있더라도 '더하기'가 가능합니다. 아래의 도전 문제 섹션에서는 큐비트를 중첩하여 준비해보고 어떤 일이 일어나는지 확인하도록 요청할 것입니다!


결론

이 모듈은 양자 컴퓨팅을 기존 컴퓨팅과 비교하여 양자 컴퓨팅의 기본 원리에 대한 기초적인 이해를 돕기 위해 고안되었습니다. 고전적인 하프 가산 회로를 살펴본 다음, 양자 컴퓨터에서 큐비트로 작동하도록 회로를 조정하는 방법을 보여드렸습니다. 이제 교실 모듈의 다른 키스킷을 살펴볼 준비가 되었습니다!

핵심 개념:

  • 0과 1의 값만 취할 수 있는 기존 비트와 달리 큐비트는 0과 1의 중첩 상태일 수도 있습니다.
  • 여러 큐비트는 계산 기준 상태라고 하는 고전적으로 허용되는 비트 문자열 위에 중첩될 수 있습니다.
  • 여러 큐비트가 얽혀서 하나의 상태가 다른 큐비트의 상태에 따라 달라지도록 할 수 있습니다.
  • 키스킷의 규칙은 리틀 엔디안 표기법을 사용하는 것으로, 가장 중요한 큐비트인 q0q_0 를 가장 오른쪽에, 가장 중요한 큐비트인 qNq_N 를 가장 왼쪽에 배치합니다.
  • 양자 게이트는 양자 상태 벡터에 작용하는 단일 행렬로 표현되는 가역적 연산입니다. 이 표기법에서는 벡터에 가장 가까운 행렬(가장 오른쪽에 있는 행렬)이 먼저 작동합니다.
  • 측정은 양자 중첩 상태를 고전적으로 허용되는 상태 중 하나로 붕괴시키며, 중첩에서 해당 계산 기저 상태의 진폭의 제곱과 같은 확률로 붕괴됩니다.
  • 양자 회로는 종종 양자 회로도를 사용하여 표현되는데, 큐비트는 수평 선으로 표시되고 양자 게이트는 이 선을 따라 왼쪽에서 오른쪽으로 나타납니다.
  • 양자 회로를 실행하려면, 키스킷 패턴 워크플로우의 네 단계를 사용합니다: 매핑, 최적화, 실행, 후처리.

질문

참/거짓 문제

  1. 기존 컴퓨터의 단일 비트는 0 또는 1 값만 저장할 수 있습니다.

  2. 얽힘은 한 큐비트의 상태가 다른 큐비트의 상태와 독립적이라는 뜻입니다.

  3. 양자 게이트는 일반적으로 되돌릴 수 없는 연산입니다.

  4. 키스킷 규칙은 가장 중요한 큐비트인 q0q_0 를 가장 왼쪽에 배치합니다.

  5. 양자 상태를 측정하면 여러 번 반복해도 항상 똑같은 결과를 얻을 수 있습니다.

  6. 하다마드 게이트는 단일 큐비트에서 중첩을 생성합니다.

  7. 양자 회로에는 중첩 상태를 고전적으로 허용되는 상태 중 하나로 축소하는 측정 연산이 포함될 수 있습니다.

  8. NN 비트에 대해 가능한 클래식 상태의 수는 2N2N 입니다.

  9. 양자 측정의 결과 확률은 고전적으로 측정 가능한 기저 상태의 제곱 진폭으로 주어집니다.

단답형 문제

  1. 비트와 큐비트의 주요 차이점은 무엇인가요?

  2. 양자 상태가 측정되면 어떻게 되나요?

  3. 키스킷에서 리틀엔디안 표기법을 사용하는 이유는 무엇인가요?

  4. 키스킷 패턴 워크플로우의 네 단계는 무엇인가요?

도전 질문:

  1. 모듈에서는 AABB 에 대해 고전적으로 허용되는 상태만 추가하는 데 가산기를 사용했습니다. 하지만 AABB 도 중첩으로 준비할 수 있습니다! 코드를 변경하여 각 큐비트를 0과 1의 동일한 중첩으로 준비한 다음 새 회로를 실행하고 새 히스토그램을 얻습니다. 뭐가 보이시나요? 무슨 일이 일어나고 있는지 설명하세요.

  2. 토폴리 게이트 분해. decompose() 을 사용하여 토폴리 게이트가 단일 큐비트 게이트와 2큐비트 게이트로 분해되는 과정을 보여준 다음 행렬 곱셈을 통해 이 구조를 검증합니다. 회로도는 왼쪽에서 오른쪽으로 읽지만, 행렬은 오른쪽에서 왼쪽으로 양자 상태에 적용된다는 점을 기억하세요!

이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.