Introduction aux primitives
Versions des packages
Le code sur 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
La version bêta d'un nouveau modèle d'exécution est maintenant disponible. Le modèle d'exécution dirigé offre plus de flexibilité pour personnaliser ton flux de travail d'atténuation des erreurs. Consulte le guide Modèle d'exécution dirigé pour plus d'informations.
Pourquoi Qiskit a-t-il introduit les primitives ?
À l'image des débuts de l'informatique classique, où les développeurs devaient manipuler directement les registres du processeur, la première interface vers les QPU renvoyait simplement les données brutes issues de l'électronique de contrôle.
Ce n'était pas un problème majeur tant que les QPU restaient dans les laboratoires et n'étaient accessibles qu'aux chercheurs.
Conscient que la plupart des développeurs ne devraient pas avoir à transformer ces données brutes en 0 et en 1, Qiskit a introduit backend.run, une première abstraction pour accéder aux QPU dans le cloud. Cela a permis aux développeurs
de travailler sur un format de données familier et de se concentrer sur l'essentiel.
À mesure que l'accès aux QPU s'est généralisé et que de nouveaux algorithmes quantiques ont été développés,
le besoin d'une abstraction de plus haut niveau s'est à nouveau fait sentir. En réponse, Qiskit a introduit
l'interface des primitives, optimisée pour deux tâches fondamentales du développement d'algorithmes quantiques :
l'estimation de valeurs d'espérance (Estimator) et l'échantillonnage de circuits (Sampler). L'objectif est encore une fois
d'aider les développeurs à se concentrer davantage sur l'innovation et moins sur la conversion de données. L'interface des primitives remplace l'interface backend.run, puisque Sampler offre le même accès direct au matériel que celui proposé par backend.run.
Qu'est-ce qu'une primitive ?
Les systèmes informatiques sont construits sur plusieurs couches d'abstraction. Les abstractions te permettent de te concentrer sur un niveau de détail particulier, adapté à la tâche en cours. Plus tu te rapproches du matériel, plus le niveau d'abstraction dont tu as besoin est bas (par exemple, tu pourrais avoir à déplacer ou manipuler des données au niveau des instructions processeur). Plus la tâche que tu veux accomplir est complexe, plus les abstractions seront de haut niveau (par exemple, tu pourrais utiliser une bibliothèque de programmation pour effectuer des calculs algébriques).
Dans ce contexte, une primitive est la plus petite instruction de traitement, le bloc de construction le plus simple à partir duquel on peut créer quelque chose d'utile pour un niveau d'abstraction donné.
Les récentes avancées en informatique quantique ont accru le besoin de travailler à des niveaux d'abstraction plus élevés. À mesure que le domaine évolue vers des unités de traitement quantique (QPU) plus grandes et des flux de travail plus complexes, l'accent se déplace de l'interaction avec les signaux individuels des qubits vers la vision des dispositifs quantiques comme des systèmes qui accomplissent des tâches précises.
Les deux tâches les plus courantes pour les ordinateurs quantiques sont l'échantillonnage d'états quantiques et le calcul de valeurs d'espérance. Ces tâches ont motivé la conception des primitives Qiskit : Estimator et Sampler.
- Estimator calcule les valeurs d'espérance d'observables par rapport aux états préparés par des circuits quantiques.
- Sampler échantillonne le registre de sortie à partir de l'exécution de circuits quantiques.
En résumé, le modèle de calcul introduit par les primitives Qiskit rapproche la programmation quantique d'un pas vers là où se trouve aujourd'hui la programmation classique, où l'accent est moins mis sur les détails matériels et davantage sur les résultats que tu cherches à obtenir.
Définition et implémentations des primitives
Il existe deux types de primitives Qiskit : les classes de base et leurs implémentations. Les primitives Qiskit sont définies par des classes de base primitives open source qui se trouvent dans le SDK Qiskit (dans le module qiskit.primitives). Les fournisseurs (tels que Qiskit Runtime) peuvent utiliser ces classes de base pour dériver leurs propres implémentations de Sampler et d'Estimator. La plupart des utilisateurs interagiront avec les implémentations des fournisseurs, et non avec les primitives de base.
Classes de base
BaseEstimatorV2 et BaseSamplerV2 - Classes de base abstraites qui définissent une interface commune pour implémenter des primitives. Toutes les autres classes du module qiskit.primitives héritent de ces classes de base. Les développeurs devraient les utiliser s'ils souhaitent créer leur propre modèle d'exécution basé sur les primitives pour un fournisseur spécifique. Ces classes peuvent également être utiles à ceux qui souhaitent réaliser un traitement très personnalisé et qui trouvent que les implémentations existantes des primitives sont trop simples pour leurs besoins. Les utilisateurs généraux n'utiliseront pas directement les classes de base.
Implémentations
Voici les implémentations des classes de base des primitives :
-
Les primitives Qiskit Runtime (
EstimatorV2etSamplerV2) fournissent une implémentation plus sophistiquée (par exemple, en incluant l'atténuation des erreurs) sous forme de service cloud. Cette implémentation des primitives de base est utilisée pour accéder au matériel IBM Quantum®. Elles sont accessibles via IBM Qiskit Runtime. -
StatevectorEstimatoretStatevectorSampler- Implémentations de référence des primitives qui utilisent le simulateur intégré à Qiskit. Elles sont construites avec le module Qiskitquantum_infoet produisent des résultats basés sur des simulations de vecteur d'état idéales. Elles sont accessibles via Qiskit. -
BackendEstimatorV2etBackendSamplerV2- Tu peux utiliser ces classes pour « envelopper » n'importe quelle ressource de calcul quantique dans une primitive. Cela te permet d'écrire du code de style primitif pour des fournisseurs qui ne disposent pas encore d'une interface basée sur les primitives. Ces classes peuvent être utilisées exactement comme le Sampler et l'Estimator habituels, sauf qu'elles doivent être initialisées avec un argumentbackendsupplémentaire pour sélectionner l'ordinateur quantique à utiliser. Elles sont accessibles via Qiskit.
Avantages des primitives Qiskit
Avec les primitives, les utilisateurs de Qiskit peuvent écrire du code quantique pour un QPU spécifique sans avoir à gérer explicitement
chaque détail. De plus, grâce à la couche d'abstraction supplémentaire, tu pourrais accéder plus facilement
aux capacités matérielles avancées d'un fournisseur donné. Par exemple, avec les primitives Qiskit Runtime,
tu peux tirer parti des dernières avancées en matière d'atténuation et de suppression des erreurs en activant des options telles que le resilience_level de la primitive, plutôt que de construire ta propre implémentation de ces techniques.
Pour les fournisseurs de matériel, implémenter les primitives nativement signifie que tu peux offrir à tes utilisateurs une façon plus « prête à l'emploi » d'accéder aux fonctionnalités de ton matériel, comme les techniques avancées de post-traitement. Il est ainsi plus facile pour tes utilisateurs de bénéficier des meilleures capacités de ton matériel.
Détails des primitives
Comme décrit précédemment, toutes les primitives sont créées à partir des classes de base ; elles ont donc la même structure générale et le même mode d'utilisation. Par exemple, le format de l'entrée pour toutes les primitives Estimator est identique. Cependant, il existe des différences dans les implémentations qui les rendent uniques.
Comme la plupart des utilisateurs accèdent aux primitives Qiskit Runtime, les exemples du reste de cette section sont basés sur les primitives Qiskit Runtime.
Estimator
La primitive Estimator calcule les valeurs d'espérance d'un ou plusieurs observables par rapport aux états préparés par des circuits quantiques. Les circuits peuvent être paramétrés, à condition que les valeurs des paramètres soient également fournies en entrée à la primitive.
L'entrée est un tableau de PUBs. Chaque PUB est au format :
(<single circuit>, <one or more observables>, <optional one or more parameter values>, <optional precision>),
où les parameter values optionnels peuvent être une liste ou un seul paramètre. Les différentes implémentations d'Estimator prennent en charge diverses options de configuration. Si l'entrée contient des mesures, elles sont ignorées.
La sortie est un PubResult qui contient les valeurs d'espérance calculées par paire, ainsi que leurs erreurs standard, sous forme de PubResult. Chaque PubResult contient à la fois des données et des métadonnées.
L'Estimator combine les éléments des observables et des valeurs de paramètres en suivant les règles de diffusion NumPy décrites dans la rubrique Entrées et sorties des primitives.
Exemple :
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# This cell is hidden from users, it creates the circuits and observables to run
from qiskit_ibm_runtime import EstimatorV2, SamplerV2, QiskitRuntimeService
from qiskit.circuit.random import random_circuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
service = QiskitRuntimeService()
backend = service.least_busy()
phi = Parameter("phi")
circuit1 = random_circuit(10, 5, seed=12345)
circuit1.rzz(phi, 1, 2)
observable1 = SparsePauliOp.from_sparse_list(
[("ZXYZ", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values1 = np.random.uniform(size=5).T
circuit2 = random_circuit(10, 5, seed=12345)
circuit2.rzz(phi, 1, 2)
observable2 = SparsePauliOp.from_sparse_list(
[("XZYX", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values2 = np.random.uniform(size=5).T
shots1 = 164
shots2 = 1024
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
circuit1 = pm.run(circuit1)
circuit2 = pm.run(circuit2)
observable1 = observable1.apply_layout(circuit1.layout)
observable2 = observable2.apply_layout(circuit2.layout)
estimator = EstimatorV2(mode=backend)
estimator_job = estimator.run(
[
(circuit1, observable1, param_values1),
(circuit2, observable2, param_values2),
]
)
Sampler
La tâche principale du Sampler est d'échantillonner le registre de sortie à partir de l'exécution d'un ou plusieurs circuits quantiques. Les circuits d'entrée peuvent être paramétrés, à condition que les valeurs des paramètres soient également fournies en entrée à la primitive.
L'entrée est un ou plusieurs PUBs, au format :
(<single circuit>, <one or more optional parameter value>, <optional shots>),
où il peut y avoir plusieurs éléments parameter values, et chaque élément peut être soit un tableau, soit un seul paramètre, selon le circuit choisi. De plus, l'entrée doit contenir des mesures.
La sortie est des comptes ou des mesures par tir, sous forme d'objets PubResult, sans pondération. La classe de résultat dispose cependant de méthodes pour retourner des échantillons pondérés, tels que des comptes. Consulte Entrées et sorties des primitives pour tous les détails.
Exemple :
# This cell is hidden from users, add measurement instructions to circuits
circuit1.measure_active()
circuit2.measure_active()
sampler = SamplerV2(mode=backend)
sampler_job = sampler.run(
[
(circuit1, param_values1, shots1),
(circuit2, param_values2, shots2),
]
)
Étapes suivantes
- Lis Démarrer avec les primitives pour mettre en œuvre les primitives dans ton travail.
- Consulte des exemples détaillés de primitives.
- Pratique avec les primitives en suivant la leçon sur les fonctions de coût dans IBM Quantum Learning.
- Consulte la référence API EstimatorV2 et la référence API SamplerV2.
- Lis Migrer vers les primitives V2.