Saltar al contenido principal

Bits cuánticos, puertas y circuitos

nota

Kifumi Numata (19 abr 2024)

Haz clic aquí para descargar el PDF de la clase original. Ten en cuenta que algunos fragmentos de código pueden estar desactualizados, ya que son imágenes estáticas.

Tiempo aproximado de QPU para este experimento: 5 segundos.

1. Introducción

Bits, puertas y circuitos son los componentes fundamentales de la computación cuántica. Aprenderás computación cuántica con el modelo de circuitos utilizando bits cuánticos y puertas, y además repasarás superposición, medición y entrelazamiento.

En esta lección aprenderás:

  • Puertas de un solo qubit
  • Esfera de Bloch
  • Superposición
  • Medición
  • Puertas de dos qubits y estados entrelazados

Al final de esta clase, aprenderás sobre la profundidad de circuito, que es crucial para la computación cuántica a escala de utilidad.

2. La computación como diagrama

Cuando utilizamos qubits o bits, necesitamos manipularlos para convertir las entradas existentes en las salidas necesarias. Para los programas más sencillos con muy pocos bits, es útil representar este proceso en un diagrama conocido como diagrama de circuito.

La figura de abajo a la izquierda muestra un ejemplo de un circuito clásico, y la figura de abajo a la derecha muestra un ejemplo de un circuito cuántico. En ambos casos, las entradas están a la izquierda y las salidas a la derecha, mientras que las operaciones se representan mediante símbolos. Los símbolos utilizados para las operaciones se denominan "puertas" principalmente por razones históricas.

"classical logic and quantum circuit"

3. Puerta cuántica de un solo qubit

3.1 Estado cuántico y esfera de Bloch

El estado de un qubit se representa como una superposición de 0|0\rangle y 1|1\rangle. Un estado cuántico arbitrario se escribe como

ψ=α0+β1|\psi\rangle =\alpha|0\rangle+ \beta|1\rangle

donde α\alpha y β\beta son números complejos tales que α2+β2=1|\alpha|^2+|\beta|^2=1.

0|0\rangle y 1|1\rangle son vectores en el espacio vectorial complejo bidimensional:

0=(10),1=(01)|0\rangle = \begin{pmatrix} 1 \\0 \end{pmatrix}, |1\rangle = \begin{pmatrix} 0\\1 \end{pmatrix}

Por lo tanto, un estado cuántico arbitrario también se representa como

ψ=α(10)+β(01)=(αβ)|\psi\rangle = \alpha\begin{pmatrix} 1 \\ 0 \end{pmatrix} + \beta\begin{pmatrix}0\\ 1 \end{pmatrix} = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}

De esto podemos ver que el estado de un bit cuántico es un vector unitario en un espacio de producto interno complejo bidimensional con una base ortonormal de 0|0\rangle y 1|1\rangle. Está normalizado a 1.

ψψ=(αβ)(αβ)=1\langle\psi|\psi\rangle = \begin{pmatrix} \alpha^* & \beta^* \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix} = 1

ψ=(αβ) |\psi\rangle =\begin{pmatrix} \alpha \\ \beta \end{pmatrix} también se denomina vector de estado (statevector).

Un estado cuántico de un solo qubit también se representa como

ψ=cosθ20+eiφsinθ21=((cosθ2eiφsinθ2))|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle =\left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix}\right)

donde θ\theta y φ\varphi son los ángulos de la esfera de Bloch en la siguiente figura.

Bloch sphere En las siguientes celdas de código, construiremos cálculos básicos a partir de los componentes en Qiskit. Construiremos un circuito vacío y luego añadiremos operaciones cuánticas, discutiendo las puertas y visualizando sus efectos. Puedes ejecutar la celda con "Shift" + "Enter". Primero importa las bibliotecas.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# Import the qiskit library
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
from qiskit_ibm_runtime import Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.visualization import plot_histogram

Preparación del circuito cuántico

Creamos y dibujamos un circuito de un solo qubit.

# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)

# Draw the circuit
qc.draw("mpl")

Output of the previous code cell

Puerta X

