Skip to main content
IBM Quantum Platform

Ejecutar circuitos dinámicos

Los circuitos dinámicos son potentes herramientas que permiten medir los qubits en medio de la ejecución de un circuito cuántico y, a continuación, realizar operaciones lógicas clásicas dentro del circuito, basándose en el resultado de esas mediciones realizadas durante la ejecución. Este proceso también se conoce como «feedforward clásico ». Aunque aún estamos en una fase inicial en lo que respecta a comprender cuál es la mejor manera de aprovechar los circuitos dinámicos, la comunidad de investigación cuántica ya ha identificado varios casos de uso, como los siguientes:

Sin embargo, estas mejoras que aportan los circuitos dinámicos conllevan algunas desventajas. Las mediciones a mitad de circuito y las operaciones clásicas suelen tener un tiempo de ejecución mayor que las puertas de dos qubits, y este aumento del tiempo podría anular las ventajas de una menor profundidad del circuito. Por lo tanto, la reducción de la duración de las mediciones a mitad de circuito constituye un área prioritaria de mejora ahora que IBM Quantum® lanza la nueva versión de circuitos dinámicos. Para conocer otras restricciones relacionadas con el uso de circuitos dinámicos, consulte la tabla de compatibilidad de las funciones «Estimador» y «Muestreador ».

La especificación « OpenQASM e 3» define varias estructuras de flujo de control, pero Qiskit Runtime actualmente solo admite la instrucción if condicional. En Qiskit SDK, esto se corresponde con el método if_test de QuantumCircuit. Este método devuelve un gestor de contexto y suele utilizarse en una with instrucción. En esta guía se explica cómo utilizar esta instrucción condicional.

Note

Los ejemplos de código de esta guía utilizan la instrucción de medición estándar para realizar mediciones en medio del circuito. No obstante, se recomienda utilizar la MidCircuitMeasure instrucción en su lugar, si el backend la admite. Consulte la sección «Mediciones en el punto medio del circuito» para obtener más información.

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o posteriores.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    

Busca backends que admitan circuitos dinámicos

Para encontrar todos los backends a los que tu cuenta tiene acceso y que admiten circuitos dinámicos, ejecuta un código como el siguiente. En este ejemplo se da por hecho que has guardado tus credenciales de inicio de sesión. También puede especificar explícitamente las credenciales al inicializar su cuenta de servicio de Qiskit Runtime. Esto te permitiría ver los backends disponibles en una instancia concreta o en un tipo de plan, por ejemplo.

Notas
  • Los backends disponibles para la cuenta dependen de la instancia especificada en las credenciales.
  • La nueva versión de los circuitos dinámicos ya está disponible para todos los usuarios en todos los servidores. Consulte el anuncio para obtener más detalles.
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)

Output:

[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_kingston')>]

Mediciones en el punto medio del circuito

Antes de « qiskit-ibm-runtimev0.43.0 », measure era la única instrucción de medición disponible en Qiskit. Sin embargo, las mediciones en el medio del circuito tienen requisitos de ajuste distintos a los de las mediciones en los extremos (mediciones que se realizan al final de un circuito). Por ejemplo, hay que tener en cuenta la duración de la instrucción al ajustar una medición en medio del circuito, ya que las instrucciones más largas provocan más ruido en los circuitos. No es necesario tener en cuenta la duración de las instrucciones en las mediciones de terminales, ya que no hay instrucciones después de ellas.

Note

La MidCircuitMeasure instrucción se corresponde con la measure_2 instrucción indicada en el archivo. del backend supported_instructions. Sin embargo, no measure_2 es compatible con todos los backends. Utiliza service.backends(filters=lambda b: "measure_2" in b.supported_instructions) para encontrar backends que lo admitan. Es posible que en el futuro se añadan nuevas medidas, pero esto no está garantizado.

Método de MidCircuitMeasure

En qiskit-ibm-runtime v0.43.0 se presentó la MidCircuitMeasure instrucción. Como su nombre indica, se trata de una nueva instrucción de medición optimizada para la fase intermedia del circuito en las QPU de IBM®. Aunque puedes utilizar QuantumCircuit.measure para realizar una medición en medio del circuito, debido a su diseño, suele MidCircuitMeasure ser una mejor opción. Por ejemplo, supone una menor carga para el circuito que cuando se utiliza QuantumCircuit.measure.

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure

service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, dynamic_circuits=True
)

circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))

Output:

     ┌───┐┌────────────┐
q_0: ┤ X ├┤0           ├
     └───┘│            │
q_1: ─────┤  Measure_2 ├
          │            │
c_0: ═════╡0           ╞
          └────────────┘
c_1: ═══════════════════
                        
Notas importantes
  • Para poder utilizar las mediciones, debe haber al menos un registro clásico.
  • La primitiva Sampler requiere mediciones del circuito. Puedes añadir medidas de circuitos con la primitiva «Estimator», pero estas se ignoran.

Store

A partir de qiskit-ibm-runtime la versión 0.47.0, puedes utilizar la store instrucción para guardar el resultado de una expresión clásica, si vas a utilizarla repetidamente. Las operaciones se paralelizan automáticamente, lo que hace que tu código sea mucho más eficiente en tiempo de ejecución.

Para obtener más información, consulta la guía sobre control de avance clásico y flujo de control.

Note

Cuando se utiliza store para guardar un valor en un registro clásico en un backend real, dicho valor solo se guarda en memoria durante la ejecución y no se copia ni se devuelve en el resultado del trabajo.

