Skip to main content
IBM Quantum Platform

Alimentación directa clásica y flujo de control (circuitos dinámicos)

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

    qiskit[all]~=2.5.0
    

Los circuitos dinámicos son potentes herramientas con las que se pueden medir qubits en mitad 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 a mitad de circuito. Este proceso también se conoce como feedforward clásico. Aunque aún es pronto para comprender cuál es la mejor manera de aprovechar los circuitos dinámicos, la comunidad de investigación cuántica ya ha identificado una serie de casos de uso, como los siguientes:

Qiskit admite cuatro construcciones de flujo de control para la red clásica de propagación hacia adelante, cada una de ellas implementada como un método en QuantumCircuit. Las estructuras y sus métodos correspondientes son:

Cada uno de estos métodos devuelve un gestor de contexto y suele utilizarse en una with instrucción. En el resto de esta guía se explican cada uno de estos constructos y cómo utilizarlos.

Caution

Existen algunas limitaciones en las operaciones clásicas de avance y de flujo de control en el hardware cuántico que podrían afectar a tu programa. Para obtener más información, consulta «Ejecutar circuitos dinámicos ».


if sentencia

La sentencia if se utiliza para realizar condicionalmente operaciones basadas en el valor de un bit o registro clásico.

En el siguiente ejemplo, aplicamos una puerta Hadamard a un qubit y lo medimos. Si el resultado es 1, entonces aplicamos una puerta X en el qubit, que tiene el efecto de voltearlo de nuevo al estado 0. A continuación, volvemos a medir el qubit. El resultado de la medición debe ser 0 con una probabilidad del 100%.

from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
    circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

Output:

Output of the previous code cell

A la sentencia with se le puede dar un objetivo de asignación que es a su vez un gestor de contexto que puede ser almacenado y posteriormente utilizado para crear un bloque else, que se ejecuta siempre que no se ejecute el contenido del bloque if .

En el siguiente ejemplo, inicializamos los registros con dos qubits y dos bits clásicos. Aplicamos una puerta Hadamard al primer qubit y lo medimos. Si el resultado es 1, aplicamos una puerta Hadamard en el segundo qubit; en caso contrario, aplicamos una puerta X en el segundo qubit. Por último, medimos también el segundo qubit.

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
    circuit.h(q1)
with else_:
    circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

Output:

Output of the previous code cell

Además de condicionar sobre un único bit clásico, también es posible condicionar sobre el valor de un registro clásico compuesto por múltiples bits.

En el ejemplo siguiente, aplicamos puertas Hadamard a dos qubits y los medimos. Si el resultado es 01, es decir, el primer qubit es 1 y el segundo qubit es 0, entonces aplicamos una puerta X a un tercer qubit. Por último, medimos el tercer qubit. Tenga en cuenta que, para mayor claridad, hemos optado por especificar el estado del tercer bit clásico, que es 0, en la if condición. En el esquema del circuito, la condición se indica mediante los círculos sobre los bits clásicos que están siendo condicionados. Un círculo sólido indica condicionamiento en 1, mientras que un círculo delineado indica condicionamiento en 0.

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
    circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

Output:

Output of the previous code cell

Instrucción «switch»

La instrucción «switch» se utiliza para seleccionar acciones en función del valor de un bit o un registro clásico. Es similar a una instrucción «if», pero permite especificar más casos para la lógica de ramificación. En el ejemplo siguiente se aplica una puerta de Hadamard a un qubit y se mide este. Si el resultado es 0, aplica una puerta X al qubit, y si el resultado es 1, aplica una puerta Z. El resultado de la medición debería ser 1 con una probabilidad del 100 %.

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.switch(c0) as case:
    with case(0):
        circuit.x(q0)
    with case(1):
        circuit.z(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

# example output counts: {'1': 1024}

Output:

Output of the previous code cell

Dado que en el ejemplo anterior se utilizaba un único bit clásico, solo había dos casos posibles, por lo que se habría podido obtener el mismo resultado utilizando una instrucción «if-else». La instrucción «switch case» resulta especialmente útil cuando se desea realizar una ramificación en función del valor de un registro clásico compuesto por varios bits. El siguiente ejemplo muestra cómo crear un caso por defecto, que se ejecuta si no se ejecuta ninguno de los casos anteriores. Ten en cuenta que, en una instrucción «switch», solo se ejecuta uno de los bloques. No hay caída.

En el ejemplo siguiente se aplican puertas de Hadamard a dos qubits y se miden estos. Si el resultado es 00 o 11, aplica una puerta Z al tercer qubit. Si el resultado es 01, aplica una puerta «Y». Si ninguno de los casos anteriores coincide, aplica una puerta X. Por último, mide el tercer qubit.

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.switch(clbits) as case:
    with case(0b000, 0b011):
        circuit.z(q2)
    with case(0b001):
        circuit.y(q2)
    with case(case.DEFAULT):
        circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 267, '110': 249, '011': 265, '000': 243}

