교실에서 Qiskit 시작하기
이 Qiskit in Classrooms 모듈을 진행하려면 학생들의 Python 환경에 다음 패키지가 설치되어 있어야 합니다.
qiskitv2.1.0 이상qiskit-ibm-runtimev0.40.1 이상qiskit-aerv0.17.0 이상qiskit.visualizationnumpypylatexenc
위 패키지의 설치 방법은 Qiskit 설치 가이드를 참고하세요. 실제 양자 컴퓨터에서 작업을 실행하려면, IBM Cloud® 계정 설정 가이드의 단계에 따라 IBM Quantum® 계정을 만들어야 합니다.
이 모듈은 Heron v2 프로세서에서 테스트되었으며 2초의 QPU 시간을 사용했습니다. 이는 추정치에 불과하며, 실제 사용량은 다를 수 있습니다.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# 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'
소개
Qiskit in the Classroom 모듈에서는 양자 컴퓨터를 활용하여 양자 역학, 컴퓨터 과학, 화학 등 양자 컴퓨팅과 인접한 다양한 분야의 개념을 탐구할 수 있습니다. 이 모듈은 다른 모듈들의 선수 과정으로, 양자 컴퓨팅의 기초와 Qiskit을 사용해 양자 Circuit을 실행하는 방법을 소개합니다.
먼저 고전 컴퓨터의 작동 원리를 간략히 살펴본 다음, 이러한 개념들이 양자 컴퓨팅 패러다임에 어떻게 적용되는지 설명합니다. 마지막으로, 이러한 개념들을 종합하여 첫 번째 양자 Circuit을 만들고 실행하는 방법을 안내합니다.
고전 컴퓨터
고전 컴퓨터의 작동 원리는 이미 익숙하실 수 있지만, 여기서는 양자 컴퓨터와 비교하기 위해 몇 가지 핵심 특징을 짚어보겠습니다.
정보의 기본 단위: 비트
고전 컴퓨터는 고전 정보를 처리하며, 고전 정보의 기본 단위는 *비트(bit)*입니다. 비트 하나는 "예/아니오" 질문 하나의 답을 저장할 수 있습니다. 비트의 두 가지 이진 상태는 보통 "0"과 "1"로 표현합니다.
이진수 복습
비트를 결합하면 더 많은 정보를 저장할 수 있습니다. 예를 들어, 0부터 15까지의 숫자를 저장하려면 다음과 같이 4개의 비트를 사용할 수 있습니다.
| 0 = 0000 | 4 = 0100 | 8 = 1000 | 12 = 1100 |
| 1 = 0001 | 5 = 0101 | 9 = 1001 | 13 = 1101 |
| 2 = 0010 | 6 = 0110 | 10 = 1010 | 14 = 1110 |
| 3 = 0011 | 7 = 0111 | 11 = 1011 | 15 = 1111 |
일반적으로, 비트의 이진수를 친숙한 10진수로 변환하려면, 최하위(가장 오른쪽) 비트에 을 곱하고, 그 왼쪽 비트에 , 다음 비트에 를 곱하는 방식으로 최상위(가장 왼쪽) 비트에 을 곱할 때까지 계속합니다.
따라서, 비트는 가지 서로 다른 상태 중 하나가 될 수 있습니다.
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
숫자 86을 표현하려면 몇 비트가 필요한가요? 이 숫자를 이진수로 인코딩하는 비트열을 작성해 보세요.
정답:
비트로는 부터 까지의 숫자를 표현할 수 있습니다. 6비트를 사용하면 최대 까지 표현할 수 있는데, 이것으로는 부족합니다. 비트를 하나 더 추가하면 까지 표현할 수 있습니다. 이제 86을 2의 거듭제곱으로 분해해 봅시다.
기본 연산: Gate
컴퓨터가 실제로 연산을 수행하려면 비트를 가지고 무언가를 할 수 있어야 합니다. 이진 Gate는 더 복잡한 알고리즘과 코드의 기본 구성 요소가 되는 연산들입니다.
단일 비트 Gate:
NOT
비트가 하나뿐일 때, 그 상태를 변환하는 방법은 단 하나입니다. 바로 0을 1로, 또는 1을 0으로 뒤집는 것입니다. 이것을 "NOT" Gate라고 합니다. 이 Gate의 효과 — 그리고 아래에서 다룰 나머지 Gate들의 효과 — 는 소위 "진리표(truth table)"로 나타낼 수 있으며, Qubit의 입력 및 출력 상태를 열로 표시합니다. NOT Gate의 진리표는 다음과 같습니다.
| 입력 | 출력 |
|---|---|
| 0 | 1 |
| 1 | 0 |
다중 비트 Gate:
AND
AND는 두 입력 비트를 받아 단일 비트를 출력하는 2비트 Gate입니다. 두 입력 비트가 모두 1일 때 1을 출력하고, 그 외에는 0을 출력합니다.
| 입력 | 출력 |
|---|---|
| 00 | 0 |
| 01 | 0 |
| 10 | 0 |
| 11 | 1 |
OR
OR은 단일 출력 비트를 가진 또 다른 2비트 Gate입니다. 두 비트 중 어느 하나라도 1이면 1을 출력합니다.
| 입력 | 출력 |
|---|---|
| 00 | 0 |
| 01 | 1 |
| 10 | 1 |
| 11 | 1 |
XOR
XOR은 "배타적 OR(exclusive OR)"의 줄임말로, OR Gate와 유사하지만 입력 비트 중 정확히 하나만 1일 때 1을 출력합니다. 두 비트가 모두 1이거나 모두 0이면 0을 출력합니다.
| 입력 | 출력 |
|---|---|
| 00 | 0 |
| 01 | 1 |
| 10 | 1 |
| 11 | 0 |
측정:
일반적으로 고전 컴퓨팅을 배울 때 비트의 상태를 읽어내는 과정에는 그다지 주의를 기울이지 않습니다. 이는 개념적으로 그리 복잡하지 않기 때문입니다. 비트는 연산 전, 중, 후 언제든지 측정할 수 있으며, 이것이 결과에 영향을 주지 않습니다. 하지만 아래에서 설명하듯이 양자 컴퓨팅에서는 그렇지 않습니다.
Circuit:
위의 Gate들을 조합하면 컴퓨터에서 원하는 모든 종류의 연산을 수행할 수 있습니다. 간단한 예를 들어보겠습니다. AND Gate와 XOR Gate를 사용하면 두 비트의 합을 계산하는 반가산기(half-adder) Circuit을 만들 수 있습니다. 이것은 논리 Circuit 다이어그램으로 표현되는데, 와이어는 비트를 나타내고 해당 와이어 위에 기호로 표시된 Gate들이 비트에 작용합니다.
두 비트는 복사되어 AND Gate와 XOR Gate 모두에 입력됩니다. XOR Gate의 결과는 "합 비트(S)"로, 이진수의 1의 자리 값을 유지하며, AND Gate의 결과는 "올림 비트(C)"로, 이진수에서 그다음으로 중요한 자리의 값이 됩니다. 진리표는 다음과 같습니다.
| 합 () | 올림 () | ||
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
위의 진리표가 가산기 Circuit에 대해 올바른 결과를 내는지 검증하세요. 즉, A와 B의 네 가지 경우 각각에 대해 가 성립함을 확인하세요.
정답:
양자 컴퓨터
비트 Qubit
비트가 고전 정보의 기본 단위이듯, 양자 비트, 즉 "Qubit"은 양자 정보의 기본 단위입니다. 고전 비트와 마찬가지로 Qubit의 상태도 0 또는 1이 될 수 있으며, 이를 보통 및 로 표기합니다. 그러나 고전 비트와 달리, 양자 비트는 상태와 상태가 동시에 존재하는 중첩 상태에도 있을 수 있습니다. 일반적으로 Qubit은 다음과 같은 형태의 임의 상태 에 있을 수 있습니다:
여기서 와 은 을 만족하는 복소 진폭입니다.
양자 위상
와 이 복소수이므로, 각각 로 쓸 수 있으며, 여기서 를 위상이라고 합니다. 전체 상태에 동일한 전체 위상 인자를 곱해도 물리적으로는 아무것도 달라지지 않습니다. 이를 전역 위상이라고 하며, 관측 가능한 결과에 아무런 영향을 미치지 않습니다.
이러한 이유로 를 "인수분해"하는 것이 관례이며, 그 결과는 다음과 같습니다:
여기서 는 양자 상태의 상대적 위상으로, 이것은 관측 결과에 실제로 영향을 미칩니다.
이 위상은 양자 컴퓨팅에서 매우 중요한 역할을 하며, 이후의 Qiskit in the Classroom 모듈에서 그 다양한 결과를 살펴보게 됩니다.
다중 Qubit
여러 비트의 상태는 단순히 0과 1의 문자열로 표현할 수 있지만, 여러 Qubit의 상태는 중첩과 얽힘의 원리로 인해 조금 더 복잡해집니다.
개의 비트는 이진수 000...000부터 111...111까지 가지의 가능한 상태 중 하나에 있을 수 있다는 것을 기억하세요. 그런데 이제 중첩의 원리 때문에 개의 Qubit은 이 모든 상태의 중첩에 동시에 있을 수 있습니다!
이를 다음과 같이 표현할 수 있습니다:
여기서 고전적인 경우와 마찬가지로, 상태 는 각 Qubit이 이진수 를 나타내는 0과 1의 적절한 조합에 있는 상태에 해당합니다. 이를 양자 시스템의 "계산 기저 상태"라고 합니다. 예를 들어, 세 Qubit 상태는 여덟 가지 계산 기저 상태의 중첩으로 쓸 수 있습니다:
시스템의 각 Qubit은 부터 까지의 인덱스로 표시됩니다. 관례에 따라 Qubit 상태를 오른쪽에서 왼쪽으로 읽으므로, Qubit 의 상태가 가장 오른쪽에, Qubit 의 상태가 가장 왼쪽에 위치합니다. 이를 "리틀 엔디언(little-endian)" 표기법이라고 하며, 왼쪽에서 오른쪽으로 읽는 데 익숙한 우리에게는 처음에 직관에 어긋나는 것처럼 느껴질 수 있습니다.
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
리틀 엔디언 표기법처럼 Qubit을 오른쪽에서 왼쪽 순서로 나열하는 것이 처음에는 직관에 어긋나는 것처럼 보일 수 있지만, 사실 매우 논리적인 방식입니다! 그 이유를 설명하세요. (위에서 설명한 이진수를 10진수로 변환하는 방법을 떠올려 보세요.)
정답:
Qubit을 오른쪽에서 왼쪽으로 순서 매겨서 Qubit 0이 가장 오른쪽에, Qubit N-1이 가장 왼쪽에 위치하도록 하면, Qubit 을 이 곱해지는 최하위 비트와, Qubit 을 이 곱해지는 최상위 비트와 논리적으로 연결할 수 있습니다.
얽힘
앞서 언급했듯이, Qubit의 또 다른 핵심 특성은 서로 얽힐 수 있다는 것입니다. 이고 인 두 Qubit 상태의 예를 살펴봅시다:
이 경우, Qubit 0의 상태는 동일한 확률로 또는 이 될 수 있으며, Qubit 1도 마찬가지입니다. 그러나 이 두 확률은 더 이상 서로 독립 적이지 않습니다. Qubit 0의 상태가 으로 측정되면, Qubit 1도 반드시 상태에 있다는 것을 알 수 있습니다. 이는 두 Qubit이 아무리 멀리 떨어져 있어도 마찬가지이며, 그래서 얽힌 상태를 측정하는 행위가 때때로 "원격 유령 작용(spooky action at a distance)"이라고 불리기도 합니다.
얽힘은 다른 형태로도 나타날 수 있습니다. 예를 들어, 다음 상태는
항상 반대의 결과를 만들어냅니다. 즉, 한 Qubit이 으로 측정되면 다른 Qubit은 반드시 상태에서 발견됩니다.
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
상태 은 얽혀 있나요? 그 이유를 설명하세요.
정답:
얽혀 있지 않습니다. 두 Qubit을 모두 측정할 때 결과가 항상 같지만, 이는 각 Qubit이 항상 상태로 고정되어 있기 때문입니다. 한 Qubit을 측정한 결과가 다른 Qubit에 실제로 의존하지 않으며, 단지 둘 다 항상 일 뿐입니다.
일반적으로 다음과 같이 각 Qubit의 상태를 개별적으로 기술한 후 곱할 수 있다면:
이를 "곱 상태(product state)"라고 하며, 얽혀 있지 않습니다.
벡터 표기법
양자 상태가 서로 다른 연산 아래에서 어떻게 변환되는지 파악하는 데 벡터와 행렬을 사용하면 도움이 됩니다. 이 표현에서 양자 상태는 벡터가 되고, 양자 Gate(다음 절에서 설명)는 해당 벡터를 변환하는 행렬이 됩니다.
단일 Qubit의 경우, 상태의 벡터 형태는 다음과 같이 정의합니다: 이를 통해 임의 상태 을 다음과 같이 쓸 수 있습니다:
일반적인 -Qubit 상태의 경우, 차원 벡터가 필요하며, 기저 상태는 예상대로 이진값의 오름차순으로 정렬됩니다:
이러한 벡터 표기법을 바탕으로, 필요한 양자 Gate와 그것이 양자 상태에 미치는 영향, 그리고 행렬 형태를 살펴볼 수 있습니다.
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
두 Qubit 시스템의 계산 기저 상태는 네 가지입니다. 각각을 켓 표기법과 벡터 표기법으로 모두 써보세요.
정답:
Gates 양자 게이트
고전적인 게이트(NOT, AND, OR, XOR 등)를 조합해 임의의 고전 Circuit을 구성할 수 있듯이, 양자 게이트는 양자 컴퓨팅에서 동일한 역할을 합니다. Qubit에는 추가적인 양자역학적 특성이 있기 때문에, 양자 게이트는 그에 상응하여 더 풍부한 표현력을 지닙니다. 기저 상태 과 에 대한 게이트의 작용을 진리표로 나타낼 수 있지만, 이것만으로는 전체 그림을 담을 수 없습니다. 양자 게이트의 경우, 기저 상태의 중첩에도 작용하기 때문에 행렬 표현이 더 자연스럽습니다.
아래에서는 가장 흔히 사용되는 양자 게이트와 해당 게이트가 상호작용하는 Qubit을 어떻게 변환하는지 소개합니다. 해당하는 경우, 익숙한 고전 게이트와 연결 지어 설명합니다.
단일 Qubit 게이트
게이트: NOT 연산에 해당하는 양자 버전입니다. 진리표는 고전적인 NOT 게이트와 완전히 동일합니다.
| 입력 | 출력 |
|---|---|
행렬 표현:
Qiskit에서 게이트가 포함된 Circuit을 만드는 방법은 다음과 같습니다.
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.x(0)
qc.draw("mpl")
이 간단한 Circuit 다이어그램에서 Qubit은 가로선(검은 수평선)으로 표현되고, 게이트는 해당 선 위의 박스로 나타납니다.
Hadamard 게이트: 중첩 상태를 생성합니다. 진리표:
| 입력 | 출력 |
|---|---|
행렬 표현:
Hadamard 게이트가 포함된 Circuit은 다음과 같이 만들 수 있습니다.
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.h(0)
qc.draw("mpl")
게이트: 상태에 의 위상 이동을 추가합니다.
| 입력 | 출력 |
|---|---|
Qiskit에서 게이트가 포함된 Circuit을 만드는 방법은 다음과 같습니다.
qc = QuantumCircuit(1)
qc.z(0)
qc.draw("mpl")
게이트: 상태에 의 위상 이동을 추가합니다.
| 입력 | 출력 |
|---|---|
Qiskit에서 게이트가 포함된 Circuit을 만드는 방법은 다음과 같습니다.
qc = QuantumCircuit(1)
qc.t(0)
qc.draw("mpl")
다중 Qubit 게이트
2-Qubit 게이트는 고전적인 2비트 게이트와 유사해 보일 수 있지만, 한 가지 중요한 차이점이 있습니다. 모든 양자 게이트는 가역적이어야 합니다. 선형대수학 관점에서 이는 유니터리 행렬로 표현된다는 의미입니다. 따라서 두 개의 입력 Qubit은 항상 두 개의 출력 Qubit으로 매핑되며, 이 연산은 원칙적으로 되돌릴 수 있습니다. 이는 앞서 살펴본 AND나 OR과 같은 고전 게이트와 대비됩니다. 고전 게이트는 정보를 잃어버리며 비가역적으로, 출력이 주어졌을 때 입력을 유일하게 결정할 수 없습니다.
CNOT(제어-NOT) 게이트: 두 입력 Qubit은 각각 "제어(control)" Qubit과 "표적(target)" Qubit으로 불립니다. 제어 Qubit은 변하지 않지만, 그 상태가 표적 Qubit에 일어나는 일을 결정합니다. 제어 Qubit이 상태이면 표적 Qubit에 게이트가 적용되고, 제어 Qubit이 상태이면 아무 변화도 없습니다. 아래 표기에서 Qubit (가장 오른쪽 Qubit)가 제어이고 Qubit (가장 왼쪽 Qubit)가 표적이라고 가정합니다. 아래에서 사용하는 표기는 입니다.
| 입력 | 출력 |
|---|---|
이 작용을 나타내는 행렬은 다음과 같습니다.
qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.draw("mpl")
이것은 두 Qubit이 등장하는 첫 번째 Circuit 다이어그램으로, 두 개의 선으로 표현됩니다. CNOT 게이트는 두 Qubit 사이에 구현되며, 이 제어이고 이 표적입니다.
이해도 확인
아래 질문을 읽고 답을 생각해본 후, 삼각형을 클릭해 정답을 확인하세요.
대부분의 게이트는 Qiskit에서도 다른 곳과 동일한 행렬 형태를 가집니다. 그런데 CNOT 게이트는 두 Qubit에 작용하기 때문에 Qubit 순서 규칙이 문제가 됩니다. Qubit을 순서로 배치하는 교재에서는 CNOT 게이트의 행렬 형태가 다르게 표현됩니다. 위의 CNOT 행렬이 상태 에 올바르게 작용함을 명시적인 행렬 곱셈으로 검증하세요.
정답:
SWAP 게이트: 이 게이트는 두 Qubit의 상태를 교환합니다. 진리표:
| 입력 | 출력 |
|---|---|
이 작용을 나타내는 행렬은 다음과 같습니다.
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.draw("mpl")
SWAP 게이트는 사실 세 개의 CNOT으로 구성할 수 있습니다. 어떻게 되는지 Qiskit의 decompose()를 사용해 확인해 보겠습니다.
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")
여기서 Circuit 다이어그램에서 여러 게이트가 어떻게 표시되는지 처음으로 확인할 수 있습니다. 다이어그램은 왼쪽에서 오른쪽으로 읽으므로, 가장 왼쪽에 있는 게이트가 먼저 적용됩니다.
이해도 확인
아래 질문을 읽고 답을 생각해본 후, 삼각형을 클릭해 정답을 확인하세요.
위의 CNOT 조합이 SWAP 게이트와 동일함을 검증하세요. 행렬 곱셈이나 다른 방법을 사용할 수 있습니다.
정답:
행렬 곱셈으로 검증:
진리표를 사용해 각 CNOT마다 상태가 어떻게 변하는지 확인하는 방법도 있습니다. 마지막 열의 상태는 SWAP 진리표의 "출력" 열과 일치해야 합니다.
| 입력 | CNOT(A,B) | CNOT(B,A) | CNOT(A,B) |
|---|---|---|---|
Toffoli 게이트 (또는 "제어-제어-NOT" (CCNOT)): 이것은 3-Qubit 게이트입니다. "제어-제어-NOT"이라는 이름에서 이미 작동 방식을 짐작할 수 있습니다. 두 개의 제어 Qubit과 하나의 표적 Qubit이 있으며, 두 제어 Qubit이 모두 상태일 때에만 표적 Qubit의 상태가 뒤집힙니다. CNOT에서 사용한 순서 규칙을 그대로 적용합니다.
따라서 진리표는 다음과 같습니다.
| 입력 | 출력 |
|---|---|
이 작용을 나타내는 행렬은 다음과 같습니다.
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.draw("mpl")
Toffoli 게이트 역시 CNOT과 일부 다른 게이트들로 분해할 수 있습니다. 단, SWAP 게이트 분해보다 훨씬 복잡하기 때문에, 이 분해를 탐구하고 검증하는 것은 모듈 말미의 선택적 연습 문제로 남겨두겠습니다.
측정
측정은 양자 컴퓨팅에서 특별한 역할을 합니다 — 이는 고전 컴퓨팅에는 해당하는 개념이 없는 부분입니다. 고전 컴퓨팅에서는 알고리즘 실행 중 원하는 시점에 언제든지 비트 상태를 확인할 수 있지만, 양자 컴퓨팅에서는 Qubit을 들여다보는 시점을 매우 신중하게 선택해야 합니다. 측정을 하면 Qubit의 상태가 붕괴하여 Qubit에 계산적 복잡성을 부여하는 중첩 상태가 파괴되기 때문입니다.
특히, 개의 비트로 이루어진 양자 상태 를 측정하면, 상태는 기저 함수 중 하나로 붕괴하며, 그 확률은 와 같습니다.
그러나 측정의 이러한 파괴적 효과가 항상 장애물인 것은 아닙니다. 실제로 양자 텔레포테이션이나 양자 키 분배와 같은 특정 알고리즘 및 프로토콜에서는 핵심 자원으로 활용됩니다.
Qiskit에서는 측정이 이루어지면, 결과가 고전 레지스터로 전송되어 고전 비트로 저장됩니다. 측정이 포함된 Circuit을 만드는 방법은 다음과 같습니다:
qc = QuantumCircuit(
1, 1
) # the second number is the number of classical bits in the circuit
qc.measure(0, 0)
qc.draw("mpl")
Circuit
이제 Qubit, Gate, 측정이 어떻게 동작하는지 알았으니, 직접 양자 Circuit을 만들고 실행해 봅시다! 이를 위해 Qiskit 패턴(Qiskit patterns)이라는 유용한 워크플로를 소개하겠습니다.
Qiskit 패턴 프레임워크
Qiskit 패턴 프레임워크는 양자 컴퓨터로 문제를 접근하고 해결하는 일반적인 절차입니다. 다음 네 단계로 구성됩니다:
- 매핑(Mapping): 문제를 양자 Circuit과 연산자로 변환하기
- 최적화(Optimizing): 대상 하드웨어에 맞게 Circuit 최적화하기
- 실행(Executing): 대상 하드웨어에서 실행하기
- 후처리(Post-processing): 결과 후처리하기
이 단계들을 설명하기 위해, 위에서 다룬 반가산기(half-adder) Circuit의 양자 버전을 구현해 보겠습니다.
1. 매핑
고전 가산기 Circuit은 XOR Gate와 AND Gate를 사용하여 각각 합(sum) 비트와 올림(carry) 비트를 계산합니다. 이 Gate들을 양자 컨텍스트에 맞게 적용하여 양자 반가산기를 만들 수 있습니다. 먼저, 양자 Gate는 가역적이므로 입력값을 단순히 덮어쓸 수 없다는 점을 기억해야 합니다. 대신, 합과 올림 출력을 저장하기 위해 으로 초기화된 두 개의 보조 Qubit을 도입합니다. 따라서 전체 양자 상태는 Qubit 와 , 그리고 합 Qubit()과 올림 Qubit()로 구성됩니다:
이제 고전 Circuit에서 XOR와 AND Gate가 수행했던 역할을 담당할 양자 Gate가 필요합니다.
합(Sum):
XOR을 구현하기 위해 두 개의 CNOT Gate를 적용합니다. 각각 제어 Qubit 와 를 가지며, 두 Gate 모두 대상 Qubit은 입니다. 와 가 서로 다르면 CNOT Gate 중 하나가 를 상태로 뒤집습니다. 와 가 모두 이면 에 아무런 변화가 없어 상태를 유지합니다. 와 가 모두 이면 의 상태가 두 번 뒤집혀 다시 상태로 돌아옵니다.
올림(Carry):
올림 비트를 위해서는 고전 AND Gate와 같은 역할을 하는 무언가가 필요합니다.
이해도 확인
아래 질문을 읽고 답을 생각해 본 후, 삼각형을 클릭하여 정답을 확인하세요.
앞에서 다룬 Gate들을 다시 살펴보고, 고전 AND Gate 대신 어떤 양자 Gate를 사용할지 추측해 보세요:
정답:
바로 Toffoli Gate입니다! Toffoli, 즉 제어-제어-NOT(controlled-controlled-NOT) Gate는 제어 Qubit 0과 제어 Qubit 1이 모두 상태일 때만 대상 Qubit의 상태를 뒤집습니다. 따라서 대상 Qubit이 상태에서 시작한다면, AND Gate와 동일한 동작을 합니다.
이제 양자 Circuit을 만드는 데 필요한 모든 구성 요소를 갖추었습니다:
# 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")
위는 양자 반가산기 Circuit의 회로도입니다. 앞서 언급했듯이, 선(wire)은 위에서 아래로 순서대로 Qubit 부터 을 나타내며, 하단의 이중 선이 고전 비트 레지스터입니다. 왼쪽에서 오른쪽으로 읽으면서, 해당 선 위에 박스가 어디에 표시되는지 보면 각 Qubit에 어떤 Gate가 적용되는지 알 수 있습니다. 마지막으로 끝부분에 측정이 표시됩니다. 측정은 Qubit 상태를 확정적인 또는 값으로 붕괴시키고, 결과는 고전 레지스터로 전송됩니다.
한 가지 세부 사항: Circuit 도표는 왼쪽에서 오른쪽으로 그려지지만, 해당 행렬 식을 쓸 때는 오른쪽에서 왼쪽으로 읽어야 합니다. 행렬 곱셈에서는 상태 벡터에 가장 가까운(즉, 가장 오른쪽에 있는) 연산자가 먼저 작용하기 때문입니다. 예를 들어, 위 Circuit(측정 제외)은 다음과 같이 표현됩니다:
2. 최적화
다음으로 양자 하드웨어에서 실행하기 위해 Circuit을 최적화해야 합니다. 이 최적화는 Transpiler를 통해 이루어지며, Transpiler는 위에서 만든 추상 Circuit을 양자 컴퓨터가 이해할 수 있는 명령어로 변환합니다. Transpiler는 위의 논리 Qubit들을 프로세서의 실제 물리 Qubit에 할당하고, 양자 컴퓨터에서 최적화되어 실행되는 고유 Gate 집합을 기준으로 Gate를 재작성합니다. 또한 Transpiler는 오류의 영향을 최소화하기 위한 "오류 억제 및 완화(error suppression and mitigation)"도 구현합니다. 우리의 매우 단순한 Circuit에서는 그다지 중요하지 않지만, 양자 컴퓨팅 여정을 계속하여 더 복잡한 Circuit을 실행하게 된다면 오류 억제 및 완화의 가치를 곧 알게 될 것입니다. 이에 대해 더 알고 싶다면 Olivia Lane의 강좌 실전 양자 컴퓨팅(Quantum Computing in Practice)을 참고하세요.
먼저, IBM® 양자 컴퓨터와 통신하는 데 필요한 패키지를 로드하고 실행할 Backend를 선택합니다. 가장 여유 있는 Backend를 선택하거나, 속성을 알고 있는 특정 Backend를 선택할 수 있습니다.
아래에는 처음 사용 시 자격 증명을 저장하는 코드가 있습니다. 환경에 저장한 후에는 노트북에서 이 정보를 반드시 삭제하여 노트북을 공유할 때 자격 증명이 실수로 노출되지 않도록 하세요. 자세한 안내는 IBM Cloud 계정 설정 및 신뢰할 수 없는 환경에서 서비스 초기화를 참고하세요.
# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService
# Load the Qiskit Runtime 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)
ibm_fez
이제 Transpiler를 사용하여 Circuit을 최적화합니다. 최적화 레벨은 0(최적화 없음)부터 3(최고 수준 최적화)까지 선택할 수 있습니다. 각 레벨에 대한 자세한 내용은 Transpiler 최적화 레벨 설정 가이드를 참고하세요. 결과 Circuit은 매핑 단계에서 만든 논리 Circuit과 크게 달라 보일 것입니다.
# 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")
"Sampler"는 양자 Circuit으로부터 나올 수 있는 상태를 샘플링하고, 어떤 상태가 어떤 확률로 측정될 수 있는지에 대한 통계를 수집하도록 설계된 기본 요소(primitive)입니다. 여기서 Qiskit Runtime Sampler를 불러옵니다:
# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler
sampler = Sampler(mode=backend)
실제 양자 컴퓨터에 할당된 시간을 모두 사용했거나 인터넷에 연결되어 있지 않은 경우, 시뮬레이터를 사용하는 것이 좋을 수 있습니다. 이를 위해 아래 셀을 실행하고 "실행(Execute)" 단계에서 해당 줄의 주석을 해제하세요.
# 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. 실행
Circuit을 준비했으니 이제 양자 컴퓨터에서 실행할 수 있습니다!
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. 후처리
이제 결과를 확인할 준비가 되었습니다! Circuit의 100번 샘플 결과를 히스토그램으로 표시합니다.
from qiskit.visualization import plot_histogram
print("counts = ", counts)
plot_histogram(counts)
counts = {'0000': 90, '0100': 4, '1100': 3, '0010': 3}
위 히스토그램은 Circuit 종료 시 네 개의 Qubit 모두에 대한 측정 결과를 보여줍니다. 잡음이 없는 이상적인 양자 컴퓨터라면 매번 동일한 Qubit 값을 측정하겠지만, 실제로는 잡음으로 인해 일부 실행에서 오류가 발생합니다.
이해도 확인하기
아래 질문을 읽고 답을 생각해본 뒤, 삼각형을 클릭하여 정답을 확인하세요.
가장 많은 횟수를 기록한 비트스트링을 , , , 의 값으로 사용하여, 양자 가산기 Circuit이 올바르게 작동했는지 검증하세요.
정답:
를 검증해야 합니다. 비트스트링의 순서는 리틀 엔디언(little-endian) 표기법을 따르므로 CSBA 순으로 읽습니다.
위의 히스토그램에서 0000 비트스트링이 가장 많이 나타남을 확인할 수 있습니다.
와 의 값을 , 로 변경한 뒤, Qiskit patterns 단계를 다시 거쳐 Circuit을 재실행하세요. 가산기 Circuit이 다시 올바르게 작동했는지 검증하세요.
정답:
주요 비트스트링이 1011인 히스토그램을 얻어야 합니다:
양자 절반 가산기가 고전 절반 가산기보다 추가적으로 갖는 기능 중 하나는 양자 입력으로도 실행할 수 있다는 점입니다. 즉, Qubit 와 가 중첩 상태에 있더라도 "덧셈"을 수행할 수 있 습니다. 아래의 도전 문제 섹션에서는 Qubit을 중첩 상태로 준비하고 어떤 결과가 나타나는지 확인해 볼 것입니다!
결론
이 모듈은 양자 컴퓨팅의 기본 원리를 고전 컴퓨팅과 비교하여 탄탄한 기초 이해를 제공하기 위해 설계되었습니다. 고전 절반 가산기 Circuit을 살펴본 뒤, 이를 양자 컴퓨터에서 Qubit으로 실행할 수 있도록 변환하는 방법을 살펴봤어요. 이제 다른 Qiskit in the Classroom 모듈을 탐색할 준비가 되었습니다!
핵심 개념:
- 0과 1만 가질 수 있는 고전 비트와 달리, Qubit은 0과 1의 중첩(superposition) 상태도 가질 수 있습니다.
- 여러 Qubit은 고전적으로 허용되는 비트스트링인 계산 기저 상태(computational basis states) 의 중첩 상태에 있을 수 있습니다.
- 여러 Qubit은 얽힘(entanglement) 상태가 될 수 있으며, 이 경우 한 Qubit의 상태가 다른 Qubit의 상태에 의존합니다.
- Qiskit의 관례는 리틀 엔디언 표기법을 사용하는 것으로, 최하위 Qubit인 를 가장 오른쪽에, 최상위 Qubit인 을 가장 왼쪽에 배치합니다.
- 양자 Gate는 양자 상태 벡터에 작용하는 유니터리 행렬로 표현되는 가역적 연산입니다. 이 표기법에서 벡터에 가장 가까운(가장 오른쪽에 있는) 행렬이 먼저 작용합니다.
- 측정(Measurement) 은 양자 중첩 상태를 고전적으 로 허용되는 상태 중 하나로 붕괴시키며, 그 확률은 중첩 상태에서 해당 계산 기저 상태의 진폭의 제곱과 같습니다.
- 양자 Circuit은 양자 회로 다이어그램으로 표현되는 경우가 많으며, 여기서 Qubit은 수평선으로 나타내고 양자 Gate는 이 선을 따라 왼쪽에서 오른쪽 순서로 나타납니다.
- 양자 Circuit을 실행하려면 Qiskit patterns 워크플로우의 네 단계인 Map, Optimize, Execute, Post-process를 사용합니다.
문제
참/거짓 문제
-
고전 컴퓨터의 단일 비트는 0 또는 1의 값만 가질 수 있다.
-
얽힘은 한 Qubit의 상태가 다른 Qubit의 상태와 독립적임을 의미한다.
-
양자 Gate는 일반적으로 비가역적 연산이다.
-
Qiskit의 관례는 최하위 Qubit인 를 가장 왼쪽에 배치하는 것이다.
-
양자 상태를 측정하면 반복해도 항상 동일한 결과가 나온다.
-
Hadamard Gate는 단일 Qubit에 중첩 상태를 생성한다.
-
양자 Circuit은 중첩 상태를 고전적으로 측정 가능한 상태 중 하나로 붕괴시키는 측정 연산을 포함할 수 있다.
-
개의 비트에 대한 가능한 고전 상태의 수는 이다.
-
양자 측정의 결과 확률은 고전적으로 측정 가능한 기저 상태의 진폭의 제곱으로 주어진다.
단답형 문 제
-
비트와 Qubit의 주요 차이점은 무엇인가요?
-
양자 상태가 측정될 때 어떤 일이 일어나나요?
-
Qiskit에서 리틀 엔디언 표기법을 사용하는 이유는 무엇인가요?
-
Qiskit patterns 워크플로우의 네 단계는 무엇인가요?
도전 문제:
-
이 모듈에서는 와 에 대해 고전적으로 허용된 상태만 사용하여 가산기를 실행했습니다. 하지만 와 를 중첩 상태로 준비할 수도 있습니다! 각 Qubit을 0과 1의 동일한 중첩 상태로 준비하도록 코드를 변경한 뒤, 새 Circuit을 실행하여 새로운 히스토그램을 구하세요. 어떤 결과가 보이나요? 어떤 일이 일어나고 있는지 설명해 보세요.
-
Toffoli Gate 분해.
decompose()를 사용하여 Toffoli Gate가 단일 및 2-Qubit Gate로 어떻게 분해되는지 보여준 뒤, 행렬 곱셈으로 이 구성을 검증하세요. 회로 다이어그램은 왼쪽에서 오른쪽으로 읽지만, 행렬은 양자 상태에 오른쪽에서 왼쪽으로 적용된다는 점에 유의하세요!