주 콘텐츠로 건너뛰기
이 프로젝트는 베타 단계입니다. 버그를 발견하셨나요? GitHub 이슈 만들기. 질문이나 아이디어가 있으신가요? Discussions에서 질문하기 — 피드백을 기다립니다!
이 페이지의 번역 품질은 어떻습니까?

Qiskit 구현

이 섹션에서는 이 레슨에서 소개한 개념들을 Qiskit으로 구현한 예제들을 살펴봅시다. 이 구현들을 직접 실행해 보기를 적극 권장합니다. Qiskit 설정 방법은 IBM Quantum 문서의 Qiskit 설치 페이지를 참고하세요.

Qiskit은 지속적으로 개발되고 있으며, 주로 그것이 작동하는 양자 컴퓨터의 성능을 최대화하는 데 중점을 두고 있으며, 이 양자 컴퓨터들도 계속 진화하고 있습니다. 따라서 Qiskit은 때때로 코드 사용 중단을 초래할 수 있는 변경 사항의 대상이 될 수 있습니다. 이를 염두에 두고, 이 코스에서 Qiskit 코드의 예제를 제시하기 전에 항상 다음 명령어들을 실행할 것입니다. 따라서 어떤 버전의 Qiskit이 사용되었는지 명확합니다. Qiskit v1.0부터는 현재 설치된 Qiskit 버전을 확인하는 간단한 방법입니다.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit import __version__

print(__version__)
2.2.3

클라우드 기반 Python 환경에서 이를 실행하고 있다면, 다음 패키지 중 일부를 설치해야 할 수 있습니다:

#!pip install qiskit
#!pip install jupyter
#!pip install sympy
#!pip install matplotlib
#!pip install pylatexenc

Python에서의 벡터와 행렬​

Qiskit은 Python 프로그래밍 언어를 사용하므로, Qiskit을 구체적으로 논의하기 전에 Python에서 행렬과 벡터 계산을 간단히 논의하는 것이 도움이 될 수 있습니다.

Python에서는 NumPy 라이브러리의 array 클래스를 사용하여 행렬과 벡터 계산을 수행할 수 있습니다. 이는 많은 수치 및 과학 계산 기능을 제공합니다. 다음 코드는 이 라이브러리를 로드하고, qubit 상태 벡터 ∣0⟩\vert 0\rangle과 ∣1⟩\vert 1\rangle에 해당하는 두 개의 열 벡터 ket0과 ket1을 정의한 다음, 그들의 평균을 출력합니다.

import numpy as np

ket0 = np.array([[1], [0]])
ket1 = np.array([[0], [1]])

print(ket0 / 2 + ket1 / 2)
[[0.5]
[0.5]]

또한 array를 사용하여 연산을 나타낼 수 있는 행렬을 만들 수 있습니다.

M1 = np.array([[1, 1], [0, 0]])
M2 = np.array([[1, 0], [0, 1]])
M = M1 / 2 + M2 / 2
print(M)
[[1. 0.5]
[0. 0.5]]

이 코스의 각 레슨에 나오는 코드는 모두 순서대로 실행한다고 가정한다는 점에 유의하세요. 따라서 여기서 NumPy를 다시 가져올 필요가 없습니다. 왜냐하면 이미 위에서 가져왔기 때문입니다.

행렬-벡터 곱셈을 특수한 경우로 포함하여 행렬 곱셈은 @ 연산자를 사용하여 수행할 수 있습니다.

print(M1 @ ket1)
print(M1 @ M2)
print(M @ M)
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

이 출력 형식은 시각적으로 다소 아쉬운 부분이 있습니다. 더 보기 좋은 것이 필요한 상황을 위한 한 가지 해결책은 Qiskit의 qiskit.visualization 모듈에 있는 array_to_latex 함수를 사용하는 것입니다. 다음 코드에서는 Python의 범용 display 함수를 사용하고 있는데(특히 노트북에서), 이는 객체를 더 풍부한 표현으로 렌더링할 수 있는 반면, print()는 단순히 객체의 문자열 표현을 사용하기 때문이라는 점에 유의하세요. 이는 NumPy 배열이 자체적인 특수 형식을 제공하는 반면 Python 리스트는 기본 리스트 표현을 사용하는 것과 유사합니다.

from qiskit.visualization import array_to_latex

display(array_to_latex(M1 @ ket1))
display(array_to_latex(M1 @ M2))
display(array_to_latex(M @ M))
[10] \begin{bmatrix} 1 \\ 0 \\ \end{bmatrix} [1100] \begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix} [134014] \begin{bmatrix} 1 & \frac{3}{4} \\ 0 & \frac{1}{4} \\ \end{bmatrix}

상태, 측정 및 연산​

