주 콘텐츠로 건너뛰기

양자 텔레포테이션과 초고밀도 코딩

참고

Kifumi Numata (26 Apr 2024)

원본 강의의 PDF를 다운로드하세요. 정적 이미지로 제공되므로 일부 코드 스니펫은 더 이상 사용되지 않을 수 있습니다.

이 실험을 실행하는 데 필요한 QPU 시간은 약 10초입니다.

1. 소개

유틸리티 규모의 양자 문제를 해결하려면 양자 컴퓨터에서 한 Qubit에서 다른 Qubit으로 정보를 이동시킬 수 있어야 합니다. 이를 위한 잘 알려진 프로토콜들이 있는데, 그중 가장 기초적인 것들은 멀리 떨어진 당사자 간에 정보를 전송하는 맥락에서 고안되었습니다. 이 레슨에서는 "멀리 떨어진 친구들이 정보를 주고받는다"와 같이 이러한 맥락에 맞는 표현을 사용할 것입니다. 하지만 이 프로토콜들은 양자 컴퓨팅에서 더 넓은 의미를 지닌다는 점을 기억하세요. 이 레슨에서는 다음의 양자 통신 프로토콜을 다룹니다:

  • 양자 텔레포테이션 공유된 얽힘 상태(e-bit라고도 불림)를 사용하여 미지의 양자 상태를 멀리 있는 친구에게 전송합니다. 이때 추가적인 고전적 통신이 필요합니다.
  • 양자 초고밀도 코딩 공유된 얽힘 Qubit을 이용하여 단일 Qubit을 전송함으로써 두 비트의 정보를 멀리 있는 친구에게 전달하는 방법입니다.

이 주제들에 관한 배경 지식은 Entanglement in action의 Basics of Quantum Information 레슨 4를 참고해 주세요.

위의 설명에서 "미지의 양자 상태"란 이전 레슨에서 설명한 다음의 형태를 가진 상태를 의미합니다:

ψ=α0+β1|\psi\rangle =\alpha|0\rangle+\beta|1\rangle

여기서 α\alphaβ\betaα2+β2=1|\alpha|^2+|\beta|^2 = 1을 만족하는 복소수입니다. 이를 통해 양자 상태를 다음과 같이 쓸 수 있습니다:

