Capítulo 1. Circuitos y operaciones cuánticas

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

En Qiskit, los programas cuánticos se expresan normalmente con circuitos cuánticos que contienen operaciones cuánticas. Los circuitos cuánticos están representados por la clase QuantumCircuit, y las operaciones cuánticas están representadas por subclases de la claseInstruction.

Construir circuitos cuánticos

Se puede crear un circuito cuántico suministrando a un argumento que indique el número de hilos cuánticos (qubits) deseados para ese circuito. A menudo se proporciona como un número entero:

from qiskit import QuantumCircuit
QuantumCircuit(2)

Opcionalmente, también se puede especificar el número de hilos clásicos (bits) deseados. El primer argumento se refiere al número de hilos cuánticos, y el segundo argumento al número de hilos clásicos:

QuantumCircuit(2, 2)

El número de hilos cuánticos y clásicos deseados también puede expresarse proporcionando instancias de QuantumRegister y ClassicalRegister como argumentos a QuantumCircuit. Estas clases se tratan en "Uso de la clase QuantumRegister" y "Uso de la clase ClassicalRegister".

Uso de la clase QuantumCircuit

La clase QuantumCircuit contiene un gran número de métodos y atributos. La finalidad de muchos de sus métodos es aplicar operaciones cuánticas a un circuito cuántico. La mayoría de sus otros métodos y atributos manipulan o proporcionan información sobre un circuito cuántico.

Puertas de uso común

La Tabla 1-1 contiene algunas puertas monocuánticas de uso común y ejemplos de código. La variable qc se refiere a una instancia de QuantumCircuit que contiene al menos cuatro hilos cuánticos.

Tabla 1-1. Compuertas de un solo qubit utilizadas habitualmente en Qiskit
Nombres Ejemplo Notas

H, Hadamard

qc.h(0)

Aplica la puerta H al qubit 0. Ver "Puerta H".

I, Identidad

qc.id(2) o qc.i(2)

Aplica la puerta I al qubit 2. Ver "Puerta I".

P, Fase

qc.p(math.pi/2,0)

Aplica la puerta P con rotación de fase π/2 al qubit 0. Ver "Puerta de fase".

RX

qc.rx(math.pi/4,2)

Aplica la puerta RX con rotación π/4 al qubit 2. Ver "PuertaRX".

RY

qc.ry(math.pi/8,0)

Aplica la puerta RY con rotación π/8 al qubit 0. Ver "Puerta RY".

RZ

qc.rz(math.pi/2,1)

Aplica la puerta RZ con rotación π/2 al qubit 1. Ver "Puerta RZ".

S

qc.s(3)

Aplica la puerta S al qubit 3. Equivale a la puerta P con rotación de fase π/2. Ver "Puerta S".

S†

qc.sdg(3)

Aplica la puerta S† al qubit 3. Equivale a la puerta P con rotación de fase 3π/2. Véase "SdgGate".

SX

qc.sx(2)

Aplica la puerta SX (raíz cuadrada de X) al qubit 2. Equivale a la puerta RX con rotación π/2. Ver "Puerta SX".

T

qc.t(1)

Aplica la puerta T al qubit 1. Equivale a la puerta P con rotación de fase π/4. Ver "Puerta T".

T†

qc.tdg(1)

Aplica la puerta T† al qubit 1. Equivale a la puerta P con rotación de fase 7π/4. Ver "TdgGate".

U

qc.u(math.pi/2,0,math.pi,1)

Aplica la rotación con 3 ángulos de Euler al qubit 1. Ver "UGate".

X

qc.x(3)

Aplica la puerta X al qubit 3. Ver "Puerta X".

Y

qc.y([0,2,3])

Aplica puertas Y a los qubits 0, 2 y 3. Ver "Compuerta Y".

Z

qc.z(2)

Aplica la puerta Z al qubit 2. Equivale a la puerta P con rotación de fase π. Ver "Puerta Z".

La Figura 1-1 contiene un circuito sin sentido con todos los ejemplos de puertas de un solo qubit de la Tabla 1-1.

Figura 1-1. Circuito absurdo con ejemplos de puertas de un solo qubit

La Tabla 1-2 contiene algunas puertas multiqubit de uso común y ejemplos de código. La variable qc se refiere a una instancia de QuantumCircuit que contiene al menos cuatro hilos cuánticos.

Tabla 1-2. Puertas multiqubit utilizadas habitualmente en Qiskit
Nombres Ejemplo Notas