Qiskit은 상태, 측정 및 연산을 생성하고 조작할 수 있는 여러 클래스를 포함하고 있습니다. 따라서 Python에서 양자 상태, 측정 및 연산을 시뮬레이션하기 위해 필요한 모든 것을 직접 프로그래밍할 필요가 없습니다. 시작하는 데 도움이 되는 몇 가지 예제가 아래에 포함되어 있습니다.

상태 벡터 정의 및 표시​

Qiskit의 Statevector 클래스는 양자 상태 벡터를 정의하고 조작하는 기능을 제공합니다. 다음 코드에서 Statevector 클래스를 가져오고 몇 개의 벡터를 정의합니다. (또한 NumPy 라이브러리에서 sqrt 함수를 가져오고 있습니다. 제곱근을 계산하기 위해 사용됩니다. 이 함수는 대안으로 np.sqrt로 호출될 수 있습니다. 단, NumPy가 이미 위에서 가져왔다면 말입니다. 이는 단지 이 특정 함수만 가져오고 사용하는 다른 방법입니다.)

from qiskit.quantum_info import Statevector
from numpy import sqrt

u = Statevector([1 / sqrt(2), 1 / sqrt(2)])
v = Statevector([(1 + 2.0j) / 3, -2 / 3])
w = Statevector([1 / 3, 2 / 3])

Statevector 클래스는 상태 벡터를 다양한 방식으로 표시하기 위한 draw 메서드를 포함합니다. 평문 텍스트의 경우 text, 렌더링된 LaTeX의 경우 latex, LaTeX 코드의 경우 latex_source가 있습니다. 이는 문서에 붙여넣기 위해 유용합니다. (LaTeX 코드를 표시하기 위해 최적의 결과를 얻으려면 display 대신 print를 사용하세요.)

display(u.draw("text"))
display(u.draw("latex"))
print(u.draw("latex_source"))
[0.70710678+0.j,0.70710678+0.j]

22∣0⟩+22∣1⟩\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

Statevector 클래스는 또한 주어진 벡터가 유효한 양자 상태 벡터인지 확인하는 is_valid 메서드를 포함합니다 (즉, 유클리드 노름이 1과 같은지 확인합니다):

display(u.is_valid())
display(w.is_valid())
True
False

Statevector를 사용한 측정 시뮬레이션​

다음으로, Qiskit에서 Statevector 클래스의 measure 메서드를 사용하여 양자 상태 측정을 시뮬레이션하는 방법을 살펴봅시다. 이전에 정의한 동일한 qubit 상태 벡터 v를 사용해봅시다.

display(v.draw("latex"))

(13+2i3)∣0⟩−23∣1⟩(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

measure 메서드를 실행하면 표준 기저 측정을 시뮬레이션합니다. 이 메서드는 그 측정의 결과와 측정 후 시스템의 새로운 양자 상태 벡터를 반환합니다. (여기서는 포맷된 인쇄를 위해 내장 표현식이 있는 f 접두사가 있는 Python의 print 함수를 사용하고 있습니다.)

outcome, state = v.measure()
print(f"Measured: {outcome}\nPost-measurement state:")
display(state.draw("latex"))
Measured: 1
Post-measurement state:

−∣1⟩- |1\rangle

측정 결과는 확률적이므로, 이 메서드를 여러 번 실행할 때 다른 결과를 반환할 수 있습니다. 위에서 정의한 벡터 v의 특정 예제의 경우, measure 메서드는 측정이 발생한 후의 양자 상태 벡터를 다음과 같이 정의합니다.

(1+2i5)∣0⟩\biggl(\frac{1 + 2i}{\sqrt{5}}\biggr) \vert 0\rangle

(∣0⟩\vert 0\rangle 대신) 또는

−∣1⟩- \vert 1\rangle

(∣1⟩\vert 1\rangle 대신), 측정 결과에 따라 다릅니다. 두 경우 모두, ∣0⟩\vert 0\rangle과 ∣1⟩\vert 1\rangle의 대안은 실제로 이러한 상태 벡터와 동등합니다. 한 벡터가 단위원 위의 복소수로 곱해진 다른 벡터와 같으므로, 이들은 전역 위상을 제외하고 동등하다고 합니다. 이 문제는 양자 회로 레슨에서 더 자세히 논의되며, 지금은 무시해도 됩니다.

Statevector는 measure 메서드가 유효하지 않은 양자 상태 벡터에 적용되면 오류를 발생시킵니다.

Statevector에는 sample_counts 메서드도 있는데, 이를 사용하면 시스템에서 임의 횟수의 측정을 시뮬레이션할 수 있으며, 매번 상태의 새로운 복사본에서 시작합니다. 예를 들어, 다음 코드는 벡터 v를 10001000번 측정한 결과를 보여주는데, (높은 확률로) 결과 00이 대략 99번 중 55번(또는 10001000번의 시도 중 약 556556번) 나오고, 결과 11이 대략 99번 중 44번(또는 10001000번 중 약 444444번) 나옵니다. 다음 코드는 결과를 시각화하기 위해 qiskit.visualization 모듈의 plot_histogram 함수도 보여줍니다.

from qiskit.visualization import plot_histogram

statistics = v.sample_counts(1000)
plot_histogram(statistics)

Output of the previous code cell

이 코드를 직접 여러 번 실행하고 10001000 대신 다른 샘플 수를 사용하면, 시행 횟수가 각 결과가 나타나는 횟수에 어떻게 영향을 미치는지에 대한 직관을 발달시키는 데 도움이 될 수 있습니다. 샘플이 많을수록, 각 결과가 나온 샘플의 비율이 해당 확률에 점점 더 가까워질 가능성이 높습니다. 일반적으로 이 현상은 확률 이론에서 큰 수의 법칙이라고 알려져 있습니다.

Operator와 Statevector로 연산 수행​

유니터리 연산(Unitary operations)은 다음 예제와 같이 Qiskit에서 Operator 클래스를 사용하여 정의할 수 있습니다. 이 클래스는 Statevector와 유사한 인수를 가진 draw 메서드를 포함합니다. latex 옵션은 array_to_latex와 동등한 결과를 생성합니다.

from qiskit.quantum_info import Operator

Y = Operator([[0, -1.0j], [1.0j, 0]])
H = Operator([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]])
S = Operator([[1, 0], [0, 1.0j]])
T = Operator([[1, 0], [0, (1 + 1.0j) / sqrt(2)]])