ψ=cosθ20+eiφsinθ21=(cosθ2eiφsinθ2)|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle= \left( \begin{matrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{matrix} \right)

임의의 양자 상태에 담긴 정보를 전송할 수 있어야 하므로, 이 레슨은 그러한 상태를 생성하는 것에서부터 시작합니다.

2. 밀도 행렬

양자 상태 ψ|\psi \rangle를 밀도 행렬로 나타낼 수도 있습니다. 이 형태는 순수 양자 상태의 확률적 혼합을 표현하는 데 유용합니다. 단일 Qubit의 경우 다음과 같이 쓸 수 있습니다:

ψψρ=((cosθ2eiφsinθ2))((cosθ2eiφsinθ2))=12((1+cosθeiφsinθeiφsinθ1cosθ))|\psi \rangle \langle \psi| \equiv \rho = \left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) \left( \begin{pmatrix} \cos\frac{\theta}{2} & e^{-i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) =\frac{1}{2}\left(\begin{pmatrix} 1+\cos\theta & e^{-i\varphi}\sin\theta\\ e^{-i\varphi}\sin\theta & 1-\cos\theta \end{pmatrix}\right)

밀도 행렬 ρ\rho는 아래와 같이 파울리 행렬의 선형 합으로 나타낼 수 있습니다:

ρ=12(I+(sinθcosφ)X+(sinθsinφ)Y+(cosθ)Z)\rho = \frac{1}{2}\bigl( \textbf{I} + (\sin{\theta}\cos{\varphi})\textbf{X}+ (\sin{\theta}\sin{\varphi})\textbf{Y} + (\cos{\theta})\textbf{Z} \bigr)

또는 일반적으로,

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

여기서 rx2+ry2+rz2=1r_{x}^2+r_{y}^2+r_{z}^2=1입니다.

블로흐 벡터는 r=(rx,ry,rz)\textbf{r} = (r_{x}, r_{y}, r_{z})입니다.

이제 난수를 사용하여 임의의 양자 상태를 만들어 보겠습니다.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np

# create a random 1-qubit state from a random (theta, varphi) to define r vector
np.random.seed(1) # fixing seed for repeatibility

theta = np.random.uniform(0.0, 1.0) * np.pi # from 0 to pi
varphi = np.random.uniform(0.0, 2.0) * np.pi # from 0 to 2*pi

def get_r_vec(theta, varphi):
rx = np.sin(theta) * np.cos(varphi)
ry = np.sin(theta) * np.sin(varphi)
rz = np.cos(theta)
return (rx, ry, rz)

# get r vector
rx, ry, rz = get_r_vec(theta, varphi)

print("theta=" + str(theta), ",varphi=" + str(varphi))
print("(rx, ry, rz) = (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ")")
theta=1.3101132663588946 ,varphi=4.525932273597346
(rx, ry, rz) = (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022)

이 블로흐 벡터를 블로흐 구(Bloch sphere) 위에 시각화할 수 있습니다.

from qiskit.visualization import plot_bloch_vector

r = [rx, ry, rz]
plot_bloch_vector(r)

Output of the previous code cell

3. 양자 상태 단층촬영

계산 기저(0|0 \rangle1|1 \rangle)에서만 양자 상태를 측정하면 위상 정보(복소수 정보)가 손실됩니다. 하지만 준비 과정을 반복하여 ψ|\psi \rangle의 복사본을 여러 개 확보한다면(상태를 복제할 수는 없지만 준비 과정은 반복할 수 있습니다), 양자 상태 단층촬영을 수행하여 밀도 행렬 ρ\rhorx,ry,rzr_{x}, r_{y}, r_{z} 값을 추정할 수 있습니다. 다음의 형태에서:

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

아래가 성립합니다:

Tr(Xρ)=rx,Tr(Yρ)=ry,Tr(Zρ)=rzTr(\textbf{X} \rho) = r_{x}, \quad Tr(\textbf{Y} \rho) = r_{y}, \quad Tr(\textbf{Z} \rho) = r_{z}

rzr_{z}의 경우,

Tr(Zρ)=0Zρ0+1Zρ1Tr(\textbf{Z} \rho) = \langle 0|\textbf{Z} \rho|0 \rangle + \langle 1|\textbf{Z} \rho|1 \rangle =0(0011)ρ0+1(0011)ρ1= \langle 0|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|0 \rangle +\langle 1|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|1 \rangle =0ρ01ρ1=\langle 0|\rho|0 \rangle- \langle 1| \rho|1 \rangle =0ψψ01ψψ1=\langle 0|\psi\rangle\langle \psi|0 \rangle - \langle 1| \psi\rangle\langle \psi|1 \rangle =α2β2=|\alpha|^2-|\beta|^2

마지막 변환은 ψ=α0+β1|\psi \rangle =\alpha|0\rangle+\beta|1\rangle에 대한 것입니다. 따라서 rzr_{z}0|0 \rangle의 확률에서 1|1 \rangle의 확률을 빼서 구할 수 있습니다.

rzr_z 값 추정

rzr_z를 추정하기 위해 양자 상태를 생성하고 측정합니다. 준비와 측정을 여러 번 반복한 후, 측정 통계를 이용하여 위의 확률들을 추정하고 rzr_z를 추산합니다.

임의의 양자 상태를 생성하기 위해 매개변수 θ,φ\theta, \varphi를 가진 일반 유니타리 Gate UU를 사용합니다. (자세한 내용은 U-gate를 참고하세요.)

from qiskit import QuantumCircuit

# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)

# measure in computational basis
qc.measure(0, 0)

qc.draw(output="mpl")

Output of the previous code cell

AerSimulator를 사용하여 계산 기저에서 측정함으로써 rzr_z를 추정합니다.

# see if the expected value of measuring in the computational basis
# approaches the limit of rz
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram

# Define backend
backend = AerSimulator()
nshots = 1000 # or 10000
# nshots = 10000

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()

# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram

plot_histogram(counts)
{'1': 375, '0': 625}

Output of the previous code cell

rz_approx = (counts["0"] - counts["1"]) / nshots

print("rz = ", rz, " and approx of rz = ", rz_approx)
rz =  0.2577405946274022  and approx of rz =  0.25

양자 상태 단층촬영 방법을 사용하여 rzr_z 값을 추정했습니다. 이 경우에는 "난수" 상태의 매개변수를 직접 설정했기 때문에 rzr_z의 실제 값을 알고 있어 결과를 검증할 수 있습니다. 하지만 본질적으로 유틸리티 규모의 작업은 항상 이렇게 간단하게 검증할 수 있는 것은 아닙니다. 양자 결과를 검증하는 방법에 대해서는 이 코스의 후반부에서 더 자세히 다룰 것입니다. 지금은 추정값이 상당히 정확하다는 점만 확인하세요.

Exercise 1: rxr_x 값 추정

IBM® 양자 컴퓨터는 zz축을 따라 측정합니다(때로는 "zz 기저에서" 또는 "계산 기저에서" 측정한다고 표현합니다). 그러나 측정 전에 회전을 적용하면 양자 상태의 x축 방향 성분도 측정할 수 있습니다. 좀 더 정확히 말하면, xx 방향을 가리키던 것이 zz 방향을 가리키도록 시스템을 회전시키면, 하드웨어의 zz 방향 측정 장치는 그대로 유지하면서 방금 전까지 xx 방향을 따르던 상태에 대한 정보를 얻을 수 있습니다. 대부분의 양자 컴퓨터(IBM 양자 컴퓨터 포함)가 여러 축 방향으로 측정을 수행하는 방식이 바로 이것입니다.

이 원리를 이해했다면, 양자 상태 토모그래피를 사용해 rxr_x 값을 추정하는 코드를 직접 작성해 보세요.

풀이:

# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)

qc.h(0)
qc.measure(0, 0)

qc.draw(output="mpl")

Output of the previous code cell

# Define backend
backend = AerSimulator()
nshots = 10000

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()

# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'1': 5925, '0': 4075}

Output of the previous code cell

rx_approx = (counts["0"] - counts["1"]) / nshots

print("rx = ", rx, " and approx of rx = ", rx_approx)
rx =  -0.1791150283307452  and approx of rx =  -0.185

Exercise 2: ryr_y 값 추정

앞서와 동일한 논리적 추론을 사용하면, 측정 전에 시스템을 회전시켜 ryr_y에 대한 정보를 얻을 수 있습니다. 양자 상태 토모그래피를 사용해 ryr_y 값을 추정하는 코드를 직접 작성해 보세요. 앞선 예제를 출발점으로 삼되, 다른 회전을 적용하면 됩니다. (sdg 등 사용되는 다양한 게이트에 대한 자세한 내용은 API 레퍼런스를 참고하세요.)

풀이:

# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)

qc.sdg(0)
qc.h(0)
qc.measure(0, 0)

qc.draw(output="mpl")

Output of the previous code cell

# Define backend
backend = AerSimulator()
nshots = 10000

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()

# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'1': 9759, '0': 241}

Output of the previous code cell

ry_approx = (counts["0"] - counts["1"]) / nshots

print("ry = ", ry, " and approx of ry = ", ry_approx)
ry =  -0.9494670044331133  and approx of ry =  -0.9518

이로써 r\vec{r}의 모든 성분을 추정하여 전체 벡터를 구할 수 있게 되었습니다.

print("Estimated vector is (", rx_approx, ",", ry_approx, ",", rz_approx, ").")
print("Original random vector was (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ").")
Estimated vector is ( -0.185 , -0.9518 , 0.25 ).
Original random vector was (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022).

이 양자 상태 토모그래피 방법을 통해 원래의 무작위 벡터를 상당히 정확하게 추정할 수 있었습니다.

4. 양자 텔레포테이션

Alice라는 인물이 알 수 없는 양자 상태 ψ|\psi \rangle를 멀리 있는 친구 Bob에게 전송하려는 상황을 생각해 봅시다. 두 사람이 이메일이나 전화와 같은 고전적인 통신 수단만 사용할 수 있다고 가정합니다. Alice는 복제 불가 정리(no-cloning theorem)에 의해 양자 상태를 복사할 수 없습니다. 준비 과정을 여러 번 반복한다면 통계를 쌓을 수 있지만, 단 하나의 알 수 없는 상태만 존재한다면 어떻게 해야 할까요? 이 상태는 연구하고 싶은 물리적 과정에서 발생했거나, 더 큰 양자 계산의 일부일 수도 있습니다. 그런 경우, Alice는 어떻게 이 상태를 Bob에게 전달할 수 있을까요? 바로 이전 단원에서 소개한 Bell 상태 00+112\frac {|00\rangle + |11\rangle}{\sqrt 2}와 같은 귀중한 양자 자원, 즉 공유된 얽힘 상태가 있다면 가능합니다. 이 얽힘 상태를 "EPR 쌍" 또는 "e-비트"(얽힘의 기본 단위)라고도 부릅니다. Alice와 Bob이 이러한 얽힘 상태를 공유하고 있다면, Alice는 일련의 양자 연산을 수행하고 두 비트의 고전 정보를 Bob에게 전달함으로써 알 수 없는 양자 상태를 Bob에게 텔레포트할 수 있습니다.

4.1 양자 텔레포테이션 프로토콜

전제 조건: Alice는 Bob에게 전송할 알 수 없는 양자 상태 ψ|\psi \rangle를 가지고 있습니다. Alice와 Bob은 2-Qubit 얽힘 상태, 즉 e-비트를 공유하며, 각자는 해당 Qubit 중 하나를 자신의 위치에 물리적으로 보유합니다.

아래에 설명 없이 절차를 간략히 정리합니다. 자세한 내용은 아래에서 구현을 통해 살펴봅니다.

  1. Alice는 CNOT Gate를 사용하여 ψ|\psi \rangle를 자신이 보유한 e-비트와 얽힙니다.
  2. Alice는 ψ|\psi \rangle에 Hadamard Gate를 적용한 뒤, 두 Qubit를 모두 계산 기저(computational basis)에서 측정합니다.
  3. Alice는 측정 결과("00", "01", "10", "11" 중 하나)를 Bob에게 전송합니다.
  4. Bob은 Alice의 두 비트 정보를 바탕으로 자신이 보유한 e-비트 쌍에 보정 연산자를 적용합니다.
    • "00"이면 Bob은 아무것도 하지 않습니다.
    • "01"이면 Bob은 X Gate를 적용합니다.
    • "10"이면 Bob은 Z Gate를 적용합니다.
    • "11"이면 Bob은 iY = ZX Gate를 적용합니다.
  5. Bob이 보유한 e-비트가 ψ|\psi \rangle 상태가 됩니다.

이 내용은 Basics of Quantum Information에서도 더 자세히 다루고 있습니다. Qiskit으로 직접 구현해 보면 상황이 더욱 명확해질 것입니다.

4.2 양자 텔레포테이션을 시뮬레이션하는 양자 Circuit

항상 그렇듯이, Qiskit 패턴 프레임워크를 적용합니다. 이 소절은 매핑(mapping) 단계에 집중합니다.

1단계: 문제를 양자 Circuit과 연산자로 매핑하기

위의 시나리오를 표현하려면 세 개의 Qubit가 필요합니다. Alice와 Bob이 공유하는 얽힘 쌍에 두 개, 그리고 알 수 없는 양자 상태 ψ|\psi\rangle에 하나입니다.

from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)

