Saltar al contenido principal

Implementación en Qiskit

En esta sección veremos algunas implementaciones en Qiskit de los conceptos introducidos en esta lección. Si deseas ejecutar estas implementaciones por tu cuenta, lo cual es muy recomendable, consulta la página Instalar Qiskit en la documentación de IBM Quantum para obtener detalles sobre cómo configurar Qiskit.

Hay que tener en cuenta que Qiskit está en continuo desarrollo y se centra principalmente en maximizar el rendimiento de las computadoras cuánticas con las que trabaja, las cuales también siguen evolucionando. Como resultado, Qiskit está sujeto a cambios que ocasionalmente pueden provocar la obsolescencia de código. Con esto en mente, siempre ejecutaremos los siguientes comandos antes de presentar ejemplos de código Qiskit en este curso, para que quede claro qué versión de Qiskit se ha utilizado. A partir de Qiskit v1.0, esta es una forma sencilla de ver qué versión de Qiskit está instalada actualmente.

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

print(__version__)
2.1.1

Si estás ejecutando esto en un entorno Python basado en la nube, puede que necesites instalar algunos de los siguientes paquetes:

#!pip install qiskit
#!pip install jupyter
#!pip install sympy
#!pip install matplotlib
#!pip install pylatexenc

Vectores y matrices en Python

Qiskit utiliza el lenguaje de programación Python, por lo que antes de hablar específicamente de Qiskit, puede ser útil repasar brevemente los cálculos con matrices y vectores en Python.

En Python, los cálculos con matrices y vectores se pueden realizar usando la clase array de la biblioteca NumPy, que ofrece funcionalidades para muchos cálculos numéricos y científicos. El siguiente código carga esta biblioteca, define dos vectores columna, ket0 y ket1, correspondientes a los vectores de estado del qubit 0\vert 0\rangle y 1\vert 1\rangle, y luego imprime su promedio.

import numpy as np

ket0 = np.array([[1], [0]])
ket1 = np.array([[0], [1]])

print(ket0 / 2 + ket1 / 2)
[[0.5]
[0.5]]

También podemos usar array para crear matrices que representen operaciones.

M1 = np.array([[1, 1], [0, 0]])
M2 = np.array([[1, 0], [0, 1]])
M = M1 / 2 + M2 / 2
print(M)
[[1.  0.5]
[0. 0.5]]

Ten en cuenta que se espera que todo el código que aparece dentro de una lección de este curso se ejecute de forma secuencial. Por ello, no es necesario importar NumPy de nuevo aquí, ya que ya ha sido importado.

La multiplicación de matrices, incluida la multiplicación matriz-vector como caso especial, se puede realizar usando la función matmul de NumPy.

print(np.matmul(M1, ket1))
print(np.matmul(M1, M2))
print(np.matmul(M, M))
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

Este formato de salida deja algo que desear visualmente. Una solución, para situaciones que requieren algo más estético, es usar la función array_to_latex de Qiskit, del módulo qiskit.visualization. Ten en cuenta que, en el código que sigue, estamos usando la función genérica display de Python. En contraste, el comportamiento específico de print puede variar según lo que se imprima, como ocurre con los arreglos definidos por NumPy.

from qiskit.visualization import array_to_latex

display(array_to_latex(np.matmul(M1, ket1)))
display(array_to_latex(np.matmul(M1, M2)))
display(array_to_latex(np.matmul(M, M)))
[10] \begin{bmatrix} 1 \\ 0 \\ \end{bmatrix} [1100] \begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix} [134014] \begin{bmatrix} 1 & \frac{3}{4} \\ 0 & \frac{1}{4} \\ \end{bmatrix}

Estados, mediciones y operaciones

Qiskit incluye varias clases que permiten crear y manipular estados, mediciones y operaciones — por lo que no es necesario programar desde cero todo lo necesario para simular estados cuánticos, mediciones y operaciones en Python. A continuación se incluyen algunos ejemplos para ayudarte a comenzar.

