Saltar al contenido principal
Este proyecto está en fase beta. ¿Encontraste un error? Abre un issue en GitHub. ¿Tienes una pregunta o una idea? Pregunta en Discussions — ¡nos encantaría tu opinión!
¿Cómo es la calidad de la traducción en esta página?

Resolución de temporización diferida con stretch

La especificación del lenguaje OpenQASM 3 incluye un tipo stretch con el que puedes especificar la temporización relativa de las operaciones en lugar de la temporización absoluta. El soporte de stretch como duraciones para las instrucciones Delay se añadió en Qiskit v2.0.0. El valor concreto de una duración stretch se resuelve en tiempo de compilación, una vez que se conocen las duraciones exactas de las puertas calibradas. El compilador intenta minimizar la duración del stretch, sujeto a las restricciones de temporización en uno o más qubits. Esto te permite expresar diseños de puertas como el espaciado uniforme de puertas (por ejemplo, para implementar una secuencia de desacoplamiento de eco de orden superior), la alineación izquierda de una secuencia de puertas, o la aplicación de una puerta durante la duración de algún subcircuito, sin necesidad de conocer la temporización exacta.

Ejemplos​

Desacoplamiento dinámico​

Un caso de uso común de stretch es aplicar desacoplamiento dinámico a un qubit inactivo mientras otro qubit realiza operaciones condicionales.

Por ejemplo, podemos usar stretch para aplicar una secuencia de desacoplamiento dinámico XX al qubit 1, mientras dura el bloque condicional aplicado al qubit 0, tal como se ilustra en el siguiente diagrama:

Imagen que ilustra el siguiente circuito

El circuito correspondiente tendría el siguiente aspecto. Ten en cuenta que se necesita un par de barreras para definir los límites de esta temporización relativa.

from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

# Add barriers to define the boundaries
circuit.barrier()
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q0)
with else_:
circuit.x(q0)

# Apply an XX DD sequence with stretch on qubit 1
s = circuit.add_stretch("s")
circuit.delay(s, q1)
circuit.x(q1)
circuit.delay(expr.mul(s, 2), q1)
circuit.x(q1)
circuit.delay(s, q1)
circuit.barrier()

Alineación de planificación​

Este ejemplo usa stretch para asegurar que una secuencia de puertas entre dos barreras quede alineada a la izquierda, independientemente de sus duraciones reales:

from qiskit import QuantumCircuit
from numpy import pi

qc = QuantumCircuit(5)
qc.barrier()
qc.cx(0, 1)
qc.u(pi/4, 0, pi/2, 2)
qc.cx(3, 4)

a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")

# Use the stretches as Delay duration.
qc.delay(a, [0, 1])
qc.delay(b, 2)
qc.delay(c, [3, 4])
qc.barrier()
nota

Al usar stretch con IBM Quantum Compute Service, cualquier resto resultante de una resolución de stretch se añade al primer delay que usa el stretch.

Ejemplo:

a = circuit.add_stretch("a")
circuit.barrier(q0, q1)
circuit.delay(100, q0)
circuit.delay(a, q1) # resolve to 26
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.barrier(q0, q1)

El código anterior se resuelve con un valor de 25 y un resto de 1. El primer delay[a] tendrá el resto añadido.

Ecuación de resolución de stretch: a+8+a+8+a+8=100=3∗a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24

Ver los valores de stretch en IBM Quantum Compute Service​

El valor real de una duración stretch se resuelve en tiempo de compilación, después de que el circuito haya sido planificado. Al ejecutar un trabajo Sampler en IBM Quantum Compute Service, puedes ver los valores de stretch resueltos en los metadatos del resultado del trabajo. El soporte de stretch en Quantum Compute es actualmente experimental, por lo que primero debes establecer una opción experimental para habilitar su obtención, y luego acceder a los datos directamente desde los metadatos de la siguiente manera:

# Enable stretch value retrieval.
sampler.options.experimental = {
"execution": {
"stretch_values": True,
"scheduler_timing": True,
},
}

# Access the stretch values from the metadata.
job_result = job.result()
circuit_stretch_values = (
job_result[0].metadata["compilation"]["stretch_values"]
)

# Visualize the timing.
# Use the sliders at the bottom, the controls at the top, and the
# legend on the side of the output to customize the view.
draw_circuit_schedule_timing(ob.result()[0].metadata['compilation']
['scheduler_timing']['timing'])
nota