qc.draw(output="mpl")

Output of the previous code cell

처음에 Alice는 알 수 없는 양자 상태 ψ|\psi \rangle를 가지고 있습니다. 이 상태는 UU Gate를 사용하여 만듭니다.

# Create the unknown quantum state using the u-gate. Alice has this.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation

qc.draw(output="mpl")

Output of the previous code cell

UU Gate에 사용된 파라미터를 알고 있기 때문에 생성된 상태를 시각화할 수 있습니다. 그러나 이 상태가 복잡한 양자 과정에서 나온 것이라면, 해당 과정을 여러 번 실행하여 토모그래피처럼 통계를 수집하지 않고서는 상태를 알 수 없습니다.

# show the quantum state on bloch sphere
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector

out_vector = Statevector(qc)

plot_bloch_multivector(out_vector)

Output of the previous code cell

이 프로토콜이 시작되기 전에, Alice와 Bob은 이미 공유 얽힘 쌍을 준비했다고 가정합니다. Alice와 Bob이 실제로 서로 다른 장소에 있다면, 알 수 없는 상태 ψ|\psi\rangle가 생성되기 이전에 공유 상태를 미리 구성했을 수도 있습니다. 이 두 과정은 서로 다른 Qubit에서 일어나기 때문에 순서는 중요하지 않으며, 시각화 편의상 현재 순서를 사용합니다.

