Saltar al contenido principal

Construir circuitos

Versiones de paquetes

El código en esta página se desarrolló utilizando los siguientes requisitos. Recomendamos usar estas versiones o versiones más recientes.

qiskit[all]~=2.3.0

Esta página examina más de cerca la clase QuantumCircuit en el SDK de Qiskit, incluidos algunos métodos más avanzados que puedes usar para crear circuitos cuánticos.

¿Qué es un circuito cuántico?

Un circuito cuántico simple es una colección de qubits y una lista de instrucciones que actúan sobre esos qubits. Para demostrarlo, la siguiente celda crea un nuevo circuito con dos nuevos qubits, luego muestra el atributo qubits del circuito, que es una lista de Qubits en orden desde el bit menos significativo q0q_0 hasta el bit más significativo qnq_n.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit import QuantumCircuit

qc = QuantumCircuit(2)
qc.qubits
[<Qubit register=(2, "q"), index=0>, <Qubit register=(2, "q"), index=1>]

Se pueden combinar múltiples objetos QuantumRegister y ClassicalRegister para crear un circuito. Cada QuantumRegister y ClassicalRegister también puede ser nombrado.

from qiskit.circuit import QuantumRegister, ClassicalRegister

qr1 = QuantumRegister(2, "qreg1") # Create a QuantumRegister with 2 qubits
qr2 = QuantumRegister(1, "qreg2") # Create a QuantumRegister with 1 qubit
cr1 = ClassicalRegister(3, "creg1") # Create a ClassicalRegister with 3 cbits

combined_circ = QuantumCircuit(
qr1, qr2, cr1
) # Create a quantum circuit with 2 QuantumRegisters and 1 ClassicalRegister
combined_circ.qubits
[<Qubit register=(2, "qreg1"), index=0>,
<Qubit register=(2, "qreg1"), index=1>,
<Qubit register=(1, "qreg2"), index=0>]

Puedes encontrar el índice y el registro de un qubit usando el método find_bit del circuito y sus atributos.

desired_qubit = qr2[0]  # Qubit 0 of register 'qreg2'

print("Index:", combined_circ.find_bit(desired_qubit).index)
print("Register:", combined_circ.find_bit(desired_qubit).registers)
Index: 2
Register: [(QuantumRegister(1, 'qreg2'), 0)]

Agregar una instrucción al circuito anexa la instrucción al atributo data del circuito. La siguiente salida de celda muestra que data es una lista de objetos CircuitInstruction, cada uno de los cuales tiene un atributo operation y un atributo qubits.

qc.x(0)  # Add X-gate to qubit 0
qc.data
[CircuitInstruction(operation=Instruction(name='x', num_qubits=1, num_clbits=0, params=[]), qubits=(<Qubit register=(2, "q"), index=0>,), clbits=())]

La forma más fácil de ver esta información es a través del método draw, que devuelve una visualización de un circuito. Consulta Visualizar circuitos para diferentes formas de mostrar circuitos cuánticos.

qc.draw("mpl")

Output of the previous code cell

Los objetos de instrucción de circuito pueden contener circuitos de "definición" que describen la instrucción en términos de instrucciones más fundamentales. Por ejemplo, la compuerta X se define como un caso específico de la compuerta U3, una compuerta de un solo qubit más general.

# Draw definition circuit of 0th instruction in `qc`
qc.data[0].operation.definition.draw("mpl")

Output of the previous code cell

Las instrucciones y los circuitos son similares en que ambos describen operaciones sobre bits y qubits, pero tienen propósitos diferentes:

  • Las instrucciones se tratan como fijas, y sus métodos generalmente devolverán nuevas instrucciones (sin mutar el objeto original).
  • Los circuitos están diseñados para construirse a lo largo de muchas líneas de código, y los métodos QuantumCircuit a menudo mutan el objeto existente.

¿Qué es la profundidad del circuito?

La profundidad (depth) de un circuito cuántico es una medida del número de "capas" de compuertas cuánticas, ejecutadas en paralelo, que se necesitan para completar el cálculo definido por el circuito. Debido a que las compuertas cuánticas toman tiempo para implementarse, la profundidad de un circuito corresponde aproximadamente a la cantidad de tiempo que tarda la computadora cuántica en ejecutar el circuito. Por lo tanto, la profundidad de un circuito es una cantidad importante que se usa para medir si un circuito cuántico puede ejecutarse en un dispositivo.

El resto de esta página ilustra cómo manipular circuitos cuánticos.

Construir circuitos

Métodos como QuantumCircuit.h y QuantumCircuit.cx agregan instrucciones específicas a los circuitos. Para agregar instrucciones a un circuito de manera más general, usa el método append. Esto toma una instrucción y una lista de qubits a los que aplicar la instrucción. Consulta la documentación de la API de Circuit Library para obtener una lista de instrucciones compatibles.

from qiskit.circuit.library import HGate

qc = QuantumCircuit(1)
qc.append(
HGate(), # New HGate instruction
[0], # Apply to qubit 0
)
qc.draw("mpl")

Output of the previous code cell

Para combinar dos circuitos, use el método compose. Este acepta otro QuantumCircuit y una lista opcional de mapeos de qubits.

nota

