Entrées et sorties de l'exécuteur
Le code présenté sur cette page a été développé en tenant compte des exigences suivantes. Nous vous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.4.0 qiskit-ibm-runtime~=0.46.1 samplomatic~=0.18.0
La primitive Executor fait partie du modèle d'exécution dirigée, qui offre davantage de souplesse lors de la personnalisation d'un workflow de gestion des erreurs.
Les entrées et les sorties de la primitive Executor sont très différentes de celles des primitives Sampler et Estimator. Par exemple, au lieu de prendre une liste de PUB en entrée, Executor accepte un QuantumProgram, qui contient une liste QuantumProgramItem d'objets. Ces classes de conteneurs vous offrent davantage de flexibilité qu'un « PUB », qui est une simple structure de données de type tuple.
La sortie de l'exécuteur est un QuantumProgramResult, qui est un objet itérable et contient un élément pour chaque entrée QuantumProgramItem.
Données d'entrée : programmes Quantum
Comme indiqué précédemment, l'entrée d'une primitive Executor est un QuantumProgram, qui est un ensemble
QuantumProgramItem itérable d'objets. Ces objets peuvent être de deux types :
CircuitItem, qui contient généralement un circuit et les valeurs de ses paramètres (le cas échéant).SamplexItem, qui contient généralement les éléments suivants :- Un circuit type
- Un objet « samplex », qui sert à générer des ensembles aléatoires de paramètres lors de l'exécution (par exemple pour effectuer un « twirling » ou injecter du bruit)
- Arguments du samplex, qui peuvent inclure les valeurs des paramètres du circuit d'origine
Chacun de ces éléments correspond à une tâche distincte que l'Executor doit accomplir.
Avant de commencer
Certains des exemples de code présentés sur cette page utilisent samplex, qui fait partie du package Samplomatic. Par conséquent, avant d'exécuter ces blocs de code, vous devez installer Samplomatic, comme indiqué dans le bloc de code suivant. Pour plus d'informations, consultez la documentation de Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]Exemple : Créer un QuantumProgram comprenant deux tâches différentes
Commencez par initialiser votre programme quantique, puis ajoutez-y des éléments à l'aide de append_circuit_item ou append_samplex_item (si un samplex est présent), comme le montrent les exemples suivants.
La cellule suivante initialise un QuantumProgram et précise qu'il doit effectuer 1 024 essais pour chaque configuration de chaque élément du programme.
Contrairement à Sampler, un QuantumProgram ne prend en charge qu'une seule valeur de prise de vue. QuantumProgramSi vous souhaitez une valeur de prise différente, vous devez créer un fichier distinct, ce qui constituerait une tâche à part entière.
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit_ibm_runtime import Executor, QiskitRuntimeService
from qiskit.circuit import Parameter, QuantumCircuit
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Initialize an empty program
program = QuantumProgram(shots=1024)
# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
# `measure_all` adds a 3-bit classical register named "meas"
circuit.measure_all()
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Generate a preset pass manager
# This will be used to convert the abstract circuit to an
# equivalent Instruction Set Architecture (ISA) circuit.
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Transpile the circuit
isa_circuit = preset_pass_manager.run(circuit)Ajouter un CircuitItem
Ajoutez ensuite le circuit cible, qui a été transcompilé en fonction de l'architecture du jeu d'instructions (ISA) du backend, au fichier QuantumProgram. Comme ce circuit comporte deux paramètres, nous devons également indiquer leurs valeurs (10 paires dans cet exemple). L'exécution de cette commande CircuitItem est la première tâche que le programme va effectuer.
# Append the transpiled circuit and an array
# containing 10 sets of parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(
10, 2
), # 10 sets of parameter values and 2 parameters
)Ajouter un SamplexItem
Les éléments du circuit sont exécutés sans aucune forme d'aléatoire. Au contraire, les éléments Samplex vous permettent de définir comment randomiser leur contenu. La cellule suivante utilise la generate_boxing_pass_manager() fonction pour regrouper les portes et les mesures du circuit dans des encadrés et ajouter une annotation tournoyante à chaque encadré. Il génère ensuite un circuit modèle et une paire de samplex à l'aide de la build() fonction.
L'exécution de cette tâche SamplexItem est la deuxième opération que le programme va effectuer.
Consultez la documentation de l'API Samplomatic pour obtenir tous les détails concernant samplex et ses arguments. Consultez le guide du Samplomatic Transpiler pour en savoir plus sur l'utilisation de la generate_boxing_pass_manager() fonction.
# Transpile the circuit, additionally grouping gates and measurements into annotated boxes
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Use the boxing pass manager to group gates
# and measurements into boxes and add
# a`Twirl` annotation.
preset_pass_manager.post_scheduling = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = preset_pass_manager.run(circuit)
# Build the template circuit and the samplex. The template circuit has parametric gates
# without fixed values and the samplex randomly generates the parameter
# values on the server side at runtime to perform twirling.
template_circuit, samplex = build(boxed_circuit)
# Determine what arguments are required by the samplex.
# Input the arguments in samplex_arguments.
print(samplex.inputs())Output:
TensorInterface(<
- 'parameter_values' <float64[2]>: Input parameter values to use during sampling.
>)
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
# the arguments required by the samplex.sample method
"parameter_values": np.random.rand(10, 2),
},
shape=(28, 10), # 28 randomizations and 10 sets of parameter values
)# Initialize an Executor with the default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()Sorties
La sortie de l'exécuteur est un QuantumProgramResult, qui est un objet itérable. Il contient une entrée par entrée QuantumProgramItem , dans le même ordre que les éléments d'entrée. Chacun de ces éléments de sortie est un dictionnaire dont les clés sont des chaînes de caractères correspondant aux noms des registres classiques des circuits d'entrée (entre autres); vous n'avez donc plus besoin de mémoriser ces noms comme vous le faisiez avec la sortie Sampler. Les valeurs du dictionnaire sont de type np.ndarray.
Le résultat de l'exemple précédent comprend les éléments suivants :
CircuitItem résultat
CircuitItemLe premier élément contient les résultats de l'exécution de la première tâche (a) du programme. Elle contient une seule clé, meas, qui correspond au nom du registre classique dans le circuit d'entrée. La valeur de cette clé correspond à un tableau np.ndarray de forme (parameter sets, shots, register bits), qui est (10, 1024, 3) dans l'exemple ci-dessus.
Le code suivant montre comment accéder à ces informations :
# Access the results of the classical register of task #0, a CircuitItem
result_0 = result[0]["meas"]
print(f"Result shape: {result_0.shape}")Output:
Result shape: (10, 1024, 3)
SamplexItem résultat
Le deuxième élément contient les résultats de l'exécution de la deuxième tâche (a SamplexItem) du programme. Cet article contient plusieurs clés. La meas clé, qui correspond au nom du registre classique du circuit d'entrée, est mise en correspondance avec le tableau de résultats de ce registre. Ce tableau a la forme (randomizations, parameter sets, shots, classical bits), soit (28, 10, 1024, 3) dans cet exemple. De plus, la sortie contient une measurement_flips.meas clé qui correspond aux corrections par inversion de bits permettant d'annuler la distorsion de mesure pour le meas registre. Dans notre exemple, cette matrice de sortie sera de forme (28, 10, 1, 3), car un seul passage suffit pour effectuer l'inversion de bits.
# Access the results of the classical register of task #1
result_1 = result[1]["meas"]
print(f"Result shape: {result_1.shape}")
# Access the bit-flip corrections
flips_1 = result[1]["measurement_flips.meas"]
print(f"Bit-flip corrections shape: {flips_1.shape}")
# Undo the bit flips via classical XOR
unflipped_result_1 = result_1 ^ flips_1Output:
Result shape: (28, 10, 1024, 3)
Bit-flip corrections shape: (28, 10, 1, 3)
Etapes suivantes
- Découvrez des exemples d'utilisation d'Executor.
- Découvrez le modèle d'exécution dirigée.
- Comprendre la diffusion par exécuteur.