Przejdź do głównej treści

Migracja z NoiseLearner do NoiseLearnerV3

Ten przewodnik prowadzi cię przez proces migracji z NoiseLearner IBM Quantum® do NoiseLearnerV3. Obie klasy wykonują eksperymenty charakteryzujące procesy szumu na podstawie modelu szumu Pauliego-Lindblada, ale dane wejściowe i wyjściowe są nieco inne.

Kontekst​

Klasa NoiseLearner została utworzona, aby umożliwić użytkownikom wykonywanie jawnego uczenia się szumu. Wynikowy model szumu można następnie przekazać do Estimator IBM Quantum w celu zastosowania technik łagodzenia błędów, takich jak PEA i PEC.

NoiseLearner został zaprojektowany do współpracy z Estimator, dlatego niejawnie stosuje tę samą strategię znajdowania warstw co Estimator. Tej strategii nie można zmienić; w przeciwnym razie kolejne kroki łagodzenia błędów nie działałyby poprawnie.

Począwszy od qiskit-ibm-runtime v0.47.0, dostępna jest nowa klasa NoiseLearnerV3, która jest kompatybilna z prymitywem Executor oraz modelem wykonania kierowanego. Ten nowy model zapewnia doświadczenie typu white-box, dostarczając elementy do przechwytywania intencji projektowej po stronie klienta, a pojedynczy prymityw po stronie serwera (Executor) przetwarza te dane wejściowe dokładnie tak, jak zostało to zlecone — nie podejmuje żadnych ukrytych decyzji w twoim imieniu. W przeciwieństwie do oryginalnego NoiseLearner, kontrolujesz sposób stratyfikacji obwodów podczas korzystania zNoiseLearnerV3, a klasa po prostu przyjmuje jako dane wejściowe listę opakowanych instrukcji obwodu (na przykład unikalnych warstw).

NoiseLearnerV3 obsługuje również uczenie się szumu pomiarowego. Dla każdej instrukcji na liście wejściowej uruchamia protokół uczenia się Pauliego-Lindblada, jeśli pudełko zawiera bramki jedno- i dwukubitowe, oraz protokół TREX, jeśli pudełko zawiera pomiary.

Czy powinieneś migrować?​

NoiseLearner działa tylko ze starszym Estimator po stronie serwera, a NoiseLearnerV3 działa tylko z Executor i Estimator po stronie klienta. Musisz migrować do NoiseLearnerV3, jeśli używasz Executor lub Estimator po stronie klienta. Starszy Estimator po stronie serwera jest przestarzały i zastąpiony odpowiednikiem po stronie klienta w qiskit-ibm-runtime v0.50.0.

uwaga

Jeśli używasz qiskit-ibm-runtime v0.50.0 lub nowszej, przeczytaj najpierw przewodnik Migracja z Sampler i Estimator po stronie serwera na wersje po stronie klienta, aby zmigrować do prymitywów po stronie klienta.

Kroki migracji​

Krok 1: Zmiana importów​

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

NoiseLearnerV3:

from qiskit_ibm_runtime import NoiseLearnerV3

Krok 2: Aktualizacja danych wejściowych​

Metoda run() klasy NoiseLearner przyjmuje listę obwodów lub PUB-ów, natomiast metoda run() klasy NoiseLearnerV3 przyjmuje listę instrukcji, z których każda musi być oznaczoną adnotacją twirling BoxOp zawierającą operacje ISA. Dostępne są metody pomocnicze do generowania oznaczonych pudełek, w zależności od tego, który prymityw planujesz użyć.

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)

NoiseLearnerV3 podczas pracy z Estimatorem po stronie klienta:

Jeśli planujesz używać Estimatora po stronie klienta do wykonywania obwodów, możesz użyć metody find_unique_layers z Estimatora, aby utworzyć oznaczone pudełka (warstwy):

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)

NoiseLearnerV3 podczas pracy z Executorem:

Jeśli planujesz używać Executora do wykonywania obwodów, rozważ użycie funkcji generate_boxing_pass_manager z Samplomatic, aby utworzyć oznaczone pudełka:

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)

Krok 3: Konwersja opcji​

Większość pól NoiseLearnerOptions mapuje się bezpośrednio na NoiseLearnerV3Options, z wyjątkiem następujących:

  • max_layers_to_learn: W przypadku NoiseLearnerV3 liczba warstw do nauczenia jest oparta na liczbie przekazanych warstw.

  • twirling_strategy: W przypadku NoiseLearnerV3 strategia twirling jest definiowana przez sposób opakowania i oznaczenia instrukcji (na przykład podczas używania 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)

NoiseLearnerV3 podczas pracy z Estimatorem po stronie klienta:

Jeśli planujesz używać Estimatora po stronie klienta do wykonywania obwodów, możesz ustawić opcję Estimatora 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])

NoiseLearnerV3 podczas pracy z Executorem:

Jeśli planujesz używać Executora do wykonywania obwodów, możesz przekazać opcję twirling_strategy do funkcji generate_boxing_pass_manager.

Zwróć uwagę, że w przypadku generate_boxing_pass_manager() wartości twirling_strategy używają podkreśleń ("active_accum", "active_circuit"), natomiast wartości NoiseLearnerOptions.twirling_strategy używają łączników ("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])

Krok 4: Analiza wyników​

Dane wyjściowe NoiseLearner i NoiseLearnerV3 zawierają podobne informacje, ale mają różne formaty. Zaktualizuj kod, jeśli jawnie analizuje on dane wyjściowe.

Mapowanie atrybutów wyniku:

(learner_result to wynik zadania learner)

AttributeNoiseLearnerNoiseLearnerV3
Typ wynikuNoiseLearnerResultNoiseLearnerV3Results, kontener podobny do sekwencji zawierający NoiseLearnerV3Result
Liczba nauczonych warstwlen(learner_result.data)len(learner_result)
Dane dla pierwszej warstwylayer_error = learner_result.data[0]noise_map = learner_result[0].to_pauli_lindblad_map()
Typ wyniku każdej warstwyLayerError (type(layer_error))PauliLindbladMap (type(noise_map))
Generatory kanału błędulayer_error.error.generatorsnoise_map.generators()
Współczynniki błędulayer_error.error.ratesnoise_map.rates

Krok 5: Przekazanie modelu szumu do prymitywu​

NoiseLearner działa tylko ze starszym Estimator po stronie serwera, a NoiseLearnerV3 działa tylko z Executor i Estimator po stronie klienta. Sposób określania modelu szumu różni się nieznacznie w zależności od tego, który prymityw jest używany.

NoiseLearner podczas pracy ze starszym Estimatorem po stronie serwera:

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)

NoiseLearnerV3 podczas pracy z Estimatorem po stronie klienta:

Ponownie użyj tego samego Estimator, który wyprodukował layers w kroku 2. Mapy szumu zwrócone przez learner są dopasowane pozycyjnie do tych warstw, więc muszą zostać przypisane do Estimatora, z którego pochodzą. PEA/PEC zostało już na nim włączone w kroku 2.

Zwróć uwagę, że chociaż NoiseLearnerV3 obsługuje zarówno protokół Pauliego-Lindblada, jak i TREX, Estimator akceptuje tylko modele szumu dla warstw dwukubitowych nauczonych za pomocą protokołu Pauliego-Lindblada.

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)

NoiseLearnerV3 podczas pracy z Executorem:

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)

Pełne przykłady​

NoiseLearnerV3 i Estimator po stronie klienta​

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 i 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()

Bibliografia​