La puerta X es una rotación de π\pi alrededor del eje xx de la esfera de Bloch. Aplicar la puerta X a 0|0\rangle produce 1|1\rangle, y aplicar la puerta X a 1|1\rangle produce 0|0\rangle, por lo que es una operación similar a la puerta clásica NOT y también se conoce como inversión de bit. La representación matricial de la puerta X se muestra a continuación.

X=(0110)X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}
qc = QuantumCircuit(1)  # Prepare the single-qubit quantum circuit

# Apply a X gate to qubit 0
qc.x(0)

# Draw the circuit
qc.draw("mpl")

Output of the previous code cell

En IBM Quantum®, el estado inicial está establecido en 0|0\rangle, por lo que el circuito cuántico anterior en representación matricial es

X0=(0110)(10)=(01)=1X|0\rangle= \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} =\begin{pmatrix} 0 \\ 1 \end{pmatrix} = |1\rangle

A continuación, ejecutamos este circuito con un simulador de vector de estado.

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))

Output of the previous code cell

El vector vertical se muestra como un vector fila, con números complejos (la parte imaginaria se indica con jj).

Puerta H

La puerta Hadamard es una rotación de π\pi alrededor de un eje a medio camino entre los ejes xx y zz en la esfera de Bloch. Aplicar la puerta H a 0|0\rangle produce un estado de superposición como 0+12\frac{|0\rangle + |1\rangle}{\sqrt{2}}. La representación matricial de la puerta H se muestra a continuación.

H=12(1111)H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}
qc = QuantumCircuit(1)  # Create the single-qubit quantum circuit

# Apply an Hadamard gate to qubit 0
qc.h(0)

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))

Output of the previous code cell

Esto es

H0=12(1111)(10)=12(11)=(0.7070.707)=12(0+1)H|0\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\0 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\ 1 \end{pmatrix} =\begin{pmatrix} 0.707 \\ 0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)

Este estado de superposición es tan frecuente e importante que tiene su propio símbolo:

+12(0+1).|+\rangle \equiv \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle).

Al aplicar la puerta HH a 0|0\rangle, hemos creado una superposición de 0|0\rangle y 1|1\rangle, donde una medición en la base computacional (a lo largo de z en la imagen de la esfera de Bloch) produce cada estado con igual probabilidad.

Estado |-\rangle

Quizás hayas adivinado que existe un estado |-\rangle correspondiente:

012.|-\rangle \equiv \frac{|0\rangle -|1\rangle}{\sqrt{2}}.

Para crear este estado, aplica primero una puerta X para obtener 1|1\rangle, y luego una puerta H.

qc = QuantumCircuit(1)  # Create the single-qubit quantum circuit

# Apply a X gate to qubit 0
qc.x(0)

# Apply an Hadamard gate to qubit 0
qc.h(0)

# draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([ 0.70710678+0.j, -0.70710678+0.j],
dims=(2,))

Output of the previous code cell

Esto es

H1=12(11 11)(0 1)=12(1 1)=(0.707 0.707)=12(01)=H|1\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\\ 1 & -1 \end{pmatrix} \begin{pmatrix} 0 \\\ 1 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\\ -1 \end{pmatrix} =\begin{pmatrix} 0.707 \\\ -0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle) = |-\rangle

Aplicar la puerta HH a 1|1\rangle produce una superposición uniforme de 0|0\rangle y 1|1\rangle, pero el signo de 1|1\rangle es negativo.

3.2 Estado cuántico de un solo qubit y evolución unitaria

Los efectos de todas las puertas que hemos visto hasta ahora han sido unitarios, lo que significa que pueden representarse mediante un operador unitario. En otras palabras, el estado de salida puede obtenerse aplicando una matriz unitaria al estado inicial:

