Freigeben über


So übermitteln Sie einen Schaltkreis mit Qiskit an Azure Quantum

Erfahren Sie, wie Sie eine Qiskit-Quantenschaltung mit dem Azure Quantum Development Kit (QDK) übermitteln. Sie können Qiskit-Schaltkreise mithilfe des Azure Quantum Development Kit (QDK) und des Jupyter-Notizbuchs in Visual Studio Code (VS Code) an Azure Quantum übermitteln. Sie können Ihre Schaltkreise auch mit dem lokalen Sparsesimulator testen. Der QDK unterstützt die Versionen 1 und 2 von Qiskit.

Weitere Informationen finden Sie unter Quantenschaltungen.

Voraussetzungen

Einzelheiten zur Installation finden Sie unter Einrichten der QDK-Erweiterung.

Erstellen eines neuen Jupyter Notebooks

  1. Öffnen Sie im VS Code das Menü "Ansicht" , und wählen Sie "Befehlspalette" aus.
  2. Geben Sie ein und wählen Sie Erstellen: Neues Jupyter-Notizbuch aus.
  3. VS Code erkennt und zeigt die Version von Python und die virtuelle Python Umgebung, die für das Notizbuch ausgewählt wurden, an. Wenn Sie über mehrere Python Umgebungen verfügen, müssen Sie möglicherweise einen Kernel mit der Kernelauswahl oben rechts auswählen. Wenn keine Umgebung erkannt wurde, finden Sie Informationen zum Einrichten unter Jupyter-Notizbücher in VS Code .

Laden der erforderlichen Importe

Führen Sie in der ersten Zelle Ihres Notizbuchs den folgenden Code aus, um die erforderlichen Importe zu laden:

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

Herstellen einer Verbindung mit dem Azure Quantum-Dienst

Um eine Verbindung mit dem Azure Quantum-Dienst herzustellen, benötigen Sie die Ressourcen-ID und den Standort Ihres Azure Quantum-Arbeitsbereichs.

  1. Melden Sie sich bei Ihrem Azure-Konto an. https://portal.azure.com
  2. Wählen Sie Ihren Azure Quantum-Arbeitsbereich aus, und wechseln Sie zu "Übersicht".
  3. Kopieren Sie die Parameter "Resource ID " und "Location ".

Fügen Sie eine neue Zelle in Ihrem Notizbuch hinzu, und verwenden Sie Ihre Kontoinformationen, um eine Workspace Verbindung mit Ihrem Azure Quantum-Arbeitsbereich herzustellen.AzureQuantumProvider

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

provider = AzureQuantumProvider(workspace)

Auflisten aller Back-Ends

Sie können jetzt alle Quantencomputing-Back-Ends ausgeben, die in Ihrem Arbeitsbereich verfügbar sind:

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

Ausführen einer einfachen Schaltung

Erstellen Sie in einer neuen Zelle einen einfachen Qiskit-Schaltkreis.

# 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 

Wählen Sie ein target , um Ihr Programm auszuführen.

Ausführen im IonQ-Simulator

Bevor Sie Ihren Schaltkreis auf realer Hardware ausführen, testen Sie Ihren Schaltkreis im Simulator. Dient get_backend zum Erstellen eines Objekts zum Herstellen einer Backend Verbindung mit dem IonQ Simulator-Back-End:

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

IonQ-Back-Ends unterstützen Gates aus einem definierten Gateset, die für die optimale Ausführung auf der Hardware kompiliert werden. Wenn Ihr Schaltkreis Tore enthält, die nicht in dieser Liste enthalten sind, müssen Sie die unterstützte gateset Funktion mithilfe der transpile von Qiskit bereitgestellten Funktion transpilieren:

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

Die Transpile-Funktion gibt ein neues Schaltkreisobjekt zurück, bei dem Gates in Tore zerlegt werden, die im angegebenen Back-End unterstützt werden.

Sie können das Programm jetzt über den Azure Quantum-Dienst ausführen und das Ergebnis abrufen. Die folgende Zelle übermittelt einen Auftrag, der die Schaltung mit 100 Ausführungen durchläuft:

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

Führen Sie Folgendes aus, um zu warten, bis der Auftrag abgeschlossen ist, und die Ergebnisse zurückzugeben:

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)

Da das Ergebnis ein Objekttyp ist, der für das Qiskit-Paket spezifisch ist, verwenden Sie result.get_counts und plot_histogram, um die Ergebnisse zu visualisieren. Um alle möglichen Beschriftungen von Bitzeichenfolgen darzustellen, fügen Sie die Beschriftungen zu counts hinzu.

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}