# Alice and Bob are together in the same place and set up an entangled pair.
qc.h(1)
qc.cx(1, 2)
qc.barrier() # for visual separation.
# We can consider that Alice and Bob might move their qubits to different physical locations, now.

qc.draw(output="mpl")

Output of the previous code cell

다음으로, Alice는 CXCX Gate와 HH Gate를 사용하여 ψ|\psi \rangle를 공유 e-비트의 자신 몫과 얽고, 계산 기저에서 측정합니다.

# Alice entangles the unknown state with her part of the e-bit, using the CNOT gate and H gate.
qc.cx(0, 1)
qc.h(0)
qc.barrier()

# Alice measures the two qubits.
qc.measure(0, 0)
qc.measure(1, 1)

qc.draw(output="mpl")

Output of the previous code cell

Alice는 측정 결과("00", "01", "10", "11" 중 하나)를 Bob에게 전달하고, Bob은 두 비트 정보를 바탕으로 공유 e-비트의 자신 몫에 보정 연산자를 적용합니다. 그러면 Bob의 Qubit이 ψ|\psi \rangle 상태가 됩니다.

# Alice sent the results to Bob. Bob applies correction
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()

qc.draw(output="mpl")

Output of the previous code cell

양자 텔레포테이션 Circuit이 완성되었습니다! 상태 벡터 시뮬레이터를 사용하여 이 Circuit의 출력 상태를 확인해 봅시다.

