Skicka specifika formaterade kretsar till Azure Quantum
Lär dig hur du använder azure-quantum
Python paketet för att skicka kretsar i specifika format till Azure Quantum-tjänsten. Den här artikeln visar hur du skickar kretsar i följande format:
Mer information finns i Kvantkretsar.
Förutsättningar
Om du vill köra dina kretsar i en notebook-fil i Azure Portal behöver du:
- Ett Azure-konto med en aktiv prenumeration. Om du inte har något Azure-konto registrerar du dig kostnadsfritt och registrerar dig för en betala per användning-prenumeration.
- En Azure Quantum-arbetsyta. Mer information finns i Skapa en Azure Quantum-arbetsyta.
För att utveckla och köra dina kretsar i Visual Studio Code behöver du också:
En Python miljö med Python och Pip installerat.
VS Code med Azure Quantum Development Kit- Pythonoch Jupyter-tillägg installerade.
Paketen Azure Quantum
qsharp
,azure-quantum
ochipykernel
.python -m pip install --upgrade qsharp azure-quantum ipykernel
Skapa en ny Jupyter Notebook
Du kan skapa en notebook-fil i VS Code eller direkt i Azure Quantum-portalen.
- Logga in på Azure Portal och välj arbetsytan från föregående steg.
- Välj Notebooks på det vänstra bladet.
- Klicka på Mina anteckningsböcker och klicka på Lägg till ny.
- I KernelTyp väljer du IPython.
- Skriv ett namn på filen och klicka på Skapa fil.
När den nya notebook-filen öppnas skapas automatiskt koden för den första cellen baserat på din prenumerations- och arbetsyteinformation.
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
Skicka QIR-formaterade kretsar
Quantum Intermediate Representation (QIR) är en mellanliggande representation som fungerar som ett gemensamt gränssnitt mellan kvantprogrammeringsspråk/ramverk och riktade kvantberäkningsplattformar. Mer information finns i Quantum Intermediate Representation(Kvantintermediär representation).
Skapa QIR-kretsen. Följande kod skapar till exempel en enkel sammanflätningskrets.
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} """
Skapa en
submit_qir_job
hjälpfunktion för att skicka QIR-kretsen till en target. Observera att indata- och utdataformaten anges somqir.v1
respektivemicrosoft.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
Välj en target och skicka QIR-kretsen till Azure Quantum. Om du till exempel vill skicka QIR-kretsen till IonQ-simulatorn 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]}
Skicka en krets med ett providerspecifikt format till Azure Quantum
Förutom QIR-språk, till exempel Q# eller Qiskit, kan du skicka kvantkretsar i providerspecifika format till Azure Quantum. Varje provider har ett eget format för att representera kvantkretsar.
Skicka en krets till IonQ med JSON-format
Skapa en kvantkrets med det språkagnostiska JSON-format som stöds av IonQtargets , enligt beskrivningen i IonQ API-dokumentationen. Följande exempel skapar till exempel en superposition mellan tre kvantbitar:
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }
Skicka kretsen till IonQ target. I följande exempel används IonQ-simulatorn, som returnerar ett
Job
objekt.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)
Vänta tills jobbet är klart och hämta sedan resultatet.
results = job.get_results() print(results)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
Du kan sedan visualisera resultaten med hjälp av 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")
Innan du kör ett jobb på QPU:n bör du uppskatta hur mycket det kommer att kosta att köra.
Kommentar
Den senaste prisinformationen finns i IonQ-priser eller hitta din arbetsyta och visa prisalternativ på fliken "Provider" på arbetsytan via: aka.ms/aq/myworkspaces.
Skicka en krets till PASQAL med Pulser SDK
Om du vill skicka en krets till PASQAL kan du använda Pulser SDK för att skapa pulssekvenser och skicka dem till PASQAL target.
Installera Pulser SDK
Pulser är ett ramverk för att skapa, simulera och köra pulssekvenser för kvantenheter med neutral atom. Den är utformad av PASQAL som en direktströmning för att skicka kvantexperiment till sina kvantprocessorer. Mer information finns i Pulser-dokumentationen.
Om du vill skicka pulssekvenserna installerar du först Pulser SDK-paketen:
try:
import pulser
except ImportError:
!pip -q install pulser
Skapa ett kvantregister
Du måste definiera både ett register och en layout innan du fortsätter. Registret anger var atomer ska ordnas, medan layouten anger placering av traps som krävs för att fånga och strukturera dessa atomer i registret.
Mer information om layouter finns i Pulser-dokumentationen.
Först skapar du ett "enheter"-objekt för att importera PASQAL-kvantdatorn target, Fresnel.
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"]
Förkalibrerade layouter
Enheten definierar en lista över förkalibrerade layouter. Du kan skapa ditt register av någon av dessa layouter.
Det här är det rekommenderade alternativet eftersom det förbättrar QPU:ns prestanda.
Alternativ 1: Definiera ditt register med förkalibrerade layouter
Granska de layouter som är tillgängliga på Fresnel och definiera ditt register från den här layouten. Mer information om hur du gör finns i pulserdokumentationen.
Exempel:
# 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()
Godtyckliga layouter
Om förkalibrerade layouter inte uppfyller kraven för experimentet kan du skapa en anpassad layout.
För varje givet godtyckligt register placerar en neutral atom QPU fällor enligt layouten, som sedan måste genomgå kalibrering. Eftersom varje kalibrering kräver tid rekommenderar vi vanligtvis att du återanvänder en befintlig kalibrerad layout när det är möjligt
Alternativ 2: Härled automatiskt en layout från ditt definierade register
Det här alternativet möjliggör automatisk generering av en layout baserat på ett angivet register. För stora register kan den här processen dock ge suboptimala lösningar på grund av begränsningar i algoritmen som används för att skapa layouten.
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)
Alternativ 3: Definiera ditt register med en manuellt definierad layout
- Skapa en godtycklig layout med 20 traps slumpmässigt placerade i ett 2D-plan
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")
- Definiera ditt register med specifika trap-ID:er
trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Skriva en pulssekvens
De neutrala atomerna styrs med laserpulser. Med Pulser SDK kan du skapa pulssekvenser som ska tillämpas på kvantregistret.
Först definierar du pulssekvensattributen genom att deklarera de kanaler som ska användas för att styra atomerna. För att skapa en
Sequence
måste du ange enRegister
instans tillsammans med enheten där sekvensen ska köras. Följande kod deklarerar till exempel en kanal:ch0
.Kommentar
Du kan använda
QPU = devices["FRESNEL"]
enheten eller importera en virtuell enhet från Pulser för mer flexibilitet. Användningen av enVirtualDevice
möjliggör sekvensskapande som är mindre begränsad av enhetsspecifikationer, vilket gör den lämplig för körning på en emulator. Mer information finns i Pulser-dokumentationen.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")
Lägg till pulser i sekvensen. Det gör du genom att skapa och lägga till pulser i de kanaler som du har deklarerat. Följande kod skapar till exempel en puls och lägger till den i kanalen
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()
Följande bild visar pulssekvensen.
Konvertera sekvensen till en JSON-sträng
Om du vill skicka pulssekvenserna måste du konvertera Pulser-objekten till en JSON-sträng som kan användas som indata.
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
Skicka pulssekvensen till PASQAL target
Först måste du ange rätt indata- och utdataformat. Följande kod anger till exempel indataformatet till
pasqal.pulser.v1
och utdataformatet tillpasqal.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
Kommentar
Den tid som krävs för att köra ett jobb på QPU:n beror på aktuella kötider. Du kan visa den genomsnittliga kötiden för en target genom att välja bladet Providers för din arbetsyta.
Skicka programmet till PASQAL. Innan du skickar koden till verklig kvantmaskinvara kan du testa koden med emulatorn
pasqal.sim.emu-tn
som .targettarget = 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 }
Skicka en krets till Quantinuum med OpenQASM
Skapa en kvantkrets i OpenQASM-representationen. I följande exempel skapas till exempel en teleporteringskrets:
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]; """
Du kan också läsa in kretsen från en fil:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
Skicka kretsen till Quantinuum target. I följande exempel används Quantinuum API-validatorn, som returnerar ett
Job
objekt.target = workspace.get_targets(name="quantinuum.sim.h1-1sc") job = target.submit(circuit, shots=500)
Vänta tills jobbet är klart och hämta sedan resultatet.
results = job.get_results() print(results)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
Du kan sedan visualisera resultaten med hjälp av Matplotlib.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
När du tittar på histogrammet kanske du märker att slumptalsgeneratorn returnerade 0 varje gång, vilket inte är särskilt slumpmässigt. Detta beror på att även om API Validator ser till att koden körs korrekt på Quantinuum-maskinvara, returnerar den också 0 för varje kvantmätning. För en verklig slumptalsgenerator måste du köra kretsen på kvantmaskinvara.
Innan du kör ett jobb på QPU:n bör du uppskatta hur mycket det kommer att kosta att köra.
Kommentar
Den senaste prisinformationen finns i Priser för Azure Quantum eller hitta din arbetsyta och visa prisalternativ på fliken "Provider" på arbetsytan via: aka.ms/aq/myworkspaces.
Skicka en krets till Rigetti med quil
Det enklaste sättet att skicka Quil-jobb är att använda pyquil-for-azure-quantum-package , eftersom du kan använda verktygen och dokumentationen för pyQuil-biblioteket . Utan det här paketet kan pyQuil användas för att skapa Quil-program men inte för att skicka dem till Azure Quantum.
Du kan också skapa Quil-program manuellt och skicka dem direkt med paketet azure-quantum
.
Läs först in de importer som krävs.
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
get_qvm
Använd funktionen ellerget_qpu
för att hämta en anslutning till QVM eller QPU.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
Skapa ett Quil-program. Alla giltiga Quil-program accepteras, men avläsningen måste ha namnet
ro
.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
Här är ennumpy
matris, så du kan användanumpy
metoder.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
Skriv ut alla data.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Viktigt!
Det går för närvarande inte att skicka flera kretsar på ett enda jobb. Som en lösning kan du anropa backend.run
metoden för att skicka varje krets asynkront och sedan hämta resultatet av varje jobb. Till exempel:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())