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는 학습기 작업의 출력입니다)
| 속성 | NoiseLearner | NoiseLearnerV3 |
|---|---|---|
| 결과 유형 | NoiseLearnerResult | NoiseLearnerV3Results, 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.generators | noise_map.generators() |
| 오류율 | layer_error.error.rates | noise_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()