CCX, Toffoli

qc.ccx(0,1,2)

Aplica la puerta X al hilo cuántico 2, en función del estado de los qubits de control de los hilos 0 y 1. Ver "CCXPuerta".

CH

qc.ch(0,1)

Aplica la puerta H al hilo cuántico 1, en función del estado del qubit de control del hilo 0. Ver "CHGate".

CP, Fase de Control

qc.cp(math.pi/4,0,1)

Aplica la puerta de fase al hilo cuántico 1, en función del estado del qubit de control del hilo 0. Ver "CPhaseGate".

CRX, Control-RX

qc.crx(math.pi/2,2,3)

Aplica la puerta RX al hilo cuántico 3, en función del estado del qubit de control del hilo 2. Ver "Puerta CRX".

CRY, Control-RY

qc.cry(math.pi/8,2,3)

Aplica la puerta RY al hilo cuántico 3, en función del estado del qubit de control del hilo 2. Véase "CRYGate".

CRZ

qc.crz(math.pi/4,0,1)

Aplica la puerta RZ al hilo cuántico 1, en función del estado del qubit de control del hilo 0. Ver "Puerta CRZ".

CSwap, Fredkin

qc.cswap(0,2,3) o qc.fredkin(0,2,3)

Intercambia los estados de los qubits de los hilos 2 y 3, en función del estado del qubit de control del hilo 0. Ver "CSwapGate".

CSX

qc.csx(0,1)

Aplica la puerta SX (raíz cuadrada de X) al hilo cuántico 1, en función del estado del qubit de control del hilo 0. Ver "Puerta CSX".

CU

qc.cu(math.pi/2,0,math.pi,0,0,1)

Aplica la puerta U con un argumento de fase global adicional al hilo cuántico 1, sujeto al estado del qubit de control del hilo 0. Ver "CUGate".

CX, CNOT

qc.cx(2,3) o qc.cnot(2,3)

Aplica la puerta X al hilo cuántico 3, en función del estado del qubit de control del hilo 2. Ver "Puerta CX".

CY, Control-Y

qc.cy(2,3)

Aplica la puerta Y al hilo cuántico 3, en función del estado del qubit de control del hilo 2. Ver "CYGate".

CZ, Control-Z

qc.cz(1,2)

Aplica la puerta Z al hilo cuántico 2, en función del estado del qubit de control del hilo 1. Ver "CZGate".

DCX

qc.dcx(2,3)

Aplica dos compuertas CNOT cuyos qubits de control estén en los hilos 2 y 3. Ver "Compuerta DCX".

iSwap

qc.iswap(0,1)

Intercambia los estados de los qubits de los hilos 0 y 1, y cambia la fase del | 01 y | 10 amplitudes mediante i. Consulta "iSwapGate".

MCP, Fase de control múltiple

qc.mcp(math.pi/4, [0,1,2],3)

Aplica la puerta de fase al hilo cuántico 3, en función del estado de los qubits de control de los hilos 0, 1 y 2. Consulta "MCPhaseGate".

MCX, Multicontrol X

qc.mcx([0,1,2],3)

Aplica la puerta X al hilo cuántico 3, en función del estado de los qubits de control de los hilos 0, 1 y 2. Ver "MCXPuerta".

Intercambia

qc.swap(2,3)

Intercambia los estados de los qubits de los hilos 2 y 3. Ver "SwapGate".

La Figura 1-2 contiene un circuito sin sentido con todos los ejemplos de puertas multiqubit de la Tabla 1-2.

Figura 1-2. Circuito absurdo con ejemplos de puertas multiqubit

Dibujar un circuito cuántico

El método draw() dibuja un circuito cuántico en variosformatos.

Utilizando el método dibujar()

El siguiente fragmento de código utiliza el método draw() en el formato por defecto:

qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.draw()

La Figura 1-3 muestra el circuito dibujado.

Figura 1-3. Ejemplo de visualización de un circuito mediante el método draw()

Crear una barrera

El método barrier() coloca una barrera en un circuito (como se muestra en la Figura 1-4), que proporciona tanto una separación visual como funcional entre las puertas de un circuito cuántico. Las puertas situadas a ambos lados de una barrera no son candidatas a ser optimizadas juntas cuando el circuito se convierte para funcionar en hardware cuántico o en un simulador.

Nota

