Construisez des circuits dans le SDK - Amazon Braket

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.

Construisez des circuits dans le SDK

Cette section fournit des exemples de définition d'un circuit, de visualisation des portes disponibles, d'extension d'un circuit et de visualisation des portes prises en charge par chaque appareil. Il contient également des instructions sur la façon d'allouer manuellementqubits, de demander au compilateur d'exécuter vos circuits exactement tels que définis et de créer des circuits bruyants à l'aide d'un simulateur de bruit.

Vous pouvez également travailler au niveau du pouls dans Braket pour différentes portes avec certains QPU. Pour plus d'informations, consultez Pulse Control sur Amazon Braket.

Portes et circuits

Les portes et circuits quantiques sont définis dans la braket.circuitsclasse du SDK Amazon Braket Python. À partir du SDK, vous pouvez instancier un nouvel objet de circuit en appelant. Circuit()

Exemple : définition d'un circuit

L'exemple commence par définir un exemple de circuit de quatre qubits (étiquetésq0, q1q2, etq3) composé de portes Hadamard standard à un qubit et de portes CNOT à deux qubits. Vous pouvez visualiser ce circuit en appelant la print fonction, comme le montre l'exemple suivant.

# import the circuit module from braket.circuits import Circuit # define circuit with 4 qubits my_circuit = Circuit().h(range(4)).cnot(control=0, target=2).cnot(control=1, target=3) print(my_circuit)
T : |0| 1 | q0 : -H-C--- | q1 : -H-|-C- | | q2 : -H-X-|- | q3 : -H---X- T : |0| 1 |

Exemple : définition d'un circuit paramétré

Dans cet exemple, nous définissons un circuit dont les portes dépendent de paramètres libres. Nous pouvons spécifier les valeurs de ces paramètres pour créer un nouveau circuit ou, lors de la soumission du circuit, pour qu'il soit exécuté en tant que tâche quantique sur certains appareils.

from braket.circuits import Circuit, FreeParameter #define a FreeParameter to represent the angle of a gate alpha = FreeParameter("alpha") #define a circuit with three qubits my_circuit = Circuit().h(range(3)).cnot(control=0, target=2).rx(0, alpha).rx(1, alpha) print(my_circuit)

Vous pouvez créer un nouveau circuit non paramétré à partir d'un circuit paramétré en fournissant soit un seul float (qui est la valeur que prendront tous les paramètres libres), soit des arguments par mot-clé spécifiant la valeur de chaque paramètre au circuit comme suit.

my_fixed_circuit = my_circuit(1.2) my_fixed_circuit = my_circuit(alpha=1.2)

Notez qu'il my_circuit n'est pas modifié, vous pouvez donc l'utiliser pour instancier de nombreux nouveaux circuits avec des valeurs de paramètres fixes.

Exemple : modifier les portes d'un circuit

L'exemple suivant définit un circuit avec des portes qui utilisent des modificateurs de commande et de puissance. Vous pouvez utiliser ces modifications pour créer de nouvelles portes, telles que la Ry porte contrôlée.

from braket.circuits import Circuit # Create a bell circuit with a controlled x gate my_circuit = Circuit().h(0).x(control=0, target=1) # Add a multi-controlled Ry gate of angle .13 my_circuit.ry(angle=.13, target=2, control=(0, 1)) # Add a 1/5 root of X gate my_circuit.x(0, power=1/5) print(my_circuit)

Les modificateurs de porte ne sont pris en charge que sur le simulateur local.

Exemple : Voir toutes les portes disponibles

L'exemple suivant montre comment examiner toutes les portes disponibles dans Amazon Braket.

from braket.circuits import Gate # print all available gates in Amazon Braket gate_set = [attr for attr in dir(Gate) if attr[0].isupper()] print(gate_set)

La sortie de ce code répertorie toutes les portes.

['CCNot', 'CNot', 'CPhaseShift', 'CPhaseShift00', 'CPhaseShift01', 'CPhaseShift10', 'CSwap', 'CV', 'CY', 'CZ', 'ECR', 'GPi', 'GPi2', 'H', 'I', 'ISwap', 'MS', 'PSwap', 'PhaseShift', 'PulseGate', 'Rx', 'Ry', 'Rz', 'S', 'Si', 'Swap', 'T', 'Ti', 'Unitary', 'V', 'Vi', 'X', 'XX', 'XY', 'Y', 'YY', 'Z', 'ZZ']

N'importe laquelle de ces portes peut être ajoutée à un circuit en appelant la méthode correspondant à ce type de circuit. Par exemple, vous circ.h(0) appelleriez pour ajouter une porte Hadamard à la première. qubit

Note

