Partager via


Comment envoyer un circuit avec Qiskit à Azure Quantum

Découvrez comment envoyer un circuit quantique Qiskit avec le Kit de développement Azure Quantum (QDK). Vous pouvez envoyer des circuits Qiskit à Azure Quantum à l’aide du Kit de développement Azure Quantum (QDK) et jupyter Notebook dans Visual Studio Code (VS Code). Vous pouvez également tester vos circuits à l’aide du simulateur partiellement alloué local. Le QDK prend en charge les versions 1 et 2 de Qiskit.

Pour plus d’informations, consultez la page Circuits quantiques.

Prérequis

Pour les détails relatifs à l’installation, veuillez consulter la section Configuration de l’extension QDK.

  • Un espace de travail Azure Quantum dans votre abonnement Azure. Pour créer un espace de travail, 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.

  • La bibliothèque qdkPython avec les extras azureqiskit et le paquet ipykernel.

    python -m pip install --upgrade "qdk[azure,qiskit]" ipykernel 
    

Créer un notebook Jupyter Notebook

  1. Dans VS Code, ouvrez le menu Affichage et choisissez Palette de commandes.
  2. Entrez et sélectionnez Créer : Nouveau bloc-notes Jupyter.
  3. VS Code détecte et affiche la version de Python et l’environnement virtuel Python sélectionnés pour le notebook. Si vous avez plusieurs Python environnements, vous devrez peut-être sélectionner un noyau à l’aide du sélecteur de noyau en haut à droite. Si aucun environnement n’a été détecté, consultez Jupyter Notebooks dans VS Code pour obtenir des informations de configuration.

Charger les importations requises

Dans la première cellule de votre notebook, exécutez le code suivant pour charger les importations requises :

from qdk.azure import Workspace
from qdk.azure.qiskit import AzureQuantumProvider
from qiskit import QuantumCircuit
from qiskit.visualization import plot_histogram

Se connecter au service Azure Quantum

Pour connecter au service Azure Quantum, vous avez besoin de l’ID de la ressource et de l’emplacement de votre espace de travail Azure Quantum.

  1. Connectez-vous à votre compte Azure. https://portal.azure.com
  2. Sélectionnez votre espace de travail Azure Quantum, puis accédez à Vue d’ensemble.
  3. Copiez les paramètres d’ID de ressource et d’emplacement .

Ajoutez une nouvelle cellule dans votre bloc-notes et utilisez les informations de votre compte pour créer Workspace et AzureQuantumProvider objets pour vous connecter à votre espace de travail Azure Quantum.

workspace = Workspace(  
    resource_id = "", # Add the resourceID of your workspace
    location = "" # Add the location of your workspace (for example "westus")
    )

provider = AzureQuantumProvider(workspace)

Liste de tous les serveurs principaux

Vous pouvez maintenant imprimer tous les serveurs principaux d’informatique quantique disponibles dans votre espace de travail :

print("This workspace's targets:")
for backend in provider.backends():
    print("- " + backend.name)
This workspace's targets:
- ionq.simulator
- ionq.qpu.aria-1
- ionq.qpu.forte-1
- ionq.qpu.forte-enterprise-1
- quantinuum.sim.h2-1sc
- quantinuum.sim.h2-2sc
- quantinuum.sim.h2-1e
- quantinuum.sim.h2-2e
- quantinuum.qpu.h2-1
- quantinuum.qpu.h2-2
- rigetti.sim.qvm
- rigetti.qpu.ankaa-3
- rigetti.qpu.cepheus-1-36q

Exécuter un circuit simple

Dans une nouvelle cellule, créez un circuit Qiskit simple.

# Create a Quantum Circuit acting on the q register
circuit = QuantumCircuit(3, 3)
circuit.name = "Qiskit Sample - 3-qubit GHZ circuit"
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure([0,1,2], [0, 1, 2])

# Print out the circuit
circuit.draw()
     ┌───┐          ┌─┐      
q_0: ┤ H ├──■───────┤M├──────
     └───┘┌─┴─┐     └╥┘┌─┐   
q_1: ─────┤ X ├──■───╫─┤M├───
          └───┘┌─┴─┐ ║ └╥┘┌─┐