El conjunto de puertas expresado mediante Qiskit representa una abstracción de las puertas reales implementadas en un determinado ordenador cuántico o simulador. Qiskit transpila las puertas a las implementadas en la plataforma de destino, combinando puertas cuando es posible para optimizar el circuito.

Utilizando el método barrera()

El método barrier() toma como argumento opcional los hilos qubit sobre los que colocar una barrera. Si no se proporciona ningún argumento, se coloca una barrera en todos los hilos cuánticos. Este método crea una instancia Barrier (ver "Barrera").

El siguiente fragmento de código demuestra el uso del método barrier() con y sin argumentos:

qc = QuantumCircuit(2)
qc.h([0,1])
qc.barrier()
qc.x(0)
qc.x(0)
qc.s(1)
qc.barrier([1])
qc.s(1)
qc.draw()

La Figura 1-4 muestra el circuito resultante.

Figura 1-4. Ejemplo de circuito que utiliza el método barrier()

Observa que las puertas S del circuito están separadas por una barrera y, por tanto, no son candidatas a combinarse en una puerta Z. Sin embargo, las puertas X pueden combinarse eliminando ambas, ya que se anulan entre sí.

Medir un circuito cuántico

Los métodos utilizados habitualmente para medir circuitos cuánticos son measure() y measure_all(). El primero es útil cuando el circuito cuántico contiene hilos clásicos en los que recibir el resultado de una medición. El segundo es útil cuando el circuito cuántico no tiene cables clásicos. Estos métodos crean instancias Measure (ver "Medida").

Utilizando el método measure()

El método measure() toma dos argumentos:

  • Los cables del qubit a medir

  • Los hilos clásicos en los que almacenar los bits resultantes

Este fragmento de código utiliza el método measure(), y la Figura 1-5 muestra un dibujo del circuito resultante:

qc = QuantumCircuit(3, 3)
qc.h([0,1,2])
qc.measure([0,1,2], [0,1,2])
qc.draw()
Figura 1-5. Ejemplo de circuito que utiliza el método measure()

Observa que el método measure() añade al circuito las operaciones de medida solicitadas.

Utilizar el método measure_all()

El método measure_all() se puede llamar sin argumentos a . Este fragmento de código utiliza el método measure_all(), y la Figura 1-6 muestra un dibujo del circuito resultante:

qc = QuantumCircuit(3)
qc.h([0,1,2])
qc.measure_all()
qc.draw()
Figura 1-6. Ejemplo de circuito que utiliza el método measure_all()

Observa que el método measure_all() creó tres hilos clásicos y añadió una barrera al circuito antes de añadir las operaciones de medición .

Obtener información sobre un circuito cuántico

Los métodos utilizados habitualmente para obtener información sobre un circuito cuántico incluyen depth(), size() y width(). Se enumeran en la Tabla 1-3. Observa que la variable qc se refiere a una instancia de QuantumCircuit.

Tabla 1-3. Métodos utilizados habitualmente para obtener información sobre un circuito cuántico
Nombres Ejemplo Notas

depth

qc.depth()

Devuelve la profundidad (ruta crítica) de un circuito si se han eliminado directivas como barrera

size

qc.size()

Devuelve el número total de operaciones de puerta en un circuito

width

qc.width()

Devuelve la suma de los hilos qubits y los hilos clásicos de un circuito

Los atributos utilizados habitualmente para obtener información sobre un circuito cuántico son clbits, data, global_phase, num_clbits, num_qubits y qubits. Se enumeran en la Tabla 1-4. Observa que la variable qc se refiere a una instancia de QuantumCircuit.

Tabla 1-4. Atributos utilizados habitualmente para obtener información sobre un circuito cuántico
Nombres Ejemplo Notas

clbits

qc.clbits

Obtiene la lista de bits clásicos en el orden en que se añadieron los registros

data

qc.data

Obtiene una lista de las operaciones (por ejemplo, puertas, barreras y operaciones de medida) del circuito

global_phase

qc.global_phase

Obtiene la fase global del circuito en radianes

num_clbits

qc.num_clbits

Obtiene el número de hilos clásicos del circuito

num_qubits

qc.num_qubits

Obtiene el número de hilos cuánticos del circuito

qubits

qc.qubits

Obtiene la lista de bits cuánticos en el orden en que se añadieron los registros

Manipular un circuito cuántico

Entre los métodos utilizados habitualmente para manipular circuitos cuánticos se incluyen append(), bind_parameters(), compose(), copy(), decompose(), from_qasm_file(), from_qasm_str(), initialize(), reset(), qasm(), to_gate(), y to_instruction().

