Compartir a través de


Envío de un circuito con Qiskit a Azure Quantum

Aprenda a enviar un circuito cuántico de Qiskit con el Kit de desarrollo de Azure Quantum (QDK). Puede enviar circuitos Qiskit a Azure Quantum mediante el Kit de desarrollo de Azure Quantum (QDK) y Jupyter Notebook en Visual Studio Code (VS Code). También puede probar los circuitos mediante el simulador disperso local. El QDK admite las versiones 1 y 2 de Qiskit.

Para más información, consulte Circuitos cuánticos.

Requisitos previos

Para obtener más información sobre la instalación, consulte Configuración de la extensión QDK.

Crear un nuevo cuaderno de Jupyter Notebook

  1. En VS Code, abra el menú Ver y elija Paleta de comandos.
  2. Introduzca y seleccione Crear: Nuevo Jupyter Notebook.
  3. VS Code detecta y muestra la versión de Python y el entorno virtual Python seleccionado para el cuaderno. Si tiene varios Python entornos, es posible que tenga que seleccionar un kernel usando el selector de kernel ubicado en la esquina superior derecha. Si no se detectó ningún entorno, consulte Jupyter Notebooks in VS Code (Cuadernos de Jupyter Notebook en VS Code ) para obtener información de configuración.

Carga de las importaciones necesarias

En la primera celda del cuaderno, ejecute el código siguiente para cargar las importaciones necesarias:

from qdk.azure import Workspace
from qdk.azure.qiskit import AzureQuantumProvider
from qiskit import QuantumCircuit
from qiskit.visualization import plot_histogram

Conexión al servicio Azure Quantum

Para conectarse al servicio Azure Quantum, necesita el identificador de recurso y la ubicación del área de trabajo de Azure Quantum.

  1. Inicie sesión en su cuenta de Azure, https://portal.azure.com.
  2. Seleccione el área de trabajo de Azure Quantum y vaya a Información general.
  3. Copie los parámetros Id. de recurso y Ubicación .

Agregue una nueva celda en el cuaderno y use la información de la cuenta para crear Workspace objetos y AzureQuantumProvider conectarse al área de trabajo de Azure Quantum.

workspace = Workspace(  
    resource_id = "", # Add the resourceID of your workspace
    location = "" # Add the location of your workspace (for example "westus")
    )

provider = AzureQuantumProvider(workspace)

Enumeración de todos los back-end

Ahora puede imprimir todos los back-end de computación cuántica que están disponibles en el área de trabajo:

print("This workspace's targets:")
for backend in provider.backends():
    print("- " + backend.name)
This workspace's targets:
- ionq.simulator
- ionq.qpu.aria-1
- ionq.qpu.forte-1
- ionq.qpu.forte-enterprise-1
- quantinuum.sim.h2-1sc
- quantinuum.sim.h2-2sc
- quantinuum.sim.h2-1e
- quantinuum.sim.h2-2e
- quantinuum.qpu.h2-1
- quantinuum.qpu.h2-2
- rigetti.sim.qvm
- rigetti.qpu.ankaa-3
- rigetti.qpu.cepheus-1-36q

Ejecución de un circuito simple

En una nueva celda, cree un circuito Qiskit simple.

# Create a Quantum Circuit acting on the q register
circuit = QuantumCircuit(3, 3)
circuit.name = "Qiskit Sample - 3-qubit GHZ circuit"
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure([0,1,2], [0, 1, 2])

# Print out the circuit
circuit.draw()
     ┌───┐          ┌─┐      
q_0: ┤ H ├──■───────┤M├──────
     └───┘┌─┴─┐     └╥┘┌─┐   
q_1: ─────┤ X ├──■───╫─┤M├───
          └───┘┌─┴─┐ ║ └╥┘┌─┐
q_2: ──────────┤ X ├─╫──╫─┤M├
               └───┘ ║  ║ └╥┘
c: 3/════════════════╩══╩══╩═
                     0  1  2 