Definir y mostrar vectores de estado

La clase Statevector en Qiskit proporciona funcionalidades para definir y manipular vectores de estado cuántico. En el código que sigue, se importa la clase Statevector y se definen algunos vectores. (También importamos la función sqrt de la biblioteca NumPy para calcular una raíz cuadrada. Esta función podría, alternativamente, llamarse como np.sqrt siempre que NumPy ya haya sido importado, como se hizo anteriormente; esta es simplemente otra forma de importar y usar solo esta función específica.)

from qiskit.quantum_info import Statevector
from numpy import sqrt

u = Statevector([1 / sqrt(2), 1 / sqrt(2)])
v = Statevector([(1 + 2.0j) / 3, -2 / 3])
w = Statevector([1 / 3, 2 / 3])

La clase Statevector incluye un método draw para mostrar vectores de estado de diversas formas, entre ellas text para texto sin formato, latex para LaTeX renderizado y latex_source para código LaTeX, lo cual puede ser útil para copiar y pegar en documentos. (Usa print en lugar de display para mostrar código LaTeX con mejores resultados.)

display(u.draw("text"))
display(u.draw("latex"))
print(u.draw("latex_source"))
[0.70710678+0.j,0.70710678+0.j]

220+221\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

La clase Statevector también incluye el método is_valid, que verifica si un vector dado es un vector de estado cuántico válido (es decir, que tiene norma euclidiana igual a 1):

display(u.is_valid())
display(w.is_valid())
True
False

Simular mediciones usando Statevector

A continuación veremos una forma de simular mediciones de estados cuánticos en Qiskit, usando el método measure de la clase Statevector. Usaremos el mismo vector de estado del qubit v definido anteriormente.

display(v.draw("latex"))

(13+2i3)0231(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

Al ejecutar el método measure se simula una medición en la base estándar. Devuelve el resultado de esa medición, más el nuevo vector de estado cuántico del sistema tras la medición. (Aquí usamos la función print de Python con el prefijo f para impresión formateada con expresiones embebidas.)

outcome, state = v.measure()
print(f"Measured: {outcome}\nPost-measurement state:")
display(state.draw("latex"))
Measured: 1
Post-measurement state:

1- |1\rangle

Los resultados de las mediciones son probabilísticos, por lo que este método puede devolver resultados diferentes cuando se ejecuta varias veces. Para el ejemplo particular del vector v definido anteriormente, el método measure define el vector de estado cuántico tras la medición como

(1+2i5)0\biggl(\frac{1 + 2i}{\sqrt{5}}\biggr) \vert 0\rangle

(en lugar de 0\vert 0\rangle) o

1- \vert 1\rangle

(en lugar de 1\vert 1\rangle), dependiendo del resultado de la medición. En ambos casos, las alternativas a 0\vert 0\rangle y 1\vert 1\rangle son, de hecho, equivalentes a estos vectores de estado; se dice que son equivalentes salvo una fase global porque uno es igual al otro multiplicado por un número complejo en el círculo unitario. Este tema se discute con más detalle en la lección de Circuitos cuánticos y puede ignorarse por ahora.

Statevector lanzará un error si el método measure se aplica a un vector de estado cuántico no válido.

Statevector también incluye el método sample_counts, que permite simular cualquier número de mediciones sobre el sistema, comenzando cada vez con una copia nueva del estado. Por ejemplo, el siguiente código muestra el resultado de medir el vector v 10001000 veces, lo que (con alta probabilidad) produce el resultado 00 aproximadamente 55 de cada 99 veces (o alrededor de 556556 de los 10001000 ensayos) y el resultado 11 aproximadamente 44 de cada 99 veces (o alrededor de 444444 de los 10001000 ensayos). El código que sigue también muestra el uso de la función plot_histogram del módulo qiskit.visualization para visualizar los resultados.

from qiskit.visualization import plot_histogram

statistics = v.sample_counts(1000)
plot_histogram(statistics)

Output of the previous code cell

Ejecutar este código varias veces por tu cuenta, con diferentes números de muestras en lugar de 10001000, puede ser útil para desarrollar cierta intuición sobre cómo el número de ensayos influye en cuántas veces aparece cada resultado. Con más y más muestras, la fracción de muestras para cada posibilidad tiende a acercarse cada vez más a la probabilidad correspondiente. Este fenómeno, en términos más generales, se conoce como la ley de los grandes números en la teoría de probabilidades.

Realizar operaciones con Operator y Statevector

Las operaciones unitarias se pueden definir en Qiskit usando la clase Operator, como en el ejemplo que sigue. Esta clase incluye un método draw con argumentos similares a los de Statevector. Ten en cuenta que la opción latex produce resultados equivalentes a array_to_latex.

from qiskit.quantum_info import Operator

Y = Operator([[0, -1.0j], [1.0j, 0]])
H = Operator([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]])
S = Operator([[1, 0], [0, 1.0j]])
T = Operator([[1, 0], [0, (1 + 1.0j) / sqrt(2)]])

