주 콘텐츠로 건너뛰기

서버 측에서 클라이언트 측 Sampler 및 Estimator로 마이그레이션

이 가이드는 IBM Quantum® Sampler 및 Estimator의 서버 측 구현에서 qiskit-ibm-runtime의 새로운 클라이언트 측 구현으로 마이그레이션하는 방법을 설명합니다. 인터페이스와 옵션은 대부분 변경되지 않았으므로 대부분의 코드는 그대로 실행되지만, 이해해야 할 몇 가지 동작 차이가 있습니다.

배경​

Sampler와 Estimator는 Qiskit에서 정의된 Primitive 인터페이스입니다. IBM Quantum Compute Service(이전 명칭 Qiskit Runtime)는 지금까지 런타임 환경 내에서 이러한 Primitive의 구현을 제공해 왔습니다. sampler.run() 또는 estimator.run()을 호출하면 요청이 서비스로 전송되고, 오류 억제 및 완화를 포함한 모든 계산이 서버 측에서 이루어집니다.

이러한 블랙박스 경험은 편리합니다. 구현 세부 사항을 신경 쓸 필요가 없기 때문입니다. 하지만 처리 과정에서 무슨 일이 일어나는지 볼 수 없기 때문에 Primitive를 디버그하거나, 사용자 지정하거나, 학습하기 어렵게 만들기도 합니다.

새로 도입된 directed execution model은 정반대의 접근 방식을 취하여 화이트박스 경험을 제공합니다. 모든 설계 의도는 클라이언트 측에서 포착되며, 단일 서버 측 Primitive인 Executor가 이러한 입력을 정확히 지시된 대로 처리합니다. 사용자를 대신하여 암묵적인 결정을 내리지 않습니다.

qiskit-ibm-runtime v0.50.0부터 Sampler와 Estimator는 Executor를 기반으로 클라이언트 측에서 재구현되었습니다. 이전과 동일한 편리함과 추상화를 제공하며, 이제 필요할 때 구현 세부 사항을 살펴볼 수 있습니다. 인터페이스와 옵션이 대부분 동일하게 유지되므로 마이그레이션은 원활할 것입니다.

참고: IBM Quantum은 Sampler와 Estimator 인터페이스의 버전 2(BaseSamplerV2 및 BaseEstimatorV2)만 지원합니다. 따라서 이 가이드에서는 이를 단순히 Sampler와 Estimator로 지칭합니다.

import 업데이트​

현재는 전용 모듈에서 새로운 구현을 명시적으로 import해야 합니다.

from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator

가까운 미래에는 최상위 import가 새로운 클라이언트 측 구현으로 해석되어 코드 변경이 필요하지 않게 됩니다.

# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator

마찬가지로, 타입이 지정된 옵션 객체를 구성하는 경우 qiskit_ibm_runtime.options_models에서 import하거나, 단순한 중첩 dict를 전달해야 합니다.

from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions

동일하게 유지되는 사항​

  • mode와 options를 사용한 Primitive 구성.

  • run() 시그니처와 PUB 형식.

  • 옵션 트리(options.twirling, options.resilience, options.default_shots 등).

  • job.result()가 반환하는 결과 데이터 구조.

새 Sampler의 호환되지 않는 변경 사항​

