Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Découvrez comment utiliser le qdk.azurePython module pour envoyer des circuits dans des formats spécifiques au service Azure Quantum. Cet article vous montre comment envoyer des circuits dans les formats suivants :
Pour plus d’informations, consultez la page Circuits quantiques.
Prérequis
Pour développer et exécuter vos circuits dans Visual Studio Code (VS Code), vous avez besoin des éléments suivants :
Un compte Azure avec un abonnement actif. Si vous n’avez pas de compte Azure, inscrivez-vous gratuitement et inscrivez-vous à un abonnement de paiement à l’utilisation.
Un espace de travail Azure Quantum. Pour plus d’informations, consultez Créer un espace de travail Azure Quantum.
Un Python environnement avec Python et Pip installé.
VS Code avec le Kit de développement Azure Quantum et Pythonles extensions Jupyter installées.
Bibliothèque
qdkPython avec l’extraazureet le paquetipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Créer un notebook Jupyter
Pour créer un notebook dans VS Code, procédez comme suit :
Dans VS Code, ouvrez le menu Affichage et choisissez Palette de commandes.
Entrez et sélectionnez Créer : Nouveau bloc-notes Jupyter.
Pour vous connecter au service Azure Quantum, votre programme a besoin de l’ID de ressource et de l’emplacement de votre espace de travail Azure Quantum.
- Connectez-vous à votre compte Azure, https://portal.azure.com
- Sélectionnez votre espace de travail Azure Quantum, puis accédez à Vue d’ensemble.
- Copiez les paramètres dans les champs.
Dans la première cellule de votre notebook, collez les valeurs dans le constructeur suivant
Workspacepour créer unworkspaceobjet qui se connecte à votre espace de travail Azure Quantum.from qdk.azure import Workspace workspace = Workspace ( resource_id = "", # Add your resource_id location = "" # Add your workspace location (for example, "westus") )
Soumettre des circuits au format QIR
QIR (Quantum Intermediate Representation) est une représentation intermédiaire qui sert d’interface commune entre les langages/frameworks de programmation quantique et les plateformes de calcul quantique ciblées. Pour plus d’informations, consultez Quantum Intermediate Representation.
Créez le circuit QIR. Par exemple, le code suivant crée un circuit d’inanglement simple.
QIR_routine = """%Result = type opaque %Qubit = type opaque define void @ENTRYPOINT__main() #0 { call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) ret void } declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) declare void @__quantum__qis__rx__body(double, %Qubit*) declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__ry__body(double, %Qubit*) declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__rz__body(double, %Qubit*) declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__h__body(%Qubit*) declare void @__quantum__qis__s__body(%Qubit*) declare void @__quantum__qis__s__adj(%Qubit*) declare void @__quantum__qis__t__body(%Qubit*) declare void @__quantum__qis__t__adj(%Qubit*) declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__y__body(%Qubit*) declare void @__quantum__qis__z__body(%Qubit*) declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare void @__quantum__rt__result_record_output(%Result*, i8*) declare void @__quantum__rt__array_record_output(i64, i8*) declare void @__quantum__rt__tuple_record_output(i64, i8*) attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="4" "required_num_results"="2" } attributes #1 = { "irreversible" } ; module flags !llvm.module.flags = !{!0, !1, !2, !3} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} """Créez une
submit_qir_jobfonction d’assistance pour envoyer le circuit QIR à un target. Notez que les formats de données d’entrée et de sortie sont spécifiés commeqir.v1etmicrosoft.quantum-results.v1, respectivement.# Submit the job with proper input and output data formats def submit_qir_job(target, input, name, count=100): job = target.submit( input_data=input, input_data_format="qir.v1", output_data_format="microsoft.quantum-results.v1", name=name, input_params = { "entryPoint": "ENTRYPOINT__main", "arguments": [], "count": count } ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") #if job.details.status == "Succeeded": result = job.get_results() return resultSélectionnez un target circuit QIR et envoyez le circuit QIR à Azure Quantum. Par exemple, pour soumettre le circuit QIR au simulateur targetIonQ :
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result{'Histogram': ['(0, 0)', 0.5, '(1, 1)', 0.5]}
Envoyer un circuit avec un format spécifique au fournisseur à Azure Quantum
Outre les langages QIR, tels que Q# ou Qiskit, vous pouvez envoyer des circuits quantiques dans des formats spécifiques au fournisseur à Azure Quantum. Chaque fournisseur a son propre format pour représenter des circuits quantiques.
Envoyer un circuit à IonQ à l’aide du format JSON
Créez un circuit quantique à l’aide du format JSON indépendant du langage pris en charge par l’IonQtargets, comme décrit dans la documentation de l’API IonQ. Par exemple, l’exemple suivant crée une superposition entre trois qubits :
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }Envoyez le circuit à l’IonQ target. L’exemple suivant utilise le simulateur IonQ qui renvoie un objet
Job.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)Attendez que le travail soit terminé, puis extrayez les résultats.
results = job.get_results() print(results)..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}Vous pouvez ensuite visualiser les résultats à l’aide de Matplotlib.
import pylab as pl pl.rcParams["font.size"] = 16 hist = {format(n, "03b"): 0 for n in range(8)} hist.update({format(int(k), "03b"): v for k, v in results["histogram"].items()}) pl.bar(hist.keys(), hist.values()) pl.ylabel("Probabilities")
Avant d’exécuter un travail sur le QPU, vous devez estimer le coût d’exécution d’un travail.
Remarque
Pour obtenir les dernières informations tarifaires, consultez Tarifs IonQ, ou recherchez votre espace de travail et consultez les options tarifaires sous l’onglet « Fournisseur » de votre espace de travail via : aka.ms/aq/myworkspaces.
Envoyer un circuit à PASQAL à l’aide du Kit de développement logiciel (SDK) Pulser
Pour soumettre un circuit à PASQAL, vous pouvez utiliser le SDK Pulser pour créer des séquences d’impulsions et les soumettre à la PASQAL target.
Installer le Kit de développement logiciel (SDK) Pulser
Pulser est une infrastructure pour la composition, la simulation et l’exécution de séquences d’impulsions pour les appareils quantiques neutres à atomes. Il est conçu par PASQAL comme pass-through pour soumettre des expériences quantiques à leurs processeurs quantiques. Pour plus d’informations, consultez la documentation Pulser.
Pour soumettre les séquences d’impulsions, installez d’abord les packages du Kit de développement logiciel (SDK) Pulser :
try:
import pulser
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
Créer un registre quantique
Vous devez définir à la fois un registre et une disposition avant de continuer. Le registre spécifie où les atomes seront disposés, tandis que la disposition spécifie le positionnement des pièges nécessaires pour capturer et structurer ces atomes dans le registre.
Pour plus d’informations sur les dispositions, consultez la documentation Pulser.
Tout d’abord, vous créez un objet « devices » pour importer l’ordinateur targetquantique PASQAL, Fresnel.
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"]
Dispositions pré-étalonnées
L’appareil définit une liste de dispositions pré-étalonnées. Vous pouvez créer votre registre à partir de l’une de ces dispositions.
Il s’agit de l’option recommandée, car elle améliore les performances du QPU.
Option 1 : Définir votre registre à l’aide de dispositions pré-étalonnées
Inspectez les dispositions disponibles sur Fresnel et définissez votre registre à partir de cette disposition. Consultez la documentation du pulsateur pour plus d’informations sur la façon de procéder.
Exemple :
# let's say we are interested in the first layout available on the device layout = QPU.pre_calibrated_layouts[0] # Select traps 1, 3 and 5 of the layout to define the register traps = [1,3,5] reg = layout.define_register(*traps) # You can draw the resulting register to verify it matches your expectations reg.draw()
Dispositions arbitraires
Si les dispositions pré-étalonnées ne répondent pas aux exigences de votre expérience, vous pouvez créer une disposition personnalisée.
Pour tout registre arbitraire donné, un QPU neutre-atome place des pièges en fonction de la disposition, qui doit ensuite subir un étalonnage. Étant donné que chaque étalonnage nécessite du temps, il est généralement conseillé de réutiliser une disposition étalonnée existante dans la mesure du possible.
Option 2 : dériver automatiquement une disposition de votre registre défini
Cette option permet la génération automatique d’une disposition basée sur un registre spécifié. Toutefois, pour les registres volumineux, ce processus peut générer des solutions sous-optimales en raison des limitations de l’algorithme utilisé pour créer la disposition.
from pulser import Register qubits = { "q0": (0, 0), "q1": (0, 10), "q2": (8, 2), "q3": (1, 15), "q4": (-10, -3), "q5": (-8, 5), } reg = Register(qubits).with_automatic_layout(device)Option 3 : Définir votre registre à l’aide d’une disposition définie manuellement
Créer une disposition arbitraire avec 20 pièges positionnés de façon aléatoire dans un plan 2D
import numpy as np from pulser.register.register_layout import RegisterLayout # Generating random coordinates np.random.seed(301122) # Keeps results consistent between runs traps = np.random.randint(0, 30, size=(20, 2)) traps = traps - np.mean(traps, axis=0) # Creating the layout layout = RegisterLayout(traps, slug="random_20")- Définir votre registre avec des ID d’interruption spécifiques
trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Écrire une séquence d’impulsions
Les atomes neutres sont contrôlés avec des impulsions laser. Le SDK Pulser vous permet de créer des séquences d’impulsions à appliquer au registre quantique.
Tout d’abord, vous définissez les attributs de séquence d’impulsions en déclarant les canaux qui seront utilisés pour contrôler les atomes. Pour créer un
Sequence, vous devez fournir uneRegisterinstance avec l’appareil sur lequel la séquence sera exécutée. Par exemple, le code suivant déclare un canal :ch0.Remarque
Vous pouvez utiliser l’appareil
QPU = devices["FRESNEL"]ou importer un appareil virtuel à partir de Pulser pour plus de flexibilité. L’utilisation d’uneVirtualDevicefonctionnalité permet la création de séquences moins contrainte par les spécifications de l’appareil, ce qui le rend adapté à l’exécution sur un émulateur. Pour plus d’informations, consultez la documentation Pulser.from pulser import Sequence seq = Sequence(reg, QPU) # print the available channels for your sequence print(seq.available_channels) # Declare a channel. In this example we will be using `rydberg_global` seq.declare_channel("ch0", "rydberg_global")Ajoutez des impulsions à votre séquence. Pour ce faire, vous créez et ajoutez des impulsions aux canaux que vous avez déclarés. Par exemple, le code suivant crée une impulsion et l’ajoute au canal
ch0.from pulser import Pulse from pulser.waveforms import RampWaveform, BlackmanWaveform import numpy as np amp_wf = BlackmanWaveform(1000, np.pi) det_wf = RampWaveform(1000, -5, 5) pulse = Pulse(amp_wf, det_wf, 0) seq.add(pulse, "ch0") seq.draw()L’image suivante montre la séquence d’impulsions.
Convertir la séquence en chaîne JSON
Pour soumettre les séquences d’impulsions, vous devez convertir les objets Pulser en une chaîne JSON qui peut être utilisée comme données d’entrée.
import json
# Convert the sequence to a JSON string
def prepare_input_data(seq):
input_data = {}
input_data["sequence_builder"] = json.loads(seq.to_abstract_repr())
to_send = json.dumps(input_data)
return to_send
Envoyer la séquence d’impulsions à PASQAL target
Tout d’abord, vous devez définir les formats de données d’entrée et de sortie appropriés. Par exemple, le code suivant définit le format de données d’entrée sur
pasqal.pulser.v1et le formatpasqal.pulser-results.v1de données de sortie sur .# Submit the job with proper input and output data formats def submit_job(target, seq, shots): job = target.submit( input_data=prepare_input_data(seq), # Take the JSON string previously defined as input data input_data_format="pasqal.pulser.v1", output_data_format="pasqal.pulser-results.v1", name="PASQAL sequence", shots=shots # Number of shots ) print(f"Queued job: {job.id}") return jobRemarque
Le temps nécessaire pour exécuter un travail sur le QPU dépend des heures de file d’attente actuelles. Vous pouvez afficher le temps moyen de file d’attente pour un en target sélectionnant le panneau Fournisseurs de votre espace de travail.
Soumettez le programme à PASQAL. Avant de soumettre votre code à du matériel quantique réel, vous pouvez tester votre code à l’aide de l’émulateur
pasqal.sim.emu-tnen tant que target.target = workspace.get_targets(name="pasqal.sim.emu-tn") # Change to "pasqal.qpu.fresnel" to use Fresnel QPU job = submit_job(target, seq, 10) job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() print(result){ "1000000": 3, "0010000": 1, "0010101": 1 }
Envoyer un circuit à Quantinuum à l’aide d’OpenQASM
Créez un circuit quantique dans la représentation OpenQASM. L’exemple suivant crée un circuit de téléportation :
circuit = """OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c0[3]; h q[0]; cx q[0], q[1]; cx q[1], q[2]; measure q[0] -> c0[0]; measure q[1] -> c0[1]; measure q[2] -> c0[2]; """Si vous le souhaitez, vous pouvez charger le circuit à partir d’un fichier :
with open("my_teleport.qasm", "r") as f: circuit = f.read()Envoyez le circuit au Quantinuum target. L’exemple suivant utilise le validateur d’API Quantinuum qui retourne un objet
Job.target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)Attendez que le travail soit terminé, puis extrayez les résultats.
results = job.get_results() print(results)........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}Vous pouvez ensuite visualiser les résultats à l’aide de Matplotlib.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
En examinant l’histogramme, vous remarquerez peut-être que le générateur de nombres aléatoires a retourné 0 chaque fois, ce qui n’est pas très aléatoire. En effet, le validateur d’API garantit que votre code s’exécute correctement sur le matériel Quantinuum, mais retourne également 0 pour chaque mesure quantique. Pour un générateur de vrais nombres aléatoires, vous devez exécuter votre circuit sur du matériel quantique.
Avant d’exécuter un travail sur le QPU, vous devez estimer le coût d’exécution d’un travail.
Remarque
Pour obtenir les informations de tarification les plus actuelles, consultez la tarification Azure Quantum ou recherchez votre espace de travail et affichez les options de tarification sous l’onglet « Fournisseur » de votre espace de travail via : aka.ms/aq/myworkspaces.
Soumettre un circuit à Rigetti à l’aide de Quil
Le moyen le plus simple de soumettre des travaux Quil utilise le package pyquil-for-azure-quantum , car il vous permet d’utiliser les outils et la documentation de la bibliothèque pyQuil . Sans ce package, pyQuil peut être utilisé pour construire des programmes Quil, mais pas pour les soumettre à Azure Quantum.
Vous pouvez également construire manuellement des programmes Quil et les envoyer directement à l’aide du package azure-quantum.
- Utiliser pyquil-for-azure-quantum
- Utiliser le Kit de développement logiciel (SDK) Azure-quantum Python
Tout d’abord, chargez les importations requises.
from pyquil.gates import CNOT, MEASURE, H from pyquil.quil import Program from pyquil.quilbase import Declare from pyquil_for_azure_quantum import get_qpu, get_qvmUtilisez ou
get_qvmget_qpula fonction pour obtenir une connexion à QVM ou QPU.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-3") for submitting to a QPUCréez un programme Quil. Tout programme Quil valide est accepté, mais la lecture doit être nommée
ro.program = Program( Declare("ro", "BIT", 2), H(0), CNOT(0, 1), MEASURE(0, ("ro", 0)), MEASURE(1, ("ro", 1)), ).wrap_in_numshots_loop(5) # Optionally pass to_native_gates=False to .compile() to skip the compilation stage result = qc.run(qc.compile(program)) data_per_shot = result.readout_data["ro"]data_per_shotVoici unnumpytableau, ce qui vous permet d’utiliser desnumpyméthodes.assert data_per_shot.shape == (5, 2) ro_data_first_shot = data_per_shot[0] assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0Imprimez toutes les données.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Important
Vous ne pouvez pas envoyer plusieurs circuits sur un seul travail. Une solution de contournement consiste à appeler la méthode backend.run pour envoyer chaque circuit de manière asynchrone, puis à extraire les résultats de chaque travail. Par exemple :
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())