display(T.draw("latex"))
[10022+2i2] \begin{bmatrix} 1 & 0 \\ 0 & \frac{\sqrt{2}}{2} + \frac{\sqrt{2} i}{2} \\ \end{bmatrix}

Podemos aplicar una operación unitaria a un vector de estado usando el método evolve.

v = Statevector([1, 0])

v = v.evolve(H)
v = v.evolve(T)
v = v.evolve(H)
v = v.evolve(S)
v = v.evolve(Y)

display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Una vista previa de los circuitos cuánticos

Los circuitos cuánticos no se introducirán formalmente hasta la lección de Circuitos cuánticos, que es la tercera lección de este curso, pero podemos experimentar desde ya con la composición de operaciones unitarias sobre qubits usando la clase QuantumCircuit en Qiskit. En particular, podemos definir un circuito cuántico (que, en este caso, será simplemente una secuencia de operaciones unitarias realizadas sobre un único qubit) de la siguiente manera.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(1)

circuit.h(0)
circuit.t(0)
circuit.h(0)
circuit.s(0)
circuit.y(0)

display(circuit.draw(output="mpl"))

Output of the previous code cell

Aquí usamos el método draw de la clase QuantumCircuit con el renderizador mpl (abreviatura de Matplotlib, una biblioteca de visualización de Python). Este es el único renderizador que usaremos para circuitos cuánticos en este curso, aunque existen otras opciones, incluyendo un renderizador basado en texto y uno basado en LaTeX.

Las operaciones se aplican de forma secuencial, comenzando por la izquierda y terminando por la derecha en el diagrama. Una forma práctica de obtener la matriz unitaria correspondiente a este circuito es usar el método from_circuit de la clase Operator.

display(Operator.from_circuit(circuit).draw("latex"))
[0.14644660940.3535533906i0.8535533906+0.3535533906i0.3535533906+0.8535533906i0.3535533906+0.1464466094i] \begin{bmatrix} 0.1464466094 - 0.3535533906 i & 0.8535533906 + 0.3535533906 i \\ -0.3535533906 + 0.8535533906 i & 0.3535533906 + 0.1464466094 i \\ \end{bmatrix}

También podemos inicializar un vector de estado cuántico de partida y luego hacer evolucionar ese estado según la secuencia de operaciones descrita por el circuito.

ket0 = Statevector([1, 0])
v = ket0.evolve(circuit)
display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

El siguiente código simula un experimento en el que el estado obtenido del circuito anterior se mide con una medición en la base estándar 4000 veces (usando una copia nueva del estado cada vez).

statistics = v.sample_counts(4000)
display(plot_histogram(statistics))

Output of the previous code cell