Skip to main content
IBM Quantum Platform

양자 잡음 및 오류 완화

Note

토시나리 이토코 (2024년 6월 28일)

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

이 실험을 실행하는 데 걸리는 대략적인 QPU 시간은 1m 40초입니다.


1. 소개

이 강의에서는 양자 컴퓨터에서 노이즈와 이를 완화할 수 있는 방법을 살펴봅니다. 실제 양자 컴퓨터의 노이즈 프로파일을 사용하는 등 몇 가지 방법으로 노이즈를 시뮬레이션할 수 있는 시뮬레이터를 사용하여 노이즈의 영향을 살펴보는 것으로 시작하겠습니다. 그런 다음 노이즈가 내재된 실제 양자 컴퓨터로 넘어가겠습니다. 영 노이즈 추정(ZNE) 및 게이트 트윌링 등의 조합을 포함한 오류 완화 효과를 살펴보겠습니다.

몇 가지 패키지를 로드하는 것부터 시작하겠습니다.

# !pip install qiskit qiskit_aer qiskit_ibm_runtime
# !pip install jupyter
# !pip install matplotlib pylatexenc
import qiskit

qiskit.__version__

Output:

'2.0.2'
import qiskit_aer

qiskit_aer.__version__

Output:

'0.17.1'
import qiskit_ibm_runtime

qiskit_ibm_runtime.__version__

Output:

'0.40.1'

2. 오류 완화 없는 시끄러운 시뮬레이션

키스킷 에어는 양자 컴퓨팅을 위한 고전적인 시뮬레이터입니다. 양자 회로의 이상적인 실행뿐만 아니라 노이즈가 있는 실행도 시뮬레이션할 수 있습니다. 이 노트북은 키스킷 에어를 사용하여 노이즈 시뮬레이션을 실행하는 방법을 보여줍니다:

  1. 노이즈 모델 구축
  2. 노이즈 모델로 노이즈 샘플러(시뮬레이터) 구축하기
  3. 노이즈 샘플러에서 양자 회로 실행하기
noise_model = NoiseModel()
...
noisy_sampler = Sampler(options={"backend_options": {"noise_model": noise_model}})
job = noisy_sampler.run([circuit])

2.1 시험 회로를 구축하십시오

X 게이트를 d 번만 반복하는 장난감 1-큐비트 회로를 고려해 보겠습니다(d=0... 100)를 측정하고 Z 관찰 가능.

from qiskit.circuit import QuantumCircuit

MAX_DEPTH = 100
circuits = []
for d in range(MAX_DEPTH + 1):
    circ = QuantumCircuit(1)
    for _ in range(d):
        circ.x(0)
        circ.barrier(0)
    circ.measure_all()
    circuits.append(circ)

display(circuits[3].draw(output="mpl"))

Output:

Output of the previous code cell
from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp.from_list([("Z", 1.0)])
obs

Output:

SparsePauliOp(['Z'],
              coeffs=[1.+0.j])

2.2 소음 모델 구축

노이즈 시뮬레이션을 수행하려면 NoiseModel 을 지정해야 합니다. 이 섹션에서는 NoiseModel 구축 방법을 설명합니다.

먼저 노이즈 모델에 추가할 양자(또는 판독) 오류를 정의해야 합니다.

from qiskit_aer.noise.errors import (
    coherent_unitary_error,
    amplitude_damping_error,
    ReadoutError,
)
from qiskit.circuit.library import RXGate

# Coherent (unitary) error: Over X-rotation error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.coherent_unitary_error.html#qiskit_aer.noise.coherent_unitary_error
OVER_ROTATION_ANGLE = 0.05
coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())

# Incoherent error: Amplitude dumping error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.amplitude_damping_error.html#qiskit_aer.noise.amplitude_damping_error
AMPLITUDE_DAMPING_PARAM = 0.02  # in [0, 1] (0: no error)
incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)

