주 콘텐츠로 건너뛰기

NoiseLearner에서 NoiseLearnerV3로 마이그레이션

이 가이드는 IBM Quantum® NoiseLearner에서 NoiseLearnerV3로 마이그레이션하는 방법을 안내합니다. 두 클래스 모두 Pauli-Lindblad 노이즈 모델을 기반으로 노이즈 프로세스를 특성화하는 실험을 수행하지만, 입력과 출력은 약간 다릅니다.

배경​

NoiseLearner 클래스는 사용자가 명시적인 노이즈 학습을 수행할 수 있도록 만들어졌습니다. 그 결과로 얻어진 노이즈 모델은 PEA와 PEC 같은 오류 완화 기법을 적용하기 위해 IBM Quantum Estimator에 전달할 수 있습니다.

NoiseLearner는 Estimator와 함께 작동하도록 설계되었으며, 따라서 Estimator와 동일한 레이어 탐색 전략을 암묵적으로 사용합니다. 이 전략은 변경할 수 없습니다. 그렇지 않으면 이후의 완화 단계가 올바르게 작동하지 않습니다.

qiskit-ibm-runtime v0.47.0부터 Executor Primitive와 directed execution model과 호환되는 새로운 NoiseLearnerV3 클래스가 있습니다. 이 새로운 모델은 설계 의도를 클라이언트 측에서 포착할 수 있는 구성 요소를 제공하여 화이트박스 경험을 제공하며, 단일 서버 측 Primitive(Executor)가 이러한 입력을 정확히 지시된 대로 처리합니다. 사용자를 대신하여 암묵적인 결정을 내리지 않습니다. 기존 NoiseLearner와 달리, NoiseLearnerV3를 사용할 때는 회로를 계층화하는 방법을 직접 제어하며, 이 클래스는 단순히 박스화된 회로 명령어 목록 (예를 들어 고유 레이어)을 입력으로 받습니다.

NoiseLearnerV3는 측정 노이즈 학습도 지원합니다. 입력 목록의 각 명령어에 대해, 박스에 1큐비트 및 2큐비트 게이트가 포함되어 있으면 Pauli-Lindblad 학습 프로토콜을 실행하고, 박스에 측정이 포함되어 있으면 TREX 프로토콜을 실행합니다.

마이그레이션해야 할까요?​

NoiseLearner는 기존 서버 측 Estimator에서만 작동하며, NoiseLearnerV3는 Executor와 클라이언트 측 Estimator에서만 작동합니다. Executor 또는 클라이언트 측 Estimator를 사용하고 있다면 반드시 NoiseLearnerV3로 마이그레이션해야 합니다. 기존 서버 측 Estimator는 지원 중단(deprecated)되었으며 qiskit-ibm-runtime v0.50.0에서 클라이언트 측 대응 항목으로 대체됩니다.

참고

qiskit-ibm-runtime v0.50.0 이상을 사용하는 경우, 먼저 서버 측에서 클라이언트 측 Sampler 및 Estimator로 마이그레이션 가이드를 읽고 클라이언트 측 Primitive로 마이그레이션하세요.

마이그레이션 단계​

1단계: import 변경하기​

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

NoiseLearnerV3:

from qiskit_ibm_runtime import NoiseLearnerV3

2단계: 입력 업데이트하기​

NoiseLearner의 run() 메서드는 회로 또는 PUB 목록을 받는 반면, NoiseLearnerV3의 run() 메서드는 명령어 목록을 받으며, 각 명령어는 ISA 연산을 포함하는 트월링 주석이 달린 BoxOp여야 합니다. 사용하려는 Primitive에 따라 주석이 달린 박스를 생성하기 위한 편의 메서드를 사용할 수 있습니다.

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

learner = NoiseLearner(mode=backend)
# `circuits_to_learn` is a list of ISA QuantumCircuit
learner_job = learner.run(circuits_to_learn)

클라이언트 측 Estimator와 함께 작업할 때의 NoiseLearnerV3:

회로 실행에 클라이언트 측 Estimator를 사용할 계획이라면, Estimator의 find_unique_layers 메서드를 사용하여 주석이 달린 박스(레이어)를 생성할 수 있습니다.

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)

Executor와 함께 작업할 때의 NoiseLearnerV3:

회로 실행에 Executor를 사용할 계획이라면, Samplomatic의 generate_boxing_pass_manager 함수를 사용하여 주석이 달린 박스를 생성하는 것을 고려하세요.

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# Run the boxing pass manager to group instructions into annotated boxes.
# `isa_circuit` is an ISA QuantumCircuit.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions.
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)

3단계: 옵션 변환하기​

대부분의 NoiseLearnerOptions 필드는 다음을 제외하고 NoiseLearnerV3Options에 직접 매핑됩니다.

  • max_layers_to_learn: NoiseLearnerV3에서는 학습할 레이어 수가 전달된 레이어 수를 기반으로 합니다.

  • twirling_strategy: NoiseLearnerV3에서는 트월링 전략이 명령어가 박스화되고 주석이 달리는 방식(예를 들어 generate_boxing_pass_manager()를 사용할 때)에 의해 정의됩니다.

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import NoiseLearnerOptions

# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)

learner = NoiseLearner(mode=backend, options=learner_options)
learner_job = learner.run(circuits_to_learn)

