Note
Cette page a été générée à partir de tutorials/circuits_advanced/06_building_pulse_schedules.ipynb.
Exécuter en mode interactif dans le ` IBM Quantum lab <https://quantum-computing.ibm.com/jupyter/tutorial/circuits_advanced/06_building_pulse_schedules.ipynb>`_.
Construire des ordonnancements d’impulsions¶
Les programmes Pulse, aussi appelés Schedule
s, décrivent des séquences d’instructions pour l’électronique de contrôle. Nous construisons ces programmes Schedule
s à l’aide de Pulse Builder (constructeur de d’impulsions). Il est facile d’initialiser un planning:
[1]:
from qiskit import pulse
with pulse.build(name='my_example') as my_program:
# Add instructions here
pass
my_program
[1]:
Schedule(, name="my_example")
Vous pouvez voir qu’il n’y a pas encore d’instruction. La section suivante de cette page explique chacune des instructions que vous pouvez ajouter à un planning, et la dernière section décrit les différents contextes d’alignement, qui déterminent comment les instructions sont placées dans le temps les unes par rapport aux autres.
Ordonnancer
des Instructions¶
Chaque type d’instruction possède son propre ensemble d’opérandes. Comme vous pouvez le voir ci-dessus, ils incluent chacun au moins un Channel
pour spécifier où l’instruction sera appliquée.
Les Channels (Canaux) sont des étiquettes pour les lignes de signal depuis le matériel de contrôle jusqu’à la puce quantique.
DriveChannel
s sont généralement utilisés pour contrôler des rotations de qubits uniques,Les
ControlChannel
s sont généralement utilisés pour des portes multi-qubits ou les lignes de commande supplémentaires pour les qubits réglables,MeasureChannel
s sont spécifiques à la transmission des impulsions qui stimulent la lecture, etAcquireChannel
s sont utilisés pour déclencher des numériseurs qui collectent des signaux de lecture.
DriveChannel
s, ControlChannel
s, et MeasureChannel
s sont tous dans la catégorie des PulseChannel
s ; cela signifie qu’ils prennent en charge les impulsions de transmission, alors que AcquireChanne
est un canal de réception seulement et ne peut pas générer de formes d’ondes.
Pour les exemples suivants, nous allons créer une instance DriveChannel
pour chaque Instruction
qui accepte un PulseChannel
. Les canaux prennent un argument entier index
. A l’exception de ControlChannel
s, l’index correspond trivialement à l’étiquette du qubit.
[2]:
from qiskit.pulse import DriveChannel
channel = DriveChannel(0)
The pulse Schedule
is independent of the backend it runs on. However, we can build our program in a context that is aware of the target backend by supplying it to pulse.build
. When possible you should supply a backend. By using the channel accessors pulse.<type>_channel(<idx>)
we can make sure we are only using available device resources.
[3]:
from qiskit.test.mock import FakeValencia
backend = FakeValencia()
with pulse.build(backend=backend, name='backend_aware') as backend_aware_program:
channel = pulse.drive_channel(0)
print(pulse.num_qubits())
# Raises an error as backend only has 5 qubits
#pulse.drive_channel(100)
5
delay
¶
L’une des instructions les plus simples que nous pouvons construire est delay
. Il s’agit d’une instruction de blocage qui indique à l’électronique de contrôle de ne générer aucun signal sur le canal donné, pour la durée spécifiée. Il est utile pour contrôler le ´´timing´´ des autres instructions.
La durée ici et ailleurs est exprimée en termes de temps de cycles du backend (1 / taux d’échantillonnage), dt
. Il s’agit d’une valeur entière.
Pour ajouter une instruction delay
, nous spécifions une durée et un canal, où channel
peut être n’importe quel type de canal, y compris AcquireChannel
. Nous utilisons pulse.build
pour commencer un contexte de générateur d’impulsions. Cela planifie automatiquement notre délai dans le planning delay_5dt
.
[4]:
with pulse.build(backend) as delay_5dt:
pulse.delay(5, channel)
C’est tout ce qu’il y a à faire. Toute instruction ajoutée après ce délai sur le même canal sera exécutée cinq temps plus tard qu’elle ne l’aurait été sans ce délai.
play
¶
L’instruction play
permet l’exécution des pulses (impulsions). Il est simple d’en construire une:
with pulse.build() as sched:
pulse.play(pulse, channel)
Précisons ce qu’est l’argument de pulse
et explorons quelques façons différentes d’en construire un.
Impulsions (Pulses
)¶
Un Pulse
permet de spécifier une enveloppe arbitraire d’impulsion. La fréquence de modulation et la phase de la forme d’onde de sortie sont contrôlées par les instructions SetFrequency
et ShiftPhase
, que nous couvrirons ensuite.
L’image ci-dessous peut fournir une certaine intuition pour la raison pour laquelle cees arguments sont spécifiés séparément. Pensez aux impulsions qui décrivent leurs enveloppes comme entrées dans un générateur de forme d’onde arbitraire (AWG, Arbitrary Waveform Generator), un instrument de laboratoire commun – ceci est représenté dans l’image de gauche. Remarquez que le taux d’échantillonnage limité rend le signal discret. Le signal produit par l’AWG peut être mélangé avec un générateur d’ondes sinusoïdales continue. La fréquence de sa sortie est contrôlée par des instructions au générateur d’onde sinusoïdale ; voir l’image du milieu. Enfin, le signal envoyé au qubit est montré sur le côté droit de l’image ci-dessous.
Note : Le matériel peut être implémenté par d’autres moyens, mais si nous gardons les instructions séparées, nous évitons de perdre des informations, comme la valeur de la fréquence de modulation.

