Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Aprenda a usar el módulo qdk.azurePython para enviar circuitos en formatos específicos al servicio Azure Quantum. En este artículo se muestra cómo enviar circuitos en los siguientes formatos:
Para más información, consulte Circuitos cuánticos.
Requisitos previos
Para desarrollar y ejecutar los circuitos en Visual Studio Code (VS Code), debe tener lo siguiente:
Una cuenta de Azure con una suscripción activa. Si no tiene una cuenta de Azure, regístrese gratuitamente y regístrese para obtener una suscripción de pago por uso.
Un área de trabajo de Azure Quantum. Para obtener más información, consulte Create an Azure Quantum workspace.
Un entorno Python con Python y Pip instalados.
VS Code con las extensiones Microsoft Quantum Development Kit (QDK), Python y Jupyter instalado.
La biblioteca
qdkPython con elazureextra y el paqueteipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Creación de un cuaderno de Jupyter Notebook y conexión al área de trabajo de Quantum
Para conectarse al área de trabajo en un cuaderno de Jupyter Notebook en VS Code, siga estos pasos:
En VS Code, abra el menú Ver y elija Paleta de comandos.
Escriba Crear: Nuevo Cuaderno Jupyter. Se abre un archivo Jupyter Notebook vacío en una nueva pestaña.
En la primera celda del cuaderno, ejecute el código siguiente. Puede encontrar el identificador de recurso en el panel Overview del área de trabajo en el portal de Azure.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Enviar circuitos con formato QIR
La representación intermedia cuántica (QIR) es una representación intermedia que actúa como una interfaz común entre los lenguajes de programación cuántica y las plataformas de cálculo cuántico de destino. Para obtener más información, consulte Quantum Intermediate Representation.
Para enviar un circuito con formato QIR, siga estos pasos:
Cree el circuito QIR. Por ejemplo, ejecute el código siguiente en una nueva celda para crear un circuito de entrelazamiento simple.
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} """Cree una
submit_qir_jobfunción auxiliar para enviar el circuito QIR a un target. En este ejemplo, los formatos de datos de entrada y salida sonqir.v1ymicrosoft.quantum-results.v1, respectivamente.# 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 resultEnvíe el circuito QIR a un Azure Quantum target específico. Por ejemplo, para enviar el circuito QIR al simulador targetde IonQ, ejecute el código siguiente:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Enviar un circuito con un formato específico del proveedor para Azure Quantum
Cada proveedor de Azure Quantum tiene su propio formato para representar circuitos cuánticos. Puede enviar circuitos a Azure Quantum en formatos específicos del proveedor en lugar de idiomas QIR, como Q# o Qiskit.
Envío de un circuito a IonQ en formato JSON
IonQ usa el formato JSON para ejecutar circuitos en su targets. Para obtener más información, consulte la documentación de IonQ targets y ionQ API.
En el ejemplo siguiente se crea una superposición entre tres cúbits en formato JSON.
En una nueva celda, cree un circuito cuántico en formato JSON.
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }Envíe el circuito a IonQ target. En el ejemplo siguiente se usa el simulador de IonQ, que devuelve un objeto
Job.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)Cuando finalice el trabajo, obtenga los resultados.
results = job.get_results() print(results)
Envío de un circuito a Pasqal en formato sdk de Pulser
Puede usar el SDK de Pulser para crear secuencias de pulso y enviarlos a Pasqal targets.
Instalación del SDK de Pulser
Pulser es una plataforma que permite crear, simular y ejecutar secuencias de pulso para dispositivos cuánticos de átomos neutros. Pulser está diseñado por PASQAL como un paso a través para enviar experimentos cuánticos a sus procesadores cuánticos. Para obtener más información, consulte la documentación de Pulser.
Para enviar las secuencias de pulso, instale primero los paquetes del SDK de Pulser:
try:
import pulser
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
Creación de un registro cuántico
Defina un registro y un diseño. El registro especifica dónde organizar los átomos y el diseño especifica las posiciones de las trampas que estructuran los átomos dentro del registro.
Para obtener más información sobre los diseños, consulte la documentación de Pulser.
Cree un devices objeto para importar el equipo targetcuántico de Pasqal, FRESNEL_CAN1.
from pulser_pasqal import PasqalCloud
devices = PasqalCloud().fetch_available_devices()
QPU = devices["FRESNEL_CAN1"]
Diseños calibrados previamente
El dispositivo define una lista de diseños pre calibrados. Puede crear el registro a partir de uno de estos diseños.
Use diseños pre calibrados siempre que sea posible porque mejoran el rendimiento de la QPU.
En el ejemplo siguiente se usa el primer diseño calibrado previamente en el 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()
Diseños arbitrarios
Use un diseño personalizado cuando los diseños pre calibrados no cumplan los requisitos del experimento.
Para un registro arbitrario determinado, una QPU de átomo neutro coloca trampas según la disposición, que luego deben calibrarse. Dado que cada calibración tarda tiempo, es un procedimiento recomendado reutilizar un diseño calibrado existente siempre que sea posible.
Para crear un diseño arbitrario, elija una de las siguientes opciones:
Genere automáticamente un diseño basado en un registro especificado. En el caso de los registros grandes, este proceso puede producir soluciones sub óptimas. Por ejemplo:
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)Defina manualmente un diseño para crear el registro. Por ejemplo, cree un diseño arbitrario con 20 trampas que se colocan aleatoriamente en un plano 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()
Escribir una secuencia de pulsos
Los átomos neutros se controlan con pulsos láser. El SDK de Pulser permite crear secuencias de pulso para aplicar al registro cuántico.
Defina los atributos de secuencia de pulso declarando los canales que controlan los átomos. Para crear un
Sequence, proporcione unaRegisterinstancia junto con el dispositivo donde se ejecutará la secuencia. Por ejemplo, el código siguiente declara un canal: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:
Puede usar el
QPU = devices["FRESNEL_CAN1"]dispositivo o importar un dispositivo virtual desde Pulser para obtener más flexibilidad. El uso deVirtualDevicepermite la creación de secuencias menos restringidas por las especificaciones del dispositivo, lo que le permite ejecutarse en un emulador. Para obtener más información, consulte la documentación de Pulser.Agregue pulsos a la secuencia. Para ello, cree y agregue pulsos a los canales que haya declarado. Por ejemplo, el código siguiente crea un pulso y lo agrega al canal
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()En la imagen siguiente se muestra la secuencia de pulsos:
Conversión de la secuencia en una cadena JSON
Para enviar las secuencias de pulso, convierta los objetos Pulser en una cadena JSON que se pueda usar como datos de entrada.
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
Enviar la secuencia de pulso a Pasqal target
Establezca los formatos de datos de entrada y salida adecuados. Por ejemplo, el código siguiente establece el formato
pasqal.pulser.v1de datos de entrada en y el formato de datos de salida enpasqal.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:
El tiempo necesario para ejecutar un trabajo en la QPU depende de los tiempos de cola actuales. Puede ver el tiempo medio de cola de un target en el panel Proveedores del área de trabajo.
Envíe el programa a Pasqal. Antes de enviar el código al hardware cuántico real, se recomienda probar el código en el emulador
pasqal.sim.emu-mpstarget.target = workspace.get_targets(name="pasqal.sim.emu-mps") # Change to "pasqal.qpu.fresnel-can1" to use FRESNEL_CAN1 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 }
Enviar un circuito OpenQASM a Quantinuum
Cree un circuito cuántico en la representación de OpenQASM. Por ejemplo, el código siguiente crea un circuito de teletransportación:
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]; """O bien, cargue el circuito desde un archivo OpenQASM:
with open("my_teleport.qasm", "r") as f: circuit = f.read()Envíe el circuito a quantinuum target. En el ejemplo siguiente se envía el trabajo a uno de los simuladores targetsde Quantinuum .
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)Espere hasta que se complete el trabajo y obtenga los resultados.
results = job.get_results() print(results)
Nota:
Estos resultados devuelven 000 para cada disparo, los cuales no son aleatorios. Esto se debe a que el validador de API solo comprueba si el código se puede ejecutar en hardware Quantinuum, pero devuelve 0 para cada medida cuántica. Para que el generador de números aleatorios sea verdadero, debe ejecutar el circuito en hardware cuántico.
Enviar un circuito Quil a Rigetti
Para enviar un trabajo de Quil a Rigetti target, use el módulo qdk.azurePython.
Cargue las importaciones necesarias.
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParamsCree un
targetobjeto y pase el nombre de Rigetti target al que desea enviar el trabajo. Por ejemplo, el siguiente código seleccionaQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)Cree un programa Quil. Para que el programa se acepte, debe establecer la lectura en
"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 failedPuede indexar un resultado con el nombre de la lectura. En el código siguiente,
data_per_shotes una lista de longitudnum_shots, y cada elemento de la lista es otra lista que contiene los datos del registro de esa captura.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]En este caso, dado que el tipo del registro es BIT, el tipo es entero y el valor 0 o 1.
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0Imprima todos los datos.
print(f"Data from '{readout}' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Importante
No se pueden enviar varios circuitos en un solo trabajo. Como solución alternativa, puede llamar al backend.run método para enviar cada circuito de forma asincrónica y, a continuación, capturar los resultados de cada trabajo. Por ejemplo:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())