ψ=Uψ|\psi^{'}\rangle = U|\psi\rangle

Una matriz unitaria es una matriz que satisface la siguiente condición

UU=UU=I.U^{\dagger}U =U U^{\dagger} = I.

En términos del funcionamiento de los computadores cuánticos, diríamos que aplicar una puerta cuántica al qubit hace evolucionar el estado cuántico. Las puertas comunes de un solo qubit incluyen las siguientes.

Puertas de Pauli:

X=(0110)=01+10X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix} = |0\rangle \langle 1|+|1\rangle \langle 0| Y=(0ii0)=i01+i10Y = \begin{pmatrix} 0 & -i \\ i & 0 \\ \end{pmatrix} = -i|0\rangle \langle 1|+i|1\rangle \langle 0| Z=(1001)=0011Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \\ \end{pmatrix} = |0\rangle \langle 0|-|1\rangle \langle 1|

donde el producto externo se calculó de la siguiente manera:

00=[10][10]=[1000],10=[01][10]=[0010],|0\rangle \langle 0|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 0 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 0|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 1 & 0 \\ \end{bmatrix}, \quad 01=[10][01]=[0100],11=[01][01]=[0001],|0\rangle \langle 1|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 1 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 1|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 0 & 1 \\ \end{bmatrix}, \quad

Otras puertas típicas de un solo qubit:

H=12[1111],S=[100i],T=[100exp(iπ/4)]H= \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \\ \end{bmatrix},\quad S = \begin{bmatrix} 1 & 0 \\ 0 & i \\ \end{bmatrix}, \quad T = \begin{bmatrix} 1 & 0 \\ 0 & exp(i\pi/4) \\ \end{bmatrix} Rx(θ)=eiθX/2=cosθ2Iisinθ2X=[cosθ2isinθ2isinθ2cosθ2]R_x(\theta) = e^{-i\theta X/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}X = \begin{bmatrix} cos\frac{\theta}{2} & -i sin \frac{\theta}{2} \\ -i sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Ry(θ)=eiθY/2=cosθ2Iisinθ2Y=[cosθ2sinθ2sinθ2cosθ2]R_y(\theta) = e^{-i\theta Y/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Y = \begin{bmatrix} cos\frac{\theta}{2} & - sin \frac{\theta}{2} \\ sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Rz(θ)=eiθZ/2=cosθ2Iisinθ2Z=[eiθ/200eiθ/2]R_z(\theta) = e^{-i\theta Z/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Z = \begin{bmatrix} e^{-i\theta /2} & 0 \\ 0 & e^{i\theta /2} \\ \end{bmatrix}

El significado y uso de estas puertas se describe con más detalle en el curso Fundamentos de la información cuántica.

Ejercicio 1

Usa Qiskit para crear circuitos cuánticos que preparen los estados descritos a continuación. Luego ejecuta cada circuito con el simulador de vector de estado y muestra el estado resultante en la esfera de Bloch. Como bonus: intenta predecir cuál debería ser el estado final basándote en la intuición sobre las puertas y rotaciones en la esfera de Bloch.

(1) XX0XX|0\rangle

(2) HH0HH|0\rangle

(3) HZH0HZH|0\rangle

Consejos: La puerta Z se puede usar de la siguiente manera

qc.z(0)

Solución:

### (1) XX|0> ###

# Create the single-qubit quantum circuit
qc = QuantumCircuit(1) ##your code goes here##

# Add a X gate to qubit 0
qc.x(0) ##your code goes here##

# Add a X gate to qubit 0
qc.x(0) ##your code goes here##

# Draw a circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))

Output of the previous code cell

### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))

Output of the previous code cell

### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))

Output of the previous code cell

3.3 Medición

La medición es teóricamente un tema muy complicado. Pero en términos prácticos, una medición a lo largo de zz (como hacen todos los computadores cuánticos de IBM®) simplemente fuerza el estado del qubit α0+β1(s.t.α2+β2=1)\alpha|0\rangle+\beta|1\rangle \quad (s.t.|\alpha|^2+|\beta|^2=1) a 0|0\rangle o 1,|1\rangle, y observamos el resultado.

  • α2|\alpha|^2 es la probabilidad de obtener 0|0\rangle al medir.
  • β2|\beta|^2 es la probabilidad de obtener 1|1\rangle al medir.

Por lo tanto, α\alpha y β\beta se denominan amplitudes de probabilidad. (ver "regla de Born")

Por ejemplo, 220+221\frac{\sqrt{2}}{2}|0\rangle+\frac{\sqrt{2}}{2}|1\rangle tiene igual probabilidad de convertirse en 0|0\rangle o 1|1\rangle al medir. 32012i1\frac{\sqrt{3}}{2}|0\rangle-\frac{1}{2}i|1\rangle tiene un 75% de probabilidad de convertirse en 0|0\rangle.

Simulador Qiskit Aer

A continuación, medimos un circuito que prepara la superposición anterior con igual probabilidad. Debemos añadir las puertas de medición, ya que el simulador Qiskit Aer simula por defecto hardware cuántico ideal (sin ruido). Nota: El simulador Aer también puede aplicar un modelo de ruido basado en un computador cuántico real. Volveremos a los modelos de ruido más adelante.

# Create a new circuit with one qubits (first argument) and one classical bits (second argument)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0) # Add the measurement gate

qc.draw(output="mpl")

Output of the previous code cell

Ahora estamos listos para ejecutar nuestro circuito en el simulador Aer. En este ejemplo usamos la configuración predeterminada shots=1024, lo que significa que medimos 1024 veces. Luego representamos estos conteos en un histograma.

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'0': 521, '1': 503}

Output of the previous code cell

Vemos que los 0s y 1s se midieron cada uno con una probabilidad de casi el 50%. Aunque no se simuló ruido aquí, los estados siguen siendo probabilísticos. Aunque esperamos aproximadamente una distribución 50-50, rara vez la encontraremos exactamente. Al igual que 100 lanzamientos de moneda rara vez darán exactamente 50 instancias de cada lado.

4. Puerta cuántica multi-qubit y entrelazamiento

4.1 Circuito cuántico multi-qubit

Podemos crear un circuito cuántico de dos qubits con el siguiente código. Aplicaremos una puerta H a cada qubit.

# Create the two qubits quantum circuit
qc = QuantumCircuit(2)

# Apply an H gate to qubit 0
qc.h(0)

# Apply an H gate to qubit 1
qc.h(1)

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j],
dims=(2, 2))