from qiskit_aer import StatevectorSimulator

backend = StatevectorSimulator()
out_vector = backend.run(qc, shots=1).result().get_statevector() # set shots = 1

plot_bloch_multivector(out_vector)

Output of the previous code cell

Qubit 0(원래 비밀 상태를 보유하고 있던 Qubit)의 UU Gate로 생성된 양자 상태가 Qubit 2(Bob의 Qubit)로 전달된 것을 확인할 수 있습니다.

위 셀을 몇 번 실행해 보세요. Qubit 0과 1의 상태는 변하지만, Qubit 2는 항상 상태 ψ|\psi\rangle에 있다는 것을 알 수 있습니다.

4.3 실행 및 U 역행렬 적용으로 결과 확인하기

위에서는 텔레포트된 상태가 시각적으로 올바른지 확인했습니다. 양자 상태가 올바르게 텔레포트되었는지 확인하는 또 다른 방법은, Bob의 Qubit에 UU Gate의 역행렬을 적용하여 '0'을 측정하는 것입니다. U1UU^{-1}U는 항등 연산이므로, Bob의 Qubit가 U0U|0\rangle로 생성된 상태에 있다면 역행렬을 적용했을 때 U1U0=0U^{-1}U|0\rangle=|0\rangle이 되어야 합니다.

# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse() # inverse of u(theta,varphi,0.0)
qc.measure(2, 2) # add measurement gate

qc.draw(output="mpl")

Output of the previous code cell

실제 양자 컴퓨터로 넘어가기 전에 먼저 AerSimulator로 Circuit을 실행합니다.

from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram

# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()

# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'011': 2510, '010': 2417, '000': 2635, '001': 2438}

Output of the previous code cell

리틀 엔디언(little endian) 표기법에서 Qubit 2는 가장 왼쪽(또는 열 레이블에서 가장 아래쪽) Qubit입니다. 열 레이블에서 가장 왼쪽·아래쪽 Qubit은 모든 가능한 결과에서 0임을 주목하세요. 이는 q2q_20|0\rangle 상태에서 측정할 확률이 100%임을 나타냅니다. 이것이 기대했던 결과이며, 텔레포테이션 프로토콜이 올바르게 작동했음을 보여줍니다.