Il existe de nombreuses méthodes pour construire les impulsions. Notre bibliothèque
dans Qiskit Pulse contient des méthodes utiles pour construire des `` Pulse`` s. Prenons par exemple une simple impulsion gaussienne – une impulsion dont l’enveloppe est décrite par une fonction gaussienne échantillonnée. Nous avons choisi arbitrairement une amplitude de 1, une déviation standard \(\sigma\) de 10, et 128 points d’échantillonnage.
Note : L’amplitude maximale autorisée est 1.0
. Chacun des système peut toutefois imposer des contraintes supplémentaires sur le nombre minimum et maximum d’échantillons autorisés dans une impulsion. Ces contraintes supplémentaires, si disponibles, sont fournies par la fonction BackendConfiguration
qui est décrite ici.
[5]:
from qiskit.pulse import library
amp = 1
sigma = 10
num_samples = 128
Impulsions paramétriques¶
Construisons notre impulsion Gaussienne en utilisant l’impulsion paramétrique Gaussian
. Une impulsion paramétrique envoie le nom de la fonction et de ses paramètres au système d’exécution (backend), plutôt qu’à chaque échantillon individuel. L’utilisation d’impulsions paramétriques rend les tâches que vous envoyez au backend beaucoup plus petites. IBM Quantum backends limitent la taille maximale de travail qu’ils acceptent, de sorte que les impulsions paramétriques peuvent vous permettre d’exécuter des programmes plus grands.
Les autres impulsions paramétriques de la librairie (library
) incluent GaussianSquare
, Drag
et Constant
.
** Note**: L’e système d’exécution (´´backend´´) est chargé de décider exactement comment échantillonner les impulsions paramétriques. Il est possible de dessiner des impulsions paramétriques, mais les échantillons affichés ne sont pas garantis d’être les mêmes que ceux exécutés sur le backend.
[6]:
gaus = pulse.library.Gaussian(num_samples, amp, sigma,
name="Parametric Gaus")
gaus.draw()
[6]:

Les formes d’onde des impulsions décrites par les échantillons¶
Une forme d’impulsion (Waveform
) est un signal défini par une liste de d’amplitudes complexes ordonnancées appelés samples (échantillons). Chaque sample est envoyé durant un temps de cycle dt
, determiné par le backend. Si nous voulons en savoir plus au sujet de la dynamique en temps réel de notre programme, il faut connaitre la valeur de dt
. L’échantillon \(i^{th}\) (l’index commençant à 0) sera envoyé depuis l’instant i*dt
jusqu’à l’instant (i + 1)*dt
, en modulation par la fréquence du qubit.
[7]:
import numpy as np
times = np.arange(num_samples)
gaussian_samples = np.exp(-1/2 *((times - num_samples / 2) ** 2 / sigma**2))
gaus = library.Waveform(gaussian_samples, name="WF Gaus")
gaus.draw()
[7]:

Fonctions de la bibliothèque (library
) d’impulsions¶
Notre propre bibliothèque d’impulsions a des méthodes d’échantillonnage pour construire un Waveform
à partir de fonctions communes.
[8]:
gaus = library.gaussian(duration=num_samples, amp=amp, sigma=sigma, name="Lib Gaus")
gaus.draw()
[8]:

Quelle que soit la méthode que vous utilisez pour spécifier votre impulsion (pulse
), play
est instanciée de la même manière :
[9]:
with pulse.build() as schedule:
pulse.play(gaus, channel)
schedule.draw()
[9]:

You may also supply a complex list or array directly to play
[10]:
with pulse.build() as schedule:
pulse.play([0.001*i for i in range(160)], channel)
schedule.draw()
[10]:

L’instruction play
récupère sa durée depuis son Pulse
: la durée d’une impulsion paramétrée est un argument explicite, et la durée d’un Waveform
est le nombre d’échantillons d’entrée.
set_frequency
¶
As explained previously, the output pulse waveform envelope is also modulated by a frequency and phase. Each channel has a default frequency listed in the backend.defaults().
La fréquence d’un canal peut être mise à jour à tout moment dans un Schedule
par l’instruction set_frequency
. Il prend une frequency
flottante et un PulseChannel
en entrée. Toutes les impulsions sur un canal suivant une instruction set_frequency
seront modulées par la fréquence donnée jusqu’à ce qu’une autre instruction set_frequency
soit rencontrée ou jusqu’à ce que le programme se termine.
L’instruction a une durée implicite de 0
.
Remarque: Les fréquences qui peuvent être demandées sont limitées par la bande passante totale et la bande passante instantanée de chaque canal matériel (hardware
). A l’avenir, ces informations seront rapportées par le backend
.
[11]:
with pulse.build(backend) as schedule:
pulse.set_frequency(4.5e9, channel)
shift_phase
¶
L’instruction shift_phase
augmentera la phase de la modulation de fréquence d’une quantité correspondant à la valeur de phase
. Comme pour set_frequency
, ce décalage de phase affectera toutes les instructions suivantes sur le même canal jusqu’à la fin du programme. Pour annuler l’effet d’une shift_phase
, il faudra en refaire une avec la quantité opposé comme valeur de phase
.
Comme set_frequency
, l’instruction a une durée implicite de 0
.
[12]:
with pulse.build(backend) as schedule:
pulse.shift_phase(np.pi, channel)
## acquire
¶
L’instruction acquire
déclenche l’acquisition de données pour effectuer une lecture. Il prend en argument une durée, un AcquireChannel
qui correspond au qubit mesuré, et un MemorySlot
ou un RegisterSlot
. Le MemorySlot
est une mémoire classique où le résultat de lecture sera stocké. Le RegisterSlot
correspond à un registre dans l’électronique de contrôle qui stocke le résultat de lecture pour un resultat rapide.
The acquire
instructions can also take custom Discriminator
s and Kernel
s as keyword arguments.
[13]:
from qiskit.pulse import Acquire, AcquireChannel, MemorySlot
with pulse.build(backend) as schedule:
pulse.acquire(1200, pulse.acquire_channel(0), MemorySlot(0))
Maintenant que nous savons comment ajouter des instructions de type Schedule
, apprenons à contrôler exactement quand elles sont executées.
Générateur d’impulsions¶
Ici, nous allons passer en revue les plus importantes fonctionnalités de Pulse Builder pour apprendre à construire des plannings. Ce n’est pas exhaustif et pour plus de détails sur ce que vous pouvez faire à l’aide de Pulse Builder, consultez la référence d’API Pulse <https://qiskit.org/documentation/apidoc/pulse.html> __.
Alignment contexts¶
Le générateur comporte des contextes d’alignement qui influencent la manière dont un planning est généré. Les contextes peuvent également être imbriqués. Essayez-les et utilisez la méthode .draw ()
pour voir comment les impulsions sont alignées.
Indépendamment du contexte d’alignement, la durée du planning résultant est aussi courte qu’elle peut l’être en incluant toutes les instructions et en suivant les règles d’alignement. Cela permet toujours quelques degrés de liberté pour planifier des instructions sur le « plus long chemin ». Les exemples ci-dessous illustrent cela.
align_left
¶
Le « Pulse Builder » possède des contextes d’alignement qui influencent la façon dont un planning est construit. La valeur par défaut est align_left
.
[14]:
with pulse.build(backend, name='Left align example') as program:
with pulse.align_left():
gaussian_pulse = library.gaussian(100, 0.5, 20)
pulse.play(gaussian_pulse, pulse.drive_channel(0))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
program.draw()
[14]:

Remarquez qu’il n’y a pas de liberté de programmation pour les impulsions sur D1
. La seconde impulsion d’onde commence immédiatement après la première. L’impulsion sur D0
peut commencer à tout moment entre t=0
et t=100
sans changer la durée de l’horaire global. Le contexte align_left
définit l’heure de début de cette impulsion à t=0
. Vous pouvez y penser comme la justification à gauche d’un document texte.
align_right
¶
Sans surprise, align_right
fait le contraire de align_left
. Il choisira t=100
dans l’exemple ci-dessus pour commencer l’impulsion gaussienne sur D0
. La gauche et la droite sont aussi parfois appelées « aussi vite que possible » et « aussi tard que possible », respectivement.
[15]:
with pulse.build(backend, name='Right align example') as program:
with pulse.align_right():
gaussian_pulse = library.gaussian(100, 0.5, 20)
pulse.play(gaussian_pulse, pulse.drive_channel(0))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
program.draw()
[15]:

align_equispaced(duration)
¶
Si la durée d’un bloc particulier est connue, vous pouvez également utiliser align_equispaced
pour insérer des délais d’égale durée entre chaque instruction.
[16]:
with pulse.build(backend, name='example') as program:
gaussian_pulse = library.gaussian(100, 0.5, 20)
with pulse.align_equispaced(2*gaussian_pulse.duration):
pulse.play(gaussian_pulse, pulse.drive_channel(0))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
program.draw()
[16]:

align_sequential
¶
Ce contexte d’alignement ne prévoit pas d’instructions en parallèle. Chaque instruction commence à la fin de l’instruction précédemment ajoutée.
[17]:
with pulse.build(backend, name='example') as program:
with pulse.align_sequential():
gaussian_pulse = library.gaussian(100, 0.5, 20)
pulse.play(gaussian_pulse, pulse.drive_channel(0))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
pulse.play(gaussian_pulse, pulse.drive_channel(1))
program.draw()
[17]:

Phase and frequency offsets¶
Nous pouvons utiliser le « Pulse builder » pour nous aider à compenser temporairement la fréquence ou la phase de nos impulsions sur un canal.
[18]:
with pulse.build(backend, name='Offset example') as program:
with pulse.phase_offset(3.14, pulse.drive_channel(0)):
pulse.play(gaussian_pulse, pulse.drive_channel(0))
with pulse.frequency_offset(10e6, pulse.drive_channel(0)):
pulse.play(gaussian_pulse, pulse.drive_channel(0))
program.draw()
[18]:

Nous vous encourageons à consulter la référence pour l’API Pulse pour en savoir plus.
[19]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
/Users/thomas/opt/anaconda3/envs/qiskit-3.8/lib/python3.8/site-packages/qiskit/aqua/operators/operator_globals.py:48: DeprecationWarning: `from_label` is deprecated and will be removed no earlier than 3 months after the release date. Use Pauli(label) instead.
X = make_immutable(PrimitiveOp(Pauli.from_label('X')))
Version Information
Qiskit Software | Version |
---|---|
Qiskit | 0.23.6 |
Terra | 0.17.0 |
Aer | 0.7.5 |
Ignis | 0.5.2 |
Aqua | 0.8.2 |
IBM Q Provider | 0.11.1 |
System information | |
Python | 3.8.5 (default, Aug 5 2020, 03:39:04) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 8 |
Memory (Gb) | 32.0 |
Sat Feb 27 11:04:23 2021 AST |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.