Output:

Output of the previous code cell

Bucle for

Un bucle «for» se utiliza para recorrer una secuencia de valores clásicos y realizar ciertas operaciones en cada iteración.

En el siguiente ejemplo se utiliza un bucle «for» para aplicar 5 puertas X a un qubit y, a continuación, se mide este. Dado que realiza un número impar de puertas X, el efecto global es invertir el estado del qubit, pasando del estado 0 al estado 1.

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

with circuit.for_loop(range(5)) as _:
    circuit.x(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

# example output counts: {'1': 1024}

Output:

Output of the previous code cell

Bucle while

El bucle «while» se utiliza para repetir instrucciones mientras se cumpla una determinada condición.

En el ejemplo siguiente se aplican puertas de Hadamard a dos qubits y se miden estos. A continuación, crea un bucle «while» que repite este procedimiento mientras el resultado de la medición sea 11. Por lo tanto, el resultado final nunca debería ser 11, y las demás posibilidades deberían aparecer con una frecuencia aproximadamente igual.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)

q0, q1 = qubits
c0, c1 = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.while_loop((clbits, 0b11)):
    circuit.h([q0, q1])
    circuit.measure(q0, c0)
    circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 334, '10': 368, '00': 322}

Output:

Output of the previous code cell

Expresiones clásicas

El módulo qiskit.circuit.classical de expresiones clásicas de Qiskit contiene una representación exploratoria de las operaciones en tiempo de ejecución sobre valores clásicos durante la ejecución del circuito.

El siguiente ejemplo muestra que se puede utilizar el cálculo de la paridad para crear un estado GHZ de n-qubit utilizando circuitos dinámicos. En primer lugar, generar n/2n/2 pares de Bell en qubits adyacentes. A continuación, pegue estos pares utilizando una capa de puertas CNOT entre pares. A continuación, se mide el qubit objetivo de todas las puertas CNOT anteriores y se restablece cada qubit medido al estado 0\vert 0 \rangle. Aplicas XX a cada sitio no medido para el que la paridad de todos los bits precedentes sea impar. Por último, se aplican puertas CNOT a los qubits medidos para restablecer el entrelazamiento perdido en la medición.

En el cálculo de paridad, el primer elemento de la expresión construida implica elevar el objeto Pythonmr[0] a un Value nodo (lift se utiliza para convertir objetos arbitrarios en expresiones clásicas). Esto no es necesario para mr[1] y el posible registro clásico siguiente, ya que son entradas a expr.bit_xor, y cualquier elevación necesaria se realiza automáticamente en estos casos. Estas expresiones se pueden construir en bucles y otras construcciones.

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

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
    raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2))  # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
    qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
    qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
    qc.measure(qr[q], mr[k])
    qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
    if tgt in meas_qubits:  # skip measured qubits
        continue
    # all measurement registers whose physical qubit index < tgt
    left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
    if not left_bits:  # skip if list empty
        continue

    # build XOR-parity expression
    parity = expr.lift(
        mr[left_bits[0]]
    )  # lift the first bit to Value so it will be treated like a boolean.
    for k in left_bits[1:]:
        parity = expr.bit_xor(
            mr[k], parity
        )  # calculate parity with all other bits
    with qc.if_test(parity):  # Add X if parity is 1
        qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
    qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

Output:

Output of the previous code cell

Store

Puedes utilizar la store instrucción para guardar el resultado de una expresión clásica, si vas a utilizar esa expresión varias veces. Las operaciones se paralelizan automáticamente, lo que hace que tu código sea mucho más eficiente en tiempo de ejecución.

Por ejemplo, resulta más natural y más eficiente en tiempo de ejecución escribir « B[0]B[1]B[2]B[0] \oplus B[1] \oplus B[2] \ldots », donde « B=¬AB = \neg A », que « (¬A[0])(¬A[1])(¬A[2])(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots ». La primera opción calcula la negación en un único paso paralelo antes de la cadena XOR, en lugar de evaluar cada negación de forma secuencial dentro de la expresión.

Ejemplo completo:

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

qregs = QuantumRegister(4, "q")
creg = ClassicalRegister(3, "c")
# temp is a plain ClassicalRegister used as the store target
temp = ClassicalRegister(3, "temp")
qc = QuantumCircuit(qregs, creg, temp)

qc.h([0, 1, 2])
qc.measure([0, 1, 2], creg)

# Store bit-NOT of the full 3-bit register into temp
qc.store(temp, expr.bit_not(creg))

# Compute parity of temp using bit-indexed XOR
parity = expr.bit_xor(
    expr.bit_xor(expr.index(temp, 0), expr.index(temp, 1)),
    expr.index(temp, 2),
)

# Flip q3 if parity of ~creg is 1
with qc.if_test(parity):
    qc.x(3)

qc.measure([0, 1, 2], creg)

qc.draw("mpl")

Output:

Output of the previous code cell

Próximos pasos

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