q_2: ──────────┤ X ├─╫──╫─┤M├
               └───┘ ║  ║ └╥┘
c: 3/════════════════╩══╩══╩═
                     0  1  2 

Sélectionnez une target option pour exécuter votre programme

Exécuter sur le simulateur IonQ

Avant d’exécuter votre circuit sur du matériel réel, testez votre circuit dans le simulateur. Permet get_backend de créer un Backend objet pour se connecter au serveur principal du simulateur IonQ :

simulator_backend = provider.get_backend("ionq.simulator")

Les back-ends IonQ prennent en charge les portes d’un ensemble de portes défini, qui sont compilées pour s’exécuter de façon optimale sur le matériel. Si votre circuit contient des portes qui ne figurent pas dans cette liste, vous devez transpiler dans la fonction prise en charge gateset à l’aide de la transpile fonction fournie par Qiskit :

from qiskit import transpile
circuit = transpile(circuit, simulator_backend)

La fonction transpile retourne un nouvel objet de circuit où les portes sont décomposées en portes prises en charge sur le back-end spécifié.

Vous pouvez maintenant exécuter le programme par le biais du service Azure Quantum et obtenir le résultat. La cellule suivante envoie un travail qui exécute le circuit avec 100 captures :

job = simulator_backend.run(circuit, shots=8)
job_id = job.id()
print("Job id", job_id)
Job id 00000000-0000-0000-0000-000000000000

Pour attendre la fin du travail et retourner les résultats, exécutez :

result = job.result()
print(result)
Result(backend_name='ionq.simulator', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=8, success=True, meas_level=2, data=ExperimentResultData(counts={'000': 4, '111': 4}, memory=['000', '000', '000', '000', '111', '111', '111', '111'], probabilities={'000': 0.5, '111': 0.5}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits=3, metadata={}), status=JobStatus.DONE, name='Qiskit Sample - 3-qubit GHZ circuit')], date=None, status=None, header=None, error_data=None)

Étant donné que le résultat est un type d’objet spécifique au package Qiskit, utilisez result.get_counts et plot_histogram visualisez les résultats. Pour représenter toutes les étiquettes de chaîne de bits possibles, ajoutez les étiquettes à counts.

counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
{'000': 4, '001': 0, '010': 0, '011': 0, '100': 0, '101': 0, '110': 0, '111': 4}

Résultat du circuit Qiskit sur le Simulateur IonQ

Vous pouvez également utiliser la fonction get_memory pour afficher des données de prise de vue individuelles à partir de la tâche :

result.get_memory(circuit)
['000', '000', '000', '000', '111', '111', '111', '111']

Remarque

Sur IonQ targets, si vous soumettez un travail avec un nombre impair de tirs, le nombre de tirs est arrondi au nombre pair le plus proche. Par exemple, si vous spécifiez 9 captures, les résultats affichent les données pour 8 captures.

Estimer le coût du travail

Estimer le coût d’exécution d’un travail avant d’exécuter un travail sur le QPU.

Pour obtenir les informations de tarification les plus actuelles, consultez tarification IonQ, 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.

Exécuter sur la QPU IonQ

Pour vous connecter à du matériel réel (une unité de processeur quantique (QPU)), indiquez simplement le nom de target"ionq.qpu.aria-1" la get_backend méthode :

qpu_backend = provider.get_backend("ionq.qpu.aria-1")

Envoyez le circuit à exécuter sur Azure Quantum, obtenez les résultats, puis exécutez-le plot_histogram pour tracer les résultats.

Remarque

Le temps nécessaire à l’exécution d’un circuit sur le processeur quantique peut varier en fonction des durées de file d’attente actuelles.

# Submit the circuit to run on Azure Quantum
job = qpu_backend.run(circuit, shots=100)
job_id = job.id()
print("Job id", job_id)

# Get the job results (this method waits for the Job to complete):
result = job.result()
print(result)
counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
Job id 00000000-0000-0000-0000-000000000000
Job Status: job has successfully run
Result(backend_name='ionq.qpu.aria-1', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2, data=ExperimentResultData(counts={'0': 505, '1': 6, '2': 1, '3': 1, '4': 1, '5': 10, '6': 11, '7': 488}, probabilities={'0': 0.4932, '1': 0.0059, '2': 0.001, '3': 0.001, '4': 0.001, '5': 0.0098, '6': 0.0117, '7': 0.4766}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits='3', qiskit='True'))])
{'000': 505, '001': 6, '010': 1, '011': 1, '100': 1, '101': 10, '110': 11, '111': 488}