Utilizar el método append()

El método append() añade una instrucción o puerta al final del circuito en los hilos especificados, modificando el circuito en su lugar. El siguiente fragmento de código utiliza el método append(), y la Figura 1-7 muestra un dibujo del circuito resultante:

from qiskit.circuit.library import CXGate

qc = QuantumCircuit(2)
qc.h(1)
cx_gate = CXGate()
qc.append(cx_gate, [1,0])
qc.draw()
Figura 1-7. Ejemplo de circuito resultante del método append()
Nota

La clase CXGate (véase "CXGate") utilizada aquí es una de las puertas definidas en el paquete qiskit.circuit.library. Te aconsejamos que añadas las declaraciones import adecuadas a los fragmentos de código de este libro.

Utilizar el método bind_parameters()

El método bind_parameters() vincula parámetros (ver "Crear una instancia de parámetro") a un circuito cuántico. El siguiente fragmento de código crea un circuito en el que hay tres puertas de fase parametrizadas. Observa que los argumentos de los constructores Parameter de este fragmento de código son cadenas, en este caso unas que contienen caracteres theta. La Figura 1-8 muestra un dibujo del circuito:

from qiskit.circuit import QuantumCircuit,\
                           Parameter

theta1 = Parameter('θ1')
theta2 = Parameter('θ2')
theta3 = Parameter('θ3')

qc = QuantumCircuit(3)
qc.h([0,1,2])
qc.p(theta1,0)
qc.p(theta2,1)
qc.p(theta3,2)

qc.draw()
Figura 1-8. Ejemplo de circuito parametrizado

Para vincular los valores de los parámetros a un circuito nuevo, pasaremos un diccionario que contenga las referencias de los parámetros y los valores deseados al método bind_parameters(). El siguiente fragmento de código utiliza esta técnica, y la Figura 1-9 muestra el circuito enlazado en el que los parámetros de la puerta de fase se sustituyen por los valores suministrados:

b_qc = qc.bind_parameters({theta1: math.pi/8,
                          theta2: math.pi/4,
                          theta3: math.pi/2})

b_qc.draw()
Figura 1-9. Ejemplo de circuito enlazado con los valores de rotación de la puerta de fase suministrados

Utilizar el método componer()

El método compose() devuelve un nuevo circuito compuesto por el original y otro circuito. El siguiente fragmento de código utiliza el método compose(), y la Figura 1-10 muestra un dibujo del circuito resultante:

qc = QuantumCircuit(2,2)
qc.h(0)
another_qc = QuantumCircuit(2,2)
another_qc.cx(0,1)
bell_qc = qc.compose(another_qc)
bell_qc.draw()
Figura 1-10. Ejemplo de circuito resultante del método compose()

Ten en cuenta que un circuito pasado al método compose() puede tener menos hilos cuánticos o clásicos que el circuito original.

Utilizar el método copy()

El método copy() devuelve una copia del circuito original . El siguiente fragmento de código utiliza el método copy():

qc = QuantumCircuit(3)
qc.h([0,1,2])
new_qc = qc.copy()

Utilizar el método descomponer()

El método decompose() devuelve un nuevo circuito tras descomponer el circuito original un nivel. El siguiente fragmento de código utiliza el método decompose(). La Figura 1-11 muestra un dibujo del circuito resultante en el que las puertas S, H y X se descomponen en las operaciones más fundamentales de la puerta U (ver "Puerta U"):

qc = QuantumCircuit(2)
qc.h(0)
qc.s(0)
qc.x(1)
decomposed_qc = qc.decompose()
decomposed_qc.draw()
Figura 1-11. Ejemplo de circuito resultante del método decompose()

Utilizar el método from_qasm_file()

El método from_qasm_file() devuelve un nuevo circuito a partir de un archivo que contiene un programa en lenguaje ensamblador cuántico (OpenQASM). El siguiente fragmento de código utiliza el método from_qasm_file():

new_qc = QuantumCircuit.from_qasm_file("file.qasm")

Utilizar el método from_qasm_str()

El método from_qasm_str() devuelve un nuevo circuito a partir de una cadena que contiene un programa OpenQASM. El siguiente fragmento de código utiliza el método from_qasm_str(), y la Figura 1-12 muestra un dibujo del circuito resultante:

qasm_str = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
new_qc = QuantumCircuit.from_qasm_str(qasm_str)
new_qc.draw()
Figura 1-12. Ejemplo de circuito resultante del método from_qasm_str()

Utilizar el método inicializar()

El método initialize() inicializa los qubits de un circuito cuántico a un estado determinado y no es una operación unitaria. El código siguiente utiliza el métodoinitialize() y la Figura 1-13 muestra un dibujo del circuito resultante. En este fragmento de código, el circuito se inicializa al vector estado normalizado | 11 :

qc = QuantumCircuit(2)
qc.initialize([0, 0, 0, 1])
qc.draw()
Figura 1-13. Ejemplo de circuito resultante del método initialize()

Utilizar el método reset()

El método reset() restablece un qubit en un circuito cuántico a la posición | 0 y no es una operación unitaria. El siguiente fragmento de código utiliza el método reset(), y la Figura 1-14 muestra un dibujo del circuito resultante. Observa que el estado del qubit es | 1 antes de la operación de reinicio. Este método crea una instancia Reset (ver "Reinicio"):

qc = QuantumCircuit(1)
qc.x(0)
qc.reset(0)
qc.draw()
Figura 1-14. Ejemplo de circuito resultante de utilizar el método reset()

Utilizar el método qasm()

El método qasm() devuelve un programa OpenQASM que representa el circuito cuántico. El siguiente fragmento de código utiliza el método qasm(), y el Ejemplo 1-1 muestra el programa OpenQASM resultante:

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qasm_str = qc.qasm()
print(qasm_str)
Ejemplo 1-1. Programa OpenQASM resultante de utilizar el método qasm()
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];

Utilizar el método to_gate()

El método to_gate() crea una puerta personalizada (ver "La clase Puerta") a partir de un circuito cuántico. El siguiente fragmento de código crea un circuito que se convertirá en una puerta, y la Figura 1-15 muestra un dibujo del circuito:

anti_cnot_qc = QuantumCircuit(2)
anti_cnot_qc.x(0)
anti_cnot_qc.cx(0,1)
anti_cnot_qc.x(0)

anti_cnot_qc.draw()
Figura 1-15. Ejemplo de circuito que se convertirá en una puerta

Esta puerta personalizada implementará una puerta NOT anticontrol en la que la puerta X sólo se aplica cuando el qubit de control es | 0 . El siguiente fragmento de código crea un circuito que utiliza esta puerta personalizada, y la Figura 1-16 muestra un dibujo descompuesto de este circuito:

anti_cnot_gate = anti_cnot_qc.to_gate()

qc = QuantumCircuit(3)
qc.x([0,1,2])
qc.append(anti_cnot_gate, [0,2])

qc.decompose().draw()
Nota

Una puerta representa una operación unitaria. Para crear una operación personalizada que no sea unitaria, utiliza el método to_instruction() que se muestra en "Utilización del método to_instruction()".

Figura 1-16. Circuito descompuesto que utiliza una puerta creada por el método to_gate()

Utilizar el método to_instruction()

El método to_instruction() crea una instrucción personalizada (ver "La clase Instrucción") a partir de un circuito cuántico. El siguiente fragmento de código crea un circuito que se convertirá en una instrucción, y la Figura 1-17 muestra un dibujo del circuito:

reset_one_qc = QuantumCircuit(1)
reset_one_qc.reset(0)
reset_one_qc.x(0)

reset_one_qc.draw()
Figura 1-17. Ejemplo de circuito que se convertirá en una instrucción
Nota

Una instrucción representa una operación que no es necesariamente unitaria. Para crear una operación personalizada que sea unitaria, utiliza el método to_gate() que se muestra en "Utilización del método to_gate()".

Esta instrucción personalizada reiniciará un qubit y aplicará una puerta X, reiniciando en efecto el qubit al estado | 1 . El siguiente fragmento de código crea un circuito que utiliza esta instrucción personalizada, y la Figura 1-18 muestra un dibujo descompuesto de este circuito en :

reset_one_inst = reset_one_qc.to_instruction()

qc = QuantumCircuit(2)
qc.h([0,1])
qc.reset(0)
qc.append(reset_one_inst, [1])

qc.decompose().draw()
Figura 1-18. Circuito que utiliza una instrucción creada por el método to_instruction()

Guardar estado al ejecutar un circuito en AerSimulator