# Readout (measurement) error: Readout error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.ReadoutError.html#qiskit_aer.noise.ReadoutError
PREP0_MEAS1 = 0.03  # P(1|0): Probability of preparing 0 and measuring 1
PREP1_MEAS0 = 0.08  # P(0|1): Probability of preparing 1 and measuring 0
readout_error = ReadoutError(
    [[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]
)
from qiskit_aer.noise import NoiseModel

noise_model = NoiseModel()
noise_model.add_quantum_error(coherent_error.compose(incoherent_error), "x", (0,))
noise_model.add_readout_error(readout_error, (0,))

2.3 노이즈 모델을 사용한 노이즈 샘플러 구축

from qiskit_aer.primitives import SamplerV2 as Sampler

noisy_sampler = Sampler(options={"backend_options": {"noise_model": noise_model}})

2.4 잡음이 있는 샘플러에서 양자 회로를 실행하다

job = noisy_sampler.run(circuits, shots=400)
result = job.result()
result[0].data.meas.get_counts()

Output:

{'0': 389, '1': 11}

2.5 결과 플롯

import matplotlib.pyplot as plt

plt.title("Noisy simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
    ds,
    [result[d].data.meas.expectation_values(["Z"]) for d in ds],
    color="gray",
    linestyle="-",
)
plt.scatter(ds, [result[d].data.meas.expectation_values(["Z"]) for d in ds], marker="o")
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Circuit depth")
plt.ylabel("Measured <Z>")
plt.show()

2.6 이상적인 시뮬레이션

ideal_sampler = Sampler()
job_ideal = ideal_sampler.run(circuits)
result_ideal = job_ideal.result()
plt.title("Ideal simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
    ds,
    [result_ideal[d].data.meas.expectation_values(["Z"]) for d in ds],
    color="gray",
    linestyle="-",
)
plt.scatter(
    ds, [result_ideal[d].data.meas.expectation_values(["Z"]) for d in ds], marker="o"
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.xlabel("Circuit depth")
plt.ylabel("Measured <Z>")
plt.show()

Output:

Output of the previous code cell

2.7 운동

아래 코드를 조정하면 됩니다,

  • 25x 샷 수(= 10_000 샷)를 시도하고 더 부드러운 플롯을 얻을 수 있는지 확인합니다
  • 노이즈 매개변수(OVER_ROTATION_ANGLE, AMPLITUDE_DAMPING_PARAM, PREP0_MEAS1, 또는 PREP1_MEAS0 )를 변경하고 플롯이 어떻게 변화하는지 확인합니다
OVER_ROTATION_ANGLE = 0.05
coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())
AMPLITUDE_DAMPING_PARAM = 0.02  # in [0, 1] (0: no error)
incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)
PREP0_MEAS1 = 0.1  # P(1|0): Probability of preparing 0 and measuring 1
PREP1_MEAS0 = 0.05  # P(0|1): Probability of preparing 1 and measuring 0
readout_error = ReadoutError(
    [[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]
)
noise_model = NoiseModel()
noise_model.add_quantum_error(coherent_error.compose(incoherent_error), "x", (0,))
noise_model.add_readout_error(readout_error, (0,))
options = {
    "backend_options": {"noise_model": noise_model},
}
noisy_sampler = Sampler(options=options)
job = noisy_sampler.run(circuits, shots=400)
result = job.result()
plt.title("Noisy simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
    ds,
    [result[d].data.meas.expectation_values(["Z"]) for d in ds],
    marker="o",
    linestyle="-",
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Depth")
plt.ylabel("Measured <Z>")
plt.show()

Output:

Output of the previous code cell

2.8 더 현실적인 잡음 시뮬레이션

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import SamplerV2 as Sampler, QiskitRuntimeService

service = QiskitRuntimeService()
real_backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=127
)  # Eagle

Output:

<IBMBackend('ibm_strasbourg')>
aer = AerSimulator.from_backend(real_backend)
noisy_sampler = Sampler(mode=aer)
job = noisy_sampler.run(circuits)
result = job.result()
plt.title("Noisy simulation with noise model from real backend")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
    ds,
    [result[d].data.meas.expectation_values(["Z"]) for d in ds],
    marker="o",
    linestyle="-",
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Depth")
plt.ylabel("Measured <Z>")
plt.show()

Output:

Output of the previous code cell

3. 오류 완화 기능을 갖춘 실제 양자 계산

이 파트에서는 키스킷 추정기를 사용하여 오류를 완화한 결과(기대값)를 얻는 방법을 설명합니다. 1차원 아이싱 모델의 시간 진화를 시뮬레이션하기 위해 6큐비트 트로터화 회로를 고려하고, 시간 단계 수에 따라 오차가 어떻게 확장되는지 살펴봅니다.

backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=127
)  # Eagle
backend

Output:

<IBMBackend('ibm_strasbourg')>
NUM_QUBITS = 6
NUM_TIME_STEPS = list(range(8))
RX_ANGLE = 0.1
RZZ_ANGLE = 0.1

3.1 회로를 구축하다

# Build circuits with different number of time steps
circuits = []
for n_steps in NUM_TIME_STEPS:
    circ = QuantumCircuit(NUM_QUBITS)
    for i in range(n_steps):
        # rx layer
        for q in range(NUM_QUBITS):
            circ.rx(RX_ANGLE, q)
        # 1st rzz layer
        for q in range(1, NUM_QUBITS - 1, 2):
            circ.rzz(RZZ_ANGLE, q, q + 1)
        # 2nd rzz layer
        for q in range(0, NUM_QUBITS - 1, 2):
            circ.rzz(RZZ_ANGLE, q, q + 1)
    circ.barrier()  # need not to optimize the circuit
    # Uncompute stage
    for i in range(n_steps):
        for q in range(0, NUM_QUBITS - 1, 2):
            circ.rzz(-RZZ_ANGLE, q, q + 1)
        for q in range(1, NUM_QUBITS - 1, 2):
            circ.rzz(-RZZ_ANGLE, q, q + 1)
        for q in range(NUM_QUBITS):
            circ.rx(-RX_ANGLE, q)
    circuits.append(circ)

