Skip to main content
IBM Quantum Platform

Primitifs

qiskit.primitives

Les primitives sont des blocs de construction informatique à utiliser dans des applications plus vastes dont les unités d'entrée, appelées blocs primitifs unifiés (PUB), nécessitent des ressources quantiques pour produire efficacement des sorties.

Il existe actuellement deux types de primitives dont les abstractions, dans leurs dernières versions, sont définies par BaseSamplerV2 et BaseEstimatorV2. Les échantillonneurs ont pour rôle d'accepter des circuits quantiques (ou des balayages de valeurs sur des circuits paramétrés) et d'échantillonner leurs registres de sortie classiques. Les estimateurs acceptent des combinaisons de circuits et d'observables (ou des balayages de ceux-ci) afin d'estimer les valeurs attendues des observables.

Qiskit propose une implémentation de référence pour chacune de ces abstractions dans les StatevectorSampler classes et StatevectorEstimator .

Les versions antérieures des abstractions « échantillonneur » et « estimateur » sont définies par BaseSamplerV1 et BaseEstimatorV1. Ces interfaces utilisent un format d'entrée-sortie différent et moins flexible pour la run méthode et ont été largement remplacées dans la pratique par BaseSamplerV2 et BaseEstimatorV2. Toutefois, les définitions d'interface abstraites d'origine ont été conservées à des fins de compatibilité ascendante. Consultez la section « Migration » de cette page pour obtenir plus de détails sur la différence entre V1 et V2.


Présentation d' EstimatorV2

BaseEstimatorV2 est une fonction primitive qui estime les valeurs d'espérance pour les combinaisons données de circuits quantiques et d'observables.

Une fois la construction terminée, on utilise un estimateur en appelant sa run() méthode avec une liste de pubs (blocs unifiés primitifs). Chaque pub contient trois valeurs qui, ensemble, définissent une unité de travail que l'estimateur doit effectuer :

  • une variable unique QuantumCircuit, éventuellement paramétrée, dont nous définissons l'état final comme suit : ψ(θ)\psi(\theta),
  • ObservablesArrayLikeune ou plusieurs variables observables (spécifiées sous la forme de n'importe quelle valeur, y compris Pauli, SparsePauliOp str, ) qui déterminent les valeurs d'espérance à estimer, notées HjH_j et
  • un ensemble de valeurs de paramètres auxquelles le circuit doit être lié, θk\theta_k.

L'exécution d'un estimateur renvoie un BasePrimitiveJob objet; l'appel de la méthode result() permet d'obtenir des estimations des valeurs d'espérance ainsi que des métadonnées pour chaque publication :

ψ(θk)Hjψ(θk)\langle\psi(\theta_k)|H_j|\psi(\theta_k)\rangle

Les observables et les valeurs des paramètres d'une pub peuvent être des tableaux de valeurs de dimensions arbitraires, où les règles de diffusion standard sont appliquées, de sorte que, à son tour, le résultat estimé pour chaque pub est en général également un tableau de valeurs. Pour plus d'informations, cliquez ici.

Voici un exemple d'utilisation d'un estimateur.

from qiskit.primitives import StatevectorEstimator as Estimator
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp

psi1 = RealAmplitudes(num_qubits=2, reps=2)
psi2 = RealAmplitudes(num_qubits=2, reps=3)

H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
H2 = SparsePauliOp.from_list([("IZ", 1)])
H3 = SparsePauliOp.from_list([("ZI", 1), ("ZZ", 1)])

theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 1, 2, 3, 5, 8, 13]
theta3 = [1, 2, 3, 4, 5, 6]

estimator = Estimator()

# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([(psi1, H1, [theta1])])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}")

# calculate [ [<psi1(theta1)|H1|psi1(theta1)>,
#              <psi1(theta3)|H3|psi1(theta3)>],
#             [<psi2(theta2)|H2|psi2(theta2)>] ]
job2 = estimator.run(
    [
        (psi1, [H1, H3], [theta1, theta3]),
        (psi2, H2, theta2)
    ],
    precision=0.01
)
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")

Présentation d' SamplerV2

BaseSamplerV2 est une primitive qui échantillonne les sorties des circuits quantiques.

Une fois la construction terminée, on utilise un échantillonneur en appelant sa run() méthode avec une liste de pubs (Primitive Unified Blocs). Chaque pub contient des valeurs qui, ensemble, définissent une unité de travail de calcul que l'échantillonneur doit effectuer :

  • Un seul QuantumCircuitéventuellement paramétrée.
  • Une collection d'ensembles de valeurs de paramètres pour lier le circuit s'il est paramétrique.
  • Optionnellement, le nombre de tirs à échantillonner, déterminé dans la méthode d'exécution s'il n'est pas défini.