El método compose devuelve un nuevo circuito y no muta ninguno de los circuitos sobre los que actúa. Para mutar el circuito sobre el que está llamando al método compose, use el argumento inplace=True.

qc_a = QuantumCircuit(4)
qc_a.x(0)

qc_b = QuantumCircuit(2, name="qc_b")
qc_b.y(0)
qc_b.z(1)

# compose qubits (0, 1) of qc_a to qubits (1, 3) of qc_b respectively
combined = qc_a.compose(qc_b, qubits=[1, 3])
combined.draw("mpl")

Output of the previous code cell

También es posible que desees compilar circuitos en instrucciones para mantener tus circuitos organizados. Puedes convertir un circuito en una instrucción usando el método to_instruction, luego anexar esto a otro circuito como lo haría con cualquier otra instrucción. El circuito dibujado en la siguiente celda es funcionalmente equivalente al circuito dibujado en la celda anterior.

inst = qc_b.to_instruction()
qc_a.append(inst, [1, 3])
qc_a.draw("mpl")

Output of the previous code cell

Si tu circuito es unitario, puedes convertirlo en un Gate usando el método to_gate. Los objetos Gate son tipos específicos de instrucciones que tienen algunas características adicionales, como el método control, que agrega un control cuántico.

gate = qc_b.to_gate().control()
qc_a.append(gate, [0, 1, 3])
qc_a.draw("mpl")

Output of the previous code cell

Para ver qué está sucediendo, puedes usar el método decompose para expandir cada instrucción en su definición.

nota

El método decompose devuelve un nuevo circuito y no muta el circuito sobre el que actúa.

qc_a.decompose().draw("mpl")

Output of the previous code cell

Medir qubits

Las mediciones se utilizan para muestrear los estados de qubits individuales y transferir los resultados a un registro clásico. Ten en cuenta que si estás enviando circuitos a una primitiva Sampler, se requieren mediciones. Sin embargo, los circuitos enviados a una primitiva Estimator no deben contener mediciones.

Los qubits se pueden medir usando tres métodos: measure, measure_all y measure_active. Para aprender cómo visualizar resultados medidos, consulte la página Visualizar resultados.

  1. QuantumCircuit.measure: mide cada qubit en el primer argumento en el bit clásico dado como segundo argumento. Este método permite un control total sobre dónde se almacena el resultado de la medición.

  2. QuantumCircuit.measure_all: no toma argumentos y se puede usar para circuitos cuánticos sin bits clásicos predefinidos. Crea cables clásicos y almacena los resultados de la medición en orden. Por ejemplo, la medición del qubit qiq_i se almacena en cbit measimeas_i). También agrega una barrera antes de la medición.

  3. QuantumCircuit.measure_active: similar a measure_all, pero mide solo los qubits que tienen operaciones.

qc1 = QuantumCircuit(2, 2)
qc1.measure(0, 1)
qc1.draw("mpl", cregbundle=False)

Output of the previous code cell

qc2 = QuantumCircuit(2)
qc2.measure_all()
qc2.draw("mpl", cregbundle=False)

Output of the previous code cell

qc3 = QuantumCircuit(2)
qc3.x(1)
qc3.measure_active()
qc3.draw("mpl", cregbundle=False)

Output of the previous code cell

Circuitos parametrizados

Muchos algoritmos cuánticos a corto plazo implican ejecutar muchas variaciones de un circuito cuántico. Dado que construir y optimizar circuitos grandes puede ser computacionalmente costoso, Qiskit admite circuitos parametrizados. Estos circuitos tienen parámetros indefinidos, y sus valores no necesitan definirse hasta justo antes de ejecutar el circuito. Esto te permite mover la construcción y optimización del circuito fuera del bucle del programa principal. La siguiente celda crea y muestra un circuito parametrizado.

from qiskit.transpiler import generate_preset_pass_manager
from qiskit.circuit import Parameter

angle = Parameter("angle") # undefined number

# Create and optimize circuit once
qc = QuantumCircuit(1)
qc.rx(angle, 0)
qc = generate_preset_pass_manager(
optimization_level=3, basis_gates=["u", "cx"]
).run(qc)

qc.draw("mpl")

Output of the previous code cell

La siguiente celda crea muchas variaciones de este circuito y muestra una de las variaciones.

circuits = []
for value in range(100):
circuits.append(qc.assign_parameters({angle: value}))

circuits[0].draw("mpl")

Output of the previous code cell

Puedes encontrar una lista de los parámetros indefinidos de un circuito en su atributo parameters.

qc.parameters
ParameterView([Parameter(angle)])

Cambiar el nombre de un parámetro

Por defecto, los nombres de los parámetros para un circuito parametrizado tienen el prefijo x- por ejemplo, x[0]. Puedes cambiar los nombres después de que se definen, como se muestra en el siguiente ejemplo.

from qiskit.circuit.library import z_feature_map
from qiskit.circuit import ParameterVector

# Define a parameterized circuit with default names
# For example, x[0]
circuit = z_feature_map(2)

# Set new parameter names
# They will now be prefixed by `hi` instead
# For example, hi[0]
training_params = ParameterVector("hi", 2)

# Assign parameter names to the quantum circuit
circuit = circuit.assign_parameters(parameters=training_params)
Forgotten the method name? Try asking Qiskit Code Assistant.

Próximos pasos

Recomendaciones