Résultat du circuit Qiskit sur la QPU IonQ

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())

Prérequis

Pour les détails relatifs à l’installation, veuillez consulter la section Configuration de l’extension QDK.

  • Un Python environnement avec Python et Pip installé.

  • VS Code avec le Kit de développement Azure Quantum et Python les extensions installées.

  • Le package qdkPython avec les extras qiskit et jupyter.

    python pip install "qdk[qiskit,jupyter]"
    

Exécuter un circuit de base

Dans VS Code, ouvrez un Python nouveau fichier pour créer et exécuter un circuit de base avec le simulateur épars intégré à partir du module qsharp.

# load the required imports 
from qdk import qsharp
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# run the circuit using the built-in sparse simulator
backend = QSharpBackend()
job = backend.run(circuit)
counts = job.result().get_counts()

print(counts)

Pour exécuter le programme, sélectionnez l’icône Exécuter en haut à droite, puis sélectionnez Exécuter Python le fichier. La sortie s’affiche dans une nouvelle fenêtre de terminal.

                  ┌─────────────────────────┐┌─┐
q_0: ─■───────────┤0                        ├┤M├───
      │P(0.79983) │  (XX-YY)(1.9337,1.7385) │└╥┘┌─┐
q_1: ─■───────────┤1                        ├─╫─┤M├
                  └─────────────────────────┘ ║ └╥┘
c: 2/═════════════════════════════════════════╩══╩═
                                              0  1
{'11': 680, '00': 344}

Générer un QIR pour le circuit

À partir de ce même circuit, vous pouvez générer un QIR utilisé pour s’exécuter sur du matériel quantique.

Remarque

Pour générer le QIR, tous les registres doivent être mesurés. S’il existe des registres inutilisés, une erreur est générée. En outre, vous obtenez une erreur lorsque vous tentez de générer QIR avec un Unrestrictedtarget profil. Le profil Unrestricted est valide uniquement pour la simulation. Vous devez utiliser TargetProfile.Base, TargetProfile.Adaptive_RIou TargetProfile.Adaptive_RIF. Vous pouvez remplacer target_profile dans l'appel backend.qir(...) pour basculer entre les profils.

  1. Importer QSharpError et TargetProfile

    from qdk.qsharp import QSharpError, TargetProfile
    
  2. Pour générer QIR, modifiez la sortie :

    print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))
    

Votre code doit maintenant ressembler à ceci :

# load the required imports 
from qdk import qsharp
from qsharp import QSharpError, TargetProfile
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# generate QIR for the circuit
print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))

La sortie de votre code doit ressembler à ceci :

     ┌────────────┐             ┌─┐   
q_0: ┤ Rx(2.7195) ├─■───────────┤M├───
     └──┬─────┬───┘ │U1(5.5924) └╥┘┌─┐
q_1: ───┤ Tdg ├─────■────────────╫─┤M├
        └─────┘                  ║ └╥┘
c: 2/════════════════════════════╩══╩═
                                 0  1
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
block_0:
  call void @__quantum__qis__rx__body(double 2.7194945105768586, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double -2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
  call void @__quantum__rt__array_record_output(i64 2, i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
  ret void
}

declare void @__quantum__qis__rx__body(double, %Qubit*)

declare void @__quantum__qis__rz__body(double, %Qubit*)

declare void @__quantum__qis__t__adj(%Qubit*)

declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__array_record_output(i64, i8*)

declare void @__quantum__rt__result_record_output(%Result*, i8*)

attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}

!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}
!4 = !{i32 1, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}

Tous les programmes ne peuvent pas s’exécuter sur tout le matériel. Ici, si vous essayez de target le profil Base, alors vous obtenez des erreurs détaillées sur les parties du programme qui ne sont pas prises en charge.

try:
    backend.qir(qc, target_profile=TargetProfile.Base)
except QSharpError as e:
    print(e)

Étapes suivantes