Les portes sont ajoutées en place, et l'exemple suivant ajoute toutes les portes répertoriées dans l'exemple précédent au même circuit.

circ = Circuit() # toffoli gate with q0, q1 the control qubits and q2 the target. circ.ccnot(0, 1, 2) # cnot gate circ.cnot(0, 1) # controlled-phase gate that phases the |11> state, cphaseshift(phi) = diag((1,1,1,exp(1j*phi))), where phi=0.15 in the examples below circ.cphaseshift(0, 1, 0.15) # controlled-phase gate that phases the |00> state, cphaseshift00(phi) = diag([exp(1j*phi),1,1,1]) circ.cphaseshift00(0, 1, 0.15) # controlled-phase gate that phases the |01> state, cphaseshift01(phi) = diag([1,exp(1j*phi),1,1]) circ.cphaseshift01(0, 1, 0.15) # controlled-phase gate that phases the |10> state, cphaseshift10(phi) = diag([1,1,exp(1j*phi),1]) circ.cphaseshift10(0, 1, 0.15) # controlled swap gate circ.cswap(0, 1, 2) # swap gate circ.swap(0,1) # phaseshift(phi)= diag([1,exp(1j*phi)]) circ.phaseshift(0,0.15) # controlled Y gate circ.cy(0, 1) # controlled phase gate circ.cz(0, 1) # Echoed cross-resonance gate applied to q0, q1 circ = Circuit().ecr(0,1) # X rotation with angle 0.15 circ.rx(0, 0.15) # Y rotation with angle 0.15 circ.ry(0, 0.15) # Z rotation with angle 0.15 circ.rz(0, 0.15) # Hadamard gates applied to q0, q1, q2 circ.h(range(3)) # identity gates applied to q0, q1, q2 circ.i([0, 1, 2]) # iswap gate, iswap = [[1,0,0,0],[0,0,1j,0],[0,1j,0,0],[0,0,0,1]] circ.iswap(0, 1) # pswap gate, PSWAP(phi) = [[1,0,0,0],[0,0,exp(1j*phi),0],[0,exp(1j*phi),0,0],[0,0,0,1]] circ.pswap(0, 1, 0.15) # X gate applied to q1, q2 circ.x([1, 2]) # Y gate applied to q1, q2 circ.y([1, 2]) # Z gate applied to q1, q2 circ.z([1, 2]) # S gate applied to q0, q1, q2 circ.s([0, 1, 2]) # conjugate transpose of S gate applied to q0, q1 circ.si([0, 1]) # T gate applied to q0, q1 circ.t([0, 1]) # conjugate transpose of T gate applied to q0, q1 circ.ti([0, 1]) # square root of not gate applied to q0, q1, q2 circ.v([0, 1, 2]) # conjugate transpose of square root of not gate applied to q0, q1, q2 circ.vi([0, 1, 2]) # exp(-iXX theta/2) circ.xx(0, 1, 0.15) # exp(i(XX+YY) theta/4), where theta=0.15 in the examples below circ.xy(0, 1, 0.15) # exp(-iYY theta/2) circ.yy(0, 1, 0.15) # exp(-iZZ theta/2) circ.zz(0, 1, 0.15) # IonQ native gate GPi with angle 0.15 applied to q0 circ.gpi(0, 0.15) # IonQ native gate GPi2 with angle 0.15 applied to q0 circ.gpi2(0, 0.15) # IonQ native gate MS with angles 0.15, 0.15, 0.15 applied to q0, q1 circ.ms(0, 1, 0.15, 0.15, 0.15)

Outre le jeu de portes prédéfini, vous pouvez également appliquer des portes unitaires auto-définies au circuit. Il peut s'agir de portes à un seul qubit (comme indiqué dans le code source suivant) ou de portes à plusieurs qubits appliquées à la valeur qubits définie par le paramètre. targets

import numpy as np # apply a general unitary my_unitary = np.array([[0, 1],[1, 0]]) circ.unitary(matrix=my_unitary, targets=[0])

Exemple : étendre les circuits existants

Vous pouvez étendre les circuits existants en ajoutant des instructions. An Instruction est une directive quantique qui décrit la tâche quantique à effectuer sur un dispositif quantique. Instructionles opérateurs incluent Gate uniquement les objets de type.