Nota: Ordenamiento de bits en Qiskit

Qiskit utiliza la notación Little-Endian al ordenar qubits y bits, lo que significa que el qubit 0 es el bit más a la derecha en las cadenas de bits. Ejemplo: 01|01\rangle significa que q0 es 1|1\rangle y q1 es 0|0\rangle. Ten cuidado, porque parte de la literatura de computación cuántica utiliza la notación Big-Endian (el qubit 0 es el bit más a la izquierda), y gran parte de la literatura de mecánica cuántica también.

Además, cabe señalar que al representar un circuito cuántico, q0|q_0\rangle siempre se coloca en la parte superior del circuito. Con este conocimiento, el estado cuántico del circuito anterior puede escribirse como un producto tensorial de estados cuánticos individuales de cada qubit.

q1q0=(a0+b1)(c0+d1)|q1\rangle \otimes|q0\rangle = (a|0\rangle+b|1\rangle) \otimes (c|0\rangle+d|1\rangle)

=ac00+ad01+bc10+bd11= ac|0\rangle|0\rangle+ad|0\rangle|1\rangle+bc|1\rangle|0\rangle+bd|1\rangle|1\rangle

=ac00+ad01+bc10+bd11= ac|00\rangle+ad|01\rangle+bc|10\rangle+bd|11\rangle

( ac2+ad2+bc2+bd2=1|ac|^2+ |ad|^2+ |bc|^2+ |bd|^2=1 )

El estado inicial de Qiskit es 00=00|0\rangle|0\rangle=|00\rangle, por lo que cambia a un estado de superposición uniforme al aplicar HH a cada qubit.

H0H0=12(0+1)12(0+1)=12(00+01+10+11)H|0\rangle \otimes H|0\rangle=\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) \otimes \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) = \frac{1}{2}(|00\rangle+|01\rangle+|10\rangle+|11\rangle)

