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 module qdk.azurePython 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 devez disposer 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 pay-as-you-go.
Espace de travail Azure Quantum. Pour plus d’informations, consultez Créer un espace de travail Azure Quantum.
Environnement Python avec Python et Pip installés.
VS Code avec les extensions Microsoft Quantum Development Kit (QDK), Python et Jupyter installées.
Bibliothèque
qdkPython avec le supplémentazureet le paquetipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Créez un notebook Jupyter et connectez-vous à votre espace de travail Quantum
Pour vous connecter à votre espace de travail dans un notebook Jupyter dans VS Code, procédez comme suit :
Dans VS Code, ouvrez le menu Affichage et choisissez Palette de commandes.
Entrez Créer : Nouveau cahier Jupyter. Un fichier Jupyter Notebook vide s’ouvre dans un nouvel onglet.
Dans la première cellule du notebook, exécutez le code suivant. Vous trouverez l’ID de ressource dans le volet Overview de votre espace de travail dans le portail Azure.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Soumettre des circuits au format QIR
La représentation intermédiaire quantique (QIR) est une représentation intermédiaire qui sert d’interface commune entre les langages de programmation quantique et les plateformes de calcul quantique ciblées. Pour plus d’informations, consultez Quantum Intermediate Representation.
Pour soumettre un circuit au format QIR, procédez comme suit :
Créez le circuit QIR. Par exemple, lancez le code suivant dans une nouvelle cellule pour créer un circuit d'enchevêtrement 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. Dans cet exemple, les formats de données d’entrée et de sortie sontqir.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 resultEnvoyez le circuit QIR à un Azure Quantum target spécifique. Par exemple, pour soumettre le circuit QIR au simulateur targetIonQ, exécutez le code suivant :
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Envoyer un circuit avec un format spécifique au fournisseur pour Azure Quantum
Chaque fournisseur Azure Quantum a son propre format pour représenter des circuits quantiques. Vous pouvez envoyer des circuits à Azure Quantum dans des formats spécifiques au fournisseur au lieu de langues QIR, telles que Q# ou Qiskit.
Envoyer un circuit à IonQ au format JSON
IonQ utilise le format JSON pour exécuter des circuits sur leur targets. Pour plus d’informations, consultez la documentation de l’API IonQ targets et IonQ.
L’exemple suivant crée une superposition entre trois qubits au format JSON.
Dans une nouvelle cellule, créez un circuit quantique au format JSON.
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)Une fois la tâche terminée, obtenez les résultats.
results = job.get_results() print(results)
Envoyer un circuit à Pasqal au format SDK Pulser
Vous pouvez utiliser le SDK Pulser pour créer des séquences d’impulsions et les soumettre à Pasqal targets.
Installer le Kit de développement logiciel (SDK) Pulser
Pulser est un framework qui vous permet de créer, simuler et exécuter des séquences d’impulsions pour les appareils quantiques neutres à atomes. Pulser 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
Définissez à la fois un registre et une disposition. Le registre spécifie où organiser les atomes, et la disposition spécifie les positions des pièges qui capturent et structurent les atomes dans le registre.
Pour plus d’informations sur les dispositions, consultez la documentation Pulser.
Créez un devices objet pour importer l’ordinateur targetquantique Pasqal, FRESNEL_CAN1.
from pulser_pasqal import PasqalCloud
devices = PasqalCloud().fetch_available_devices()
QPU = devices["FRESNEL_CAN1"]
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.
Utilisez des dispositions pré-étalonnées si possible, car elles améliorent les performances du QPU.
L’exemple suivant utilise la première disposition pré-étalonnée sur l’appareil :
# Use 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,v3,v5]
reg = layout.define_register(*traps)
# Draw the register to verify that it matches your expectations
reg.draw()
Dispositions arbitraires
Utilisez une disposition personnalisée lorsque les dispositions pré-étalonnées ne répondent pas aux exigences de votre expérience.
Pour un registre arbitraire donné, un QPU neutre-atome place des pièges en fonction de la disposition, qui doit ensuite être étalonné. Étant donné que chaque étalonnage prend du temps, il est recommandé de réutiliser une disposition étalonnée existante si possible.
Pour créer une disposition arbitraire, choisissez l’une des options suivantes :
Générez automatiquement une disposition basée sur un registre spécifié. Pour les grands registres, ce processus peut produire des solutions sous-optimales. Par exemple :
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)Définissez manuellement une disposition pour créer votre registre. Par exemple, créez 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 # Generate 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) # Create the layout layout = RegisterLayout(traps, slug="random_20") # Define your register with specific trap IDs 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.
Définissez les attributs de séquence d’impulsions en déclarant les canaux qui contrôlent les atomes. Pour créer un
Sequence, fournissez uneRegisterinstance avec l’appareil sur lequel la séquence sera exécutée. Par exemple, le code suivant déclare un canal :ch0.from pulser import Sequence seq = Sequence(reg, QPU) # Print the available channels for your sequence print(seq.available_channels) # Declare a channel. For example, `rydberg_global` seq.declare_channel("ch0", "rydberg_global")Remarque
Vous pouvez utiliser l’appareil
QPU = devices["FRESNEL_CAN1"]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 vous permet d’exécuter sur un émulateur. Pour plus d’informations, consultez la documentation Pulser.Ajoutez des impulsions à votre séquence. Pour ce faire, 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, convertissez 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
Définissez 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 d’un target dans le volet Fournisseurs de votre espace de travail.
Envoyez le programme à Pasqal. Avant de soumettre votre code à du matériel quantique réel, il est recommandé de tester votre code sur l’émulateur
pasqal.sim.emu-mpstarget.target = workspace.get_targets(name="pasqal.sim.emu-mps") # Change to "pasqal.qpu.fresnel-can1" to use FRESNEL_CAN1 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 OpenQASM à Quantinuum
Créez un circuit quantique dans la représentation OpenQASM. Par exemple, le code 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]; """Vous pouvez également charger le circuit à partir d’un fichier OpenQASM :
with open("my_teleport.qasm", "r") as f: circuit = f.read()Soumettez le circuit à Quantinuum target. L’exemple suivant soumet la tâche à l’un des simulateurs Quantinuum targets.
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)
Remarque
Ces résultats retournent 000 pour chaque coup, ce qui n’est pas aléatoire. Cela est dû au fait que le validateur d’API vérifie uniquement si votre code peut s’exécuter sur le matériel Quantinuum, mais retourne 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.
Soumettre un circuit Quil à Rigetti
Pour soumettre un travail Quil à un Rigetti target, utilisez le module qdk.azurePython.
Chargez les importations requises.
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParamsCréez un
targetobjet et passez le nom du Rigetti target auquel vous souhaitez soumettre votre travail. Par exemple, le code suivant sélectionne leQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)Créez un programme Quil. Pour que votre programme soit accepté, vous devez configurer la lecture sur
"ro".readout = "ro" bell_state_quil = f""" DECLARE {readout} BIT[2] H 0 CNOT 0 1 MEASURE 0 {readout}[0] MEASURE 1 {readout}[1] """ num_shots = 5 job = target.submit( input_data=bell_state_quil, name="bell state", shots=100, input_params=InputParams(skip_quilc=False) ) print(f"Job completed with state: {job.details.status}") result = Result(job) # This throws an exception if the job failedVous pouvez indexer un résultat avec le nom de la valeur lue. Dans le code suivant,
data_per_shotest une liste de longueurnum_shots, et chaque élément de la liste est une autre liste qui contient les données du registre de cette capture.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]Dans ce cas, étant donné que le type du registre est BIT, le type est entier et la valeur 0 ou 1.
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0Imprimez toutes les données.
print(f"Data from '{readout}' 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. Pour contourner ce problème, vous pouvez appeler la backend.run méthode 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())