변경 사항마이그레이션 작업
기본 프리미티브가 이제 Executor입니다. IBM Quantum Platform 사용자 인터페이스와 job.primitive_id 모두 sampler 대신 executor를 표시합니다.job.primitive_id를 참조하는 모든 코드를 업데이트하세요.
새 구현은 Sampler 입력을 Executor 입력으로 매핑하므로 job.inputs는 Executor 입력을 반환합니다.job.inputs를 참조하는 모든 코드를 업데이트하세요. 작업 입력을 참조하세요.
이제 더 많은 사전 및 사후 처리가 클라이언트 측에서 이루어지므로 sampler.run()과 job.result()가 이전보다 오래 걸릴 수 있습니다.클라이언트 측 처리 진행 상황을 확인하려면 INFO 로깅을 활성화하세요. INFO 로깅 활성화를 참조하세요.
회로 메타데이터가 결과 메타데이터로 복사됩니다. 결과 메타데이터에 허용되는 데이터 유형은 이제 str, float, int, bool 및 이러한 유형의 목록 또는 딕셔너리로 제한됩니다.다른 데이터 유형이 필요한 경우 먼저 문자열로 인코딩하세요(예: base64 사용).
옵션 클래스(options_models.SamplerOptions 등)는 이제 dataclass가 아닌 Pydantic 모델이므로 더 이상 asdict()를 사용하여 Python 딕셔너리로 변환할 수 없습니다.대신 options.model_dump()를 사용하세요.
이전에 V2 접미사가 있던 옵션 클래스(ExecutionOptionsV2 등)는 V1 프리미티브가 더 이상 지원되지 않으므로 이제 접미사가 없습니다.이러한 옵션 클래스의 V2 접미사를 제거하세요. ExecutionOptionsV2를 ExecutionOptions로, ResilienceOptionsV2를 ResilienceOptions로, SamplerExecutionOptionsV2를 SamplerExecutionOptions로 바꾸세요.
twirling이 활성화되어 있고 shots(PUB 또는 run()에서), shots_per_randomization, num_randomizations가 모두 지정된 경우, num_randomizations * shots_per_randomization이 shots보다 우선 적용됩니다.shots 값을 사용하려면 num_randomizations와 shots_per_randomization을 생략하세요.
일부 입력 검증이 서버 측으로 이동했으며 이제 IBMInputValueError 대신 RuntimeError를 발생시킵니다.코드에서 처리하는 예외 유형을 업데이트하세요.
단일 작업 내에서 혼합된 shot 값은 더 이상 지원되지 않습니다.각 shot 값에 대해 별도의 작업을 제출하세요. 고려 사항은 작업 분할을 참조하세요.

새 Estimator의 호환되지 않는 변경 사항​

변경 사항마이그레이션 작업
기본 프리미티브가 이제 Executor입니다. IBM Quantum Platform 사용자 인터페이스와 job.primitive_id 모두 estimator 대신 executor를 표시합니다.job.primitive_id를 참조하는 모든 코드를 업데이트하세요.
새 구현은 Estimator 입력을 Executor 입력으로 매핑하므로 job.inputs는 Executor 입력을 반환합니다.job.inputs를 참조하는 모든 코드를 업데이트하세요. 작업 입력을 참조하세요.
이제 더 많은 사전 및 사후 처리가 클라이언트 측에서 이루어지므로 estimator.run()과 job.result()가 이전보다 오래 걸릴 수 있습니다.클라이언트 측 처리 진행 상황을 확인하려면 INFO 로깅을 활성화하세요. INFO 로깅 활성화를 참조하세요.
회로 메타데이터가 결과 메타데이터로 복사됩니다. 결과 메타데이터에 허용되는 데이터 유형은 이제 str, float, int, bool 및 이러한 유형의 목록 또는 딕셔너리로 제한됩니다.다른 데이터 유형이 필요한 경우 먼저 문자열로 인코딩하세요(예: base64 사용).
옵션 클래스(options_models.EstimatorOptions 등)는 이제 dataclass가 아닌 Pydantic 모델이므로 더 이상 asdict()를 사용하여 Python 딕셔너리로 변환할 수 없습니다.대신 options.model_dump()를 사용하세요.
이전에 V2 접미사가 있던 옵션 클래스(ExecutionOptionsV2 등)는 V1 프리미티브가 더 이상 지원되지 않으므로 이제 접미사가 없습니다.이러한 옵션 클래스의 V2 접미사를 제거하세요. ExecutionOptionsV2를 ExecutionOptions로, ResilienceOptionsV2를 ResilienceOptions로 바꾸세요.
선택된 하위 집합이 아니라 모든 입력 옵션이 결과 메타데이터에 반환됩니다.없음 — 참고용입니다.
일부 입력 검증이 서버 측으로 이동했으며 이제 IBMInputValueError 대신 RuntimeError를 발생시킵니다.코드에서 처리하는 예외 유형을 업데이트하세요.
PEA와 PEC에 대한 암시적 노이즈 학습이 더 이상 제공되지 않습니다. TREX에 대한 측정 노이즈 학습은 계속 지원됩니다.노이즈 모델을 별도로 학습시켜 Estimator에 전달하세요. PEA 및 PEC에 대한 명시적 노이즈 학습 수행을 참조하세요.
ResilienceOptions.layer_noise_model의 입력 유형이 변경되었으며 NoiseLearnerV3 결과로부터 구성할 수 있습니다.NoiseLearnerV3를 사용하여 노이즈 모델을 학습시키고 Estimator에 전달하는 방법은 PEA 및 PEC에 대한 명시적 노이즈 학습 수행을 참조하세요.
MeasureNoiseLearningOptions.shots_per_randomization는 더 이상 지원되지 않습니다.측정 노이즈 학습 회로를 포함하여 작업 내 모든 회로에 단일 shot 값이 사용됩니다. 다른 shot 값을 사용해야 하는 경우 Estimator 외부에서 qiskit-mitigation으로 TREX를 적용하세요.
단일 작업 내에서 혼합된 precision 값은 더 이상 지원되지 않습니다.원하는 각 precision에 대해 별도의 작업을 제출하세요. 고려 사항은 작업 분할을 참조하세요.
seed_estimator 옵션은 더 이상 지원되지 않습니다.options.seed_estimator 할당을 제거하세요(설정 시 ValidationError가 발생합니다). 클라이언트 측에 해당하는 기능이 없으므로 이 시드를 통한 결과 재현은 더 이상 불가능합니다.

