Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Scopri come usare il modulo qdk.azurePython per inviare circuiti in formati specifici al servizio Azure Quantum. Questo articolo illustra come inviare circuiti nei formati seguenti:
Per altre informazioni, vedere Circuiti quantistici.
Prerequisiti
Per sviluppare ed eseguire i circuiti in Visual Studio Code (VS Code), è necessario disporre degli elementi seguenti:
Un account Azure con una sottoscrizione attiva. Se non si ha un account Azure, registrarsi gratuitamente e iscriversi per ottenere un abbonamento pay-as-you-go.
Un'area di lavoro Azure Quantum. Per altre informazioni, vedere Creare un'area di lavoro Azure Quantum.
Ambiente Python con Python e Pip installato.
VS Code con Microsoft Quantum Development Kit (QDK), Python e Jupyter installati.
Libreria
qdkPython con l'extraazuree il pacchettoipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Creare un nuovo notebook jupyter e connettersi all'area di lavoro Quantum
Per connettersi all'area di lavoro in un notebook di Jupyter in VS Code, seguire questa procedura:
In VS Code aprire il menu Visualizza e scegliere Riquadro comandi.
Inserisci Crea: Nuovo Jupyter Notebook. Viene aperto un file Jupyter Notebook vuoto in una nuova scheda.
Nella prima cella del notebook eseguire il codice seguente. È possibile trovare l'ID risorsa nel riquadro Overview dell'area di lavoro nel portale di Azure.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Inviare circuiti in formato QIR
Quantum Intermediate Representation (QIR) è una rappresentazione intermedia che funge da interfaccia comune tra i linguaggi di programmazione quantistica e le piattaforme di calcolo quantistico di destinazione. Per altre informazioni, vedere Rappresentazione intermedia quantistica.
Per inviare un circuito in formato QIR, seguire questa procedura:
Creare il circuito QIR. Ad esempio, eseguire il codice seguente in una nuova cella per creare un circuito di entanglement semplice.
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} """Crea una
submit_qir_jobfunzione helper per inviare il circuito QIR a un oggetto target. In questo esempio i formati di dati di input e output sonoqir.v1rispettivamente emicrosoft.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 resultInviare il circuito QIR a uno specifico Azure Quantum target. Ad esempio, per inviare il circuito QIR al simulatore targetIonQ, eseguire il codice seguente:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Inviare un circuito con un formato specifico del provider a Azure Quantum
Ogni provider Azure Quantum ha un proprio formato per rappresentare i circuiti quantistici. È possibile inviare circuiti a Azure Quantum in formati specifici del provider anziché in linguaggi QIR, ad esempio Q# o Qiskit.
Inviare un circuito a IonQ in formato JSON
IonQ usa il formato JSON per eseguire circuiti nel suo targets. Per altre informazioni, vedere IonQ targets e la documentazione dell'API IonQ.
L'esempio seguente crea una sovrapposizione tra tre qubit in formato JSON.
In una nuova cella creare un circuito quantistico in formato JSON.
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }Inviare il circuito a IonQ target. Nell'esempio seguente viene utilizzato il simulatore IonQ, che restituisce un oggetto
Job.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)Al termine del processo, ottieni i risultati.
results = job.get_results() print(results)
Inviare un circuito a PASQAL in formato Pulser SDK
È possibile usare Pulser SDK per creare sequenze di impulsi e inviarle a PASQAL targets.
Installare Pulser SDK
Pulser è un framework che consente di creare, simulare ed eseguire sequenze di impulsi per dispositivi quantistici atom neutrali. Pulser è progettato da PASQAL come pass-through per inviare esperimenti quantistici ai processori quantistici. Per altre informazioni, vedere la documentazione di Pulser.
Per inviare le sequenze di impulsi, installare prima di tutto i pacchetti Pulser SDK:
try:
import pulser
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
Creare un registro quantistico
Definire sia un registro che un layout. Il registro specifica dove disporre gli atomi e il layout specifica le posizioni delle trappole che acquisisce e strutturano gli atomi all'interno del registro.
Per informazioni dettagliate sui layout, vedere la documentazione di Pulser.
Creare un devices oggetto per importare il computer targetquantistico PASQAL , Fresnel.
from pulser_pasqal import PasqalCloud
devices = PasqalCloud().fetch_available_devices()
QPU = devices["FRESNEL"]
Layout pre-calibrato
Il dispositivo definisce un elenco di layout pre-calibrati. È possibile compilare il registro da uno di questi layout.
Usare layout pre-calibrati quando possibile perché migliorano le prestazioni della QPU.
L'esempio seguente usa il primo layout pre-calibrato nel dispositivo:
# Use 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,v3,v5]
reg = layout.define_register(*traps)
# Draw the register to verify that it matches your expectations
reg.draw()
Layout arbitrari
Usa un layout personalizzato quando i layout pre-calibrati non soddisfano i requisiti dell'esperimento.
Per un registro arbitrario dato, una QPU di atomi neutri posiziona le trappole secondo il layout, che deve poi essere calibrato. Poiché ogni calibrazione richiede tempo, è consigliabile riutilizzare un layout calibrato esistente quando possibile.
Per creare un layout arbitrario, scegliere una delle opzioni seguenti:
Generare automaticamente un layout in base a un registro specificato. Per registri di grandi dimensioni, questo processo può produrre soluzioni sub-ottimali. Ad esempio:
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)Definire manualmente un layout per creare il registro. Ad esempio, creare un layout arbitrario con 20 trap posizionati in modo casuale in un piano 2D:
import numpy as np from pulser.register.register_layout import RegisterLayout # Generate 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) # Create the layout layout = RegisterLayout(traps, slug="random_20") # Define your register with specific trap IDs trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Scrivere una sequenza di impulsi
Gli atomi neutri sono controllati con impulsi laser. Pulser SDK consente di creare sequenze di impulsi da applicare al registro quantistico.
Definire gli attributi della sequenza di impulsi dichiarando i canali che controllano gli atomi. Per creare un
Sequence, fornire un'istanza diRegisterinsieme al dispositivo dove la sequenza verrà eseguita. Ad esempio, il codice seguente dichiara un canale:ch0.from pulser import Sequence seq = Sequence(reg, QPU) # Print the available channels for your sequence print(seq.available_channels) # Declare a channel. For example, `rydberg_global` seq.declare_channel("ch0", "rydberg_global")Nota
È possibile usare il
QPU = devices["FRESNEL"]dispositivo o importare un dispositivo virtuale da Pulser per una maggiore flessibilità. L'uso diVirtualDevicepermette la creazione di sequenze meno vincolate dalle specifiche del dispositivo, consentendo così l'esecuzione su un emulatore. Per altre informazioni, vedere la documentazione di Pulser.Aggiungi impulsi alla tua sequenza. Per fare ciò, creare impulsi e aggiungerli ai canali dichiarati. Ad esempio, il codice seguente crea un impulso e lo aggiunge al canale
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()L'immagine seguente mostra la sequenza di impulsi:
Convertire la sequenza in una stringa JSON
Per inviare le sequenze di impulsi, convertire gli oggetti Pulser in una stringa JSON che può essere usata come dati di input.
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
Inviare la sequenza di impulsi a un PASQAL target
Impostare i formati di dati di input e output appropriati. Ad esempio, il codice seguente imposta il formato dei dati di input su
pasqal.pulser.v1e il formato dei dati di output supasqal.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 jobNota
Il tempo necessario per eseguire un processo nella QPU dipende dai tempi correnti della coda. È possibile visualizzare il tempo medio della coda per un target oggetto nel riquadro Provider dell'area di lavoro.
Inviare il programma a PASQAL. Prima di inviare il codice all'hardware quantistico reale, è consigliabile testare il codice nell'emulatore
pasqal.sim.emu-tntarget.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 }
Inviare un circuito OpenQASM a Quantinuum
Creare un circuito quantistico nella rappresentazione OpenQASM. Ad esempio, il codice seguente crea un circuito di teletrasporto:
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]; """In alternativa, caricare il circuito da un file OpenQASM:
with open("my_teleport.qasm", "r") as f: circuit = f.read()Invia il circuito a Quantinuum target. Nell'esempio seguente l'operazione viene inviata a uno dei simulatori Quantinuum targets.
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)Attendere il completamento del processo e quindi recuperare i risultati.
results = job.get_results() print(results)
Nota
Questi risultati restituiscono 000 per ogni colpo, che non è casuale. Ciò è dovuto al fatto che il validator API controlla solo se il codice può essere eseguito nell'hardware Quantinuum, ma restituisce 0 per ogni misura quantistica. Per un vero generatore di numeri casuali, è necessario eseguire il circuito su hardware quantistico.
Inviare un circuito Quil a Rigetti
Per inviare un lavoro Quil a un Rigetti target, usare il modulo qdk.azurePython.
Caricare le importazioni necessarie.
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParamsCreare un oggetto
targete fornire il nome del Rigetti target al quale si desidera inviare il processo. Ad esempio, il codice seguente selezionaQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)Creare un programma Quil. Affinché il programma venga accettato, è necessario impostare il readout su
"ro".readout = "ro" bell_state_quil = f""" DECLARE {readout} BIT[2] H 0 CNOT 0 1 MEASURE 0 {readout}[0] MEASURE 1 {readout}[1] """ num_shots = 5 job = target.submit( input_data=bell_state_quil, name="bell state", shots=100, input_params=InputParams(skip_quilc=False) ) print(f"Job completed with state: {job.details.status}") result = Result(job) # This throws an exception if the job failedÈ possibile indicizzare un oggetto Result con il nome del readout. Nel codice seguente,
data_per_shotè un elenco di lunghezzanum_shots, e ogni elemento nell'elenco è un altro elenco che contiene i dati per il registro da quello scatto.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]In questo caso, poiché il tipo del registro è BIT, il tipo è integer e il valore 0 o 1.
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0Stampa tutti i dati.
print(f"Data from '{readout}' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Importante
Non è possibile inviare più circuiti in un singolo lavoro. Come soluzione alternativa, è possibile chiamare il backend.run metodo per inviare ogni circuito in modo asincrono e quindi recuperare i risultati di ogni processo. Ad esempio:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())