L'exécution d'un échantillonneur renvoie un BasePrimitiveJob objet; l'appel de la méthode result() permet d'obtenir des échantillons de sortie et des métadonnées pour chaque publication.

Voici un exemple d'utilisation d'un échantillonneur.

from qiskit.primitives import StatevectorSampler as Sampler
from qiskit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes

# create a Bell circuit
bell = QuantumCircuit(2)
bell.h(0)
bell.cx(0, 1)
bell.measure_all()

# create two parameterized circuits
pqc = RealAmplitudes(num_qubits=2, reps=2)
pqc.measure_all()
pqc2 = RealAmplitudes(num_qubits=2, reps=3)
pqc2.measure_all()

theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 2, 3, 4, 5, 6, 7]

# initialization of the sampler
sampler = Sampler()

# collect 128 shots from the Bell circuit
job = sampler.run([bell], shots=128)
job_result = job.result()
print(f"The primitive-job finished with result {job_result}")

# run a sampler job on the parameterized circuits
job2 = sampler.run([(pqc, theta1), (pqc2, theta2)])
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")

Présentation d' EstimatorV1

Il n'existe actuellement aucune implémentation de l'interface héritée EstimatorV1 dans Qiskit. Cependant, la définition de l'interface abstraite provenant de BaseEstimatorV1 fait toujours partie du paquet afin d'assurer la compatibilité ascendante avec les implémentations externes.

Une EstimatorV1 implémentation est initialisée avec un ensemble de paramètres vide. BaseEstimatorV1 peut être appelée via la .run() méthode avec les paramètres suivants :

  • circuits quantiques ( ψi(θ)\psi_i(\theta) ) : liste de circuits quantiques (paramétrés) (une liste d'objets) QuantumCircuit objets).
  • observables ( HjH_j ) : une liste SparsePauliOp d'objets.
  • valeurs des paramètres ( θk\theta_k ) : liste d'ensembles de valeurs à associer aux paramètres des circuits quantiques (liste de listes de valeurs flottantes).

La méthode doit renvoyer un JobV1 objet. L'appel de cette fonction qiskit.providers.JobV1.result() renvoie une liste de valeurs attendues ainsi que des métadonnées facultatives, telles que les intervalles de confiance pour l'estimation.

ψi(θk)Hjψi(θk)\langle\psi_i(\theta_k)|H_j|\psi_i(\theta_k)\rangle

Voici un exemple de mise en œuvre de EstimatorV1 . Notez qu'il n'existe actuellement aucune implémentation de l'ancienne interface EstimatorV1 dans Qiskit.

# This is a fictional import path.
# There are currently no EstimatorV1 implementations in Qiskit.
from estimator_v1_location import EstimatorV1
from qiskit.circuit.library import RealAmplitudes
from qiskit.quantum_info import SparsePauliOp

psi1 = RealAmplitudes(num_qubits=2, reps=2)
psi2 = RealAmplitudes(num_qubits=2, reps=3)

H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)])
H2 = SparsePauliOp.from_list([("IZ", 1)])
H3 = SparsePauliOp.from_list([("ZI", 1), ("ZZ", 1)])

theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 1, 2, 3, 5, 8, 13]
theta3 = [1, 2, 3, 4, 5, 6]

estimator = EstimatorV1()

# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([psi1], [H1], [theta1])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}")

# calculate [ <psi1(theta1)|H1|psi1(theta1)>,
#             <psi2(theta2)|H2|psi2(theta2)>,
#             <psi1(theta3)|H3|psi1(theta3)> ]
job2 = estimator.run(
    [psi1, psi2, psi1],
    [H1, H2, H3],
    [theta1, theta2, theta3]
)
job_result = job2.result()
print(f"The primitive-job finished with result {job_result}")

Présentation d' SamplerV1

Il n'existe actuellement aucune implémentation de l'interface héritée SamplerV1 dans Qiskit. Toutefois, la définition de l'interface abstraite provenant de BaseSamplerV1 fait toujours partie du paquet afin d'assurer la compatibilité ascendante avec les implémentations externes.

Les classes d'échantillonneurs calculent les probabilités ou les quasi-probabilités des chaînes de bits des circuits quantiques.

