Skip to main content
IBM Quantum Platform

Migration de Sampler vers Executor

Ce guide explique comment transférer les charges de travail d'échantillonnage quantique de la primitive « Sampler » d' IBM Quantum® vers la primitive « Executor ».

Edition bêta

La primitive « Executor » fait partie du modèle d'exécution dirigée. Tous les composants du modèle d'exécution dirigée sont actuellement en version bêta et peuvent ne pas être stables. Nous vous invitons à les tester et à nous faire part de vos commentaires en créant un ticket dans les dépôts Samplomatic ou qiskit-ibm-runtime sur GitHub.


Devriez-vous migrer?

Tout le monde ne devrait pas passer de Sampler à Executor. Il existe de nombreuses différences entre les types primitifs, mais les conseils suivants peuvent vous aider à décider s'il convient de procéder à la migration :

Optez pour Executor si vous êtes un chercheur en information quantique qui mène des expériences à grande échelle et qui a besoin d’un contrôle précis et reproductible sur des techniques telles que la rotation de Pauli, l’apprentissage et l’injection de modèles de bruit, ainsi que les changements de base — ou si vous avez besoin de l’une des fonctionnalités supplémentaires offertes par Executor.

Continuez à utiliser Sampler si vous recherchez une interface simple et intuitive et si vous souhaitez que la primitive se charge pour vous de la suppression et de l'atténuation des erreurs.

Limites et mises en garde

Executor et le modèle d'exécution dirigée étant encore en version bêta, veuillez tenir compte des points suivants avant de décider de procéder à la migration :

  • Pas encore de prise en charge du simulateur : contrairement à Sampler, qui dispose d’une AerSamplerimplémentation permettant qiskit-aer la simulation locale, il n’existe actuellement aucun backend de simulateur pour Executor. La prise en charge du simulateur devrait être disponible prochainement. En attendant, vous pouvez toujours examiner et tester le circuit type en local afin de valider votre processus avant de le soumettre à la fabrication.
  • Ce guide concerne uniquement Sampler, et non Estimator. La migration d'Estimator vers Executor est nettement plus complexe que celle depuis Sampler, car Estimator calcule des valeurs attendues plutôt que de renvoyer des échantillons bruts. Pour reproduire le comportement d'Estimator avec Executor, un post-traitement supplémentaire est nécessaire. Les fonctions utilitaires destinées à faciliter la migration d' Estimator vers Executor sont encore en cours de développement; c'est pourquoi ce guide ne décrit, à dessein, que le workflow Sampler.

Principales différences entre Executor et Sampler