4.4 실제 양자 컴퓨터에서의 텔레포테이션

다음으로, 실제 양자 컴퓨터에서 텔레포테이션을 수행해 보겠습니다. 동적 Circuit 기능을 사용하면 측정 결과를 기반으로 Circuit 중간에 연산을 수행할 수 있으며, 텔레포테이션 Circuit의 조건부 연산을 실시간으로 구현할 수 있습니다. 실제 양자 컴퓨터로 문제를 해결하기 위해 Qiskit 패턴의 네 단계를 따릅니다.

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

연습 3: 텔레포테이션 Circuit 구성하기

이해도를 확인하기 위해 텔레포테이션 Circuit 전체를 처음부터 구성해 보세요. 필요하다면 위로 스크롤하여 내용을 다시 확인하세요.

풀이:

# Step 1: Map problem to quantum circuits and operators
# Create the circuit with 3-qubits and 1-bit
qc = QuantumCircuit(3, 3)

# Alice creates an unknown quantum state using the u-gate.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation

# Eve creates EPR pair and sends q1 to Alice and q2 to Bob
##your code goes here##
qc.h(1)
qc.cx(1, 2)
qc.barrier()

# Alice entangles the unknown state with her EPR part, using the CNOT gate and H gate.
##your code goes here##
qc.cx(0, 1)
qc.h(0)
qc.barrier()

# Alice measures the two qubits.
##your code goes here##
qc.measure(0, 0)
qc.measure(1, 1)

# Alice sent the results to Bob. Now, Bob applies correction
##your code goes here##
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()

# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse()
qc.measure(2, 2)

qc.draw(output="mpl")

Output of the previous code cell

참고로, UU Gate의 역연산을 적용하는 것은 예상되는 동작을 검증하기 위한 것입니다. 이는 Bob에게 양자 상태를 전송하는 과정의 일부가 아니며, 양자 정보를 전달하는 것만이 목표라면 역 UU Gate는 사용하지 않아도 됩니다.

2단계: 대상 하드웨어에 맞게 최적화