=12((11)(11))=12(1111)=12((1000)+(0100)+(0010)+(0001))=\frac{1}{2}\left( \begin{pmatrix} 1 \\ 1 \end{pmatrix} \otimes \begin{pmatrix} 1 \\ 1 \end{pmatrix}\right) = \frac{1}{2}\begin{pmatrix} 1 \\ 1 \\ 1 \\ 1 \end{pmatrix}=\frac{1}{2}\left(\begin{pmatrix} 1 \\ 0 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}\right)

La regla de medición es la misma que en el caso de un solo qubit: la probabilidad de medir 00|00\rangle es ac2|ac|^2.

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Output of the previous code cell

A continuación, medimos este circuito.

# Create a new circuit with two qubits (first argument) and two classical bits (second argument)
qc = QuantumCircuit(2, 2)

# Apply the gates
qc.h(0)
qc.h(1)

# Add the measurement gates
qc.measure(0, 0) # Measure qubit 0 and save the result in bit 0
qc.measure(1, 1) # Measure qubit 1 and save the result in bit 1

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

Ahora utilizaremos nuevamente el simulador Aer para verificar experimentalmente que las probabilidades relativas de todos los posibles estados de salida son aproximadamente iguales.

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'10': 262, '01': 246, '00': 265, '11': 251}

Output of the previous code cell

Como se esperaba, los estados 00|00\rangle, 01|01\rangle, 10|10\rangle, 11|11\rangle se midieron cada uno con casi el 25%.

4.2 Puertas cuánticas multi-qubit

Puerta CNOT

Una puerta CNOT ("controlled NOT" o CX) es una puerta de dos qubits cuya acción involucra dos qubits simultáneamente: el qubit de control y el qubit objetivo. Una CNOT invierte el qubit objetivo solo cuando el qubit de control es 1|1\rangle.

Entrada (Objetivo,Control)Salida (Objetivo,Control)
0000
0111
1010
1101

Primero simulemos el efecto de esta puerta de dos qubits cuando q0 y q1 son ambos 0|0\rangle, y obtengamos el vector de estado de salida. La sintaxis de Qiskit utilizada es qc.cx(control qubit, target qubit).

# Create a circuit with two quantum registers and two classical registers
qc = QuantumCircuit(2, 2)

# Apply the CNOT (cx) gate to a |00> state.
qc.cx(0, 1) # Here the control is set to q0 and the target is set to q1.

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
dims=(2, 2))

Como se esperaba, aplicar una puerta CNOT a 00|00\rangle no cambió el estado, ya que el qubit de control estaba en el estado 0|0\rangle. Volvamos a nuestra operación CNOT. Esta vez aplicamos una puerta CNOT a 01|01\rangle y veamos qué sucede.

qc = QuantumCircuit(2, 2)

# q0=1, q1=0
qc.x(0) # Apply a X gate to initialize q0 to 1
qc.cx(0, 1) # Set the control bit to q0 and the target bit to q1.

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2))

Al aplicar una puerta CNOT, el estado 01|01\rangle se ha convertido en 11|11\rangle.

Verifiquemos estos resultados ejecutando el circuito en un simulador.

# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)

# Draw the circuit
qc.draw(output="mpl")

Output of the previous code cell

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'11': 1024}

Output of the previous code cell

Los resultados deberían mostrar que 11|11\rangle se midió con un 100% de probabilidad.

4.3 Entrelazamiento cuántico y ejecución en un dispositivo cuántico real

Comencemos introduciendo un estado entrelazado específico que es particularmente importante en la computación cuántica, y luego definamos el término "entrelazado":

1200+1211\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle

y este estado se denomina estado de Bell.

Un estado entrelazado es un estado ψAB|\psi_{AB}\rangle, que consiste en los estados cuánticos ψA|\psi_A\rangle y ψB|\psi_B\rangle, que no puede representarse como un producto tensorial de estados cuánticos individuales.

Si ψAB|\psi_{AB}\rangle a continuación tiene dos estados ψA|\psi\rangle_A y ψB|\psi\rangle_B;

ψAB=12(00+11)=12(0A0B+1A1B)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) = \frac{1}{\sqrt{2}}(|0\rangle_A|0\rangle_B +|1\rangle_A|1\rangle_B) ψA=a00+a11|\psi\rangle_A = a_0|0\rangle+a_1|1\rangle ψB=b00+b11|\psi\rangle_B = b_0|0\rangle+b_1|1\rangle

