Aller au contenu principal

Spécifier les options

Versions des packages

Le code de cette page a été développé avec les dépendances suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Tu peux utiliser des options pour personnaliser les primitives Estimator et Sampler. Cette section explique comment spécifier les options des primitives Qiskit Runtime. Bien que l'interface de la méthode run() des primitives soit commune à toutes les implémentations, leurs options ne le sont pas. Consulte les références API correspondantes pour en savoir plus sur les options de qiskit.primitives et qiskit_aer.primitives.

Remarques sur la spécification des options dans les primitives :

  • SamplerV2 et EstimatorV2 ont des classes d'options séparées. Tu peux consulter les options disponibles et mettre à jour leurs valeurs pendant ou après l'initialisation de la primitive.
  • Utilise la méthode update() pour appliquer des modifications à l'attribut options.
  • Si tu ne spécifies pas de valeur pour une option, elle reçoit la valeur spéciale Unset et les valeurs par défaut du serveur sont utilisées.
  • L'attribut options est du type Python dataclass. Tu peux utiliser la méthode intégrée asdict pour le convertir en dictionnaire.

Définir les options des primitives

Tu peux définir les options lors de l'initialisation de la primitive, après son initialisation, ou dans la méthode run(). Consulte la section sur les règles de priorité pour comprendre ce qui se passe lorsqu'une même option est spécifiée à plusieurs endroits.

Initialisation de la primitive

Tu peux passer une instance de la classe d'options ou un dictionnaire lors de l'initialisation d'une primitive ; celle-ci en fait alors une copie. Ainsi, modifier le dictionnaire ou l'instance d'options d'origine n'affecte pas les options détenues par les primitives.

Classe d'options

Lors de la création d'une instance des classes EstimatorV2 ou SamplerV2, tu peux passer une instance de la classe d'options. Ces options seront ensuite appliquées lors de l'appel à run() pour effectuer le calcul. Spécifie les options selon ce format : options.option.sous-option.sous-sous-option = valeur. Par exemple : options.dynamical_decoupling.enable = True

Exemple :

SamplerV2 et EstimatorV2 ont des classes d'options séparées (EstimatorOptions et SamplerOptions).

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime.options import EstimatorOptions

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

options = EstimatorOptions(
resilience_level=2,
resilience={"zne_mitigation": True, "zne": {"noise_factors": [1, 3, 5]}},
)

# or...
options = EstimatorOptions()
options.resilience_level = 2
options.resilience.zne_mitigation = True
options.resilience.zne.noise_factors = [1, 3, 5]

estimator = Estimator(mode=backend, options=options)

Dictionnaire

Tu peux spécifier les options sous forme de dictionnaire lors de l'initialisation de la primitive.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(
backend,
options={
"resilience_level": 2,
"resilience": {
"zne_mitigation": True,
"zne": {"noise_factors": [1, 3, 5]},
},
},
)

Mettre à jour les options après l'initialisation

Tu peux spécifier les options au format primitive.options.option.sous-option.sous-sous-option = valeur pour profiter de l'autocomplétion, ou utiliser la méthode update() pour effectuer des mises à jour groupées.

Les classes d'options de SamplerV2 et EstimatorV2 (EstimatorOptions et SamplerOptions) n'ont pas besoin d'être instanciées si tu définis les options après l'initialisation de la primitive.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

estimator = Estimator(mode=backend)

# Setting options after primitive initialization
# This uses auto-complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(
default_shots=4000, resilience={"zne_mitigation": True}
)

Méthode Run()

Les seules valeurs que tu peux passer à run() sont celles définies dans l'interface, c'est-à-dire shots pour le Sampler et precision pour l'Estimator. Ces valeurs écrasent toute valeur définie pour default_shots ou default_precision pour l'exécution en cours.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.circuit.library import random_iqp
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

circuit1 = random_iqp(3)
circuit1.measure_all()
circuit2 = random_iqp(3)
circuit2.measure_all()

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend
)

transpiled1 = pass_manager.run(circuit1)
transpiled2 = pass_manager.run(circuit2)

sampler = Sampler(mode=backend)
# Default shots to use if not specified in run()
sampler.options.default_shots = 500
# Sample two circuits at 128 shots each.
sampler.run([transpiled1, transpiled2], shots=128)

# Sample two circuits with different numbers of shots.
# 100 shots is used for transpiled1 and 200 for transpiled.
sampler.run([(transpiled1, None, 100), (transpiled2, None, 200)])
<RuntimeJobV2('d5k96cn853es738djikg', 'sampler')>

Cas particuliers

Niveau de résilience (Estimator uniquement)

Le niveau de résilience n'est pas réellement une option qui impacte directement la requête de la primitive, mais il spécifie un ensemble de base d'options préconfigurées sur lequel s'appuyer. En général, le niveau 0 désactive toute atténuation d'erreurs, le niveau 1 active les options d'atténuation des erreurs de mesure, et le niveau 2 active les options d'atténuation des erreurs de porte et de mesure.

Toutes les options que tu spécifies manuellement en plus du niveau de résilience s'appliquent par-dessus l'ensemble d'options de base défini par ce niveau. En principe, tu pourrais donc définir le niveau de résilience à 1 puis désactiver l'atténuation des mesures, bien que cela ne soit pas recommandé.

Dans l'exemple suivant, définir le niveau de résilience à 0 désactive initialement zne_mitigation, mais estimator.options.resilience.zne_mitigation = True remplace la configuration correspondante issue de estimator.options.resilience_level = 0.

from qiskit_ibm_runtime import EstimatorV2, QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

