So übermitteln Sie bestimmte formatierte Schaltkreise an Azure Quantum
Erfahren Sie, wie Sie das azure-quantum
Python Paket verwenden, um Schaltkreise in bestimmten Formaten an den Azure Quantum-Dienst zu übermitteln. In diesem Artikel wird gezeigt, wie Sie Schaltkreise in den folgenden Formaten übermitteln:
Weitere Informationen finden Sie unter Quantenschaltungen.
Voraussetzungen
Um Ihre Schaltkreise in einem Notizbuch in Azure-Portal auszuführen, benötigen Sie Folgendes:
- Ein Azure-Konto mit einem aktiven Abonnement. Wenn Sie nicht über ein Azure-Konto verfügen, registrieren Sie sich kostenlos, und registrieren Sie sich für ein Kostenpflichtiges Abonnement.
- Azure Quantum-Arbeitsbereich Weitere Informationen finden Sie unter Erstellen eines Azure Quantum-Arbeitsbereichs.
Um Ihre Schaltkreise in Visual Studio Code zu entwickeln und auszuführen, benötigen Sie außerdem Folgendes:
Eine Python Umgebung mit Python installierter Pip .
VS Code mit installierten Azure Quantum Development Kit- Pythonund Jupyter-Erweiterungen .
Azure Quantum
qsharp
,azure-quantum
undipykernel
Pakete.python -m pip install --upgrade qsharp azure-quantum ipykernel
Erstellen eines neuen Jupyter Notebooks
Sie können ein Notizbuch im VS Code oder direkt im Azure Quantum-Portal erstellen.
- Melden Sie sich beim Azure-Portal an, und wählen Sie den Arbeitsbereich aus dem vorherigen Schritt aus.
- Wählen Sie auf dem linken Blatt Notebooks aus.
- Klicken Sie auf Meine Notebooks und dann auf Neu hinzufügen.
- Wählen Sie in Kerneltyp die Option IPython aus.
- Geben Sie einen Namen für die Datei ein, und klicken Sie auf " Datei erstellen".
Wenn Ihr neues Notebook geöffnet wird, wird automatisch basierend auf Ihren Abonnement- und Arbeitsbereichsinformationen der Code für die erste Zelle erstellt.
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
Übermitteln von QIR-formatierten Schaltkreisen
Die Quantenzwischendarstellung (Quantum Intermediate Representation, QIR) ist eine Zwischendarstellung, die als allgemeine Schnittstelle zwischen Quantenprogrammiersprachen/-frameworks und zielgerichteten Quantencomputingplattformen fungiert. Weitere Informationen finden Sie unter Quantum Intermediate Representation.
Erstellen Sie den QIR-Schaltkreis. Der folgende Code erstellt z. B. einen einfachen Veranglement-Schaltkreis.
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} """
Erstellen Sie eine
submit_qir_job
Hilfsfunktion, um den QIR-Schaltkreis an eine target. Beachten Sie, dass die Eingabe- und Ausgabedatenformate entsprechendqir.v1
microsoft.quantum-results.v1
angegeben werden.# 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 result
Wählen Sie einen target QIR-Schaltkreis aus, und übermitteln Sie sie an Azure Quantum. Um z. B. den QIR-Schaltkreis an den IonQ Simulator targetzu übermitteln:
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]}
Übermitteln eines Schaltkreises mit einem anbieterspezifischen Format an Azure Quantum
Neben QIR-Sprachen wie Q# oder Qiskit können Sie Quantenschaltungen in anbieterspezifischen Formaten an Azure Quantum übermitteln. Jeder Anbieter hat ein eigenes Format für die Darstellung von Quantenschaltungen.
Übermitteln eines Schaltkreises an IonQ im JSON-Format
Erstellen Sie eine Quantenschaltung mit dem sprachunabhängigen JSON-Format, das vom IonQ unterstützt wird, wie in der IonQ-API-Dokumentation targetsbeschrieben. So wird etwa im folgenden Beispiel eine Superposition zwischen drei Qubits erstellt:
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }
Übermitteln Sie den Schaltkreis an den IonQ target. Im folgenden Beispiel wird der IonQ-Simulator verwendet, der ein
Job
-Objekt zurückgibt.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)
Warten Sie, bis der Auftrag abgeschlossen ist, und rufen Sie dann die Ergebnisse ab.
results = job.get_results() print(results)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
Anschließend können die Ergebnisse mithilfe von Matplotlib visualisiert werden.
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")
Bevor Sie einen Auftrag auf der QPU ausführen, sollten Sie schätzen, wie viel die Ausführung kostet.
Hinweis
Die aktuellen Preisdetails finden Sie unter Preise. Suchen Sie alternativ nach Ihrem Arbeitsbereich, und zeigen Sie die Preisoptionen auf der Registerkarte „Anbieter“ Ihres Arbeitsbereichs über aka.ms/aq/myworkspaces an.
Übermitteln eines Schaltkreises an PASQAL mit Pulser SDK
Um einen Schaltkreis an PASQAL zu übermitteln, können Sie das Pulser SDK verwenden, um Impulssequenzen zu erstellen und an den PASQAL targetzu übermitteln.
Installieren des Pulser SDK
Pulser ist ein Framework zum Verfassen, Simulieren und Ausführen von Impulssequenzen für neutrale Atom-Quantengeräte. Es wurde von PASQAL als Pass-Through entwickelt, um Quantenexperimente an ihre Quantenprozessoren zu übermitteln. Weitere Informationen finden Sie in der Pulser-Dokumentation.
Um die Impulssequenzen zu übermitteln, installieren Sie zuerst die Pulser SDK-Pakete:
try:
import pulser
except ImportError:
!pip -q install pulser
Erstellen eines Quantenregisters
Sie müssen sowohl ein Register als auch ein Layout definieren, bevor Sie fortfahren. Das Register gibt an, wo Atome angeordnet werden sollen, während das Layout die Positionierung von Fallen angibt, die zum Erfassen und Strukturieren dieser Atome innerhalb des Registers erforderlich sind.
Ausführliche Informationen zu Layouts finden Sie in der Pulser-Dokumentation.
Zunächst erstellen Sie ein "Devices"-Objekt zum Importieren des PASQAL-Quantencomputers targetFresnel.
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"]
Vorkalibrierungslayouts
Das Gerät definiert eine Liste der vordefinierten Layouts. Sie können Ihr Register aus einem dieser Layouts erstellen.
Dies ist die empfohlene Option, da sie die Leistung der QPU verbessert.
Option 1: Definieren Ihres Registers mithilfe von vorkalibrierungsbasierten Layouts
Überprüfen Sie die auf Fresnel verfügbaren Layouts, und definieren Sie Ihr Register aus diesem Layout. Weitere Informationen dazu finden Sie in der Pulserdokumentation.
Beispiel:
# 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()
Beliebige Layouts
Wenn vordefinierte Layouts nicht den Anforderungen Ihres Experiments entsprechen, können Sie ein benutzerdefiniertes Layout erstellen.
Für jedes gegebene beliebige Register platziert eine neutrale Atom-QPU Fallen nach dem Layout, die dann einer Kalibrierung unterzogen werden müssen. Da jede Kalibrierung Zeit erfordert, ist es im Allgemeinen ratsam, ein vorhandenes kalibriertes Layout nach Möglichkeit wiederzuverwenden.
Option 2: Automatisches Ableiten eines Layouts aus dem definierten Register
Diese Option ermöglicht die automatische Generierung eines Layouts basierend auf einem angegebenen Register. Bei großen Registern kann dieser Prozess jedoch aufgrund von Einschränkungen im Algorithmus, der zum Erstellen des Layouts verwendet wird, suboptimale Lösungen liefern.
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: Definieren Des Registers mithilfe eines manuell definierten Layouts
- Erstellen eines beliebigen Layouts mit 20 Fallen, die zufällig in einer 2D-Ebene positioniert sind
import numpy as np # 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")
- Definieren Des Registers mit bestimmten Trap-IDs
trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Schreiben einer Impulssequenz
Die neutralen Atome werden mit Laserpulsen gesteuert. Mit dem Pulser SDK können Sie Impulssequenzen erstellen, die auf das Quantenregister angewendet werden.
Zunächst definieren Sie die Impulssequenzattribute, indem Sie die Kanäle deklarieren, die zum Steuern der Atome verwendet werden. Zum Erstellen eines
Sequence
, müssen Sie eineRegister
Instanz zusammen mit dem Gerät bereitstellen, auf dem die Sequenz ausgeführt wird. Der folgende Code deklariert beispielsweise einen Kanal:ch0
.Hinweis
Sie können das
QPU = devices["FRESNEL"]
Gerät verwenden oder ein virtuelles Gerät aus Pulser importieren, um mehr Flexibilität zu erhalten. Die Verwendung einerVirtualDevice
Ermöglicht die Sequenzerstellung, die von Gerätespezifikationen weniger eingeschränkt ist, sodass sie für die Ausführung in einem Emulator geeignet ist. Weitere Informationen finden Sie in der Pulser-Dokumentation.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")
Fügen Sie Ihrer Sequenz Impulse hinzu. Dazu erstellen und fügen Sie Impulse zu den kanälen hinzu, die Sie deklariert haben. Der folgende Code erstellt z. B. einen Impuls und fügt ihn dem Kanal
ch0
hinzu.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()
Die folgende Abbildung zeigt die Impulssequenz.
Konvertieren der Sequenz in eine JSON-Zeichenfolge
Um die Impulssequenzen zu übermitteln, müssen Sie die Pulser-Objekte in eine JSON-Zeichenfolge konvertieren, die als Eingabedaten verwendet werden kann.
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
Übermitteln der Impulssequenz an PASQAL target
Zunächst müssen Sie die richtigen Eingabe- und Ausgabedatenformate festlegen. Der folgende Code legt z. B. das Eingabedatenformat
pasqal.pulser.v1
und das Ausgabedatenformat aufpasqal.pulser-results.v1
.# 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 job
Hinweis
Die zum Ausführen eines Auftrags auf der QPU erforderliche Zeit hängt von den aktuellen Warteschlangenzeiten ab. Sie können die durchschnittliche Warteschlangenzeit für ein Objekt target anzeigen, indem Sie das Blatt "Anbieter " Ihres Arbeitsbereichs auswählen.
Übermitteln Sie das Programm an PASQAL. Bevor Sie Ihren Code an reale Quantenhardware übermitteln, können Sie ihren Code mit dem Emulator als eins
pasqal.sim.emu-tn
targettesten.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 }
Übermitteln eines Schaltkreises an Quantinuum mit OpenQASM
Erstellen Sie eine Quantenschaltung in der OpenQASM-Darstellung. Im folgenden Beispiel wird etwa eine Teleportationsschaltung erstellt:
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]; """
Die Schaltung kann optional auch aus einer Datei geladen werden:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
Übermitteln Sie den Schaltkreis an das Quantinuum target. Im folgenden Beispiel wird die Quantinuum-API-Validierung verwendet, die ein
Job
-Objekt zurückgibt.target = workspace.get_targets(name="quantinuum.sim.h1-1sc") job = target.submit(circuit, shots=500)
Warten Sie, bis der Auftrag abgeschlossen ist, und rufen Sie dann die Ergebnisse ab.
results = job.get_results() print(results)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
Anschließend können die Ergebnisse mithilfe von Matplotlib visualisiert werden.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
Im Histogramm sehen Sie, dass der Zufallszahlengenerator jedes Mal 0 zurückgegeben hat, was nicht gerade unserer Vorstellung von Zufälligkeit entspricht. Das liegt daran, dass die API-Validierung zwar sicherstellt, dass Ihr Code erfolgreich auf Quantinuum-Hardware ausgeführt wird, aber auch 0 für jede Quantenmessung zurückgegeben wird. Für einen echten Zufallszahlengenerator müssen Sie Ihre Schaltung auf Quantenhardware ausführen.
Bevor Sie einen Auftrag auf der QPU ausführen, sollten Sie schätzen, wie viel die Ausführung kostet.
Hinweis
Die aktuellsten Preisdetails finden Sie unter Azure Quantum-Preise, oder suchen Sie Ihren Arbeitsbereich und zeigen Sie Preisoptionen auf der Registerkarte "Anbieter" Ihres Arbeitsbereichs über: aka.ms/aq/myworkspaces an.
Übermitteln eines Schaltkreises an Rigetti mithilfe von Quil
Die einfachste Möglichkeit zum Übermitteln von Quil-Aufträgen ist die Verwendung des pyquil-for-azure-quantum-Pakets , da Sie die Tools und Dokumentationen der pyQuil-Bibliothek verwenden können. Ohne dieses Paket kann pyQuil verwendet werden, um Quil-Programme zu erstellen , aber nicht, um sie an Azure Quantum zu übermitteln.
Sie können quil-Programme auch manuell erstellen und mithilfe des azure-quantum
Pakets direkt übermitteln.
Laden Sie zunächst die erforderlichen Importe.
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_qvm
Verwenden Sie die
get_qvm
Funktion,get_qpu
um eine Verbindung mit QVM oder QPU abzurufen.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
Erstellen Sie ein Quil-Programm. Jedes gültige Quil-Programm wird akzeptiert, aber das Lesen muss benannt
ro
werden.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_shot
Hier ist einnumpy
Array, sodass Sie Methoden verwendennumpy
können.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] == 0
Drucken Sie alle Daten aus.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Wichtig
Das Übermitteln mehrerer Schaltungen in einem einzelnen Auftrag wird derzeit nicht unterstützt. Sie können allerdings die Methode backend.run
aufrufen, um die einzelnen Schaltungen asynchron zu übermitteln, und anschließend die Ergebnisse jedes Auftrags abrufen. Zum Beispiel:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())