하드웨어에서 실행하려면 QiskitRuntimeService를 가져오고 저장된 자격 증명을 불러오세요. 대기 중인 작업 수가 가장 적은 Backend를 선택하세요.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
service.backends()
[<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_torino')>]
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is  <IBMBackend('ibm_brisbane')>
# You can specify the device
# backend = service.backend('ibm_brisbane')

선택한 디바이스의 커플링 맵을 확인해 보겠습니다.

from qiskit.visualization import plot_gate_map

plot_gate_map(backend)

Output of the previous code cell

디바이스마다 커플링 맵이 다를 수 있으며, 각 디바이스에는 성능이 더 뛰어난 Qubit과 커플러가 있습니다. 또한 양자 컴퓨터마다 네이티브 Gate(하드웨어가 실행할 수 있는 Gate)가 다를 수 있습니다. Circuit을 Transpile하면 추상적인 양자 Circuit을 대상 양자 컴퓨터가 실행할 수 있는 Gate로 재작성하고, 물리적 Qubit에 대한 최적의 매핑을 선택합니다(그 외에도 다양한 작업을 수행합니다). Transpilation은 복잡하고 풍부한 주제입니다. Transpilation에 대한 자세한 내용은 API 레퍼런스를 참고하세요.

# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)

qc_compiled.draw("mpl", idle_wires=False, fold=-1)

Output of the previous code cell

3단계: Circuit 실행

Sampler 런타임 프리미티브를 사용하여 대상 Circuit을 실행합니다.

# Step 3: Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nkhpn2txg008jt0d0
# Check the job status
job.status()
'DONE'

IBM Quantum® 대시보드에서도 작업 상태를 확인할 수 있습니다.

# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
job_real = service.job(job.job_id()) # Input your job-id between the quotations
job_real.status()
'DONE'

'DONE'이 표시되면 아래 셀을 실행하여 결과를 가져올 수 있습니다.

# Execute after 'DONE' is displayed
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
{'001': 992, '110': 430, '011': 579, '010': 605, '111': 402, '000': 925, '100': 57, '101': 106}

4단계: 결과 후처리

# Step 4: Post-process the results
from qiskit.visualization import plot_histogram

plot_histogram(result_real[0].data.c.get_counts())

Output of the previous code cell

위의 결과를 직접 해석할 수 있습니다. 또는 marginal_count를 사용하여 Qubit 2에 대한 Bob의 결과를 추적할 수 있습니다.

# trace out Bob's results on qubit 2
from qiskit.result import marginal_counts

bobs_qubit = 2
real_counts = result_real[0].data.c.get_counts()
bobs_counts = marginal_counts(real_counts, [bobs_qubit])
plot_histogram(bobs_counts)

Output of the previous code cell

여기서 볼 수 있듯이, 1|1 \rangle로 측정된 결과가 일부 있습니다. 이는 노이즈와 오류 때문입니다. 특히 동적 Circuit은 Circuit 중간의 측정이 시간이 많이 소요되기 때문에 오류율이 더 높은 경향이 있습니다.

4.5 양자 텔레포테이션의 핵심 요점

얽힌 Qubit 쌍(e-bit)을 공유함으로써 먼 곳의 친구에게 양자 상태를 전송할 수 있습니다.

  1. 양자 텔레포테이션은 빛보다 빠르게 양자 상태를 전송할 수 있을까요? 아니요, Alice가 고전적인 방식으로 Bob에게 측정 결과를 알려줘야 하기 때문입니다.

  2. 양자 텔레포테이션이 양자 상태의 복사를 금지하는 "복제 불가 정리"를 위반할까요? 아니요, 왜냐하면 Alice의 Qubit 중 하나에 주어진 원래 양자 상태가 측정 과정에서 소멸되기 때문입니다. 해당 상태는 0|0\rangle 또는 1|1\rangle로 붕괴됩니다.

5. 초고밀도 부호화

거의 동일한 설정을 전혀 다른 목적에 활용할 수 있습니다. Alice가 Bob에게 두 비트의 고전 정보를 보내고 싶지만, Bob과 고전적인 통신 수단이 없는 상황을 가정해 봅시다. 그러나 Alice와 Bob은 얽힘 쌍을 공유하고 있고, Alice는 자신의 Qubit을 Bob의 위치로 전송할 수 있습니다. 이 점이 양자 텔레포테이션 프로토콜과 대비됩니다. 텔레포테이션에서는 고전적 통신이 가능하고 목표가 양자 상태를 전송하는 것이었다면, 여기서는 고전적 통신이 불가능하고 Qubit의 전송을 이용해 두 비트의 고전 정보를 공유합니다.

5.1 초고밀도 부호화 프로토콜

가정: Alice는 두 비트의 정보, 즉 a1a2{00,01,10,11}a_1a_2 \in \{00, 01, 10, 11\}를 가지고 있습니다. Alice와 Bob은 얽힘 쌍(e-bit)을 공유하고 있지만, 고전적 통신은 할 수 없습니다.

  1. Alice는 자신이 가진 e-bit 부분에 다음 중 하나의 연산을 수행합니다.
    • a1a2=00a_1a_2 = 00이면, 아무것도 하지 않습니다
    • a1a2=01a_1a_2 = 01이면, Z Gate를 적용합니다
    • a1a2=10a_1a_2 = 10이면, X Gate를 적용합니다
    • a1a2=11a_1a_2 = 11이면, Z Gate와 X Gate를 적용합니다.
  2. Alice는 자신의 e-bit 부분을 Bob의 위치로 전송합니다.
  3. Bob은 Alice로부터 받은 Qubit을 제어 Qubit으로, 자신의 Qubit을 타깃으로 하여 CNOT Gate를 적용한 뒤, Alice로부터 받은 Qubit에 H Gate를 적용하고 두 Qubit을 측정합니다. 가능한 초기 상태와 Bob의 연산 결과는 다음과 같습니다:
00+112CX01H000\frac {|00\rangle + |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |00\rangle 00112CX01H001\frac {|00\rangle - |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |01\rangle 10+012CX01H010\frac {|10\rangle + |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |10\rangle 10012CX01H011\frac {|10\rangle - |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow -|11\rangle

11-|11\rangle의 음수 부호는 전역 위상(global phase)이므로 측정 불가능합니다.

5.2 초고밀도 부호화를 시뮬레이션하는 양자 Circuit

초고밀도 부호화 프로토콜을 바탕으로, 아래와 같이 초고밀도 부호화 Circuit을 구성할 수 있습니다. Alice가 Bob에게 전달하려는 메시지인 msg를 변경해 보세요.

from qiskit import QuantumCircuit

Qiskit 패턴의 각 단계는 코드 주석에 표시되어 있습니다.

# Step 1: Map problem to quantum circuits and operators
# Create 2-qubits circuit
qc = QuantumCircuit(2, 2)

# Eve creates EPR pair and send q0 to Alice and q1 to Bob
qc.h(0)
qc.cx(0, 1)
qc.barrier()

# set message which Alice wants to transform to Bob
msg = "11" # You can change the message

if msg == "00":
pass
elif msg == "10":
qc.x(0)
elif msg == "01":
qc.z(0)
elif msg == "11":
qc.z(0)
qc.x(0)

qc.barrier()
# Bob receives EPR qubit from Alice and performs unitary operations
qc.cx(0, 1)
qc.h(0)
qc.barrier()

# Bob measures q0 and q1
qc.measure(0, 0)
qc.measure(1, 1)

qc.draw(output="mpl")

이전 코드 셀의 출력

# We will execute on a simulator first
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler

# Define backend
backend = AerSimulator()
shots = 1000

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job_sim = sampler.run([isa_qc], shots=shots)
result_sim = job_sim.result()

# Extract counts data
counts = result_sim[0].data.c.get_counts()
print(counts)
{'11': 1000}
# Visualize the results
from qiskit.visualization import plot_histogram

plot_histogram(counts)

이전 코드 셀의 출력

Bob이 Alice가 전달하려 했던 메시지를 정확히 수신한 것을 확인할 수 있습니다.

이제 실제 양자 컴퓨터에서도 시도해 봅시다.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is  <IBMBackend('ibm_brisbane')>
# Step 1 was already completed before the simulator job above.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)

qc_compiled.draw("mpl", idle_wires=False)

이전 코드 셀의 출력

# Step 3:Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nnyq3grvg008j0zag
# Check the job status
job.status()
'DONE'
# If the Notebook session got disconnected you can also check your job status by running the following code
# from qiskit_ibm_runtime import QiskitRuntimeService
# service = QiskitRuntimeService()
job = service.job(job_id) # Input your job-id between the quotations
job.status()
'DONE'
# Execute after job has successfully run
real_result = job.result()
print(real_result[0].data.c.get_counts())
{'11': 3942, '01': 107, '10': 41, '00': 6}
# Step 4: post-process the results
from qiskit.visualization import plot_histogram

plot_histogram(real_result[0].data.c.get_counts())

이전 코드 셀의 출력

결과는 예상과 일치합니다. 실제 양자 컴퓨터에서 실행한 초고밀도 부호화는 양자 텔레포테이션에 비해 오류가 더 적게 나타났습니다. 그 이유 중 하나는 양자 텔레포테이션이 동적 Circuit을 사용하는 반면, 초고밀도 부호화는 그렇지 않기 때문입니다. 양자 Circuit의 오류에 대해서는 이후 강의에서 더 자세히 다룰 예정입니다.

6. 요약

이번 세션에서는 두 가지 양자 프로토콜을 구현했습니다. 두 프로토콜 모두 먼 거리의 친구 사이에서 통신하는 시나리오를 다루고 있어 단일 QPU에서의 양자 컴퓨팅과는 다소 거리가 있어 보이지만, 양자 컴퓨팅에서도 실제 응용 분야가 있으며 양자 정보의 전송 원리를 이해하는 데 도움이 됩니다.

  • 양자 텔레포테이션: 양자 상태를 복사할 수는 없지만, 얽힘을 공유함으로써 미지의 양자 상태를 텔레포트할 수 있습니다.
  • 양자 초고밀도 부호화: 얽힘 쌍을 공유하고 Qubit 하나를 전송함으로써 두 비트의 고전 정보를 전달할 수 있습니다.
# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'