# import the Gate and Instruction modules from braket.circuits import Gate, Instruction # add instructions directly. circ = Circuit([Instruction(Gate.H(), 4), Instruction(Gate.CNot(), [4, 5])]) # or with add_instruction/add functions instr = Instruction(Gate.CNot(), [0, 1]) circ.add_instruction(instr) circ.add(instr) # specify where the circuit is appended circ.add_instruction(instr, target=[3, 4]) circ.add_instruction(instr, target_mapping={0: 3, 1: 4}) # print the instructions print(circ.instructions) # if there are multiple instructions, you can print them in a for loop for instr in circ.instructions: print(instr) # instructions can be copied new_instr = instr.copy() # appoint the instruction to target new_instr = instr.copy(target=[5]) new_instr = instr.copy(target_mapping={0: 5})

Exemple : Afficher les barrières prises en charge par chaque appareil

Les simulateurs prennent en charge toutes les portes du SDK Braket, mais les appareils QPU en prennent en charge un sous-ensemble plus restreint. Vous pouvez trouver les portes compatibles d'un appareil dans les propriétés de l'appareil. Voici un exemple avec un appareil IonQ :

# import the device module from braket.aws import AwsDevice device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Harmony") # get device name device_name = device.name # show supportedQuantumOperations (supported gates for a device) device_operations = device.properties.dict()['action']['braket.ir.openqasm.program']['supportedOperations'] print('Quantum Gates supported by {}:\n {}'.format(device_name, device_operations))
Quantum Gates supported by the Harmony device: ['x', 'y', 'z', 'rx', 'ry', 'rz', 'h', 'cnot', 's', 'si', 't', 'ti', 'v', 'vi', 'xx', 'yy', 'zz', 'swap', 'i']

Les portes prises en charge devront peut-être être compilées dans des portes natives avant de pouvoir fonctionner sur du matériel quantique. Lorsque vous soumettez un circuit, Amazon Braket effectue cette compilation automatiquement.

Exemple : récupérer par programmation la fidélité des portes natives prises en charge par un appareil

Vous pouvez consulter les informations de fidélité sur la page Appareils de la console Braket. Il est parfois utile d'accéder aux mêmes informations par le biais d'un programme. Le code suivant montre comment extraire la fidélité à deux qubit portes entre deux portes d'un QPU.

# import the device module from braket.aws import AwsDevice device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3") #specify the qubits a=10 b=113 print(f"Fidelity of the XY gate between qubits {a} and {b}: ", device.properties.provider.specs["2Q"][f"{a}-{b}"]["fXY"])

Mesure partielle

En suivant les exemples précédents, nous avons mesuré tous les qubits du circuit quantique. Cependant, il est possible de mesurer des qubits individuels ou un sous-ensemble de qubits.

Exemple : mesurer un sous-ensemble de qubits

Dans cet exemple, nous démontrons une mesure partielle en ajoutant une measure instruction avec les qubits cibles à la fin du circuit.

# Use the local state vector simulator device = LocalSimulator() # Define an example bell circuit and measure qubit 0 circuit = Circuit().h(0).cnot(0, 1).measure(0) # Run the circuit task = device.run(circuit, shots=10) # Get the results result = task.result() # Print the circuit and measured qubits print(circuit) print() print("Measured qubits: ", result.measured_qubits)

qubitAllocation manuelle

Lorsque vous exécutez un circuit quantique sur des ordinateurs quantiques à partir deRigetti, vous pouvez éventuellement utiliser qubit l'allocation manuelle pour contrôler ceux qui qubits sont utilisés pour votre algorithme. La console Amazon Braket et le SDK Amazon Braket vous aident à inspecter les données d'étalonnage les plus récentes de l'unité de traitement quantique (QPU) que vous avez sélectionnée, afin que vous puissiez sélectionner celle qui convient le mieux à votre expérience. qubits

qubitL'allocation manuelle vous permet d'exécuter des circuits avec une plus grande précision et d'étudier les qubit propriétés individuelles. Les chercheurs et les utilisateurs expérimentés optimisent la conception de leurs circuits en fonction des dernières données d'étalonnage des appareils et peuvent obtenir des résultats plus précis.

L'exemple suivant montre comment allouer de qubits manière explicite.

circ = Circuit().h(0).cnot(0, 7) # Indices of actual qubits in the QPU my_task = device.run(circ, s3_location, shots=100, disable_qubit_rewiring=True)

Pour plus d'informations, consultez les exemples d'Amazon Braket sur GitHub, ou plus précisément, ce bloc-notes : Allocation de qubits sur les appareils QPU.

Note

Le OQC compilateur ne prend pas en charge les paramètresdisable_qubit_rewiring=True. La définition de cet indicateur sur True génère l'erreur suivante :An error occurred (ValidationException) when calling the CreateQuantumTask operation: Device arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy does not support disabled qubit rewiring.

Compilation textuelle