estimator = EstimatorV2(backend)

estimator.options.default_shots = 100
estimator.options.resilience_level = 0
estimator.options.resilience.zne_mitigation = True

Shots (Sampler uniquement)

La méthode SamplerV2.run accepte deux arguments : une liste de PUBs, dont chacun peut spécifier une valeur de shots propre au PUB, et un argument mot-clé shots. Ces valeurs de shots font partie de l'interface d'exécution du Sampler et sont indépendantes des options du Sampler Runtime. Elles ont la priorité sur toute valeur spécifiée dans les options, afin de respecter l'abstraction du Sampler.

Cependant, si shots n'est spécifié ni dans un PUB ni dans l'argument mot-clé de run (ou s'ils sont tous None), la valeur de shots provenant des options est utilisée, notamment default_shots.

En résumé, voici l'ordre de priorité pour spécifier les shots dans le Sampler, pour un PUB donné :

  1. Si le PUB spécifie des shots, cette valeur est utilisée.
  2. Si l'argument mot-clé shots est spécifié dans run, cette valeur est utilisée.
  3. Si num_randomizations et shots_per_randomization sont spécifiés comme options de twirling, les shots correspondent au produit de ces deux valeurs.
  4. Si sampler.options.default_shots est spécifié, cette valeur est utilisée.

Ainsi, si des shots sont spécifiés à tous les emplacements possibles, c'est celui ayant la priorité la plus haute (les shots spécifiés dans le PUB) qui est utilisé.

Précision (Estimator uniquement)

La précision est analogue aux shots, décrits dans la section précédente, sauf que les options de l'Estimator contiennent à la fois default_shots et default_precision. De plus, comme le twirling de portes est activé par défaut, le produit de num_randomizations et shots_per_randomization a la priorité sur ces deux options.

Plus précisément, pour un PUB Estimator donné :

  1. Si le PUB spécifie une précision, cette valeur est utilisée.
  2. Si l'argument mot-clé precision est spécifié dans run, cette valeur est utilisée.
  3. Si num_randomizations et shots_per_randomization sont spécifiés comme options de twirling (activées par défaut), leur produit est utilisé pour contrôler la quantité de données.
  4. Si estimator.options.default_shots est spécifié, cette valeur est utilisée pour contrôler la quantité de données.
  5. Si estimator.options.default_precision est spécifié, cette valeur est utilisée.

Par exemple, si la précision est spécifiée aux quatre emplacements, c'est celui ayant la priorité la plus haute (la précision spécifiée dans le PUB) qui est utilisé.

remarque

La précision varie inversement avec l'utilisation. Autrement dit, plus la précision est faible, plus le temps QPU nécessaire à l'exécution est long.

Options couramment utilisées

Il existe de nombreuses options disponibles, mais voici les plus couramment utilisées :

Shots

Pour certains algorithmes, définir un nombre précis de shots est une étape centrale de leur routine. Les shots (ou la précision) peuvent être spécifiés à plusieurs endroits. Voici leur ordre de priorité :

Pour tout PUB du Sampler :

  1. Les shots entiers contenus dans le PUB
  2. La valeur run(...,shots=val)
  3. La valeur options.default_shots

Pour tout PUB de l'Estimator :

  1. La précision en virgule flottante contenue dans le PUB
  2. La valeur run(...,precision=val)
  3. La valeur options.default_shots
  4. La valeur options.default_precision

Exemple :

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.circuit.library import random_iqp
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

circuit1 = random_iqp(3)
circuit1.measure_all()
circuit2 = random_iqp(3)
circuit2.measure_all()

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend
)

transpiled1 = pass_manager.run(circuit1)
transpiled2 = pass_manager.run(circuit2)

# Setting shots during primitive initialization
sampler = Sampler(mode=backend, options={"default_shots": 4096})

# Setting options after primitive initialization
# This uses auto-complete.
sampler.options.default_shots = 2000

# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(
default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"}
)

# Sample two circuits at 128 shots each.
sampler.run([transpiled1, transpiled2], shots=128)
<RuntimeJobV2('d5k96icjt3vs73ds5t0g', 'sampler')>

Temps d'exécution maximal

Le temps d'exécution maximal (max_execution_time) limite la durée d'exécution d'un job. Si un job dépasse cette limite, il est annulé de force. Cette valeur s'applique aux jobs individuels, qu'ils soient exécutés en mode job, session ou batch.

La valeur est exprimée en secondes, sur la base du temps quantique (et non du temps horloge), c'est-à-dire le temps pendant lequel le QPU est dédié au traitement de ton job. Elle est ignorée en mode de test local car ce mode n'utilise pas de temps quantique.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

estimator = Estimator(mode=backend)

estimator.options.max_execution_time = 2500

Désactiver toute atténuation et suppression d'erreurs

Tu peux désactiver toute atténuation et suppression d'erreurs si tu mènes, par exemple, des recherches sur tes propres techniques d'atténuation. Pour ce faire, avec EstimatorV2, définis resilience_level = 0. Pour SamplerV2, aucune modification n'est nécessaire car aucune option d'atténuation ou de suppression d'erreurs n'est activée par défaut.

Exemple :

Désactiver toute atténuation et suppression d'erreurs dans l'Estimator.

from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

# Define the service. This allows you to access IBM QPU.
service = QiskitRuntimeService()

# Get a backend
backend = service.least_busy(operational=True, simulator=False)

# Define Estimator
estimator = Estimator(backend)

options = estimator.options

# Turn off all error mitigation and suppression
options.resilience_level = 0

Prochaines étapes

Recommandations