Specifieke geformatteerde circuits verzenden naar Azure Quantum
Meer informatie over het gebruik van het pakket voor het azure-quantum
Python verzenden van circuits in specifieke indelingen naar de Azure Quantum-service. In dit artikel leest u hoe u circuits in de volgende indelingen verzendt:
Zie Quantum-circuits voor meer informatie.
Vereisten
Als u uw circuits wilt uitvoeren in een notebook in Azure Portal, hebt u het volgende nodig:
- Een Azure-account met een actief abonnement. Als u geen Azure-account hebt, registreert u zich gratis en registreert u zich voor een abonnement op basis van betalen per gebruik.
- Een Azure Quantum-werkruimte. Zie Een Azure Quantum-werkruimte maken voor meer informatie.
Als u uw circuits wilt ontwikkelen en uitvoeren in Visual Studio Code, hebt u ook het volgende nodig:
Een Python omgeving waarop Python Pip is geïnstalleerd.
VS Code met de Azure Quantum Development KitPython en Jupyter-extensies geïnstalleerd.
De Azure Quantum
qsharp
-azure-quantum
enipykernel
pakketten.python -m pip install --upgrade qsharp azure-quantum ipykernel
Een nieuw Jupyter-notebook maken
U kunt een notebook maken in VS Code of rechtstreeks in de Azure Quantum-portal.
- Meld u aan bij Azure Portal en selecteer de werkruimte in de vorige stap.
- Selecteer Notitieblokken op de linkerblade.
- Klik op Mijn notitieblokken en klik op Nieuw toevoegen.
- Selecteer IPython in kerneltype.
- Typ een naam voor het bestand en klik op Bestand maken.
Wanneer uw nieuwe notitieblok wordt geopend, wordt automatisch de code voor de eerste cel gemaakt op basis van uw abonnements- en werkruimtegegevens.
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
QIR-geformatteerde circuits verzenden
Quantum Intermediate Representation (QIR) is een tussenliggende weergave die fungeert als een gemeenschappelijke interface tussen kwantumprogrammeertalen/frameworks en doelplatforms voor kwantumberekeningen. Zie Quantum Intermediate Representation voor meer informatie.
Maak het QIR-circuit. Met de volgende code wordt bijvoorbeeld een eenvoudig verstrengelingscircuit gemaakt.
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} """
Maak een
submit_qir_job
helperfunctie om het QIR-circuit naar een target. Houd er rekening mee dat de indelingen voor invoer- en uitvoergegevens worden opgegeven alsqir.v1
respectievelijkmicrosoft.quantum-results.v1
.# 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
Selecteer een target en verzend het QIR-circuit naar Azure Quantum. Als u bijvoorbeeld het QIR-circuit wilt verzenden naar de IonQ-simulator target:
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]}
Een circuit met een providerspecifieke indeling verzenden naar Azure Quantum
Naast QIR-talen, zoals Q# of Qiskit, kunt u kwantumcircuits in providerspecifieke indelingen verzenden naar Azure Quantum. Elke provider heeft een eigen indeling voor het weergeven van kwantumcircuits.
Een circuit verzenden naar IonQ met behulp van JSON-indeling
Maak een kwantumcircuit met behulp van de taalagnostische JSON-indeling die wordt ondersteund door de IonQtargets, zoals beschreven in de IonQ API-documentatie. In het volgende voorbeeld wordt bijvoorbeeld een superpositie gemaakt tussen drie qubits:
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }
Verzend het circuit naar de IonQ target. In het volgende voorbeeld wordt de IonQ-simulator gebruikt, die een
Job
object retourneert.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)
Wacht totdat de taak is voltooid en haal vervolgens de resultaten op.
results = job.get_results() print(results)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
Vervolgens kunt u de resultaten visualiseren met 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")
Voordat u een taak uitvoert op de QPU, moet u schatten hoeveel het kost om uit te voeren.
Notitie
Zie IonQ-prijzen voor de meest recente prijsinformatie of zoek uw werkruimte en bekijk prijsopties op het tabblad Provider van uw werkruimte via: aka.ms/aq/myworkspaces.
Een circuit verzenden naar PASQAL met behulp van Pulser SDK
Als u een circuit wilt verzenden naar PASQAL, kunt u de Pulser SDK gebruiken om pulse-reeksen te maken en deze naar de PASQAL targette verzenden.
De Pulser SDK installeren
Pulser is een framework voor het opstellen, simuleren en uitvoeren van pulse-reeksen voor kwantumapparaten met neutraal atoom. Het is ontworpen door PASQAL als passthrough om kwantumexperimenten naar hun kwantumprocessors te verzenden. Zie de Pulser-documentatie voor meer informatie.
Als u de pulse-reeksen wilt verzenden, installeert u eerst de Pulser SDK-pakketten:
try:
import pulser
except ImportError:
!pip -q install pulser pulser-pasqal
Een kwantumregister maken
U moet zowel een register als een indeling definiëren voordat u doorgaat. Het register geeft aan waar atomen worden gerangschikt, terwijl de indeling de plaatsing van traps specificeert die nodig zijn om deze atomen in het register vast te leggen en te structuren.
Zie de Pulser-documentatie voor meer informatie over indelingen.
Eerst maakt u een apparaatobject om de PASQAL-kwantumcomputertarget, Fresnel, te importeren.
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"]
Vooraf gekalibreerde indelingen
Het apparaat definieert een lijst met vooraf gekalibreerde indelingen. U kunt uw register uit een van deze indelingen bouwen.
Dit is de aanbevolen optie omdat hiermee de prestaties van de QPU worden verbeterd.
Optie 1: Uw register definiëren met vooraf gekalibreerde indelingen
Inspecteer de beschikbare indelingen op Fresnel en definieer uw register vanuit deze indeling. Raadpleeg de pulser-documentatie voor meer informatie over hoe u dit doet.
Voorbeeld:
# 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()
Willekeurige indelingen
Als vooraf gekalibreerde indelingen niet voldoen aan de vereisten van uw experiment, kunt u een aangepaste indeling maken.
Voor elk willekeurig register plaatst een neutral-atom QPU traps volgens de lay-out, die vervolgens kalibratie moet ondergaan. Aangezien elke kalibratie tijd vereist, is het over het algemeen raadzaam om een bestaande gekalibreerde indeling waar mogelijk opnieuw te gebruiken
Optie 2: Automatisch een indeling afleiden uit uw gedefinieerde register
Met deze optie kunt u een indeling automatisch genereren op basis van een opgegeven register. Voor grote registers kan dit proces echter suboptimale oplossingen opleveren vanwege beperkingen in het algoritme dat wordt gebruikt om de indeling te maken.
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)
Optie 3: Uw register definiëren met behulp van een handmatig gedefinieerde indeling
- Een willekeurige indeling maken met 20 traps willekeurig geplaatst in een 2D-vlak
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")
- Uw register definiëren met specifieke trap-id's
trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Een pulsreeks schrijven
De neutrale atomen worden geregeld met laserpulsen. Met de Pulser SDK kunt u pulse-reeksen maken die van toepassing zijn op het kwantumregister.
Eerst definieert u de pulsreekskenmerken door de kanalen te declareren die worden gebruikt om de atomen te beheren. Als u een
Sequence
exemplaar wilt maken, moet u eenRegister
exemplaar opgeven, samen met het apparaat waarop de reeks wordt uitgevoerd. De volgende code declareert bijvoorbeeld één kanaal:ch0
.Notitie
U kunt het
QPU = devices["FRESNEL"]
apparaat gebruiken of een virtueel apparaat importeren vanuit Pulser voor meer flexibiliteit. Het gebruik van eenVirtualDevice
maakt het mogelijk om reeksen te maken die minder beperkt zijn door apparaatspecificaties, waardoor deze geschikt is voor uitvoering op een emulator. Zie de Pulser-documentatie voor meer informatie.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")
Voeg pulsen toe aan uw reeks. Hiervoor maakt en voegt u pulsen toe aan de kanalen die u hebt gedeclareerd. Met de volgende code wordt bijvoorbeeld een puls gemaakt en toegevoegd aan het kanaal
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()
In de volgende afbeelding ziet u de pulsreeks.
De reeks converteren naar een JSON-tekenreeks
Als u de pulse-reeksen wilt verzenden, moet u de Pulser-objecten converteren naar een JSON-tekenreeks die kan worden gebruikt als invoergegevens.
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
De pulsreeks verzenden naar PASQAL target
Eerst moet u de juiste indelingen voor invoer- en uitvoergegevens instellen. Met de volgende code wordt bijvoorbeeld de indeling van de invoergegevens ingesteld op
pasqal.pulser.v1
en de indeling van de uitvoergegevens oppasqal.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
Notitie
De tijd die nodig is om een taak uit te voeren op de QPU, is afhankelijk van de huidige wachtrijtijden. U kunt de gemiddelde wachtrijtijd voor een target weergeven door de blade Providers van uw werkruimte te selecteren.
Verzend het programma naar PASQAL. Voordat u uw code indient bij echte kwantumhardware, kunt u uw code testen met behulp van de emulator
pasqal.sim.emu-tn
als een 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 }
Een circuit verzenden naar Quantinuum met behulp van OpenQASM
Maak een kwantumcircuit in de OpenQASM-weergave . In het volgende voorbeeld wordt bijvoorbeeld een teleportatiecircuit gemaakt:
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]; """
U kunt het circuit desgewenst laden vanuit een bestand:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
Verzend het circuit naar quantinuum target. In het volgende voorbeeld wordt de Quantinuum API-validator gebruikt, die een
Job
object retourneert.target = workspace.get_targets(name="quantinuum.sim.h1-1sc") job = target.submit(circuit, shots=500)
Wacht totdat de taak is voltooid en haal vervolgens de resultaten op.
results = job.get_results() print(results)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
Vervolgens kunt u de resultaten visualiseren met Matplotlib.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
Als u naar het histogram kijkt, ziet u mogelijk dat de generator voor willekeurige getallen elke keer 0 heeft geretourneerd, wat niet erg willekeurig is. Dit komt doordat de API Validator ervoor zorgt dat uw code correct wordt uitgevoerd op Quantinuum-hardware, maar ook 0 retourneert voor elke kwantummeting. Voor een echte generator voor willekeurige getallen moet u uw circuit uitvoeren op kwantumhardware.
Voordat u een taak uitvoert op de QPU, moet u schatten hoeveel het kost om uit te voeren.
Notitie
Zie Azure Quantum-prijzen voor de meest recente prijsinformatie of zoek uw werkruimte en bekijk de prijsopties op het tabblad Provider van uw werkruimte via: aka.ms/aq/myworkspaces.
Een circuit verzenden naar Rigetti met behulp van Quil
De eenvoudigste manier om Quil-taken in te dienen, is het pyquil-for-azure-quantum-pakket , omdat u hiermee de hulpprogramma's en documentatie van de pyQuil-bibliotheek kunt gebruiken. Zonder dit pakket kan pyQuil worden gebruikt om Quil-programma's te maken , maar niet om ze in te dienen bij Azure Quantum.
U kunt Quil-programma's ook handmatig maken en verzenden met behulp van het azure-quantum
pakket rechtstreeks.
Laad eerst de vereiste importbewerkingen.
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
Gebruik de
get_qvm
ofget_qpu
functie om verbinding te maken met de QVM of QPU.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
Maak een Quil-programma. Elk geldig Quil-programma wordt geaccepteerd, maar de leesbewerking moet de naam
ro
hebben.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 is eennumpy
matrix, zodat u methoden kunt gebruikennumpy
.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
Alle gegevens afdrukken.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Belangrijk
Het verzenden van meerdere circuits op één taak wordt momenteel niet ondersteund. Als tijdelijke oplossing kunt u de backend.run
methode aanroepen om elk circuit asynchroon te verzenden en vervolgens de resultaten van elke taak op te halen. Voorbeeld:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())