주 콘텐츠로 건너뛰기

양자 텔레포테이션과 초고밀도 부호화

참고

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⁡θ2∣0⟩+eiφsin⁡θ2∣1⟩=(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⁡θ2e−iφsin⁡θ2))=12((1+cos⁡θe−iφsin⁡θe−iφsin⁡θ1−cos⁡θ))|\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 \rangle 및 ∣1⟩|1 \rangle)에서만 양자 상태를 측정하면 위상 정보(복소수 정보)가 손실됩니다. 하지만 준비 과정을 반복하여 ∣ψ⟩|\psi \rangle의 복사본을 여러 개 확보한다면(상태를 복제할 수는 없지만 준비 과정은 반복할 수 있습니다), 양자 상태 토모그래피를 수행하여 밀도 행렬 ρ\rho의 rx,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ρ)=⟨0∣Zρ∣0⟩+⟨1∣Zρ∣1⟩Tr(\textbf{Z} \rho) = \langle 0|\textbf{Z} \rho|0 \rangle + \langle 1|\textbf{Z} \rho|1 \rangle =⟨0∣(∣0⟩⟨0∣−∣1⟩⟨1∣)ρ∣0⟩+⟨1∣(∣0⟩⟨0∣−∣1⟩⟨1∣)ρ∣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∣ρ∣0⟩−⟨1∣ρ∣1⟩=\langle 0|\rho|0 \rangle- \langle 1| \rho|1 \rangle =⟨0∣ψ⟩⟨ψ∣0⟩−⟨1∣ψ⟩⟨ψ∣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⟩+∣11⟩2\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'을 측정하는 것입니다. U−1UU^{-1}U는 항등 연산이므로, Bob의 Qubit가 U∣0⟩U|0\rangle로 생성된 상태에 있다면 역행렬을 적용했을 때 U−1U∣0⟩=∣0⟩U^{-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_2를 ∣0⟩|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⟩+∣11⟩2→CX01⊗H0→∣00⟩\frac {|00\rangle + |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |00\rangle ∣00⟩−∣11⟩2→CX01⊗H0→∣01⟩\frac {|00\rangle - |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |01\rangle ∣10⟩+∣01⟩2→CX01⊗H0→∣10⟩\frac {|10\rangle + |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |10\rangle ∣10⟩−∣01⟩2→CX01⊗H0→−∣11⟩\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'