el producto tensorial de estos dos estados es el siguiente

ψAψB=a0b000+a0b101+a1b010+a1b111|\psi\rangle _A\otimes |\psi\rangle _B = a_0 b_0|00\rangle+a_0 b_1|01\rangle+a_1 b_0|10\rangle+a_1 b_1|11\rangle

pero no existen coeficientes a0,a1,b0,a_0, a_1, b_0, y b1b_1 que satisfagan estas dos ecuaciones. Por lo tanto, ψAB|\psi_{AB}\rangle no se representa como un producto tensorial de estados cuánticos individuales ψA|\psi\rangle_A y ψB|\psi\rangle_B, y esto significa que ψAB=12(00+11)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) es un estado entrelazado.

Creemos el estado de Bell y ejecutémoslo en un computador cuántico real. Ahora seguimos los cuatro pasos para escribir un programa cuántico, denominados Qiskit Patterns:

  1. Mapear el problema a circuitos cuánticos y operadores
  2. Optimizar para el hardware objetivo
  3. Ejecutar en el hardware objetivo
  4. Postprocesar los resultados

Paso 1. Mapear el problema a circuitos cuánticos y operadores

En un programa cuántico, los circuitos cuánticos son el formato nativo para representar instrucciones cuánticas. Al crear un circuito, normalmente creas un nuevo objeto QuantumCircuit y luego añades instrucciones secuencialmente.

La siguiente celda de código crea un circuito que genera un estado de Bell, el estado entrelazado específico de dos qubits descrito anteriormente.

qc = QuantumCircuit(2, 2)

qc.h(0)
qc.cx(0, 1)

qc.measure(0, 0)
qc.measure(1, 1)

qc.draw("mpl")

Output of the previous code cell

Paso 2. Optimizar para el hardware objetivo

Qiskit convierte circuitos abstractos en circuitos QISA (Quantum Instruction Set Architecture) que tienen en cuenta las restricciones del hardware objetivo, y optimiza el rendimiento de los circuitos. Antes de la optimización, establecemos el hardware objetivo. Si no tienes qiskit-ibm-runtime, primero debes instalarlo. Puedes encontrar más información sobre Qiskit Runtime en la referencia de la API.

# Install
# !pip install qiskit-ibm-runtime

Establecemos el hardware objetivo.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
service.backends()
# You can specify the device
# backend = service.backend('ibm_kingston')
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)

La transpilación del circuito es otro proceso complejo. En resumen: reescribe el circuito en uno lógicamente equivalente que utiliza "puertas nativas" (puertas que un computador cuántico específico puede implementar), y mapea los qubits de tu circuito a qubits reales óptimos en el computador cuántico objetivo. Puedes encontrar más información sobre la transpilación en esta documentación.

# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
target_circuit = pm.run(qc)

target_circuit.draw("mpl", idle_wires=False)

Puedes ver que el circuito fue reescrito con nuevas puertas durante la transpilación. Para más información, consulta la documentación de ECRGate.

Paso 3. Ejecutar el circuito objetivo

Ahora ejecutamos el circuito objetivo en el dispositivo real.

sampler = Sampler(backend)
job_real = sampler.run([target_circuit])

job_id = job_real.job_id()
print("job id:", job_id)

La ejecución en el dispositivo real puede requerir un tiempo de espera en cola, ya que los computadores cuánticos son recursos valiosos y tienen una gran demanda. El job_id se utiliza para verificar el estado de ejecución y los resultados del trabajo posteriormente.

# Check the job status (replace the job id below with your own)
job_real.status(job_id)

También puedes verificar el estado del trabajo a través de tu panel de IBM Quantum: https://quantum.cloud.ibm.com/workloads

# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
job_real = service.job(job_id) # Input your job-id between the quotations
job_real.status()
# Execute after job has successfully run
result_real = job_real.result()
print(result_real[0].data.c.get_counts())

Paso 4. Postprocesar los resultados

Finalmente, necesitamos postprocesar nuestros resultados para crear salidas en el formato esperado, como valores o gráficos.