Ergebnis der Qiskit-Schaltung im IonQ-Simulator

Sie können die get_memory Funktion auch verwenden, um einzelne Aufnahmedaten aus dem Auftrag anzuzeigen:

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

Hinweis

Wenn Sie auf IonQ targetseinen Auftrag mit einer ungeraden Anzahl von Aufnahmen einreichen, wird die Anzahl der Aufnahmen auf die nächste gerade Zahl abgerundet. Wenn Sie beispielsweise 9 Aufnahmen angeben, werden in den Ergebnissen Daten für 8 Aufnahmen angezeigt.

Schätzen der Auftragskosten

Schätzen Sie, wie hoch die Kosten für einen Job sind, bevor Sie einen auf der QPU ausführen.

Die aktuellsten Preisdetails finden Sie unter IonQ-Preise, oder suchen Sie Ihren Arbeitsbereich und sehen Sie sich die Preisoptionen auf der Registerkarte Anbieter Ihres Arbeitsbereichs über aka.ms/aq/myworkspaces an.

Ausführen auf der IonQ-QPU

Um eine Verbindung mit echter Hardware herzustellen (eine Quantum Processor Unit (QPU)), geben Sie einfach den Namen der target"ionq.qpu.aria-1" Methode get_backend an:

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

Übermitteln Sie den Schaltkreis, der auf Azure Quantum ausgeführt werden soll, rufen Sie die Ergebnisse ab, und führen Sie dann aus plot_histogram , um die Ergebnisse zu zeichnen.

Hinweis

Die zum Ausführen einer Schaltung auf der QPU erforderliche Zeit hängt von den aktuellen Warteschlangenzeiten ab.

# 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}

Ergebnis der Qiskit-Schaltung auf der IonQ-QPU

Wichtig

Sie können nicht mehrere Schaltkreise in einem einzigen Auftrag einreichen. Als Problemumgehung können Sie die backend.run Methode aufrufen, um jeden Schaltkreis asynchron zu übermitteln, und dann die Ergebnisse jedes Auftrags abzurufen. Zum Beispiel:

jobs = []
for circuit in circuits:
    jobs.append(backend.run(circuit, shots=N))

results = []
for job in jobs:
    results.append(job.result())

Voraussetzungen

Einzelheiten zur Installation finden Sie unter Einrichten der QDK-Erweiterung.

Ausführen einer Basisschaltung

Öffnen Sie in VS Code eine neue Python Datei, um eine einfache Schaltung mit dem integrierten Sparsesimulator aus dem qsharp Modul zu erstellen und auszuführen.

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

Um das Programm auszuführen, wählen Sie oben rechts das Symbol "Ausführen" und dann Python ausführen" aus. Die Ausgabe wird in einem neuen Terminalfenster angezeigt.

                  ┌─────────────────────────┐┌─┐
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}

QIR für den Circuit generieren

Aus demselben Schaltkreis können Sie QIR generieren, der für die Ausführung auf Quantenhardware verwendet wird.

Hinweis

Um QIR zu generieren, müssen alle Register gemessen werden. Wenn keine nicht verwendeten Register vorhanden sind, wird ein Fehler ausgelöst. Darüber hinaus erhalten Sie eine Fehlermeldung, wenn Sie versuchen, QIR mit einem Unrestrictedtarget Profil zu generieren. Das Profil Unrestricted ist nur für die Simulation gültig. Sie müssen TargetProfile.Base, TargetProfile.Adaptive_RI oder TargetProfile.Adaptive_RIF verwenden. Sie können den Aufruf von target_profile in backend.qir(...) außer Kraft setzen, um die Profile zu wechseln.

  1. QSharpError und TargetProfile importieren

    from qdk.qsharp import QSharpError, TargetProfile
    
  2. Um QIR zu generieren, ändern Sie die Ausgabe:

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

Ihr Code sollte nun wie folgt aussehen:

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

Die Codeausgabe sollte wie folgt aussehen:

     ┌────────────┐             ┌─┐   
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}

Nicht alle Programme können auf der gesamten Hardware ausgeführt werden. Wenn Sie hier versuchen, das target Profil zu Base, erhalten Sie detaillierte Fehlermeldungen dazu, welche Programmteile nicht unterstützt werden.

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

Nächste Schritte