

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Contrôle du pouls sur Amazon Braket
<a name="braket-pulse-control"></a>

Les impulsions sont les signaux analogiques qui contrôlent les qubits d'un ordinateur quantique. Sur certains appareils d'Amazon Braket, vous pouvez accéder à la fonction de contrôle des impulsions pour soumettre des circuits à l'aide d'impulsions. Vous pouvez accéder au contrôle du pouls via le SDK Braket, à l'aide d'OpenQASM 3.0, ou directement via le Braket. APIs Tout d'abord, introduisez quelques concepts clés pour le contrôle du pouls dans Braket.

**Topics**
+ [Frames (Images)](#braket-frame)
+ [Ports](#braket-port)
+ [Formes d'onde](#braket-waveform)
+ [Travailler avec Hello Pulse](braket-hello-pulse.md)
+ [Accès aux portes natives à l'aide d'impulsions](braket-native-gate-pulse.md)

## Frames (Images)
<a name="braket-frame"></a>

Un cadre est une abstraction logicielle qui agit à la fois comme une horloge dans le programme quantique et comme une phase. Le temps d'horloge est incrémenté à chaque utilisation et un signal porteur dynamique est défini par une fréquence. Lors de la transmission de signaux vers le qubit, une trame détermine la fréquence porteuse du qubit, le décalage de phase et l'heure à laquelle l'enveloppe de forme d'onde est émise. Dans Braket Pulse, la construction des cadres dépend de l'appareil, de la fréquence et de la phase. Selon le périphérique, vous pouvez choisir un cadre prédéfini ou instancier de nouveaux cadres en fournissant un port.

```
from braket.aws import AwsDevice
from braket.pulse import Frame, Port

# Predefined frame from a device
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")
drive_frame = device.frames["Transmon_5_charge_tx"]

# Create a custom frame
readout_frame = Frame(frame_id="r0_measure", port=Port("channel_0", dt=1e-9), frequency=5e9, phase=0)
```

## Ports
<a name="braket-port"></a>

Un port est une abstraction logicielle représentant n'importe quel composant input/output matériel contrôlant les qubits. Il aide les fournisseurs de matériel à fournir une interface avec laquelle les utilisateurs peuvent interagir pour manipuler et observer les qubits. Les ports sont caractérisés par une chaîne unique qui représente le nom du connecteur. Cette chaîne expose également un incrément de temps minimum qui indique avec quelle précision nous pouvons définir les formes d'onde.

```
from braket.pulse import Port

Port0 = Port("channel_0", dt=1e-9)
```

## Formes d'onde
<a name="braket-waveform"></a>

Une forme d'onde est une enveloppe dépendant du temps que nous pouvons utiliser pour émettre des signaux sur un port de sortie ou pour capturer des signaux via un port d'entrée. Vous pouvez spécifier vos formes d'onde directement via une liste de nombres complexes ou en utilisant un modèle de forme d'onde pour générer une liste auprès du fournisseur du matériel.

```
from braket.pulse import ArbitraryWaveform, ConstantWaveform
import numpy as np

cst_wfm = ConstantWaveform(length=1e-7, iq=0.1)
arb_wf = ArbitraryWaveform(amplitudes=np.linspace(0, 100))
```

 Braket Pulse fournit une bibliothèque standard de formes d'onde, notamment une forme d'onde constante, une forme d'onde gaussienne et une forme d'onde DRAG (Derivative Removal by Adiabatic Gate). Vous pouvez récupérer les données de forme d'onde à l'aide de la `sample` fonction pour dessiner la forme de la forme d'onde, comme indiqué dans l'exemple suivant.

```
from braket.pulse import GaussianWaveform
import numpy as np
import matplotlib.pyplot as plt

zero_at_edge1 = GaussianWaveform(1e-7, 25e-9, 0.1, True)
# or zero_at_edge1 = GaussianWaveform(1e-7, 25e-9, 0.1)
zero_at_edge2 = GaussianWaveform(1e-7, 25e-9, 0.1, False)

times_1 = np.arange(0, zero_at_edge1.length, drive_frame.port.dt)
times_2 = np.arange(0, zero_at_edge2.length, drive_frame.port.dt)

plt.plot(times_1, zero_at_edge1.sample(drive_frame.port.dt))
plt.plot(times_2, zero_at_edge2.sample(drive_frame.port.dt))
```

![\[Graphique montrant l'amplitude dans le temps pour deux cas : ZaE = vrai (courbe inférieure) et zAe = faux (courbe supérieure). Les courbes ont une forme de cloche culminant à environ 0,5 seconde avec une amplitude de 0,10 a. u.\]](http://docs.aws.amazon.com/fr_fr/braket/latest/developerguide/images/gaussianwaveform.png)


L'image précédente représente les formes d'onde gaussiennes créées à partir de. `GaussianWaveform` Nous avons choisi une durée d'impulsion de 100 ns, une largeur de 25 ns et une amplitude de 0,1 (unités arbitraires). Les formes d'onde sont centrées dans la fenêtre d'impulsion. `GaussianWaveform`accepte un argument booléen `zero_at_edges` (ZaE dans la légende). Lorsqu'il est défini sur`True`, cet argument décale la forme d'onde gaussienne de telle sorte que les points à t=0 et t= `length` soient à zéro et redimensionne son amplitude de telle sorte que la valeur maximale corresponde à l'argument. `amplitude`

# Travailler avec Hello Pulse
<a name="braket-hello-pulse"></a>

Dans cette section, vous apprendrez à caractériser et à construire une porte à qubit unique directement en utilisant l'impulsion d'un Rigetti appareil. L'application d'un champ électromagnétique à un qubit entraîne une oscillation de Rabi, faisant passer le qubit entre son état 0 et son état 1. Avec une longueur et une phase d'impulsion calibrées, l'oscillation de Rabi peut calculer une seule porte qubit. Ici, nous allons déterminer la longueur d'impulsion optimale pour mesurer une impulsion pi/2, un bloc élémentaire utilisé pour construire des séquences d'impulsions plus complexes.

Tout d'abord, pour créer une séquence d'impulsions, importez la `PulseSequence` classe.

```
from braket.aws import AwsDevice
from braket.circuits import FreeParameter
from braket.devices import Devices
from braket.pulse import PulseSequence, GaussianWaveform

import numpy as np
```

Ensuite, instanciez un nouveau périphérique Braket à l'aide de l'Amazon Resource NameARN du QPU. Le bloc de code suivant utiliseRigetti Ankaa-3.

```
device = AwsDevice(Devices.Rigetti.Ankaa3)
```

La séquence d'impulsions suivante comprend deux composantes : jouer une forme d'onde et mesurer un qubit. La séquence d'impulsions peut généralement être appliquée aux trames. À quelques exceptions près, telles que la barrière et le délai, qui peuvent être appliqués aux qubits. Avant de construire la séquence d'impulsions, vous devez récupérer les trames disponibles. Le cadre d'entraînement est utilisé pour appliquer l'impulsion nécessaire à l'oscillation de Rabi, et le cadre de lecture sert à mesurer l'état du qubit. Cet exemple utilise les images du qubit 25.

```
drive_frame = device.frames["Transmon_25_charge_tx"]
readout_frame = device.frames["Transmon_25_readout_rx"]
```

Créez maintenant la forme d'onde qui sera jouée dans le cadre du lecteur. L'objectif est de caractériser le comportement des qubits pour différentes durées d'impulsion. Vous allez jouer une forme d'onde avec des longueurs différentes à chaque fois. Au lieu d'instancier une nouvelle forme d'onde à chaque fois, utilisez la séquence d'impulsions supportée par le frein. `FreeParameter` Vous pouvez créer la forme d'onde et la séquence d'impulsions une seule fois avec des paramètres libres, puis exécuter la même séquence d'impulsions avec des valeurs d'entrée différentes. 

```
waveform = GaussianWaveform(FreeParameter("length"), FreeParameter("length") * 0.25, 0.2, False)
```

Enfin, assemblez-les sous forme de séquence d'impulsions. Dans la séquence d'impulsions, `play` lit la forme d'onde spécifiée sur le cadre du lecteur et `capture_v0` mesure l'état à partir du cadre de lecture.

```
pulse_sequence = (
    PulseSequence()
    .play(drive_frame, waveform)
    .capture_v0(readout_frame)
)
```

Scannez une plage de durées d'impulsion et soumettez-les au QPU. Avant d'exécuter les séquences d'impulsions sur un QPU, liez la valeur des paramètres libres.

```
start_length = 12e-9
end_length = 2e-7
lengths = np.arange(start_length, end_length, 12e-9)
N_shots = 100

tasks = [
    device.run(pulse_sequence(length=length), shots=N_shots)
    for length in lengths
]

probability_of_zero = [
    task.result().measurement_counts['0']/N_shots
    for task in tasks
]
```

Les statistiques de la mesure du qubit montrent la dynamique oscillatoire du qubit qui oscille entre l'état 0 et l'état 1. À partir des données de mesure, vous pouvez extraire la fréquence Rabi et affiner la durée de l'impulsion pour implémenter une porte à 1 qubit particulière. Par exemple, d'après les données de la figure ci-dessous, la périodicité est d'environ 154 ns. Ainsi, une porte de rotation pi/2 correspondrait à la séquence d'impulsions d'une longueur = 38,5 ns. 

![\[Graphique linéaire qui montre le nombre de personnes par rapport à la durée du pouls en secondes. Il y a deux pics et un creux sur le graphique.\]](http://docs.aws.amazon.com/fr_fr/braket/latest/developerguide/images/Rabi-frequency.png)


## Hello Pulse utilise OpenPulse
<a name="braket-hello-pulse-openpulse"></a>

 [OpenPulse](https://openqasm.com/language/openpulse.html)est un langage permettant de spécifier le contrôle au niveau des impulsions d'un dispositif quantique général et fait partie de la spécification OpenQASM 3.0. Amazon Braket prend en charge la programmation directe OpenPulse d'impulsions à l'aide de la représentation OpenQASM 3.0.

 Braket utilise OpenPulse comme représentation intermédiaire sous-jacente pour exprimer des impulsions dans des instructions natives. OpenPulsepermet d'ajouter des étalonnages d'instructions sous la forme de déclarations `defcal` (abréviation de « définir l'étalonnage »). Avec ces déclarations, vous pouvez spécifier une implémentation d'une instruction de porte dans une grammaire de contrôle de niveau inférieur.

Vous pouvez visualiser le OpenPulse programme d'un Braket à `PulseSequence` l'aide de la commande suivante.

```
print(pulse_sequence.to_ir())
```

Vous pouvez également créer un OpenPulse programme directement.

```
from braket.ir.openqasm import Program
 
openpulse_script = """
OPENQASM 3.0;
cal {
    bit[1] psb;
    waveform my_waveform = gaussian(12.0ns, 3.0ns, 0.2, false);
    play(Transmon_25_charge_tx, my_waveform);
    psb[0] = capture_v0(Transmon_25_readout_rx);
}
"""
```

Créez un `Program` objet à l'aide de votre script. Soumettez ensuite le programme à un QPU.

```
from braket.aws import AwsDevice
from braket.devices import Devices
from braket.ir.openqasm import Program

program = Program(source=openpulse_script)

device = AwsDevice(Devices.Rigetti.Ankaa3)
task = device.run(program, shots=100)
```

# Accès aux portes natives à l'aide d'impulsions
<a name="braket-native-gate-pulse"></a>

Les chercheurs ont souvent besoin de savoir exactement comment les portes *natives* prises en charge par un QPU particulier sont implémentées sous forme d'impulsions. Les séquences d'impulsions sont soigneusement calibrées par les fournisseurs de matériel, mais l'accès à celles-ci permet aux chercheurs de concevoir de meilleures portes ou d'explorer des protocoles pour atténuer les erreurs, tels que l'extrapolation sans bruit en étirant les impulsions de portes spécifiques.

Amazon Braket prend en charge l'accès programmatique aux portails natifs de Rigetti.

```
import math
from braket.aws import AwsDevice
from braket.circuits import Circuit, GateCalibrations, QubitSet
from braket.circuits.gates import Rx

device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")

calibrations = device.gate_calibrations
print(f"Downloaded {len(calibrations)} calibrations.")
```

**Note**  
Les fournisseurs de matériel étalonnent régulièrement le QPU, souvent plus d'une fois par jour. Le SDK Braket vous permet d'obtenir les derniers étalonnages de portail.

```
device.refresh_gate_calibrations()
```

Pour récupérer une porte native donnée, telle que la porte RX ou XY, vous devez transmettre l'`Gate`objet et les qubits qui vous intéressent. Par exemple, vous pouvez inspecter l'implémentation par impulsion du RX (π/2) appliqué à qubit 0.

```
rx_pi_2_q0 = (Rx(math.pi/2), QubitSet(0))

pulse_sequence_rx_pi_2_q0 = calibrations.pulse_sequences[rx_pi_2_q0]
```

Vous pouvez créer un ensemble filtré d'étalonnages à l'aide de cette `filter` fonction. Vous passez une liste de portes ou une liste de`QubitSet`. Le code suivant crée deux ensembles contenant tous les étalonnages pour RX (π/2) et pour 0. qubit

```
rx_calibrations = calibrations.filter(gates=[Rx(math.pi/2)])
q0_calibrations = calibrations.filter(qubits=QubitSet([0]))
```

Vous pouvez désormais fournir ou modifier l'action des portes natives en joignant un ensemble de calibrage personnalisé. Par exemple, considérez le circuit suivant.

```
bell_circuit = (
    Circuit()
    .rx(0, math.pi/2)
    .rx(1, math.pi/2)
    .iswap(0, 1)
    .rx(1, -math.pi/2)
)
```

Vous pouvez l'exécuter avec un calibrage de porte personnalisé pour la `rx` porte `qubit 0` en transmettant un dictionnaire d'`PulseSequence`objets à l'argument du `gate_definitions` mot clé. Vous pouvez créer un dictionnaire à partir de l'attribut `pulse_sequences` de l'`GateCalibrations`objet. Toutes les portes non spécifiées sont remplacées par l'étalonnage des impulsions du fournisseur de matériel quantique.

```
nb_shots = 50
custom_calibration = GateCalibrations({rx_pi_2_q0: pulse_sequence_rx_pi_2_q0})
task = device.run(bell_circuit, gate_definitions=custom_calibration.pulse_sequences, shots=nb_shots)
```