Por ejemplo, en el siguiente código, temp tiene el mismo valor que creg durante la if_test ejecución, y funciona como se esperaba. Sin embargo, una vez finalizado el trabajo, el temp objeto BitArray devuelto en el resultado del trabajo no contiene el valor de creg. Es decir, job.result()[0].data.temp es 0.

creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
    ...

Ejemplo completo

El siguiente código crea y ejecuta un circuito dinámico en un dispositivo de hardware de la seri IBM®.

from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, dynamic_circuits=True
)

# Create a dynamic circuit

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
    qc.x(q0)
qc.measure(q0, c0)


# Convert to an ISA circuit for the given backend

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

# Generate samplers for backend targets
sampler = SamplerV2(backend)

# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

print(
    f">>> {' Job ID:':<10}  {sampler_job.job_id()} ({sampler_job.status()})"
)

Output:

>>>  Job ID:    d9bjipbv6alc73ct567g (DONE)

Qiskit Runtime limitaciones

Ten en cuenta las siguientes restricciones al ejecutar circuitos dinámicos en Qiskit Runtime.

  • Debido a la memoria física limitada de la electrónica de control, también hay un límite en el número de if instrucciones y en el tamaño de sus operandos. Este límite depende del número de transmisiones y del número de bits transmitidos en un trabajo (no en un circuito).

    Al procesar una if condición, es necesario transferir los datos de medición a la lógica de control para que esta pueda realizar dicha evaluación. Una transmisión es una transferencia de datos clásicos únicos, y los bits transmitidos son el número de bits clásicos que se transfieren. Tenga en cuenta lo siguiente:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    En el ejemplo de código anterior, los dos primeros if_test objetos de c0 se consideran una sola difusión, ya que el contenido de no c0 ha cambiado y, por lo tanto, no es necesario volver a difundirlo. La emisión if_test de hoy c1 es una retransmisión. La primera transmite los tres bits, c0 y la segunda solo transmite un bit, lo que hace un total de cuatro bits transmitidos.

    Actualmente, si se transmiten 60 bits cada vez, el trabajo puede tener aproximadamente 300 transmisiones. Sin embargo, si se transmite solo un bit cada vez, la tarea puede llegar a tener 2400 transmisiones.

  • El operando utilizado en una if_test instrucción debe tener 32 bits o menos. Por lo tanto, si estás comparando un entero ClassicalRegister, el tamaño de ese entero ClassicalRegister debe ser de 32 bits o menos. Sin embargo, si solo estás comparando un único bit de un ClassicalRegister, este ClassicalRegister puede tener cualquier tamaño (ya que el operando es de un solo bit).

    Por ejemplo, el bloque de código «No válido» no funciona porque cr tiene más de 32 bits. No obstante, puedes utilizar un registro clásico de más de 32 bits si solo vas a comprobar un bit, tal y como se muestra en el bloque de código «Válido».

       cr = ClassicalRegister(50)
       qr = QuantumRegister(50)
       circuit = QuantumCircuit(qr, cr)
       ...
       circ.measure(qr, cr)
       with circ.if_test((cr, 15)):
          ...
  • No se permiten las condiciones anidadas. Por ejemplo, el siguiente fragmento de código no funcionará porque contiene un if_test dentro de otro if_test:

       c1 = ClassicalRegister(1, "c1")
       c2 = ClassicalRegister(2, "c2")
       ...
       with circ.if_test((c1, 1)):
        with circ.if_test(c2, 1)):
         ...
  • No se admite el uso de reset o medidas dentro de las condiciones.

  • No se admiten operaciones aritméticas.

  • Consulte la tabla de características de « OpenQASM 3» para determinar qué funciones de « OpenQASM 3» son compatibles con Qiskit y Qiskit Runtime.

  • Cuando se utiliza « OpenQASM 3» (en lugar de QuantumCircuit) como formato de entrada para pasar circuitos a las primitivas de « Qiskit Runtime », solo se admiten las instrucciones que se pueden cargar en Qiskit. Las operaciones clásicas, por ejemplo, no son compatibles porque no se pueden cargar en Qiskit. Para obtener más información, consulta «Importar un programa de OpenQASM 3 a Qiskit ».

  • Las switch instrucciones for, while, y no son compatibles.


Utiliza circuitos dinámicos con Estimator

Dado que Estimator no admite circuitos dinámicos, puedes utilizar Sampler y crear tus propios circuitos de medición.

Para reproducir el comportamiento de Estimator, sigue estos pasos:

  1. Agrupa los términos de todas las variables observables en una partición. Esto se puede hacer utilizando, por ejemplo, PauliList APIel.
    Note

    Puedes utilizar el BitArray atributo «primitive» para calcular los valores esperados de las observables proporcionadas.

  2. Realiza un ciclo de cambio de base por cada partición (el cambio de base que sea necesario para cada partición). Consulte el módulo measurement_bases complementario «Measurement bases» para obtener más información. Para obtener más información, consulta la documentación del paquete de utilidades del complemento Qiskit.
  3. Suma los resultados de cada partición.

Restricciones

Consulte cualquier tabla de compatibilidad de funciones para conocer las restricciones que se aplican al utilizar circuitos dinámicos. Ten en cuenta que la compatibilidad de las funciones no depende de los primitivos.


Próximos pasos

Recomendaciones
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.