Vai al contenuto principale

Migra da NoiseLearner a NoiseLearnerV3

Questa guida ti accompagna nella migrazione da IBM Quantum® NoiseLearner a NoiseLearnerV3. Entrambe le classi eseguono esperimenti che caratterizzano i processi di rumore basati su un modello di rumore Pauli-Lindblad, ma gli input e gli output sono leggermente diversi.

Contesto​

La classe NoiseLearner è stata creata per consentire agli utenti di eseguire l'apprendimento esplicito del rumore. Il modello di rumore risultante può quindi essere passato a Estimator di IBM Quantum per applicare tecniche di mitigazione degli errori come PEA e PEC.

NoiseLearner è stato progettato per funzionare con Estimator, e pertanto impiega implicitamente la stessa strategia di individuazione dei livelli di Estimator. Questa strategia non può essere modificata; altrimenti, i successivi passaggi di mitigazione non funzionerebbero correttamente.

A partire da qiskit-ibm-runtime v0.47.0, è disponibile una nuova classe NoiseLearnerV3 compatibile con la primitiva Executor e il modello di esecuzione diretta. Questo nuovo modello offre un'esperienza white-box fornendo gli elementi per catturare l'intenzione progettuale lato client, e un'unica primitiva lato server (Executor) elabora tali input esattamente come indicato — non prende decisioni implicite per tuo conto. A differenza dell'originale NoiseLearner, controlli tu come stratificare i tuoi circuiti quando usi NoiseLearnerV3, e la classe accetta semplicemente una lista di istruzioni di circuito boxed (per esempio, livelli unici) come suo input.

NoiseLearnerV3 supporta anche l'apprendimento del rumore di misurazione. Per ogni istruzione nella lista di input, esegue il protocollo di apprendimento Pauli-Lindblad se il box contiene gate a uno e due qubit, e il protocollo TREX se il box contiene misurazioni.

Devi eseguire la migrazione?​

NoiseLearner funziona solo con il vecchio Estimator lato server, e NoiseLearnerV3 funziona solo con Executor e l'Estimator lato client. Devi eseguire la migrazione a NoiseLearnerV3 se stai usando Executor o l'Estimator lato client. Il vecchio Estimator lato server è deprecato e sostituito dall'equivalente lato client in qiskit-ibm-runtime v0.50.0.

nota

Se stai usando qiskit-ibm-runtime v0.50.0 o versioni successive, leggi prima la guida Migra da Sampler ed Estimator lato server a lato client per eseguire la migrazione alle primitive lato client.

Passaggi di migrazione​

Passaggio 1: modifica gli import​

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

NoiseLearnerV3:

from qiskit_ibm_runtime import NoiseLearnerV3

Passaggio 2: aggiorna gli input​

Il metodo run() di NoiseLearner accetta una lista di circuiti o PUB, mentre il metodo run() di NoiseLearnerV3 accetta una lista di istruzioni, ognuna delle quali deve essere un BoxOp annotato con twirling che contiene operazioni ISA. Sono disponibili metodi di convenienza per generare i box annotati, a seconda di quale primitiva prevedi di usare.

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, quando si lavora con Estimator lato client:

Se prevedi di usare l'Estimator lato client per l'esecuzione dei circuiti, puoi usare il metodo find_unique_layers di Estimator per creare box annotati (livelli):

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, quando si lavora con Executor:

Se prevedi di usare Executor per l'esecuzione dei circuiti, valuta di usare la funzione generate_boxing_pass_manager di Samplomatic per creare box annotati:

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)

Passaggio 3: converti le opzioni​

La maggior parte dei campi di NoiseLearnerOptions si mappa direttamente su NoiseLearnerV3Options, ad eccezione dei seguenti:

  • max_layers_to_learn: Con NoiseLearnerV3, il numero di livelli da apprendere si basa sul numero di livelli passati in ingresso.

  • twirling_strategy: Con NoiseLearnerV3, la strategia di twirling è definita dal modo in cui le istruzioni vengono racchiuse in box e annotate (ad esempio quando si usa 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, quando si lavora con Estimator lato client:

Se prevedi di usare l'Estimator lato client per l'esecuzione dei circuiti, puoi impostare l'opzione twirling.strategy di Estimator:

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, quando si lavora con Executor:

Se prevedi di usare Executor per l'esecuzione dei circuiti, puoi passare l'opzione twirling_strategy alla funzione generate_boxing_pass_manager.

Nota che con generate_boxing_pass_manager(), i valori di twirling_strategy usano underscore ("active_accum", "active_circuit"), mentre i valori di NoiseLearnerOptions.twirling_strategy usano trattini ("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])

Passaggio 4: esamina i risultati​

Gli output di NoiseLearner e NoiseLearnerV3 contengono informazioni simili ma in formati diversi. Aggiorna il tuo codice se esamina esplicitamente l'output.

Mappatura degli attributi del risultato:

(learner_result è l'output del job del learner)

AttributoNoiseLearnerNoiseLearnerV3
Tipo di risultatoNoiseLearnerResultNoiseLearnerV3Results, un contenitore simile a una sequenza di NoiseLearnerV3Result
Numero di livelli appresilen(learner_result.data)len(learner_result)
Dati per il primo livellolayer_error = learner_result.data[0]noise_map = learner_result[0].to_pauli_lindblad_map()
Tipo di risultato di ogni livelloLayerError (type(layer_error))PauliLindbladMap (type(noise_map))
Generatori per il canale di errorelayer_error.error.generatorsnoise_map.generators()
Tassi di errorelayer_error.error.ratesnoise_map.rates

Passaggio 5: inserisci il modello di rumore in una primitiva​

NoiseLearner funziona solo con il vecchio Estimator lato server, e NoiseLearnerV3 funziona solo con Executor e l'Estimator lato client. Il modo in cui viene specificato un modello di rumore varia leggermente a seconda di quale primitiva viene usata.

NoiseLearner, quando si lavora con il vecchio Estimator lato server:

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, quando si lavora con Estimator lato client:

Riusa lo stesso Estimator che ha prodotto layers nel passaggio 2. Le mappe di rumore restituite dal learner sono associate posizionalmente a quei livelli, quindi devono essere assegnate all'Estimator da cui provengono. PEA/PEC era già stato abilitato su di esso nel passaggio 2.

Nota che mentre NoiseLearnerV3 supporta sia il protocollo Pauli-Lindblad sia quello TREX, Estimator accetta solo modelli di rumore per livelli a due qubit appresi con il protocollo Pauli-Lindblad.

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, quando si lavora con Executor:

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)

Esempi completi​

NoiseLearnerV3 e Estimator lato client​

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

Riferimenti​