Lorsque vous exécutez un circuit quantique sur des ordinateurs quantiques à partir de RigettiIonQ, ou Oxford Quantum Circuits (OQC), vous pouvez demander au compilateur d'exécuter vos circuits exactement tels que définis sans aucune modification. À l'aide de la compilation textuelle, vous pouvez spécifier soit qu'un circuit entier soit préservé avec précision (supporté par RigettiIonQ, etOQC) tel que spécifié, soit que seules des parties spécifiques de celui-ci soient préservées (prises en charge Rigetti uniquement par). Lorsque vous développez des algorithmes pour l'analyse comparative du matériel ou des protocoles d'atténuation des erreurs, vous devez avoir la possibilité de spécifier exactement les portes et les configurations de circuits que vous utilisez sur le matériel. La compilation Verbatim vous permet de contrôler directement le processus de compilation en désactivant certaines étapes d'optimisation, garantissant ainsi que vos circuits fonctionnent exactement comme prévu.

La compilation Verbatim est actuellement prise en charge sur les appareils RigettiIonQ,, et Oxford Quantum Circuits (OQC) et nécessite l'utilisation de portes natives. Lors de l'utilisation de la compilation textuelle, il est conseillé de vérifier la topologie de l'appareil pour s'assurer que les portes sont connectées qubits et que le circuit utilise les portes natives prises en charge par le matériel. L'exemple suivant montre comment accéder par programmation à la liste des portes natives prises en charge par un appareil.

device.properties.paradigm.nativeGateSet

En effetRigetti, qubit le recâblage doit être désactivé en le configurant disableQubitRewiring=True pour une utilisation avec la compilation textuelle. S'il disableQubitRewiring=False est défini lors de l'utilisation de boîtes verbatim dans une compilation, le circuit quantique échoue à la validation et ne s'exécute pas.

Si la compilation textuelle est activée pour un circuit et exécutée sur un QPU qui ne le prend pas en charge, une erreur est générée indiquant qu'une opération non prise en charge a entraîné l'échec de la tâche. Au fur et à mesure que de plus en plus de matériels quantiques prennent en charge nativement les fonctions de compilation, cette fonctionnalité sera étendue pour inclure ces appareils. Les appareils qui prennent en charge la compilation textuelle l'incluent en tant qu'opération prise en charge lorsqu'ils sont interrogés avec le code suivant.

from braket.aws import AwsDevice from braket.device_schema.device_action_properties import DeviceActionType device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3") device.properties.action[DeviceActionType.OPENQASM].supportedPragmas

Il n'y a aucun coût supplémentaire associé à l'utilisation de la compilation textuelle. Les tâches quantiques exécutées sur les appareils Braket QPU, les instances d'ordinateurs portables et les simulateurs à la demande continuent de vous être facturées sur la base des tarifs actuels, tels que spécifiés sur la page de tarification d'Amazon Braket. Pour plus d'informations, consultez le bloc-notes d'exemple de compilation Verbatim.

Note

Si vous utilisez OpenQASM pour écrire vos circuits pour les IonQ périphériques OQC et, et que vous souhaitez mapper votre circuit directement aux qubits physiques, vous devez utiliser le #pragma braket verbatim car le disableQubitRewiring drapeau est complètement ignoré par OpenQASM.

Simulation du bruit

Pour instancier le simulateur de bruit local, vous pouvez modifier le backend comme suit.

device = LocalSimulator(backend="braket_dm")

Vous pouvez créer des circuits bruyants de deux manières :

  1. Construisez le circuit bruyant de bas en haut.

  2. Prenez un circuit sans bruit existant et injectez du bruit partout.

L'exemple suivant montre les approches utilisant un circuit simple avec un bruit dépolarisant et un canal Kraus personnalisé.

# Bottom up approach # apply depolarizing noise to qubit 0 with probability of 0.1 circ = Circuit().x(0).x(1).depolarizing(0, probability=0.1) # create an arbitrary 2-qubit Kraus channel E0 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.8) E1 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.2) K = [E0, E1] # apply a two-qubit Kraus channel to qubits 0 and 2 circ = circ.kraus([0,2], K)
# Inject noise approach # define phase damping noise noise = Noise.PhaseDamping(gamma=0.1) # the noise channel is applied to all the X gates in the circuit circ = Circuit().x(0).y(1).cnot(0,2).x(1).z(2) circ_noise = circ.copy() circ_noise.apply_gate_noise(noise, target_gates = Gate.X)

L'exécution d'un circuit offre la même expérience utilisateur qu'auparavant, comme le montrent les deux exemples suivants.

Exemple 1

task = device.run(circ, s3_location)

Ou

Exemple 2

task = device.run(circ_noise, s3_location)

Pour plus d'exemples, consultez l'exemple d'introduction du simulateur de bruit Braket