Migrer de Sampler et Estimator côté serveur vers côté client
Ce guide décrit comment migrer des implémentations côté serveur de Sampler et Estimator d'IBM Quantum®
vers leurs nouvelles implémentations côté client dans
qiskit-ibm-runtime. Les interfaces et les options restent globalement inchangées, donc la plupart du code
fonctionne tel quel, mais il existe quelques différences de comportement à comprendre.
Contexte
Sampler et Estimator sont des interfaces de primitives définies dans Qiskit. IBM Quantum
Compute Service (anciennement Qiskit Runtime) a historiquement fourni l'implémentation
de ces primitives au sein de son environnement d'exécution. Lorsque tu appelles sampler.run() ou
estimator.run(), la requête est envoyée au service, et tout le calcul — y compris
la suppression et la mitigation d'erreurs — se déroule côté serveur.
Cette expérience en boîte noire est pratique : tu n'as pas à te soucier des détails d'implémentation. Mais cela rend également les primitives difficiles à déboguer, personnaliser ou à partir desquelles apprendre, car tu ne peux pas voir ce qui se passe pendant le traitement.
Le modèle d'exécution dirigée récemment introduit adopte l'approche opposée et offre une expérience en boîte blanche. Toutes les intentions de conception sont capturées côté client, et une seule primitive côté serveur Executor traite ces entrées exactement comme indiqué — elle ne prend aucune décision implicite en ton nom.
À partir de qiskit-ibm-runtime v0.50.0, Sampler et Estimator sont réimplémentés
côté client au-dessus d'Executor. Ils offrent la même commodité et
la même abstraction qu'auparavant, et tu peux désormais inspecter les détails d'implémentation lorsque tu en as
besoin. Comme les interfaces et les options restent globalement les mêmes, la migration devrait être
transparente.
Remarque : IBM Quantum ne prend en charge que la version 2 des interfaces Sampler et Estimator (BaseSamplerV2 et BaseEstimatorV2). Elles sont donc simplement désignées par Sampler et Estimator dans ce guide.
Mettre à jour les imports
Aujourd'hui, tu dois importer explicitement les nouvelles implémentations depuis leurs modules dédiés :
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
Dans un avenir proche, les imports de premier niveau pointeront vers les nouvelles implémentations côté client, et aucune modification de code ne sera nécessaire :
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
De même, si tu construis des objets d'options typés, tu dois les importer depuis
qiskit_ibm_runtime.options_models à la place, ou simplement passer un dict imbriqué simple :
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
Ce qui reste identique
-
Construction de la primitive avec un
modeet desoptions. -
La signature de
run()et le format PUB. -
L'arborescence des options (
options.twirling,options.resilience,options.default_shots, et ainsi de suite). -
La structure de données de résultat renvoyée par
job.result().
Changements incompatibles dans le nouveau Sampler
| Change | Migration action |
|---|---|
La primitive sous-jacente est désormais Executor. L'interface utilisateur d'IBM Quantum Platform et job.primitive_id afficheront tous deux executor au lieu de sampler. | Met à jour tout code qui référence job.primitive_id. |
La nouvelle implémentation mappe les entrées de Sampler vers les entrées d'Executor, donc job.inputs renvoie les entrées d'Executor. | Met à jour tout code qui référence job.inputs. Voir Entrées du job. |
Davantage de pré- et post-traitement se déroule désormais côté client, donc sampler.run() et job.result() peuvent prendre plus de temps qu'auparavant. | Active la journalisation INFO pour suivre la progression du traitement côté client. Voir Activer la journalisation INFO. |
Les métadonnées du circuit sont copiées dans les métadonnées du résultat. Les types de données autorisés dans les métadonnées du résultat sont désormais limités à str, float, int, bool, ainsi qu'aux listes ou dictionnaires de ces types. | Si tu as besoin d'autres types de données, encode-les d'abord sous forme de chaîne (par exemple, avec base64). |
Les classes d'options (options_models.SamplerOptions et ainsi de suite) sont désormais des modèles Pydantic au lieu de dataclasses, elles ne peuvent donc plus être converties en dictionnaires Python en utilisant asdict(). | Utilise options.model_dump() à la place. |
Les classes d'options qui avaient auparavant le suffixe V2 (ExecutionOptionsV2 et ainsi de suite) ne l'ont plus, car les primitives V1 ne sont plus prises en charge. | Supprime le suffixe V2 de ces classes d'options : remplace ExecutionOptionsV2 par ExecutionOptions, ResilienceOptionsV2 par ResilienceOptions, et SamplerExecutionOptionsV2 par SamplerExecutionOptions. |
Si twirling est activé et que shots (dans les PUB ou dans run()), shots_per_randomization et num_randomizations sont tous spécifiés, alors num_randomizations * shots_per_randomization prend le pas sur shots. | Omets num_randomizations et shots_per_randomization si tu veux que la valeur shots soit utilisée. |
Une partie de la validation des entrées a été déplacée côté serveur et lève désormais RuntimeError au lieu de IBMInputValueError. | Met à jour les types d'exceptions que ton code intercepte. |
| Les valeurs de shots mixtes dans un même job ne sont plus prises en charge. | Soumets un job séparé pour chaque valeur de shots. Voir Fractionnement des jobs pour les points à considérer. |
Changements incompatibles dans le nouvel Estimator
| Change | Migration action |
|---|---|
La primitive sous-jacente est désormais Executor. L'interface utilisateur d'IBM Quantum Platform et job.primitive_id afficheront tous deux executor au lieu de estimator. | Met à jour tout code qui référence job.primitive_id. |
La nouvelle implémentation mappe les entrées d'Estimator vers les entrées d'Executor, donc job.inputs renvoie les entrées d'Executor. | Met à jour tout code qui référence job.inputs. Voir Entrées du job. |
Davantage de pré- et post-traitement se déroule désormais côté client, donc estimator.run() et job.result() peuvent prendre plus de temps qu'auparavant. | Active la journalisation INFO pour suivre la progression du traitement côté client. Voir Activer la journalisation INFO. |
Les métadonnées du circuit sont copiées dans les métadonnées du résultat. Les types de données autorisés dans les métadonnées du résultat sont désormais limités à str, float, int, bool, ainsi qu'aux listes ou dictionnaires de ces types. | Si tu as besoin d'autres types de données, encode-les d'abord sous forme de chaîne (par exemple, avec base64). |
Les classes d'options (options_models.EstimatorOptions et ainsi de suite) sont désormais des modèles Pydantic au lieu de dataclasses, elles ne peuvent donc plus être converties en dictionnaires Python en utilisant asdict(). | Utilise options.model_dump() à la place. |
Les classes d'options qui avaient auparavant le suffixe V2 (ExecutionOptionsV2 et ainsi de suite) ne l'ont plus, car les primitives V1 ne sont plus prises en charge. | Supprime le suffixe V2 de ces classes d'options : remplace ExecutionOptionsV2 par ExecutionOptions et ResilienceOptionsV2 par ResilienceOptions. |
| Toutes les options d'entrée sont renvoyées dans les métadonnées du résultat, plutôt qu'un sous-ensemble sélectionné. | Aucune — ceci est informatif. |
Une partie de la validation des entrées a été déplacée côté serveur et lève désormais RuntimeError au lieu de IBMInputValueError. | Met à jour les types d'exceptions que ton code intercepte. |
| Il n'y a plus d'apprentissage implicite du bruit pour PEA et PEC. L'apprentissage du bruit de mesure pour TREX est toujours pris en charge. | Apprends les modèles de bruit séparément et passe-les à Estimator. Voir Effectuer un apprentissage explicite du bruit pour PEA et PEC. |
Le type d'entrée de ResilienceOptions.layer_noise_model est différent et peut être construit à partir des résultats de NoiseLearnerV3. | Voir Effectuer un apprentissage explicite du bruit pour PEA et PEC pour savoir comment apprendre les modèles de bruit en utilisant NoiseLearnerV3 et les passer à Estimator. |
MeasureNoiseLearningOptions.shots_per_randomization n'est plus pris en charge. | Une seule valeur de shots est utilisée pour tous les circuits du job, y compris les circuits d'apprentissage du bruit de mesure. Si tu dois utiliser une valeur de shots différente, applique TREX avec qiskit-mitigation en dehors d'Estimator. |
| Les valeurs de précision mixtes dans un même job ne sont plus prises en charge. | Soumets un job séparé pour chaque précision souhaitée. Voir Fractionnement des jobs pour les points à considérer. |
L'option seed_estimator n'est plus prise en charge. | Supprime toute affectation de options.seed_estimator (sa définition lève une ValidationError). Il n'y a pas d'équivalent côté client, donc les résultats ne sont plus reproductibles via cette graine. |
Activer la journalisation INFO
Comme davantage de travail se déroule désormais côté client, il est utile de voir la progression de ce
traitement. Active la journalisation de niveau INFO pour le logger qiskit_ibm_runtime :
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
Effectuer un apprentissage explicite du bruit pour PEA et PEC
Le nouvel Estimator n'effectue plus d'apprentissage implicite du bruit lorsque la méthode de mitigation
d'erreurs PEA ou PEC est sélectionnée. Tu dois apprendre les modèles de bruit explicitement et les
passer. Utilise le nouveau NoiseLearnerV3 pour contrôler comment les circuits
sont stratifiés en couches. Il prend en entrée une liste d'instructions de circuit encadrées (boxed) (par exemple,
les couches uniques).
PEA et PEC nécessitent désormais ce schéma explicite. Ne saute pas l'étape d'apprentissage du bruit, sinon ton code échouera. L'apprentissage du bruit de mesure pour TREX n'est pas affecté et continue de fonctionner comme avant.
De même, si ton code utilise NoiseLearner et passe le modèle de bruit résultant à Estimator côté serveur, tu dois migrer vers NoiseLearnerV3. N'utilise PAS l'ancien NoiseLearner, qui est incompatible avec le nouvel Estimator.
Toutes les options d'apprentissage du bruit dans Estimator côté serveur (LayerNoiseLearningOptions) sont directement mappées vers l'option de NoiseLearnerV3 (NoiseLearnerV3Options), à l'exception de max_layers_to_learn. Le nombre de couches à apprendre est à la place basé sur le nombre de couches passées à NoiseLearnerV3.
Par exemple :
Estimator côté serveur (avec PEC activé) :
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 côté client (avec PEC activé) :
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)
Migrer de NoiseLearner vers NoiseLearnerV3
NoiseLearner fonctionne uniquement avec l'implémentation côté serveur d'Estimator. Par conséquent, si ton code utilise NoiseLearner pour apprendre le modèle de bruit et le passer à Estimator, tu dois mettre à jour ton code pour utiliser NoiseLearnerV3.
Voir le guide Migrer de NoiseLearner vers NoiseLearnerV3 pour plus de détails.
Fractionnement des jobs
Lorsque tu dois fractionner un job en plusieurs parce que les valeurs de shots ou de précision mixtes dans un même job ne sont plus prises en charge, prends en compte les points suivants :
-
Regroupe les PUB par leur valeur cible — un job par valeur distincte, pas un job par PUB. Le fractionnement est un regroupement, donc le nombre total de PUB que tu soumets ne change pas. Par exemple, étant donné
[A@0.01, B@0.05, C@0.01], soumets deux jobs :[A, C]àprecision=0.01et[B]àprecision=0.05. SoumettreAetCcomme des jobs séparés est moins efficace, car chaque job comporte un surcoût fixe. -
Apprends une seule fois et utilise les modèles de bruit dans tous les jobs fractionnés. Il est plus efficace d'exécuter un seul job
NoiseLearnerV3sur l'union de toutes les couches. Le résultat d'un job de noise learner contient une liste d'objetsNoiseLearnerV3Result, un pour chaque instruction d'entrée, dans le même ordre que la liste d'entrée. Tu peux utiliser la sortie de ce job de noise learner dans tous les jobs fractionnés (Estimator), et les modèles de bruit pour les couches absentes des PUB d'un job fractionné sont ignorés. -
Soumets d'abord tous les jobs fractionnés dans un
Batch, puis récupère leurs résultats. Le mode d'exécutionBatchoffre une exécution parallèle efficace lorsqu'il y a plusieurs jobs. Cependant,job.result()est bloquant, donc l'appeler à l'intérieur de la boucle de soumission sérialise les jobs et annule les avantages de l'utilisation deBatch. Assure-toi d'utiliser le schéma soumettre-tout-puis-récupérer (illustré ci-dessous).
Dans l'exemple suivant, pub1 et pub2 nécessitent precision=0.5, tandis que pub3 nécessite 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]
Structure des entrées du job
La nouvelle implémentation mappe les entrées de Sampler ou Estimator vers les entrées d'Executor, donc job.inputs renvoie un dictionnaire contenant les entrées d'Executor. Ce dictionnaire comporte les clés suivantes :
-
options: L'entréeExecutorOption. -
quantum_program: L'entréeQuantumProgram -
schema_version: La version du schéma côté serveur utilisée.
Si ton code utilisait job.inputs['options'] pour trouver les options spécifiées pour le job, tu peux désormais utiliser job.result().metadata['options'] à la place.
Tester localement avec un backend fictif
Avant de soumettre au matériel, tu peux valider le code migré par rapport à un backend Fake*
pour détecter rapidement les erreurs de syntaxe. Note les détails suivants concernant le mode de test local :
-
Il ne reproduit pas les résultats matériels. La simulation bruitée locale ne reproduit pas parfaitement le bruit d'un appareil réel, donc les sorties peuvent différer. L'exécution valide bien que les chemins d'options et les types de valeurs sont corrects.
-
NoiseLearnerV3n'a aucun mode de test local : sonmoden'accepte qu'unBackend, uneSession, ou unBatchréel, donc tu ne peux pas exercer l'étape d'apprentissage du bruit sur un backend fictif. Vérifie plutôt cette partie de ton code par rapport à la référence API deNoiseLearnerV3. Confirme que le constructeur, la forme d'entrée derun(instructions), et tout assistant (tel que l'assistant de couche unique) sont utilisés comme documenté.
Cliffordiser le circuit pour une simulation locale efficace
Un backend fictif utilise un simulateur (bruité) à vecteur d'état, dont le coût croît exponentiellement avec
le nombre de qubits et la profondeur. Ainsi, un circuit de charge de travail réaliste peut se bloquer ou épuiser la mémoire. Comme
le test local a seulement besoin d'exercer les chemins d'options (et non de reproduire des résultats physiques),
réduis d'abord le circuit à un circuit de Clifford avec
ConvertISAToClifford,
qui arrondit chaque angle RZ/RZZ/RX au multiple de π/2 le plus proche. Les circuits de Clifford
se simulent efficacement (simulation de stabilisateurs) quelle que soit leur taille.
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 nécessite un circuit ISA en entrée (la sortie de
generate_preset_pass_manager(...).run(...) ciblant le backend). Tu dois tenir compte des conséquences suivantes
lors de la construction du PUB local :
-
L'attribut
.layoutest supprimé. Le circuit cliffordisé conserve le même nombre de qubits, maisclifford.layoutvautNone, doncobservable.apply_layout(clifford.layout)échoue. Positionne plutôt l'observable à partir du circuit ISA pré-Clifford :isa_obs = observable.apply_layout(isa_circuit.layout), puis exécute(clifford, isa_obs). -
Les paramètres sont liés et disparaissent. L'arrondi des angles de rotation transforme un circuit ISA paramétrique en un circuit de Clifford concret, donc
clifford.num_parametersdevient0. Un PUB qui porte encore un tableau de valeurs de paramètres échoue lors de la coercition. Pour l'exécution locale, supprime le tableau de paramètres du PUB ; l'exécution matérielle conserve le circuit paramétrique original et ses valeurs.
Prochaines étapes
- Modèle d'exécution dirigée
- Entrées et sorties d'Estimator
- Spécifier les options d'Estimator
- Entrées et sorties de Sampler
- Spécifier les options de Sampler
- Assistant d'apprentissage du bruit (NoiseLearnerV3)
- Référence API de NoiseLearnerV3
- Passe de transpileur
ConvertISAToClifford - Techniques de mitigation et de suppression d'erreurs