Selección de para target ejecutar el programa

Ejecución en el simulador de IonQ

Antes de ejecutar el circuito en hardware real, pruebe el circuito en el simulador. Use get_backend para crear un Backend objeto para conectarse al back-end del simulador de IonQ:

simulator_backend = provider.get_backend("ionq.simulator")

Los back-end de IonQ admiten puertas de un conjunto de puertas definido, que se compilan para ejecutarse de forma óptima en el hardware. Si el circuito contiene puertas que no están en esta lista, debe transpile en el compatible gateset mediante la transpile función proporcionada por Qiskit:

from qiskit import transpile
circuit = transpile(circuit, simulator_backend)

La función transpile devuelve un nuevo objeto de circuito donde las puertas se descompone en puertas compatibles con el back-end especificado.

Ahora puede ejecutar el programa a través del servicio Azure Quantum y obtener el resultado. La celda siguiente envía un trabajo que ejecuta el circuito con 100 tomas:

job = simulator_backend.run(circuit, shots=8)
job_id = job.id()
print("Job id", job_id)
Job id 00000000-0000-0000-0000-000000000000

Para esperar hasta que se complete el trabajo y devolver los resultados, ejecute:

result = job.result()
print(result)
Result(backend_name='ionq.simulator', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=8, success=True, meas_level=2, data=ExperimentResultData(counts={'000': 4, '111': 4}, memory=['000', '000', '000', '000', '111', '111', '111', '111'], probabilities={'000': 0.5, '111': 0.5}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits=3, metadata={}), status=JobStatus.DONE, name='Qiskit Sample - 3-qubit GHZ circuit')], date=None, status=None, header=None, error_data=None)

Dado que el resultado es un tipo de objeto específico del paquete Qiskit, use result.get_counts y plot_histogram para visualizar los resultados. Para representar todas las etiquetas de cadena de bits posibles, agregue las etiquetas a counts.

counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
{'000': 4, '001': 0, '010': 0, '011': 0, '100': 0, '101': 0, '110': 0, '111': 4}

Resultado del circuito de Qiskit en el simulador de IonQ

También puede usar la get_memory función para mostrar datos de captura individuales del trabajo:

result.get_memory(circuit)
['000', '000', '000', '000', '111', '111', '111', '111']

Nota:

En IonQ targets, si envía un trabajo con un número impar de disparos, el número de disparos se redondea hacia abajo hasta el número par más cercano. Por ejemplo, si especifica 9 disparos, los resultados mostrarán los datos de 8 disparos.

Estimación del costo del trabajo

Estime el coste de ejecutar un trabajo antes de llevarlo a cabo en la QPU.

Para obtener los detalles de precios más actuales, consulte Precios de IonQ 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.

Ejecución en la QPU de IonQ

Para conectarse al hardware real (una unidad de procesador cuántico (QPU)), simplemente proporcione el nombre del target"ionq.qpu.aria-1" al get_backend método :

qpu_backend = provider.get_backend("ionq.qpu.aria-1")

Envíe el circuito para que se ejecute en Azure Quantum, obtenga los resultados y, a continuación, ejecútelo plot_histogram para trazar los resultados.

Nota:

El tiempo necesario para ejecutar un circuito en la QPU puede variar en función de los tiempos de cola actuales.

# Submit the circuit to run on Azure Quantum
job = qpu_backend.run(circuit, shots=100)
job_id = job.id()
print("Job id", job_id)

# Get the job results (this method waits for the Job to complete):
result = job.result()
print(result)
counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
Job id 00000000-0000-0000-0000-000000000000
Job Status: job has successfully run
Result(backend_name='ionq.qpu.aria-1', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2, data=ExperimentResultData(counts={'0': 505, '1': 6, '2': 1, '3': 1, '4': 1, '5': 10, '6': 11, '7': 488}, probabilities={'0': 0.4932, '1': 0.0059, '2': 0.001, '3': 0.001, '4': 0.001, '5': 0.0098, '6': 0.0117, '7': 0.4766}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits='3', qiskit='True'))])
{'000': 505, '001': 6, '010': 1, '011': 1, '100': 1, '101': 10, '110': 11, '111': 488}