이상적인 출력을 미리 알기 위해 원래 회로 UU 가 적용되는 첫 번째 단계와 이를 반전시키는 두 번째 단계 UU^\dagger 로 구성된 계산-비계산 회로를 사용합니다. 이러한 회로의 이상적인 결과는 사소하게도 입력 상태 000000|000000\rangle 이며, 이는 모든 폴리 관측 가능성에 대한 사소한 기대값(예: IIIIIZ=1\langle IIIIIZ \rangle = 1 )을 갖습니다.

# Print the circuit with 2 time steps
circuits[2].draw(output="mpl")

Output:

Output of the previous code cell

참고: 위 그림과 같이 kk 시간 단계가 있는 회로는 4k4k 2쿼비트 게이트 레이어를 갖게 됩니다.

obs = SparsePauliOp.from_sparse_list([("Z", [0], 1.0)], num_qubits=NUM_QUBITS)
obs

Output:

SparsePauliOp(['IIIIIZ'],
              coeffs=[1.+0.j])

3.2 회로를 트랜스파일하다

최적화를 통해 백엔드용 회로를 트랜스파일링합니다(optimization_level=1).

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuits = pm.run(circuits)
display(isa_circuits[2].draw("mpl", idle_wires=False, fold=-1))

Output:

Output of the previous code cell

3.3 Estimator를 사용하여 실행(다양한 복원력 수준으로)

Qiskit Estimator를 사용할 때 오류 완화 기능을 적용하는 가장 쉬운 방법은 복원력 수준(estimator.options.resilience_level)을 설정하는 것입니다. Estimator는 다음의 복원력 수준을 지원합니다(2024년 6월 28일 기준). 자세한 내용은 오류 완화 구성 가이드에서 확인하세요.

image.png
from qiskit_ibm_runtime import Batch
from qiskit_ibm_runtime import EstimatorV2 as Estimator

jobs = []
job_ids = []
with Batch(backend=backend):
    for resilience_level in [0, 1, 2]:
        estimator = Estimator()
        estimator.options.resilience_level = resilience_level
        job = estimator.run(
            [(circ, obs.apply_layout(circ.layout)) for circ in isa_circuits]
        )
        job_ids.append(job.job_id())
        print(f"Job ID (rl={resilience_level}): {job.job_id()}")
        jobs.append(job)

Output:

Job ID (rl=0): d146vcnmya70008emprg
Job ID (rl=1): d146vdnqf56g0081sva0
Job ID (rl=2): d146ven5z6q00087c61g
# check job status
for job in jobs:
    print(job.status())

Output:

DONE
DONE
DONE
# REPLACE WITH YOUR OWN JOB IDS
jobs = [service.job(job_id) for job_id in job_ids]
# Get results
results = [job.result() for job in jobs]

3.4 결과 플롯

plt.title("Error mitigation with different resilience levels")
labels = ["0 (No mitigation)", "1 (TREX)", "2 (ZNE + Gate twirling)"]
steps = NUM_TIME_STEPS
for result, label in zip(results, labels):
    plt.errorbar(
        x=steps,
        y=[result[s].data.evs for s in steps],
        yerr=[result[s].data.stds for s in steps],
        marker="o",
        linestyle="-",
        capsize=4,
        label=label,
    )
plt.hlines(
    1.0, min(steps), max(steps), linestyle="dashed", label="Ideal", colors="black"
)
plt.xlabel("Time steps")
plt.ylabel("Mitigated <IIIIIZ>")
plt.legend()
plt.show()

Output:

Output of the previous code cell

4: (선택 사항) 오류 완화 옵션 사용자 지정

아래와 같은 옵션을 통해 오류 완화 기술의 적용을 사용자 지정할 수 있습니다.

# TREX
estimator.options.twirling.enable_measure = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.twirling.shots_per_randomization = "auto"

# Gate twirling
estimator.options.twirling.enable_gates = True
# ZNE
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = [1, 3, 5]
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")

# Dynamical decoupling
estimator.options.dynamical_decoupling.enable = True  # Default: False
estimator.options.dynamical_decoupling.sequence_type = "XX"

# Other options
estimator.options.default_shots = 10_000

오류 완화 옵션에 대한 자세한 내용은 다음 가이드와 API 참조를 참조하세요.

이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.