Cuando se ejecuta un circuito en un backend AerSimulator (ver "Uso de los simuladores Aer"), el estado del simulador puede guardarse en la instancia del circuito utilizando los métodos QuantumCircuit de la Tabla 1-5. Ten en cuenta que estos métodos están disponibles tras obtener un AerSimulator backend.

Tabla 1-5. Métodos utilizados para guardar el estado del simulador en una instancia de circuito
Nombre del método Descripción

save_state

Guarda el estado del simulador según convenga al método de simulación

save_density_matrix

Guarda el estado del simulador como una matriz de densidad

save_matrix_product_state

Guarda el estado del simulador como un tensor de estado producto de matrices

save_stabilizer

Guarda el estado del simulador como un estabilizador Clifford

save_statevector

Guarda el estado del simulador como un vector de estado

save_superop

Guarda el estado del simulador como una matriz de superoperadores del circuito de ejecución

save_unitary

Guarda el estado del simulador como una matriz unitaria del circuito de ejecución

Uso de la clase QuantumRegister

A veces resulta útil tratar grupos de hilos cuánticos o clásicos como una unidad. Por ejemplo, los qubits de control de las puertas CNOT del circuito cuántico expresado en el siguiente fragmento de código, así como en la Figura 1-19, esperan tres qubits en superposiciones iguales. El cable cuántico adicional del circuito se utiliza como zona de rascado cuya salida se desprecia:

from qiskit import QuantumRegister, \
                   ClassicalRegister

qr = QuantumRegister(3, 'q')
scratch = QuantumRegister(1, 'scratch')
cr = ClassicalRegister(3, 'c')
qc = QuantumCircuit(qr, scratch, cr)

qc.h(qr)
qc.x(scratch)
qc.h(scratch)
qc.cx(qr[0], scratch)
qc.cx(qr[2], scratch)
qc.barrier(qr)
qc.h(qr)
qc.measure(qr, cr)

qc.draw()
Figura 1-19. Ejemplo de circuito con QuantumRegister yClassicalRegister clases

Al definir un QuantumRegister formado por tres qubits, métodos como h(), barrier() y measure() pueden aplicarse a los tres hilos pasando una referencia QuantumRegister. Del mismo modo, definir un ClassicalRegister (ver "Uso de la clase ClassicalRegister") formado por tres bits permite al método measure() especificar los tres hilos clásicos pasando una referencia ClassicalRegister. Además, los nombres suministrados a los constructores QuantumRegister y ClassicalRegister se muestran en el dibujo del circuito.

Uso de los atributos de QuantumRegister

Los atributos QuantumRegister y utilizados habitualmente son name y size. Se enumeran en la Tabla 1-6. Ten en cuenta que la variable qr se refiere a una instancia de QuantumRegister.

Tabla 1-6. Algunos atributos de QuantumRegister
Nombres Ejemplo Notas

name

qr.name

Obtiene el nombre del registro cuántico

size

qr.size

Obtiene el número de hilos qubit del registro cuántico

Utilización de la clase ClassicalRegister

Consulta "Utilización de la clase QuantumRegister" para conocer las razones para utilizar la clase ClassicalRegister.

Uso de los atributos de ClassicalRegister

Los atributos ClassicalRegister y utilizados habitualmente son name y size. Se enumeran en la Tabla 1-7. Ten en cuenta que la variable cr se refiere a una instancia de ClassicalRegister.

Tabla 1-7. Algunos atributos de ClassicalRegister
Nombres Ejemplo Notas

name

cr.name

Obtiene el nombre del registro clásico

size

cr.size

Obtiene el número de hilos de bits en el registro clásico

Instrucciones y puertas

En Qiskit, todas las operaciones que pueden aplicarse a un circuito cuántico derivan de la clase Instruction. Las operaciones unitarias se derivan de la clase Gate, que es una subclase de Instruction. Las operaciones unitarias controladas se derivan de la clase ControlledGate, que es una subclase de Gate. Estas clases pueden utilizarse para definir nuevas instrucciones, puertas unitarias y puertas unitarias controladas, respectivamente.

La Clase de Instrucción

Las operaciones no unitarias de Qiskit (como Measure y Reset) son subclases directas de Instruction. Aunque es posible definir tus propias instrucciones personalizadas subclasificando Instruction, otra forma es utilizar el método to_instruction() de la clase QuantumCircuit (consulta un ejemplo de esto en "Utilización del método to_instruction()").

Los métodos de la clase Instruction incluyen copy(), repeat() y reverse_ops(). Se enumeran en la Tabla 1-8. Observa que la variable inst se refiere a una instancia de Instruction.