INFO 로깅 활성화​

더 많은 작업이 이제 클라이언트 측에서 이루어지므로, 해당 처리 과정의 진행 상황을 확인하는 것이 유용합니다. qiskit_ibm_runtime 로거에 대해 INFO 수준 로깅을 활성화하세요.

import logging

logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)

PEA 및 PEC에 대한 명시적 노이즈 학습 수행​

새로운 Estimator는 PEA 또는 PEC 오류 완화 방법이 선택되었을 때 더 이상 암묵적으로 노이즈 학습을 수행하지 않습니다. 노이즈 모델을 명시적으로 학습하여 전달해야 합니다. 회로가 레이어로 계층화되는 방식을 제어하려면 새로운 NoiseLearnerV3를 사용하세요. 이는 박스화된 회로 명령어 목록(예를 들어 고유 레이어)을 입력으로 받습니다.

중요

PEA와 PEC는 이제 이 명시적 패턴을 요구합니다. 노이즈 학습 단계를 건너뛰지 마세요. 건너뛰면 코드가 실패합니다. TREX의 측정 노이즈 학습은 영향을 받지 않으며 이전과 동일하게 작동합니다.

마찬가지로, 코드가 NoiseLearner를 사용하고 결과 노이즈 모델을 서버 측 Estimator에 전달하는 경우 NoiseLearnerV3로 마이그레이션해야 합니다. 새 Estimator와 호환되지 않는 이전 NoiseLearner를 사용하지 마세요.

서버 측 Estimator(LayerNoiseLearningOptions)의 모든 노이즈 학습 옵션은 max_layers_to_learn을 제외하고 NoiseLearnerV3 옵션(NoiseLearnerV3Options)에 직접 매핑됩니다. 학습할 레이어 수는 대신 NoiseLearnerV3에 전달된 레이어 수에 기반합니다.

예시:

서버 측 Estimator(PEC 활성화됨):

from qiskit_ibm_runtime import Estimator

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64

job = estimator.run(pubs)

클라이언트 측 Estimator(PEC 활성화됨):

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
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(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
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)

NoiseLearner에서 NoiseLearnerV3로 마이그레이션​

NoiseLearner는 Estimator의 서버 측 구현에서만 작동합니다. 따라서 코드가 NoiseLearner를 사용하여 노이즈 모델을 학습하고 Estimator에 전달하는 경우, NoiseLearnerV3를 사용하도록 코드를 업데이트해야 합니다.

자세한 내용은 NoiseLearner에서 NoiseLearnerV3로 마이그레이션 가이드를 참조하세요.

작업 분할​

단일 작업에서 혼합된 shot 또는 정밀도 값이 더 이상 지원되지 않아 하나의 작업을 여러 개로 분할해야 하는 경우, 다음을 고려하세요.

  • PUB당 하나의 작업이 아니라 대상 값별로 PUB를 그룹화하세요. 서로 다른 값마다 하나의 작업입니다. 분할은 재그룹화이므로 제출하는 PUB의 총 개수는 변하지 않습니다. 예를 들어 [A@0.01, B@0.05, C@0.01]이 주어지면, precision=0.01에서 [A, C]와 precision=0.05에서 [B], 이렇게 두 개의 작업을 제출하세요. A와 C를 별도의 작업으로 제출하는 것은 각 작업마다 고정된 오버헤드가 발생하므로 덜 효율적입니다.

  • 한 번만 학습하고 모든 분할된 작업에서 그 노이즈 모델을 사용하세요. 모든 레이어의 합집합에 대해 단일 NoiseLearnerV3 작업을 실행하는 것이 더 효율적입니다. 노이즈 학습기 작업의 결과에는 NoiseLearnerV3Result 객체 목록이 포함되며, 입력 명령어마다 하나씩 있고 입력 목록과 동일한 순서로 되어 있습니다. 이 노이즈 학습기 작업의 출력을 분할된 모든 (Estimator) 작업에서 사용할 수 있으며, 분할된 작업의 PUB에 없는 레이어에 대한 노이즈 모델은 무시됩니다.

  • 먼저 분할된 모든 작업을 Batch에 제출한 다음, 결과를 수집하세요. Batch 실행 모드는 여러 작업이 있을 때 효율적인 병렬 실행을 제공합니다. 그러나 job.result()는 블로킹 호출이므로, 제출 루프 안에서 호출하면 작업이 직렬화되어 Batch 사용의 이점이 사라집니다. 아래에 나와 있는 것처럼 모두 제출한 후 수집하는 패턴을 사용해야 합니다.