클라이언트 측 Estimator와 함께 작업할 때의 NoiseLearnerV3:

회로 실행에 클라이언트 측 Estimator를 사용할 계획이라면, Estimator의 twirling.strategy 옵션을 설정할 수 있습니다.

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options

pubs = [...] # Your PUBs
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.twirling.strategy = "all" # set twirling strategy here

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner_options = NoiseLearnerV3Options(num_randomizations=32)
learner = NoiseLearnerV3(backend, options=learner_options)

# Learn just the first 3 layers.
learner_job = learner.run(layers[:3])

Executor와 함께 작업할 때의 NoiseLearnerV3:

회로 실행에 Executor를 사용할 계획이라면, generate_boxing_pass_manager 함수에 twirling_strategy 옵션을 전달할 수 있습니다.

generate_boxing_pass_manager()에서는 twirling_strategy 값이 밑줄을 사용하지만("active_accum", "active_circuit"), NoiseLearnerOptions.twirling_strategy 값은 하이픈을 사용한다는("active-accum", "active-circuit") 점에 유의하세요.

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# Run the boxing pass manager to group instructions into annotated boxes
# `isa_circuit` is an ISA QuantumCircuit
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
twirling_strategy="all", # twirling strategy can be specified here
inject_noise_targets="gates",
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

learner_options = NoiseLearnerV3Options(num_randomizations=32)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend, options=learner_options)
# Learn just the first 3 layers.
learner_job = learner.run(unique_box_instructions[:3])

4단계: 결과 검사하기​

NoiseLearner와 NoiseLearnerV3의 출력은 유사한 정보를 포함하지만 형식이 다릅니다. 출력을 명시적으로 검사하는 코드가 있다면 업데이트하세요.

결과 속성 매핑:

(learner_result는 학습기 작업의 출력입니다)

속성NoiseLearnerNoiseLearnerV3
결과 유형NoiseLearnerResultNoiseLearnerV3Results, NoiseLearnerV3Result의 시퀀스와 유사한 컨테이너
학습된 레이어 수len(learner_result.data)len(learner_result)
첫 번째 레이어의 데이터layer_error = learner_result.data[0]noise_map = learner_result[0].to_pauli_lindblad_map()
각 레이어의 결과 유형LayerError (type(layer_error))PauliLindbladMap (type(noise_map))
오류 채널의 생성자layer_error.error.generatorsnoise_map.generators()
오류율layer_error.error.ratesnoise_map.rates

5단계: Primitive에 노이즈 모델 입력하기​

NoiseLearner는 기존 서버 측 Estimator에서만 작동하며, NoiseLearnerV3는 Executor와 클라이언트 측 Estimator에서만 작동합니다. 노이즈 모델을 지정하는 방식은 사용하는 Primitive에 따라 약간 다릅니다.

기존 서버 측 Estimator와 함께 작업할 때의 NoiseLearner:

from qiskit_ibm_runtime import Estimator as LegacyEstimator

learner_result = learner_job.result()

# Pass the noise model to the `estimator.options` attribute directly
estimator = LegacyEstimator(mode=backend)
estimator.options.resilience.layer_noise_model = learner_result
job = estimator.run(pubs)

클라이언트 측 Estimator와 함께 작업할 때의 NoiseLearnerV3:

2단계에서 layers를 생성한 것과 동일한 Estimator를 재사용하세요. 학습기가 반환하는 노이즈 맵은 해당 레이어와 위치별로 매칭되므로, 그것이 유래한 Estimator에 할당해야 합니다. PEA/PEC는 2단계에서 이미 해당 Estimator에 활성화되어 있습니다.

NoiseLearnerV3는 Pauli-Lindblad와 TREX 프로토콜을 모두 지원하지만, Estimator는 Pauli-Lindblad 프로토콜로 학습된 2큐비트 레이어의 노이즈 모델만 허용한다는 점에 유의하세요.

learner_result = learner_job.result()

# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# Now execute the target PUBs.
job = estimator.run(pubs)

Executor와 함께 작업할 때의 NoiseLearnerV3:

from qiskit_ibm_runtime import Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Convert the NoiseLearnerV3 result to a dictionary
learner_result = learner_job.result()
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# Append the samplex item and execute
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
executor_job = executor.run(program)

전체 예시​

NoiseLearnerV3와 클라이언트 측 Estimator​

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
from qiskit_ibm_runtime.executor_estimator import Estimator

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)

observable = SparsePauliOp("ZZ")

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
pubs = [(isa_circuit, isa_observable)]

# 4. Initialize Estimator with options
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True

# 5. Extract the unique boxed layers from PUBs
layers = estimator.find_unique_layers(pubs)

# 6. Learn the noise model for those layers
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()

# 7. Convert the result to Pauli-Lindblad maps and pass them to Estimator
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# 8. Execute the target PUBs
job = estimator.run(pubs)
result = job.result()

NoiseLearnerV3와 Executor​

from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor, NoiseLearnerV3
from qiskit_ibm_runtime.quantum_program import QuantumProgram

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# 4. Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# 5. Find unique boxed instructions (layers)
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

# 6. Learn the noise model for those layers
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()

# 7. Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# 8. Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# 9. Prepare a quantum program
program = QuantumProgram(shots=1000)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
job = executor.run(program)
result = job.result()

참고 자료​