Tabla 1-8. Métodos más utilizados en la clase Instruction
Nombres Ejemplo Notas

copy

inst.copy("My inst")

Devuelve una copia de la instrucción, dando el nombre suministrado a la copia

repeat

inst.repeat(2)

Devuelve una instrucción con esta instrucción repetida un número determinado de veces

reverse_ops

inst.reverse_ops()

Devuelve una instrucción con sus operaciones en orden inverso

Los atributos más utilizados en la clase Instruction incluyen definition y params. Se enumeran en la Tabla 1-9. Observa que la variable inst se refiere a una instancia de Instruction.

Tabla 1-9. Atributos de uso común en la clase Instruction
Nombres Ejemplo Notas

definition

inst. definition

Devuelve la definición en términos de puertas básicas

params

inst.params

Obtiene los parámetros de la instrucción

La Clase Puerta

Las operaciones unitarias de Qiskit (como HGate y XGate) son subclases de Gate. Aunque es posible definir tus propias compuertas personalizadas subclasificando Gate, otra forma es utilizar el método to_gate() de la clase QuantumCircuit (consulta un ejemplo de ello en "Utilización del método to_gate()").

Los métodos más utilizados en la clase Gate incluyen los métodos Instruction enumerados en la Tabla 1-8, así como control(), inverse(), power() y to_matrix(). Todos ellos se enumeran en la Tabla 1-10. Observa que la variable gate se refiere a una instancia de Gate.

Tabla 1-10. Métodos más utilizados en la clase Gate
Nombres Ejemplo Notas

control

gate.control(1)

Dado un número de qubits de control, devuelve una versión controlada de la puerta

copy

gate.copy("My gate")

Devuelve una copia de la puerta, dando el nombre suministrado a la copia

inverse

gate.inverse()

Devuelve la inversa de la puerta

power

gate.power(2)

Devuelve la puerta elevada a una potencia de coma flotante dada

repeat

gate.repeat(3)

Devuelve una puerta con esta puerta repetida un número determinado de veces

reverse_ops

gate.reverse_ops()

Devuelve una puerta con sus operaciones en orden inverso

to_matrix

gate.to_matrix()

Devuelve una matriz para la matriz unitaria de la puerta

Los atributos de uso común en la clase Gate incluyen los atributos Instruction enumerados en la Tabla 1-9, así como label. Todos ellos se enumeran en la Tabla 1-11. Ten en cuenta que la variable gate se refiere a una instancia de Gate.

Tabla 1-11. Atributos de uso común en la clase Gate
Nombres Ejemplo Notas

definition

gate​.def⁠ini⁠tion

Devuelve la definición en términos de puertas básicas

label

gate.label

Obtiene la etiqueta de la instrucción

params

gate.params

Obtiene los parámetros de la instrucción

La clase ControlledGate

Las operaciones unitarias controladas en Qiskit (como CZGate y CCXGate) son subclases de ControlledGate, que es una subclase de Gate.

Los métodos más utilizados en la clase ControlledGate son los métodos Gate enumerados en la Tabla 1-10

Los atributos de uso común en la clase ControlledGate incluyen los atributos Gate enumerados en la Tabla 1-11, así como num_ctrl_qubits y ctrl_state.

Utilizar el atributo num_ctrl_qubits

El atributo num_ctrl_qubits contiene un entero que representa el número de qubits de control en una ControlledGate. El siguiente fragmento de código, cuya salida impresa sería 2, utiliza el atributo num_ctrl_qubits de una puerta Toffoli:

from qiskit.circuit.library import CCXGate

toffoli = CCXGate()
print(toffoli.num_ctrl_qubits)

Utilizar el método ctrl_state()

Un ControlledGate puede tener uno o más qubits de control, cada uno de los cuales puede ser, en realidad, qubits de control o de anticontrol (consulta el ejemplo de anticontrol en "Uso del método to_gate()"). El atributo ctrl_state contiene un número entero cuyo valor binario representa qué qubits son de control y cuáles son de anticontrol. En concreto, el dígito binario 1 representa un qubit de control, y el dígito binario 0 representa un qubit de anticontrol.

El atributo ctrl_state permite tanto acceder a su valor como modificarlo. El siguiente fragmento de código utiliza el atributo ctrl_state, en el que el valor binario 10 hace que el qubit de control superior sea un qubit de anticontrol. La Figura 1-20 muestra un dibujo del circuito resultante:

