Envío de circuitos con formato específicos a Azure Quantum
Aprenda a usar el azure-quantum
Python paquete 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.
Nota
El Kit de desarrollo de Microsoft Quantum (QDK clásico) ya no se admitirá después del 30 de junio de 2024. Si es un desarrollador de QDK existente, se recomienda pasar al nuevo Kit de desarrollo de Azure Quantum (QDK moderno) para seguir desarrollando soluciones cuánticas. Para obtener más información, consulte Migración del código de Q# al QDK moderno.
Requisitos previos
Para ejecutar los circuitos en un cuaderno en Azure Portal, necesita lo siguiente:
- Una cuenta de Azure con una suscripción activa. Si no tiene una cuenta de Azure, regístrese de forma gratuita y regístrese para obtener una suscripción de pago por uso.
- Un área de trabajo de Azure Quantum. Para más información, consulte Creación de un área de trabajo de Azure Quantum.
Para desarrollar y ejecutar los circuitos en Visual Studio Code, también necesita:
Un Python entorno con Python y Pip instalado.
VS Code con las extensiones de Azure Quantum Development Kit, Pythony Jupyter instaladas.
Paquetes de Azure Quantum
qsharp
,azure-quantum
yipykernel
.python -m pip install --upgrade qsharp azure-quantum ipykernel
Crear un nuevo cuaderno de Jupyter Notebook
- Inicie sesión en Azure Portal y seleccione el área de trabajo del paso anterior.
- En la hoja de la izquierda, seleccione Cuadernos.
- Haga clic en Mis cuadernos y, luego, en Agregar nuevo.
- En Tipo de kernel, seleccione IPython.
- Escriba un nombre para el archivo y haga clic en Crear archivo.
Cuando se abre el nuevo cuaderno, se crea automáticamente el código de la primera celda, en función de la información de la suscripción y del área de trabajo.
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
Envío de 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 targetde cálculo cuántico. Para obtener más información, consulte Quantum Intermediate Representation.
Cree el circuito QIR. Por ejemplo, el código siguiente crea 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_job
función auxiliar para enviar el circuito QIR a .target Tenga en cuenta que los formatos de datos de entrada y salida se especifican comoqir.v1
ymicrosoft.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 result
Seleccione y target envíe el circuito QIR a Azure Quantum. Por ejemplo, para enviar el circuito QIR al simulador targetionQ :
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]}
Envío de un circuito con un formato específico del proveedor a Azure Quantum
Además de los lenguajes QIR, como Q# o Qiskit, puede enviar circuitos cuánticos en formatos específicos del proveedor a Azure Quantum. Cada proveedor tiene su propio formato para representar circuitos cuánticos.
Envío de un circuito a IonQ mediante formato JSON
Cree un circuito cuántico con el formato JSON independiente del lenguaje admitido por IonQ targets, tal como se describe en la documentación de La API de IonQ. El ejemplo siguiente crea una superposición entre tres cúbits:
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)
Espere hasta que se complete el trabajo y obtenga los resultados.
results = job.get_results() print(results)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
A continuación, podemos visualizar los resultados mediante 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")
Antes de ejecutar un trabajo en la QPU, puede calcular cuánto cuesta ejecutarse. Para calcular el costo de ejecutar un trabajo en la QPU, puede usar el método
estimate_cost
.target = workspace.get_targets(name="ionq.qpu") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
El costo estimado se muestra en USD.
Nota
Para obtener los precios más actuales, consulte los precios de IonQ o vaya a su área de trabajo y consulte las opciones de precios en la hoja "Proveedores" en: aka.ms/aq/myworkspaces.
Envío de un circuito a PASQAL mediante el SDK de Pulser
Para enviar un circuito a PASQAL, puede usar el SDK de Pulser para crear secuencias de pulsos y enviarlos al PASQAL target.
Instalación del SDK de Pulser
Pulser es un marco para componer, simular y ejecutar secuencias de pulso para dispositivos cuánticos neutral-atom. Está diseñado por PASQAL como 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
except ImportError:
!pip -q install pulser
!pip -q install pulser-core
Creación de un registro cuántico
En primer lugar, cargue las importaciones necesarias:
import numpy as np import pulser from pprint import pprint from pulser import Pulse, Sequence, Register
La QPU de PASQAL está hecha de átomos neutros atrapados en posiciones bien definidas en una red. Para definir los registros cuánticos, cree una matriz de cúbits en una red. Por ejemplo, el código siguiente crea una red de 4x4 cuadrados de cúbits:
L = 4 square = np.array([[i, j] for i in range(L) for j in range(L)], dtype=float) square -= np.mean(square, axis=0) square *= 5 qubits = dict(enumerate(square)) reg = Register(qubits) 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 que se aplicarán al registro cuántico.
En primer lugar, debe configurar una secuencia de pulsos y declarar los canales que se usarán para controlar los átomos. Por ejemplo, el código siguiente declara dos canales:
ch0
ych1
.from pulser.devices import Chadoq2 seq = Sequence(reg, Chadoq2) seq.declare_channel("ch0", "raman_local") print("Available channels after declaring 'ch0':") pprint(seq.available_channels) seq.declare_channel("ch1", "rydberg_local", initial_target=4) print("\nAvailable channels after declaring 'ch1':") pprint(seq.available_channels)
Algunos aspectos que se deben tener en cuenta son:
- En
Sequence
Pulser se trata de una serie de operaciones que se van a ejecutar en un registro cuántico. - El código configura una secuencia de operaciones que se ejecutarán en un
AnalogDevice
dispositivo.AnalogDevice
es un dispositivo predefinido en Pulser que representa un equipo cuántico equivalente a Fresnel1.
- En
Cree una secuencia de pulsos. Para ello, cree y agregue pulsos a los canales declarados. Por ejemplo, el código siguiente crea un pulso simple y lo agrega al canal
ch0
y, a continuación, crea un pulso complejo y lo agrega al canalch1
.seq.target(1, "ch0") # Target qubit 1 with channel "ch0" simple_pulse = Pulse.ConstantPulse(200, 2, -10, 0) seq.add(simple_pulse, "ch0") # Add the pulse to channel "ch0" seq.delay(100, "ch1") from pulser.waveforms import RampWaveform, BlackmanWaveform duration = 1000 # Create a Blackman waveform with a duration of 1000 ns and an area of pi/2 rad amp_wf = BlackmanWaveform(duration, np.pi / 2) # Create a ramp waveform with a duration of 1000 ns and a linear sweep from -20 to 20 rad/µs detuning_wf = RampWaveform(duration, -20, 20) # Create a pulse with the amplitude waveform amp_wf, the detuning waveform detuning_wf, and a phase of 0 rad. complex_pulse = Pulse(amp_wf, detuning_wf, phase=0) complex_pulse.draw() seq.add(complex_pulse, "ch1") # Add the pulse to channel "ch1"
La imagen muestra el pulso simple y complejo.
Conversión de la secuencia en una cadena JSON
Para enviar las secuencias de pulso, debe convertir los objetos Pulser en una cadena JSON que se puede 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)
#print(json.dumps(input_data, indent=4, sort_keys=True))
return to_send
Enviar la secuencia de pulso a PASQAL target
En primer lugar, debe establecer los formatos de datos de entrada y salida adecuados. Por ejemplo, el código siguiente establece el formato de datos de entrada en
pasqal.pulser.v1
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): 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=100 # Number of shots ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() return result
Nota
El tiempo necesario para ejecutar un circuito en la QPU puede variar en función de los tiempos de cola actuales. Para ver el tiempo medio de cola de un elemento target , seleccione la hoja Proveedores del área de trabajo.
Envíe el programa a PASQAL. Por ejemplo, puede enviar el programa a PASQAL Emu-TN target.
target = workspace.get_targets(name="pasqal.sim.emu-tn") submit_job(target, seq)
{'0000000000000000': 59, '0000100000000000': 39, '0100000000000000': 1, '0100100000000000': 1}
Envío de un circuito a Quantinuum mediante OpenQASM
Cree un circuito cuántico en la representación de OpenQASM. En el ejemplo siguiente, se crea un circuito de teletransporte:
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]; """
También puede cargar el circuito desde un archivo:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
Envíe el circuito al quantinuum target. El siguiente ejemplo utiliza el validador de API de Quantinuum, que devuelve un objeto
Job
.target = workspace.get_targets(name="quantinuum.sim.h1-1sc") job = target.submit(circuit, shots=500)
Espere hasta que se complete el trabajo y obtenga los resultados.
results = job.get_results() print(results)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
A continuación, podemos visualizar los resultados mediante Matplotlib.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
Si observa el histograma, puede observar que el generador de números aleatorios devuelve 0 cada vez, lo que no es muy aleatorio. Esto se debe a que, aunque el validador de API garantiza que el código se ejecutará correctamente en el hardware de Quantinuum, también 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.
Antes de ejecutar un trabajo en la QPU, puede calcular cuánto cuesta ejecutarse. Para calcular el costo de ejecutar un trabajo en la QPU, puede usar el método
estimate_cost
.target = workspace.get_targets(name="quantinuum.qpu.h1-1") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
Esto imprime el costo estimado en créditos cuánticos de H-System (HQC).
Nota
Para ejecutar una estimación de costos con un Quantinuumtarget, primero debe volver a cargar el paquete azure-quantumPython con el parámetro [qiskit]. Para más información, consulte Actualización del paquete azure-quantumPython.
Nota
Para obtener los detalles de precios más actuales, consulte Precios de Azure Quantum o busque el área de trabajo y vea las opciones de precios en la pestaña "Proveedor" del área de trabajo a través de: aka.ms/aq/myworkspaces.
Envío de un circuito a Rigetti mediante Quil
La manera más fácil de enviar trabajos de Quil es usar el paquete pyquil-for-azure-quantum , ya que permite usar las herramientas y la documentación de la biblioteca pyQuil . Sin este paquete, pyQuil se puede usar para construir programas de Quil, pero no para enviarlos a Azure Quantum.
También puede construir programas de Quil manualmente y enviarlos directamente mediante el paquete azure-quantum
.
En primer lugar, cargue las importaciones necesarias.
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
Use la
get_qvm
función oget_qpu
para obtener una conexión a QVM o QPU.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-2") for submitting to a QPU
Cree un programa Quil. Se acepta cualquier programa Quil válido, pero el archivo readout debe tener el nombre
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"]
Aquí,
data_per_shot
es unanumpy
matriz, por lo que puede usarnumpy
métodos.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
Imprima todos los datos.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Importante
Actualmente, no se admite el envío de varios circuitos en un solo trabajo. Como solución alternativa, puede llamar al método backend.run
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())
Pasos siguientes
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de