Aunque el tiempo total del circuito se devuelve en los metadatos de "compilation", este NO es el tiempo usado para la facturación (QPU time).

Entender la salida de metadatos​

Los metadatos stretch_values devuelven la siguiente información:

  • Name: El nombre del stretch aplicado.

  • Value: El valor objetivo solicitado.

  • Remainder: El resto de la resolución del stretch, que se añade al primer delay que usa el stretch.

  • Expanded values: Conjuntos de valores que especifican el inicio del stretch y su duración.

Ejemplo​

# Define the circuit
circuit = QuantumCircuit(4)
foo = circuit.add_stretch("foo")
bar = circuit.add_stretch("bar")
circuit.barrier()
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)

circuit.delay(foo, 2)
circuit.x(2)
# 3*foo
circuit.delay(expr.mul(3, foo), 2)
circuit.x(2)
# 2*foo
circuit.delay(expr.mul(2, foo), 2)

circuit.delay(bar, 3)
circuit.x(3)
circuit.delay(bar, 3)

circuit.measure_all()

Salida de metadatos​

[{'name': 'bar',
'value': 29,
'remainder': 1,
'expanded_values': [[1365, 30], [1404, 29]]},
{'name': 'foo',
'value': 8,
'remainder': 2,
'expanded_values': [[1365, 10], [1384, 24], [1417, 16]]}
]

Los valores devueltos para la duración dependen del valor objetivo y del resto calculado. Por ejemplo, estas son las duraciones devueltas para foo:

  • foo value + remainder (8+2 = 10)

  • foo value * 3 (8 x 3 = 24)

  • foo value * 2 (8 x 2 = 16)

Puedes usar una visualización para ayudar a entender y verificar la temporización.

draw_circuit_schedule_timing(job.result()[0].metadata
['compilation']['scheduler_timing']['timing'])

En la siguiente imagen, basada en la salida del ejemplo, foo corresponde a los stretches en el Qubit 2. El primer stretch delay que usa foo comienza al final de init_play (1365). La duración del stretch es 10, por lo que ese delay termina cuando comienza la puerta x (1365+10=1375). Puedes interpretar de forma análoga el segundo y el tercer stretch.

Se muestra la salida del comando draw_circuit_schedule_timing.

Usa los controles deslizantes de la parte inferior, los controles de la parte superior (pasa el cursor sobre la imagen de salida para mostrarlos) y la leyenda del lateral para personalizar la vista. Pasa el cursor sobre la imagen para ver los datos exactos.

Para obtener más detalles, consulta el tema Visualizar la temporización del circuito.

Limitaciones de Quantum Compute​

El soporte de stretch en Quantum Compute es actualmente experimental y tiene las siguientes restricciones:

  • Como máximo una variable stretch por conjunto de qubits entre barreras (implícitas y explícitas). Un conjunto de qubits es uno o más qubits; estos conjuntos deben ser mutuamente excluyentes.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(a, (q0, q1))
    circuit.delay(b, q0) # Invalid because 2 stretches are applied on q0
  • El área rodeada por un conjunto de barreras se denomina región de barrera. Una variable stretch no puede usarse en múltiples regiones de barrera.

    # Stretch a is used in two barrier regions
    a = circuit.add_stretch("a")
    circuit.barrier((q0, q1))
    circuit.delay(a, q0)
    circuit.barrier((q0, q1))
    circuit.delay(a, q0)
    circuit.barrier((q0, q1))

    Ilustración de la salida del código anterior

  • Las expresiones stretch están limitadas a las de la forma X*stretch + Y donde X e Y son constantes de punto flotante o enteras.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    c = circuit.add_stretch("c")

    # (a / b) * c is not supported
    circuit.delay(expr.mul(expr.div(a, b), c), q1)
  • Las expresiones stretch solo pueden incluir una única variable stretch.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 0)
  • Las expresiones stretch no pueden resolverse a valores de delay negativos. El solucionador actual no infiere restricciones de no negatividad.

    from qiskit.circuit import Duration

    circuit.barrier((q0, q1))
    circuit.delay(20, q1)
    # The length of this barrier region is 20dt, meaning the
    # equation for solving stretch 'a' is a + 40dt = 20dt, giving a = -20dt.
    circuit.delay(expr.add(a, Duration.dt(40)), q0)
    circuit.barrier((q0, q1))