A SamplerV1 est initialisé avec un ensemble de paramètres vide. BaseSamplerV1 Les implémentations peuvent être appelées via la .run() méthode avec les paramètres suivants :

  • circuits quantiques ( ψi(θ)\psi_i(\theta) ) : liste de circuits quantiques (paramétrés). (une liste d QuantumCircuit objets)
  • valeurs des paramètres ( θk\theta_k ) : liste d'ensembles de valeurs de paramètres à associer aux paramètres des circuits quantiques. (liste de la liste des flottants)

.run() renverra un JobV1 objet. L'appel de cette fonction qiskit.providers.JobV1.result() renvoie un SamplerResult objet contenant les probabilités ou quasi-probabilités des chaînes de bits, ainsi que des métadonnées facultatives telles que les marges d'erreur dans les échantillons.

Voici un exemple de mise en œuvre de SamplerV1 . Notez qu'il n'existe actuellement aucune implémentation de l'ancienne interface SamplerV1 dans Qiskit.

# This is a fictional import path.
# There are currently no SamplerV1 implementations in Qiskit.
from sampler_v1_location import Sampler
from qiskit import QuantumCircuit
from qiskit.circuit.library import RealAmplitudes

# a Bell circuit
bell = QuantumCircuit(2)
bell.h(0)
bell.cx(0, 1)
bell.measure_all()

# two parameterized circuits
pqc = RealAmplitudes(num_qubits=2, reps=2)
pqc.measure_all()
pqc2 = RealAmplitudes(num_qubits=2, reps=3)
pqc2.measure_all()

theta1 = [0, 1, 1, 2, 3, 5]
theta2 = [0, 1, 2, 3, 4, 5, 6, 7]

# initialization of the sampler
sampler = SamplerV1()

# Sampler runs a job on the Bell circuit
job = sampler.run(
    circuits=[bell], parameter_values=[[]], parameters=[[]]
)
job_result = job.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])

# Sampler runs a job on the parameterized circuits
job2 = sampler.run(
    circuits=[pqc, pqc2],
    parameter_values=[theta1, theta2],
    parameters=[pqc.parameters, pqc2.parameters])
job_result = job2.result()
print([q.binary_probabilities() for q in job_result.quasi_dists])

Migration depuis Primitives V1 vers V2

La distinction formelle entre les API « Primitives » V1 et V2 réside dans les classes de base dont héritent les implémentations des types primitifs, qui sont toutes répertoriées au bas de la page. D'un point de vue conceptuel, il convient toutefois de garder à l'esprit certaines différences notables lors de la migration de V1 vers V2:

  1. Les primitives V2 favorisent les entrées vectorisées, où les circuits individuels peuvent être regroupés avec des spécifications à valeur vectorielle (ou, plus généralement, à valeur de tableau). Chaque groupe est appelé bloc primitif unifié (pub), et chaque pub obtient son propre résultat. Par exemple, dans l'estimateur, vous pouvez comparer les différences suivantes :

    # Favoured V2 pattern. There is only one pub here, but there could be more.
    job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
    evs = job.result()[0].data.evs
    
    # V1 equivalent, where the same circuit must be provided four times.
    job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
    evs = job.result().values

    L'exemple ci-dessus ne montre pas, par souci de concision, que le circuit peut être paramétrique, avec des tableaux d'ensembles de valeurs de paramètres diffusés contre le tableau d'observables. L'échantillonneur est similaire, mais sans observables :

    # Favoured V2 pattern. There is only one pub here, but there could be more.
    job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
    samples = job.result()[0].data
    
    # V1 equivalent, where the same circuit must be provided three times.
    sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
    quasi_dists = job.result().quasi_dists
  2. L'échantillonneur V2 renvoie des échantillons de résultats classiques, en préservant l'ordre dans lequel ils ont été mesurés. Contrairement à l'échantillonneur V1 qui produit des distributions de quasi-probabilité qui sont plutôt une estimation de la distribution des résultats classiques. En outre, les objets de résultat de l'échantillonneur V2 organisent les données en fonction des noms de registres classiques de leurs circuits d'entrée, ce qui assure une compatibilité naturelle avec les circuits dynamiques.

    L'équivalent le plus proche des distributions de quasi-probabilité dans l'interface « V2 » est la get_counts() méthode illustrée dans l'exemple ci-dessous. Nous tenons toutefois à souligner que, pour les expériences à grande échelle (plus de 100 qubits), les chances de mesurer deux fois la même chaîne de bits sont faibles, de sorte que le regroupement des comptages sous forme de dictionnaire ne constituera généralement pas une stratégie efficace de traitement des données.

    circuit = QuantumCircuit(QuantumRegister(2, "qreg"), ClassicalRegister(2, "alpha"))
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure([0, 1], [0, 1])
    
    # V1 sampler usage
    result = sampler_v1.run([circuit]).result()
    quasi_dist = result.quasi_dists[0]
    
    # V2 sampler usage
    result = sampler_v2.run([circuit]).result()
    # these are the bit values from the alpha register, over all shots
    bitvals = result[0].data.alpha
    # we can use it to generate a Counts mapping, which is similar to a quasi prob distribution
    counts = bitvals.get_counts()
    # which can in turn be converted to the V1 type through normalization
    quasi_dist = QuasiDistribution({outcome: freq / shots for outcome, freq in counts.items()})
  3. Les primitives de l' V2 ont fait passer le concept de surcoût d'échantillonnage, inhérent à tous les systèmes quantiques en raison de leur nature probabiliste intrinsèque, du stade des options à celui de l'API elle-même. Pour l'échantillonneur, cela signifie que l'argument shots fait désormais partie de la run() signature, et surtout que chaque pub peut définir sa propre valeur pour shots, laquelle prévaut sur toute valeur attribuée à la méthode. L'estimateur dispose d'un argument analogue precision qui spécifie les intervalles de confiance que l'implémentation primitive doit viser pour les estimations de la valeur attendue.

    Ce concept n'est pas présent dans l'API des primitives V1, bien que toutes les implémentations des primitives V1 aient des paramètres correspondants quelque part dans leurs options.

    # Sample two circuits at 128 shots each.
    sampler_v2.run([circuit1, circuit2], shots=128)
    
    # Sample two circuits at different amounts of shots. The "None"s are necessary as placeholders
    # for the lack of parameter values in this example.
    sampler_v2.run([(circuit1, None, 123), (circuit2, None, 456)])
    
    # Estimate expectation values for two pubs, both with 0.05 precision.
    estimator_v2.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