Resultado del circuito de Qiskit en la QPU de IonQ

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())

Requisitos previos

Para obtener más información sobre la instalación, consulte Configuración de la extensión QDK.

  • Un Python entorno con Python y Pip instalado.

  • VS Code con el Kit de desarrollo de Azure Quantum y Python las extensiones instaladas.

  • El paquete qdkPython con las extras qiskit y jupyter.

    python pip install "qdk[qiskit,jupyter]"
    

Ejecución de un circuito básico

En VS Code, abra un nuevo Python archivo para crear y ejecutar un circuito básico con el simulador disperso integrado desde el qsharp módulo.

# load the required imports 
from qdk import qsharp
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# run the circuit using the built-in sparse simulator
backend = QSharpBackend()
job = backend.run(circuit)
counts = job.result().get_counts()

print(counts)

Para ejecutar el programa, seleccione el icono Ejecutar en la esquina superior derecha y seleccione Ejecutar Python archivo. La salida se muestra en una nueva ventana de terminal.

                  ┌─────────────────────────┐┌─┐
q_0: ─■───────────┤0                        ├┤M├───
      │P(0.79983) │  (XX-YY)(1.9337,1.7385) │└╥┘┌─┐
q_1: ─■───────────┤1                        ├─╫─┤M├
                  └─────────────────────────┘ ║ └╥┘
c: 2/═════════════════════════════════════════╩══╩═
                                              0  1
{'11': 680, '00': 344}

Generación de QIR para el circuito

Desde ese mismo circuito, se puede generar QIR que se emplea para ejecutarse en hardware cuántico.

Nota:

Para generar QIR, todas las mediciones deben almacenarse en los registros. Si hay registros sin usar, se genera un error. Además, se produce un error al intentar generar QIR con un Unrestrictedtarget perfil. El perfil de Unrestricted solo es válido para la simulación. Debe usar TargetProfile.Base, TargetProfile.Adaptive_RIo TargetProfile.Adaptive_RIF. Puede sobrescribir el target_profile en la llamada backend.qir(...) para cambiar perfiles.

  1. Importar QSharpError y TargetProfile

    from qdk.qsharp import QSharpError, TargetProfile
    
  2. Para generar QIR, modifique la salida:

    print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))
    

El código debería tener ahora un aspecto similar al siguiente:

# load the required imports 
from qdk import qsharp
from qsharp import QSharpError, TargetProfile
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# generate QIR for the circuit
print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))

La salida del código debe tener este aspecto:

     ┌────────────┐             ┌─┐   
q_0: ┤ Rx(2.7195) ├─■───────────┤M├───
     └──┬─────┬───┘ │U1(5.5924) └╥┘┌─┐
q_1: ───┤ Tdg ├─────■────────────╫─┤M├
        └─────┘                  ║ └╥┘
c: 2/════════════════════════════╩══╩═
                                 0  1
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
block_0:
  call void @__quantum__qis__rx__body(double 2.7194945105768586, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double -2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
  call void @__quantum__rt__array_record_output(i64 2, i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
  ret void
}

declare void @__quantum__qis__rx__body(double, %Qubit*)

declare void @__quantum__qis__rz__body(double, %Qubit*)

declare void @__quantum__qis__t__adj(%Qubit*)

declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__array_record_output(i64, i8*)

declare void @__quantum__rt__result_record_output(%Result*, i8*)

attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}

!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}
!4 = !{i32 1, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}

No todos los programas se pueden ejecutar en todo el hardware. Aquí, si intenta usar target el Base perfil, obtendrá errores detallados sobre qué partes del programa no se admiten.

try:
    backend.qir(qc, target_profile=TargetProfile.Base)
except QSharpError as e:
    print(e)

Pasos siguientes