Les fonctions « Sampler » et « Executor » échantillonnent toutes deux les registres de sortie des circuits quantiques, mais elles s'adressent à des utilisateurs différents :

  • Sampler est une abstraction de haut niveau. Il présente les caractéristiques suivantes :
    • Il intègre des mécanismes de suppression des erreurs (découplage dynamique et twirling).
    • Il prend des décisions implicites à votre place.
    • Il est conçu pour permettre aux développeurs d'algorithmes de se concentrer sur l'innovation plutôt que sur les données. Conversion.
  • L'exécuteur fait partie du modèle d'exécution dirigée. Il diffère de Sampler à bien des égards et présente les caractéristiques suivantes :
    • Il ne dispose d'aucun mécanisme intégré de suppression ou d'atténuation des erreurs. Au lieu de cela, vous définissez votre intention de conception côté client (à l'aide d'annotations de circuit et d'un samplex), et la génération, coûteuse en ressources, des variantes de circuit est transférée côté serveur.

    • Il ne prend aucune décision implicite. Il suit vos instructions à la lettre, vous offrant ainsi un contrôle total et une transparence absolue.

    • Executor et Samplomatic offrent ensemble des fonctionnalités supplémentaires que Sampler ne propose pas, notamment (mais sans s'y limiter) les suivantes :

      • Davantage de groupes de rotation : Samplomatic vous permet de choisir le groupe de rotation à appliquer à chaque case, plutôt que d'être limité à la stratégie unique que Sampler applique pour vous. Il prend également en charge les groupes de torsion autres que celui de Pauli, tels que le groupe de torsion "local_c1" .
      • Mesures avec noyau et classées regroupées : ce paramètre QuantumProgram.meas_level = "both" (ajouté dans v0.48.0qiskit-ibm-runtime ) demande que les résultats contiennent à la fois les mesures classées et celles avec noyau, au lieu de choisir un seul type de mesure par tâche.
      • Twirling pour les circuits comportant des portes fractionnaires : Executor peut appliquer le twirling à des circuits contenant des portes fractionnaires.
      • Atténuation des erreurs fine et modulable : par exemple, choisir les couches du circuit à traiter et ajuster les taux de bruit injectés dans le circuit.
      Remarques
      • Les nouvelles fonctionnalités à venir devraient être intégrées en priorité à Executor et pourraient ne pas être portées sur Sampler. Si vous tenez à pouvoir bénéficier des dernières fonctionnalités, Executor est le choix le plus pérenne.
      • Le paquet Qiskit de base ne fournit pas encore de classe de base pour la primitive Executor (contrairement àSamplerV2).

Cartographie conceptuelle

Le tableau suivant montre comment les concepts de Sampler correspondent à ceux d'Executor.

Concept
Echantillonneur
Exécuteur
Importerfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
EntréeListe des PUB (tuples)Un ensemble QuantumProgram d'objets QuantumProgramItem
Circuit et paramètres(circuit, params, shots) bloc de donnéesprogram.append_circuit_item(circuit, circuit_arguments=...)
VirevolterTwirlingOptionsExplicite grâce à des encadrés annotés et à un samplex (append_samplex_item)
Exécuter l'appelsampler.run([pub, ...])executor.run(program)
Type de résultatPrimitiveResultdeSamplerPubResultQuantumProgramResult (itérable)
Accès aux donnéesresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Gérer le bruitOptions intégréesDoit être composé manuellement (annotations, samplex, etc. NoiseLearnerV3)

Présentation générale des étapes de migration

  1. Installez Samplomatic.
  2. Modifiez les importations.
  3. Remplacer les tuples « PUB ».
  4. Modifier la manière dont les plans sont décrits.
  5. Modifiez les autres options si nécessaire.
  6. Mettez à jour la commande run .
  7. Mise à jour de l'analyse des résultats.
  8. Annuler la rotation.

Etape 1. Installez les paquets nécessaires

Executor et le modèle d'exécution dirigée nécessitent le samplomatic package :

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Notes de mise à jour
  • qiskit-ibm-runtime v0.48.0 est recommandé car il ajoute l'option meas_level = "both" et le groupe de rotation local_c1 .
  • qiskit >= 2.3.0 est obligatoire.
  • samplomatic >= 0.18.0 est obligatoire.

Étape 2. Modifier les importations

Extrait :

from qiskit_ibm_runtime import SamplerV2 as Sampler

Exécuteur testamentaire :

from qiskit_ibm_runtime import Executor, QuantumProgram

Étape 3. Remplacer les tuples de type « PUB » par un QuantumProgram

Au lieu de passer une liste de tuples (PUB), lorsque vous utilisez Executor, vous créez un QuantumProgram et y ajoutez des éléments.

A accepte QuantumProgram les articles de circuit et les articles Samplex :

  • append_circuit_item: Ajoute un CircuitItem, qui est un circuit, ainsi que (facultativement) ses valeurs de paramètres. Il est exécuté tel quel, sans aucune randomisation.

    Utilisez cette option lorsque vous souhaitez simplement échantillonner un circuit, exactement comme le ferait Sampler avec un fichier « PUB » ne comportant aucun « twirling »; par exemple, lorsque vous soumettez une tâche d'échantillonnage simple, ou lorsque vous avez déjà inclus manuellement toutes les variantes souhaitées.

  • append_samplex_item: Ajoute un samplexItem, qui est un circuit modèle associé à un samplex générant des ensembles de paramètres aléatoires côté serveur.

    Utilisez cette option lorsque vous souhaitez que le contenu du circuit soit aléatoire. Le cas principal concerne la rotation (porte ou mesure) ou l'injection de bruit. Cette fonctionnalité remplace la fonction « twirling » intégrée à Sampler.

Un seul QuantumProgram peut accepter les deux types d'éléments; chaque élément ajouté est exécuté en tant que tâche indépendante et génère sa propre entrée dans les résultats. En règle générale, utilisez append_circuit_item lorsque votre circuit n'a pas besoin d'être aléatoire. Sinon, utilisez append_samplex_item.

Les sections suivantes présentent successivement : les circuits paramétrés qui utilisent append_circuit_item, et la migration du twirling à l'aide de append_samplex_item.

Dans les exemples de code suivants, isa_circuit désigne le circuit qui a été transpilé afin de se conformer à l' architecture de jeu d'instructions (ISA) du backend cible. Ceci comporte isa_circuit deux paramètres.

Étape n° 3a e. Migrer des circuits paramétrés

Avec Sampler, les valeurs des paramètres correspondent au deuxième élément du tuple « PUB ». Avec Executor, transmettez-les circuit_arguments à append_circuit_item.

Extrait :

params = np.random.rand(10, circuit.num_parameters)  # 10 parameter sets
pubs = (isa_circuit, params)

Exécuteur

program = QuantumProgram(shots=1024)
program.append_circuit_item(
    isa_circuit,
    circuit_arguments=np.random.rand(10, circuit.num_parameters),  # 10 sets
)

# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]

Étape n° 3b e. Migrer les « twirls » intégrés vers des annotations explicites

C'est le changement le plus important. Sampler effectue des mouvements de rotation à votre place grâce à ses options. Avec Executor, vous déclarez explicitement cette intention à l'aide de cases annotées et d'un « samplex » (de Samplomatic ).

Exemple (rotation à l'aide des options) :

sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True

Exécuteur (figurations à l'aide de boîtes et d'un samplex) :

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
    enable_gates=True,     # gate twirling
    enable_measures=True,  # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)

# 2. Build the (template circuit, samplex) pair.
#    The template circuit's single-qubit gates are replaced by parameterized gates;
#    the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)

# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # original circuit params
    },
    shape=(28, 10),  # 28 randomizations x 10 parameter sets
)

Comme le circuit modèle et le samplex sont générés côté client, vous pouvez les examiner et les tester localement afin de vérifier le résultat avant d'envoyer quoi que ce soit vers le matériel.

Vérification : tester le circuit type localement

Vous pouvez extraire des échantillons aléatoires du samplex et les associer au circuit modèle afin de vérifier que le samplex génère bien les valeurs de paramètres attendues. Les valeurs des paramètres renvoyées par samplex.sample sont directement compatibles avec les paramètres du circuit modèle.

# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())

# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
    parameter_values=np.random.rand(2),  # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)

# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)

Pour aller plus loin, vous pouvez vérifier que chaque randomisation est logiquement équivalente au circuit d’origine, par exemple en convertissant les deux en objets Operator et en comparant leurs implémentations unitaires (après avoir pris en compte les corrections outputs["measurement_flips.<register>"] qui annulent l’effet de « measurement twirling »), ou en comparant les valeurs d’espérance issues d’une exécution locale StatevectorSampler ou d’une StatevectorEstimator exécution. Consultez le guide des entrées et sorties du Samplomatic Samplex pour un aperçu complet.

Étape 4. Modifier la manière dont les prises de vue sont demandées

Déplacer les images de l' PUB vers QuantumProgram(shots=...). Dans Executor, cela s'applique shots à l'ensemble de la tâche. Envoyez plusieurs demandes si vous avez besoin d'un nombre de prises différent.

Extrait :

# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])

Exécuteur testamentaire :

# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

Étape 5. Mettez à jour les options si nécessaire

Executor propose moins d'options que Sampler, car les paramètres de gestion des erreurs se trouvent désormais dans vos annotations et dans samplex, et non plus dans les options.

Il existe également une différence structurelle quant à l'emplacement des paramètres.

  • Avec Sampler, tout, y compris les choix qui influent sur le post-traitement des résultats, se configure dans les options de la primitive ou dans le fichier « PUB ».

  • Avec Executor, les paramètres qui influent sur la manière dont les résultats du travail sont générés et post-traités sont définis dans le QuantumProgram, et non dans le ExecutorOptions.

Exemples :

Echantillonneur
Exécuteur
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

ExecutorOptions ne contient que des paramètres d'exécution et d'environnement de bas niveau qui ne modifient pas la structure des données renvoyées. Elle comprend trois groupes de premier niveau :

Il convient de noter que les options dynamical_decoupling et twirling existent dans Sampler, mais pas dans Executor. Au contraire, ces valeurs d'option sont exprimées par le biais du modèle d'exécution dirigée.

Exemple :

from qiskit_ibm_runtime import Executor, ExecutorOptions

options = ExecutorOptions(
    environment={"log_level": "INFO"},
    execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True

executor = Executor(mode=backend, options=options)

Étape 6. Mettre à jour la commande run

L'entrée d'une tâche Executor est le programme, et non des PUB.

Extrait :

# Submit a job
sampler.run([(isa_circuit, parameter_values)])

Exécuteur testamentaire :

# Submit a job
executor.run(program)

Etape 7. Modifier la manière dont vous accédez aux résultats

Dans Executor, les résultats sont des tableaux de type « NumPy », et non des objets BitArray . Utilisez la chaîne de caractères comme index (result[0]["meas"]) et récupérez un np.ndarray résultat. Il n'est pas nécessaire de mémoriser le chemin d'accès à l'attribut .data.<register> .

Pour passer de Sampler à Executor, remplacez result[i].data.<reg> (BitArray) par result[i]["<reg>"] (np.ndarray), puis réécrivez le post-traitement get_countsbasé sur - en opérations de type « NumPy ».

Tâche
Echantillonneur
Exécuteur
Récupérer les données du registreresult[0].data.measresult[0]["meas"]
Type de donnéesBitArraynp.ndarray
Dictionnaire des nombres cardinauxresult[0].data.meas.get_counts()Traiter manuellement le tableau
Registres multiplesresult[0].data.<name> par registreresult[0]["<name>"] par registre
CircuitItem forme du tableau-(parameter_sets, shots, register_bits)
SamplexItem forme du tableau-(randomizations, parameter_sets, shots, register_bits)
Annuler la rotation de la mesureAutomatiqueresult[i]["measurement_flips.<name>"] + XOR
Note

Sampler's BitArray propose des aides (get_counts, slice_bits, slice_shots, expectation_values, et des masques de post-sélection). L'exécuteur renvoie des tableaux d' NumPy s brutes, ce qui vous permet d'effectuer ce post-traitement à l'aide d'opérations standard d' NumPy.

Étape 8. Gérer les résultats altérés (corrections de bits inversés)

Lorsque vous appliquez une transformation de mesure par rotation via un SamplexItem, Executor renvoie les mesures brutes (rotées) ainsi que les corrections par inversion de bits nécessaires pour annuler la rotation. Vous devez les appliquer manuellement; aucune correction n'est effectuée automatiquement.

Lorsque vous utilisez Executor, annulez explicitement le « twirling » à l'aide des corrections measurement_flips.<reg> et d'une opération XOR, comme le montre l'exemple suivant :

# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"]                       # example: (28, 10, 1024, 2)

# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"]      # example: (28, 10, 1, 2)

# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1

Il n'existe pas d'étape équivalente dans Sampler, car celui-ci annule automatiquement le « twirling » à votre place.


Exemple complet : migration d'une tâche d'échantillonnage de base

Echantillonneur

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()

# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()

Exécuteur

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()

# 6. Access results: a plain np.ndarray keyed by register name
#    shape = (shots, register_bits)
meas = result[0]["meas"]

Etapes suivantes

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.