Migra da Sampler ed Estimator lato server a lato client
Questa guida descrive come eseguire la migrazione dalle implementazioni lato server di IBM Quantum®
Sampler ed Estimator alle loro nuove implementazioni lato client in
qiskit-ibm-runtime. Le interfacce e le opzioni sono in gran parte invariate, quindi la maggior parte del codice
viene eseguita così com'è, ma ci sono alcune differenze di comportamento da comprendere.
Contesto
Sampler ed Estimator sono interfacce primitive definite in Qiskit. IBM Quantum
Compute Service (precedentemente Qiskit Runtime) ha storicamente fornito l'implementazione
di queste primitive all'interno del proprio ambiente runtime. Quando chiami sampler.run() o
estimator.run(), la richiesta viene inviata al servizio e tutto il calcolo — inclusi
la soppressione e la mitigazione degli errori — avviene lato server.
Questa esperienza black-box è conveniente: non devi preoccuparti dei dettagli implementativi. Ma rende anche le primitive difficili da debuggare, personalizzare o da cui imparare, perché non puoi vedere cosa succede durante l'elaborazione.
Il modello di esecuzione diretta introdotto di recente adotta l'approccio opposto e fornisce un'esperienza white-box. Tutte le intenzioni progettuali vengono catturate lato client, e un'unica primitiva lato server Executor elabora tali input esattamente come indicato — non prende decisioni implicite per tuo conto.
A partire da qiskit-ibm-runtime v0.50.0, Sampler ed Estimator vengono reimplementati
lato client sopra Executor. Offrono la stessa comodità e
astrazione di prima, e ora puoi ispezionare i dettagli implementativi quando ne hai
bisogno. Poiché le interfacce e le opzioni rimangono in gran parte le stesse, la migrazione dovrebbe essere
senza soluzione di continuità.
Nota: IBM Quantum supporta solo la versione 2 delle interfacce Sampler ed Estimator (BaseSamplerV2 e BaseEstimatorV2). Pertanto, in questa guida vengono semplicemente indicati come Sampler ed Estimator.
Aggiorna gli import
Attualmente, devi importare esplicitamente le nuove implementazioni dai loro moduli dedicati:
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
Nel prossimo futuro, gli import di primo livello faranno riferimento alle nuove implementazioni lato client, e non sarà necessaria alcuna modifica al codice:
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
Analogamente, se costruisci oggetti di opzioni tipizzati, devi importarli da
qiskit_ibm_runtime.options_models invece, oppure semplicemente passare un dizionario nidificato semplice:
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
Cosa rimane invariato
-
Costruzione della primitiva con
modeeoptions. -
La firma di
run()e il formato PUB. -
L'albero delle opzioni (
options.twirling,options.resilience,options.default_shots, e così via). -
La struttura dati dei risultati restituita da
job.result().
Modifiche incompatibili nel nuovo Sampler
| Modifica | Azione di migrazione |
|---|---|
La primitiva sottostante ora è Executor. Sia l'interfaccia utente di IBM Quantum Platform sia job.primitive_id mostreranno executor invece di sampler. | Aggiorna qualsiasi codice che fa riferimento a job.primitive_id. |
La nuova implementazione mappa gli input di Sampler sugli input di Executor, quindi job.inputs restituisce gli input di Executor. | Aggiorna qualsiasi codice che fa riferimento a job.inputs. Consulta Job inputs. |
Ora una maggiore quantità di pre- e post-elaborazione avviene lato client, quindi sampler.run() e job.result() potrebbero richiedere più tempo di prima. | Abilita il logging INFO per seguire l'avanzamento dell'elaborazione lato client. Consulta Abilita il logging INFO. |
I metadati del circuito vengono copiati nei metadati del risultato. I tipi di dati consentiti nei metadati del risultato sono ora limitati a str, float, int, bool, e liste o dizionari di questi tipi. | Se hai bisogno di altri tipi di dati, codificali prima come stringa (per esempio, con base64). |
Le classi di opzioni (options_models.SamplerOptions e così via) ora sono modelli Pydantic invece di dataclass, quindi non possono più essere convertite in dizionari Python usando asdict(). | Usa invece options.model_dump(). |
Le classi di opzioni che in precedenza avevano il suffisso V2 (ExecutionOptionsV2 e così via) non lo hanno più, poiché le primitive V1 non sono più supportate. | Rimuovi il suffisso V2 da queste classi di opzioni: sostituisci ExecutionOptionsV2 con ExecutionOptions, ResilienceOptionsV2 con ResilienceOptions e SamplerExecutionOptionsV2 con SamplerExecutionOptions. |
Se twirling è abilitato e shots (nei PUB o in run()), shots_per_randomization e num_randomizations sono tutti specificati, allora num_randomizations * shots_per_randomization ha la precedenza su shots. | Ometti num_randomizations e shots_per_randomization se vuoi che venga usato il valore di shots. |
Alcune convalide degli input sono state spostate lato server e ora sollevano RuntimeError invece di IBMInputValueError. | Aggiorna i tipi di eccezione intercettati dal tuo codice. |
| I valori di shot misti in un unico job non sono più supportati. | Invia un job separato per ogni valore di shot. Consulta Job splitting per approfondimenti. |
Modifiche incompatibili nel nuovo Estimator
| Modifica | Azione di migrazione |
|---|---|
La primitiva sottostante ora è Executor. Sia l'interfaccia utente di IBM Quantum Platform sia job.primitive_id mostreranno executor invece di estimator. | Aggiorna qualsiasi codice che fa riferimento a job.primitive_id. |
La nuova implementazione mappa gli input di Estimator sugli input di Executor, quindi job.inputs restituisce gli input di Executor. | Aggiorna qualsiasi codice che fa riferimento a job.inputs. Consulta Job inputs. |
Ora una maggiore quantità di pre- e post-elaborazione avviene lato client, quindi estimator.run() e job.result() potrebbero richiedere più tempo di prima. | Abilita il logging INFO per seguire l'avanzamento dell'elaborazione lato client. Consulta Abilita il logging INFO. |
I metadati del circuito vengono copiati nei metadati del risultato. I tipi di dati consentiti nei metadati del risultato sono ora limitati a str, float, int, bool, e liste o dizionari di questi tipi. | Se hai bisogno di altri tipi di dati, codificali prima come stringa (per esempio, con base64). |
Le classi di opzioni (options_models.EstimatorOptions e così via) ora sono modelli Pydantic invece di dataclass, quindi non possono più essere convertite in dizionari Python usando asdict(). | Usa invece options.model_dump(). |
Le classi di opzioni che in precedenza avevano il suffisso V2 (ExecutionOptionsV2 e così via) non lo hanno più, poiché le primitive V1 non sono più supportate. | Rimuovi il suffisso V2 da queste classi di opzioni: sostituisci ExecutionOptionsV2 con ExecutionOptions e ResilienceOptionsV2 con ResilienceOptions. |
| Tutte le opzioni di input vengono restituite nei metadati del risultato, anziché un sottoinsieme selezionato. | Nessuna — questa è solo informativa. |
Alcune convalide degli input sono state spostate lato server e ora sollevano RuntimeError invece di IBMInputValueError. | Aggiorna i tipi di eccezione intercettati dal tuo codice. |
| Non c'è più apprendimento implicito del rumore per PEA e PEC. L'apprendimento del rumore di misurazione per TREX è ancora supportato. | Apprendi i modelli di rumore separatamente e passali a Estimator. Consulta Esegui l'apprendimento esplicito del rumore per PEA e PEC. |
Il tipo di input di ResilienceOptions.layer_noise_model è diverso e può essere costruito a partire dai risultati di NoiseLearnerV3. | Consulta Esegui l'apprendimento esplicito del rumore per PEA e PEC su come apprendere i modelli di rumore usando NoiseLearnerV3 e passarli a Estimator. |
MeasureNoiseLearningOptions.shots_per_randomization non è più supportato. | Un singolo valore di shot viene usato per tutti i circuiti nel job, inclusi i circuiti di apprendimento del rumore di misurazione. Se devi usare un valore di shot diverso, applica TREX con qiskit-mitigation al di fuori di Estimator. |
| I valori di precisione misti in un unico job non sono più supportati. | Invia un job separato per ogni precisione desiderata. Consulta Job splitting per approfondimenti. |
L'opzione seed_estimator non è più supportata. | Rimuovi qualsiasi assegnazione a options.seed_estimator (impostarla solleva un ValidationError). Non esiste un equivalente lato client, quindi i risultati non sono più riproducibili tramite questo seed. |
Abilita il logging INFO
Poiché ora una maggiore quantità di lavoro avviene lato client, è utile vedere l'avanzamento di tale
elaborazione. Abilita il logging di livello INFO per il logger qiskit_ibm_runtime:
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
Esegui l'apprendimento esplicito del rumore per PEA e PEC
Il nuovo Estimator non esegue più l'apprendimento del rumore implicito quando viene selezionato il metodo di
mitigazione degli errori PEA o PEC. Devi apprendere i modelli di rumore esplicitamente e passarli
in ingresso. Usa il nuovo NoiseLearnerV3 per controllare come i circuiti
vengono stratificati in livelli. Accetta come input una lista di istruzioni di circuito boxed (per esempio,
i livelli unici).
PEA e PEC ora richiedono questo pattern esplicito. Non saltare il passaggio di apprendimento del rumore o il tuo codice fallirà. L'apprendimento del rumore di misurazione per TREX non è interessato e continua a funzionare come prima.
Analogamente, se il tuo codice usa NoiseLearner e passa il modello di rumore risultante a Estimator lato server, devi eseguire la migrazione a NoiseLearnerV3. NON usare il vecchio NoiseLearner, che è incompatibile con il nuovo Estimator.
Tutte le opzioni di apprendimento del rumore nell'Estimator lato server (LayerNoiseLearningOptions) si mappano direttamente sull'opzione NoiseLearnerV3 (NoiseLearnerV3Options), ad eccezione di max_layers_to_learn. Il numero di livelli da apprendere si basa invece sul numero di livelli passati a NoiseLearnerV3.
Per esempio:
Estimator lato server (con PEC abilitato):
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 lato client (con PEC abilitato):
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)
Migra da NoiseLearner a NoiseLearnerV3
NoiseLearner funziona solo con l'implementazione lato server di Estimator. Pertanto, se il tuo codice usa NoiseLearner per apprendere il modello di rumore e passarlo a Estimator, devi aggiornare il tuo codice per usare NoiseLearnerV3.
Consulta la guida Migra da NoiseLearner a NoiseLearnerV3 per i dettagli.
Suddivisione dei job
Quando devi suddividere un job in diversi job perché i valori di shot o precisione misti in un unico job non sono più supportati, considera quanto segue:
-
Raggruppa i PUB in base al loro valore target — un job per ogni valore distinto, non un job per PUB. La suddivisione è un riraggruppamento, quindi il numero totale di PUB che invii non cambia. Per esempio, dato
[A@0.01, B@0.05, C@0.01], invia due job:[A, C]conprecision=0.01e[B]conprecision=0.05. InviareAeCcome job separati è meno efficiente, poiché ogni job comporta un overhead fisso. -
Apprendi una sola volta e usa i modelli di rumore in tutti i job suddivisi. È più efficiente eseguire un unico job
NoiseLearnerV3sull'unione di tutti i livelli. Il risultato di un job del noise learner contiene una lista di oggettiNoiseLearnerV3Result, uno per ogni istruzione di input, nello stesso ordine della lista di input. Puoi usare l'output di questo job del noise learner in tutti i job (Estimator) suddivisi, e i modelli di rumore per livelli non presenti nei PUB di un job suddiviso vengono ignorati. -
Invia prima tutti i job suddivisi in un
Batch, poi raccogli i loro risultati. La modalità di esecuzioneBatchoffre un'esecuzione parallela efficiente quando ci sono più job. Tuttavia,job.result()è bloccante, quindi chiamarlo all'interno del ciclo di invio serializza i job e annulla i vantaggi dell'uso diBatch. Assicurati di usare il pattern invia-tutto-poi-raccogli (mostrato di seguito).
Nell'esempio seguente, pub1 e pub2 richiedono precision=0.5, mentre pub3 richiede 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]
Struttura degli input del job
La nuova implementazione mappa gli input di Sampler o Estimator sugli input di Executor, quindi job.inputs restituisce un dizionario che contiene gli input di Executor. Questo dizionario ha le seguenti chiavi:
-
options: L'ExecutorOptiondi input. -
quantum_program: IlQuantumProgramdi input -
schema_version: La versione dello schema lato server utilizzata.
Se il tuo codice usava job.inputs['options'] per trovare le opzioni specificate per il job, ora puoi usare invece job.result().metadata['options'].
Esegui test in locale con un backend fittizio
Prima di inviare all'hardware, puoi convalidare il codice migrato rispetto a un backend Fake*
per individuare tempestivamente eventuali errori di sintassi. Nota i seguenti dettagli sulla modalità di test locale:
-
Non riproduce i risultati dell'hardware. La simulazione rumorosa locale non replica perfettamente il rumore di un dispositivo reale, e pertanto gli output potrebbero differire. L'esecuzione convalida che i percorsi delle opzioni e i tipi di valore siano corretti.
-
NoiseLearnerV3non ha una modalità di test locale: il suomodeaccetta solo unBackendreale, unaSessiono unBatch, quindi non puoi esercitare il passaggio di apprendimento del rumore rispetto a un backend fittizio. Verifica quella parte del tuo codice rispetto al riferimento API diNoiseLearnerV3. Conferma che il costruttore, la forma di input dirun(instructions)e qualsiasi helper (come l'helper dei livelli unici) siano usati come documentato.
Cliffordizza il circuito per una simulazione locale efficiente
Un backend fittizio usa un simulatore statevector (rumoroso), il cui costo cresce esponenzialmente con
il numero di qubit e la profondità. Pertanto, un circuito di workload realistico può bloccarsi o esaurire la memoria. Poiché
il test locale deve solo esercitare i percorsi delle opzioni (non riprodurre risultati fisici),
riduci prima il circuito a uno di Clifford con
ConvertISAToClifford,
che arrotonda ogni angolo RZ/RZZ/RX al multiplo più vicino di π/2. I circuiti di Clifford
si simulano in modo efficiente (simulazione stabilizzatore) indipendentemente dalla dimensione.
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 richiede un circuito ISA come input (l'output di
generate_preset_pass_manager(...).run(...) mirato al backend). Devi tenere conto delle seguenti conseguenze
quando costruisci il PUB locale:
-
L'attributo
.layoutviene eliminato. Il circuito Cliffordizzato mantiene lo stesso numero di qubit, maclifford.layoutèNone, quindiobservable.apply_layout(clifford.layout)fallisce. Disponi invece l'osservabile a partire dal circuito ISA pre-Clifford:isa_obs = observable.apply_layout(isa_circuit.layout), quindi esegui(clifford, isa_obs). -
I parametri vengono vincolati. Arrotondando gli angoli di rotazione, un circuito ISA parametrico diventa uno concreto di Clifford, quindi
clifford.num_parametersdiventa0. Un PUB che porta ancora un array di valori dei parametri fallisce la coercizione. Per l'esecuzione locale, rimuovi l'array dei parametri dal PUB; l'esecuzione sull'hardware mantiene il circuito parametrico originale e i suoi valori.
Passaggi successivi
- Modello di esecuzione diretta
- Input e output di Estimator
- Specifica le opzioni di Estimator
- Input e output di Sampler
- Specifica le opzioni di Sampler
- Helper di apprendimento del rumore (NoiseLearnerV3)
- Riferimento API di NoiseLearnerV3
- Pass del transpiler
ConvertISAToClifford - Tecniche di mitigazione e soppressione degli errori