toffoli = CCXGate()
toffoli.ctrl_state = 2

toffoli.definition.draw()
Figura 1-20. Puerta de Toffoli con un qubit de control y un qubit de anticontrol

Definir una puerta controlada personalizada

Aunque es posible definir tus propias compuertas controladas personalizadas subclasificando ControlledGate, otra forma es seguir estos dos pasos:

  1. Crea una puerta personalizada con el método to_gate() de la clase QuantumCircuit (consulta un ejemplo en "Utilización del método to_gate()").

  2. Añade qubits de control a tu puerta personalizada utilizando elcontrol() que se muestra en la Tabla 1-10.

Seguiremos esos dos pasos para definir una puerta controlada personalizada que aplique una rotación de fase π/16 cuando sus dos qubits de control sean | 1 . En primer lugar, el siguiente fragmento de código define un circuito que contiene una puerta π/16 P y lo convierte en una puerta personalizada, y la Figura 1-21 muestra un dibujo de la puerta personalizada:

from qiskit import QuantumCircuit
import math

p16_qc = QuantumCircuit(1)
p16_qc.p(math.pi/16, 0)

p16_gate = p16_qc.to_gate()

p16_gate.definition.draw()
Figura 1-21. Dibujo personalizado de la puerta de fase π/16

En segundo lugar, el siguiente fragmento de código utiliza el método control() para crear un ControlledGate a partir de nuestra compuerta personalizada, y la Figura 1-22 muestra un dibujo de la compuerta controlada personalizada:

ctrl_p16 = p16_gate.control(2)

ctrl_p16.definition.draw()
Figura 1-22. Dibujo de la compuerta de fase π/16 controlada a medida

Aprovecharemos el método append() (ver "Uso del método append()") en el siguiente fragmento de código para utilizar nuestra puerta controlada personalizada en un circuito cuántico. La Figura 1-23 muestra un dibujo de el circuito:

qc = QuantumCircuit(4)
qc.h([0,1,2,3])
qc.append(ctrl_p16,[0,1,3])

qc.decompose().draw()
Figura 1-23. Circuito descompuesto que utiliza la puerta controlada personalizada

Circuitos cuánticos parametrizados

A veces es útil crear un circuito cuántico en el que se puedan suministrar valores en tiempo de ejecución. Esta capacidad está disponible en Qiskit mediante circuitos parametrizados, implementados en parte por las clases Parameter y ParameterVector.

Crear una instancia de parámetro

La clase Parameter se utiliza para representar un parámetro en un circuito cuántico. Consulta "Utilización del método bind_parameters()" para ver un ejemplo de definición y utilización de un circuito parametrizado. Como se muestra en ese ejemplo, se puede crear un parámetro suministrando una cadena unicode a su constructor de la siguiente manera:

theta1 = Parameter("θ1")

La referencia al objeto Parameter denominada theta1 puede utilizarse posteriormente en el método bind_parameters() o, alternativamente, en el método assign_parameters() de la clase QuantumCircuit.

Utilización de la clase ParameterVector

La clase ParameterVector puede aprovecharse para crear y utilizar parámetros como una colección en lugar de variables individuales. El siguiente fragmento de código crea un circuito en el que hay tres puertas de fase parametrizadas. La Figura 1-24 muestra un dibujo del circuito:

from qiskit.circuit import QuantumCircuit,\
                           ParameterVector

theta = ParameterVector('θ', 3)

qc = QuantumCircuit(3)
qc.h([0,1,2])
qc.p(theta[0],0)
qc.p(theta[1],1)
qc.p(theta[2],2)

qc.draw()
Figura 1-24. Ejemplo de circuito parametrizado que aprovecha ParameterVector

Para vincular los valores de los parámetros a un circuito nuevo, pasaremos un diccionario que contenga la referencia ParameterVector y la lista de valores deseada al método bind_parameters().

El siguiente fragmento de código muestra esta técnica, y la Figura 1-25 muestra el circuito límite en el que los parámetros de la puerta de fase se sustituyen por los valores suministrados por :

import math

b_qc = qc.bind_parameters({theta: [math.pi/8,
                                   math.pi/4,
                                   math.pi/2]})

b_qc.draw()
Figura 1-25. Ejemplo de circuito enlazado con los valores de rotación de la puerta de fase suministrados

Get Guía de bolsillo Qiskit now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.