주 콘텐츠로 건너뛰기

Qiskit 구현

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

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

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import __version__

print(__version__)
2.1.1

클라우드 기반 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\rangle1\vert 1\rangle에 해당하는 두 개의 열 벡터 ket0ket1을 정의한 다음, 그들의 평균을 출력합니다.

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를 다시 가져올 필요가 없습니다. 왜냐하면 이미 위에서 가져왔기 때문입니다.

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

print(np.matmul(M1, ket1))
print(np.matmul(M1, M2))
print(np.matmul(M, M))
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

이 출력 형식은 시각적으로 바랄 점이 있습니다. 더 좋은 출력이 필요한 상황에 대한 한 가지 해결책은 qiskit.visualization 모듈에서 Qiskit의 array_to_latex 함수를 사용하는 것입니다. 다음 코드에서 Python의 일반적인 display 함수를 사용하고 있다는 점을 주목하세요. 반대로 print의 특정 동작은 배열과 같이 인쇄되는 것에 따라 달라질 수 있습니다.

from qiskit.visualization import array_to_latex

display(array_to_latex(np.matmul(M1, ket1)))
display(array_to_latex(np.matmul(M1, M2)))
display(array_to_latex(np.matmul(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]

220+221\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)0231(\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\rangle1\vert 1\rangle의 대안은 실제로 이러한 상태 벡터와 동등합니다. 한 벡터가 단위원 위의 복소수로 곱해진 다른 벡터와 같으므로, 이들은 전역 위상까지 동등하다고 합니다. 이 문제는 양자 회로 강의에서 더 자세히 논의되며, 지금은 무시해도 됩니다.

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

Statevector는 또한 시스템에 대한 임의의 수의 측정을 시뮬레이션할 수 있는 sample_counts 메서드를 제공합니다. 매번 상태의 신선한 복사본으로 시작합니다. 예를 들어, 다음 코드는 벡터 v10001000번 측정한 결과를 보여줍니다. 높은 확률로 결과 00이 약 9번 중 5번 (또는 약 1000번 중 556번) 나타나고, 결과 11이 약 9번 중 4번 (또는 약 1000번 중 444번) 나타납니다. 다음 코드는 또한 결과를 시각화하기 위해 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 대신 다른 샘플 수를 사용하면, 시행 횟수가 각 결과가 나타나는 횟수에 어떻게 영향을 미치는지에 대한 직관을 발달시키는 데 도움이 될 수 있습니다. 샘플이 많을수록, 각 가능성에 대한 표본의 분수가 해당 확률에 더 가까워질 가능성이 높습니다. 일반적으로 이 현상은 확률 이론에서 큰 수의 법칙이라고 알려져 있습니다.

OperatorStatevector로 연산 수행

단항 연산(Unitary operations)은 다음 예제와 같이 Qiskit에서 Operator 클래스를 사용하여 정의할 수 있습니다. 이 클래스는 Statevector와 유사한 인수를 가진 draw 메서드를 포함합니다. latex 옵션은 array_from_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.14644660940.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.14644660940.3535533906i0.8535533906+0.3535533906i0.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.14644660940.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