plot_histogram(result_real[0].data.c.get_counts())

Como puedes ver, 00|00\rangle y 11|11\rangle se observan con mayor frecuencia. Hay algunos resultados que se desvían de los datos esperados, y estos se deben al ruido y la decoherencia de los qubits. Aprenderemos más sobre errores y ruido en los computadores cuánticos en lecciones posteriores de este curso.

4.4 Estado GHZ

El concepto de entrelazamiento puede extenderse a sistemas con más de dos qubits. El estado GHZ (estado Greenberger-Horne-Zeilinger) es un estado máximamente entrelazado de tres o más qubits. El estado GHZ para tres qubits se define como

12(000+111)\frac{1}{\sqrt 2}(|000\rangle + |111\rangle)

Se puede crear con el siguiente circuito cuántico.

qc = QuantumCircuit(3, 3)

qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)

qc.measure(0, 0)
qc.measure(1, 1)
qc.measure(2, 2)

qc.draw("mpl")

Output of the previous code cell

La "profundidad" de un circuito cuántico es una métrica útil y común para describir circuitos cuánticos. Sigue un camino a través del circuito cuántico de izquierda a derecha, cambiando de qubits solo cuando están conectados por una puerta multi-qubit. Cuenta el número de puertas a lo largo de ese camino. El número máximo de puertas para cualquier camino de este tipo a través de un circuito es la profundidad. En los computadores cuánticos ruidosos modernos, los circuitos con poca profundidad tienen menos errores y probablemente producen buenos resultados. Los circuitos muy profundos, en cambio, no.

Con QuantumCircuit.depth() podemos verificar la profundidad de nuestro circuito cuántico. La profundidad del circuito anterior es 4. El qubit superior solo tiene tres puertas incluyendo la medición. Pero hay un camino desde el qubit superior hasta el qubit 1 o el qubit 2 que incluye una puerta CNOT adicional.

qc.depth()
4

Ejercicio 2

El estado GHZ de un sistema de 8 qubits es

12(00000000+11111111)\frac{1}{\sqrt 2}(|00000000\rangle + |11111111\rangle)

Escribe código para preparar este estado con el circuito más plano posible. La profundidad del circuito cuántico más plano es 5, incluyendo las puertas de medición.

Solución:

# Step 1
qc = QuantumCircuit(8, 8)

##your code goes here##
qc.h(0)
qc.cx(0, 4)
qc.cx(4, 6)
qc.cx(6, 7)

qc.cx(4, 5)

qc.cx(0, 2)
qc.cx(2, 3)

qc.cx(0, 1)
qc.barrier() # for visual separation

# measure
for i in range(8):
qc.measure(i, i)

qc.draw("mpl")
# print(qc.depth())

Output of the previous code cell

print(qc.depth())
5
from qiskit.visualization import plot_histogram
# Step 2
# For this exercise, the circuit and operators are simple, so no optimizations are needed.

# Step 3
# Run the circuit on a simulator to get the results
backend = AerSimulator()

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

sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=1024)
result = job.result()

counts = result[0].data.c.get_counts()
print(counts)

# Step 4
# Plot the counts in a histogram

plot_histogram(counts)
{'11111111': 535, '00000000': 489}

Output of the previous code cell

5. Resumen

Has aprendido computación cuántica con el modelo de circuitos utilizando bits cuánticos y puertas, y has repasado superposición, medición y entrelazamiento. También has aprendido el método para ejecutar el circuito cuántico en un dispositivo cuántico real.

En el último ejercicio de creación de un circuito GHZ, intentaste reducir la profundidad del circuito, que es un factor importante para lograr una solución a escala de utilidad en un computador cuántico ruidoso. En lecciones posteriores de este curso, aprenderás más sobre el ruido y los métodos de mitigación de errores en detalle. En esta lección, como introducción, consideramos la reducción de la profundidad del circuito en un dispositivo ideal, pero en la realidad necesitamos tener en cuenta las restricciones del dispositivo real, como la conectividad de los qubits. Aprenderás más sobre esto en las siguientes lecciones de este curso.

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'