display(T.draw("latex"))
[10022+2i2] \begin{bmatrix} 1 & 0 \\ 0 & \frac{\sqrt{2}}{2} + \frac{\sqrt{2} i}{2} \\ \end{bmatrix}

evolve 메서드를 사용하여 유니터리 연산을 상태 벡터에 적용할 수 있습니다.

v = Statevector([1, 0])

v = v.evolve(H)
v = v.evolve(T)
v = v.evolve(H)
v = v.evolve(S)
v = v.evolve(Y)

display(v.draw("latex"))

(0.1464466094−0.3535533906i)∣0⟩+(−0.3535533906+0.8535533906i)∣1⟩(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

양자 회로의 미리보기​

양자 회로는 이 코스의 세 번째 레슨인 양자 회로 레슨에서 공식적으로 소개될 것입니다. 그러나 우리는 여전히 Qiskit의 QuantumCircuit 클래스를 사용하여 단일 qubit에 대한 유니터리 연산들을 조합하는 것을 실험해볼 수 있습니다. 특히, 양자 회로를 (이 경우, 단순히 단일 qubit에 수행된 유니터리 연산들의 나열이 될) 다음과 같이 정의할 수 있습니다.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(1)

circuit.h(0)
circuit.t(0)
circuit.h(0)
circuit.s(0)
circuit.y(0)

display(circuit.draw(output="mpl"))

Output of the previous code cell

여기서 우리는 QuantumCircuit 클래스의 draw 메서드를 mpl 렌더러(Matplotlib의 약자이며, Python 시각화 라이브러리)와 함께 사용하고 있습니다. 이것은 이 코스에서 양자 회로에 사용할 유일한 렌더러이지만, 텍스트 기반 및 LaTeX 기반 렌더러를 포함한 다른 옵션들이 있습니다.

연산들은 순차적으로 적용되며, 다이어그램에서 왼쪽에서 시작하여 오른쪽으로 끝납니다. 이 회로에 해당하는 유니터리 행렬을 얻는 편리한 방법은 Operator 클래스의 from_circuit 메서드를 사용하는 것입니다.

display(Operator.from_circuit(circuit).draw("latex"))
[0.1464466094−0.3535533906i0.8535533906+0.3535533906i−0.3535533906+0.8535533906i0.3535533906+0.1464466094i] \begin{bmatrix} 0.1464466094 - 0.3535533906 i & 0.8535533906 + 0.3535533906 i \\ -0.3535533906 + 0.8535533906 i & 0.3535533906 + 0.1464466094 i \\ \end{bmatrix}

시작 양자 상태 벡터를 초기화하고 회로에 의해 설명된 연산들의 수열에 따라 그 상태를 진화시킬 수도 있습니다.

ket0 = Statevector([1, 0])
v = ket0.evolve(circuit)
display(v.draw("latex"))

(0.1464466094−0.3535533906i)∣0⟩+(−0.3535533906+0.8535533906i)∣1⟩(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

다음 코드는 위의 회로에서 얻은 상태를 표준 기저 측정으로 4000번 측정하는 실험을 시뮬레이션합니다. (매번 상태의 새로운 복사본을 사용합니다.)

statistics = v.sample_counts(4000)
display(plot_histogram(statistics))

Output of the previous code cell