다음 예시에서 pub1과 pub2는 precision=0.5가 필요하고, pub3는 precision=0.1이 필요합니다.

group1_pubs = [pub1, pub2]
group2_pubs = [pub3]

with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True

# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)

# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))

# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]

작업 입력 구조​

새로운 구현은 Sampler 또는 Estimator 입력을 Executor 입력에 매핑하므로, job.inputs는 Executor 입력을 포함하는 dictionary를 반환합니다. 이 dictionary는 다음 키를 갖습니다.

코드가 job.inputs['options']를 사용하여 작업에 지정된 옵션을 찾았다면, 이제는 대신 job.result().metadata['options']를 사용할 수 있습니다.

가짜 백엔드로 로컬에서 테스트하기​

하드웨어에 제출하기 전에, 마이그레이션된 코드를 Fake* 백엔드에 대해 검증하여 구문 오류를 조기에 잡을 수 있습니다. 로컬 테스트 모드에 대해 다음 세부 사항에 유의하세요.

  • 하드웨어 결과를 재현하지 않습니다. 로컬 노이즈 시뮬레이션은 실제 디바이스의 노이즈를 완벽하게 재현하지 않으므로, 출력이 다를 수 있습니다. 실행하면 옵션 경로와 값 타입이 올바른지 검증할 수 있습니다.

  • NoiseLearnerV3는 로컬 테스트 모드를 지원하지 않습니다. 이 클래스의 mode는 실제 Backend, Session, Batch만 허용하므로 가짜 백엔드에 대해 노이즈 학습 단계를 실행할 수 없습니다. 코드의 해당 부분은 NoiseLearnerV3 API 레퍼런스에 대해 검증하세요. 생성자, run(instructions) 입력 형태, 그리고 (고유 레이어 헬퍼와 같은) 헬퍼가 문서화된 대로 사용되고 있는지 확인하세요.

효율적인 로컬 시뮬레이션을 위해 회로를 Clifford화하기​

가짜 백엔드는 상태벡터(노이즈) 시뮬레이터를 사용하며, 그 비용은 큐비트 수와 깊이에 따라 기하급수적으로 증가합니다. 따라서 실제와 같은 워크로드 회로는 멈추거나 메모리를 소진할 수 있습니다. 로컬 테스트는 물리적 결과를 재현할 필요 없이 옵션 경로만 검증하면 되므로, 먼저 ConvertISAToClifford를 사용하여 회로를 Clifford 회로로 축소하세요. 이는 각 RZ/RZZ/RX 각도를 가장 가까운 π/2의 배수로 반올림합니다. Clifford 회로는 크기와 상관없이 효율적으로(안정자(stabilizer) 시뮬레이션) 시뮬레이션됩니다.

from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford

clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive

ConvertISAToClifford는 입력으로 ISA 회로를 필요로 합니다(백엔드를 대상으로 한 generate_preset_pass_manager(...).run(...)의 출력). 로컬 PUB를 구성할 때 다음과 같은 결과를 고려해야 합니다.

  • .layout 속성이 제거됩니다. Clifford화된 회로는 동일한 큐비트 수를 유지하지만, clifford.layout은 None이므로 observable.apply_layout(clifford.layout)은 실패합니다. 대신 Clifford화 이전의 ISA 회로에서 관측량(observable)을 배치하세요. isa_obs = observable.apply_layout(isa_circuit.layout), 그런 다음 (clifford, isa_obs)를 실행합니다.

  • 매개변수가 바인딩되어 사라집니다. 회전 각도를 반올림하면 매개변수화된 ISA 회로가 구체적인 Clifford 회로로 바뀌므로, clifford.num_parameters는 0이 됩니다. 여전히 매개변수 값 배열을 포함하고 있는 PUB는 강제 변환에 실패합니다. 로컬 실행에서는 PUB에서 매개변수 배열을 제거하세요. 하드웨어 실행은 원래의 매개변수화된 회로와 그 값을 유지합니다.

다음 단계​