API Primitives

Paramètres V2

ParameterLikeReprésenter un type d'union
BindingsArray( [données, forme] )Enregistre les ensembles de valeurs de liaison des paramètres pour un qiskit.QuantumCircuit.
BindingsArrayLikeAlias de `Mapping[ParameterLike

V2 de l'estimateur

BaseEstimatorV2()Classe de base pour les implémentations de EstimatorV2 .
StatevectorEstimator(*[, précision_par_défaut,...] )Implémentation simple de BaseEstimatorV2 avec simulation complète du vecteur d'état.
BackendEstimatorV2(*, backend[, options] )Évalue les valeurs d'espérance pour des combinaisons de circuits quantiques et d'observables fournies.
EstimatorPub(circuit, grandeurs observables[,...] )Bloc unifié de primitives pour toute primitive d'estimateur.
ObservablesArray(observables[, num_qubits,...] )Un tableau ND d'observables hermitiennes pour une Estimator primitive.
ObservableLikeReprésenter un type d'union
EstimatorPubLikealias de EstimatorPub
ObservablesArrayLikeAlias de `ObservableLike

V2 de l'échantillonneur

BaseSamplerV2()Classe de base pour les implémentations de SamplerV2 .
StatevectorSampler(*[, nombre_de_tirs_par_défaut, graine] )Implémentation simple de BaseSamplerV2 à l'aide d'une simulation par vecteur d'état complet.
BackendSamplerV2(*, backend[, options] )Évalue les chaînes de bits pour les circuits quantiques fournis
SamplerPub(circuit[, valeurs_des_paramètres,...] )Pub (Bloc unifié primitif) pour un échantillonneur.
SamplerPubLikealias de SamplerPub

Résultats V2

BitArray(tableau, nombre_de_bits)Stocke un tableau de valeurs de bits.
DataBin(*[, forme] )Les données principales proviennent d'un seul pub parmi PubResult.
PrimitiveResult(pub_results[, metadata] )Un conteneur pour les résultats de plusieurs pubs et les métadonnées globales.
PubResult(données[, métadonnées] )L'objet résultat pour un seul pub (bloc unifié primitif).
SamplerPubResult(données[, métadonnées] )Résultat de Sampler Pub.
BasePrimitiveJob(job_id, **kwargs)Classe de base abstraite de l'emploi primitif.
PrimitiveJob(function, *args, **kwargs)Gérer une tâche à partir des implémentations de référence des primitives dans Qiskit.

V1 de l'estimateur

BaseEstimatorV1(*[, options] )Classe de base pour les implémentations de EstimatorV1 .
EstimatorResult(valeurs, métadonnées)Résultat de l'estimateur V1.

V1 de l'échantillonneur

BaseSamplerV1(*[, options] )Classe de base de l'échantillonneur V1
SamplerResult(quasi_dists, métadonnées)Résultat de l'échantillonneur V1.
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.