La curiosidad impulsa el aprendizaje y el progreso humano. La integración de principios físicos como el mínimo de acción y la mínima producción de entropía en la optimización, inspirada en la física, nos lleva al concepto de recocido físico, que precede al recocido simulado. El principio de adiabaticidad, una manifestación cuántica, da lugar al recocido cuántico.
Este capítulo profundiza en el problema del viajante de comercio (TSP), un desafío NP-difícil, utilizando el solucionador qbsolve de D-Wave. Además, exploraremos las opciones de aprendizaje profundo cuántico aprovechando las bibliotecas PennyLane de Xanadu y TensorFlow Quantum de Google, inspiradas en las redes neuronales clásicas.
Para que las computadoras cuánticas aborden problemas del mundo real, deben procesar datos de entrada. Estos datos pueden provenir de sistemas cuánticos naturales o artificiales. El proceso general de computación cuántica impulsada por datos clásicos implica tres etapas principales, como se ilustra en la Figura 5-6.

La formulación de circuitos cuánticos para codificar datos clásicos en estados cuánticos es un área activa de investigación. Existen varios enfoques para la codificación de datos, como la codificación básica y la codificación de amplitud, debido a las diversas restricciones de los algoritmos cuánticos sobre cómo se deben cargar y formatear los datos de entrada.
Aprendizaje a través de la Optimización con D-Wave
Continuando con el Capítulo 7, examinaremos cómo resolver el TSP, un conocido problema NP-difícil, utilizando qbsolve de D-Wave. Los problemas de optimización clásicos, como se describe en la Ecuación 7.27, pueden formularse para su implementación en la plataforma D-Wave.
La Ecuación 7.27, cuantificada a través del Hamiltoniano del modelo de Ising, se mapea a una plataforma impulsada por recocido para la optimización mejorada cuánticamente. Formulando los Hamiltonianos basados en las Ecuaciones 7.4, 7.5 y 7.27, obtenemos:
$$ \mathcal{H} = \sum_i \mathcal{H}_{i} $$ (8.1)
donde $A(s)$ y $B(s)$ son multiplicadores de Lagrange, y $A(s)$ se conoce como el parámetro gamma en programación.
qbsolve es un solucionador híbrido cuántico/clásico de descomposición que encuentra mínimos dividiendo problemas grandes de optimización binaria cuadrática sin restricciones (QUBO) en partes más pequeñas, que luego se resuelven utilizando sistemas D-Wave o solucionadores de recocido simulado clásicos. Es una herramienta de código abierto diseñada para problemas demasiado grandes o densos para que se ejecuten en una computadora cuántica D-Wave, descomponiendo el problema en subproblemas cuánticos iterativos. El proceso qbsolve puede ejecutarse en una computadora clásica.
qbsolve utiliza el formato de archivo QUBO o una matriz Q como entrada y produce una cadena de bits como salida, como se muestra en la Figura 8-1.

El flujo de trabajo del algoritmo qbsolve incluye:
- Lectura de la instancia QUBO.
- Descomposición en sub-QUBOs más pequeños.
- Resolución de los sub-QUBOs (en CPU o QPU).
- Combinación de los resultados.
Un ejemplo excelente de qbsolve es el Problema del Viajante de Comercio.
Problema del Viajante de Comercio (TSP) con qbsolve
El TSP es un problema NP-difícil bien conocido que ha intrigado a matemáticos y científicos de la computación. En contextos de aplicaciones, los problemas tipo TSP son cruciales en las industrias financiera y de marketing. El TSP se considera importante debido a su dureza y similitud con otros problemas de optimización combinatoria que surgen en la práctica.
Formulado matemáticamente en el siglo XIX por W. R. Hamilton y Thomas Kirkman, el TSP se puede abstraer como un problema de teoría de grafos. El problema en un grafo con nodos requiere encontrar el ciclo Hamiltoniano más corto que visite cada vértice una vez. No se conoce una solución general eficiente, y no se espera un algoritmo que funcione en tiempo polinomial.
Formalmente, el TSP involucra a un viajante de comercio que visita una lista de ciudades, con distancias dadas entre cada par de ciudades. El objetivo es encontrar la ruta más corta posible que visite cada ciudad una vez y regrese a la ciudad de origen, maximizando así las ventas potenciales en el menor tiempo.
Consideraremos un TSP de nueve ciudades. Hemos definido un archivo de datos (data_9cities.txt) con los parámetros de ubicación (latitud y longitud) de nueve capitales de EE. UU., indexadas para facilitar la programación.
0,Colorado,Denver,39.7391667,-104.984167
1,Connecticut,Hartford,41.767,-72.677
2,Delaware,Dover,39.161921,-75.526755
3,Illinois,Springfield,39.783250,-89.650373
4,Indiana,Indianapolis,39.790942,-86.147685
5,Massachusetts,Boston,42.2352,-71.0275
6,Michigan,Lansing,42.7335,-84.5467
7,New Hampshire,Concord,43.220093,-71.549127
8,New Jersey,Trenton,40.221741,-74.756138
Otro archivo de datos (data_distance9.txt) contiene las distancias entre cada par de ciudades (72 distancias en total). Un ejemplo de ruta y tiempo de viaje sería:

o

Para el modelo QUBO, consideramos dos restricciones:
- El viajante debe terminar donde comenzó.
- Cada ciudad se visita exactamente una vez.
Definimos variables binarias $x_{i,j}$, donde $x_{i,j} = 1$ si el viajante visita la ciudad $i$ en la parada $j$, y $0$ en caso contrario. Las restricciones implican que cada ciudad debe ser visitada exactamente una vez, y cada parada debe visitar exactamente una ciudad.
$$ \sum_{j=0}^{N-1} x_{i,j} = 1 \quad \forall i \in {0, ..., N-1} $$
$$ \sum_{i=0}^{N-1} x_{i,j} = 1 \quad \forall j \in {0, ..., N-1} $$
Minimizamos la distancia total de viaje:
$$ \text{Minimize} \sum_{i=0}^{N-1} \sum_{j=0}^{N-1} \sum_{k=0}^{N-1} D_{i,k} x_{i,j} x_{k,(j+1)%N} $$
donde $D_{i,k}$ es la distancia entre la ciudad $i$ y la ciudad $k$. El término $(j+1)%N$ asegura el cierre del ciclo.
Combinando las restricciones y el objetivo en un formato QUBO, utilizamos el parámetro de Lagrange $\gamma$ (o $A$ en el código) para ponderar la importancia de las restricciones.
Comenzaremos con el archivo de cuaderno Jupyter TSP_9City_Dwave.ipynb.
1. Bibliotecas y herramientas de visualización
## Bibliotecas y herramientas de visualización que utilizaremos.
import networkx as nx
import pylab
import matplotlib.pyplot as plt
from matplotlib.pyplot import pause
import sys
from bokeh.sampledata import us_states
from bokeh.plotting import *
from bokeh.models import Plot, Range1d, MultiLine, Circle, HoverTool, TapTool, BoxSelectTool
from bokeh.io import output_notebook
from bokeh.palettes import Spectral4
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes
output_notebook()
%matplotlib inline
import argparse
import re
# Listing 8-1a Bibliotecas y Matrices de Pauli para TSP_9City_Dwave.ipynb
bokeh es una biblioteca de Python para crear visualizaciones interactivas. La usaremos para mapear las ciudades visitadas.
2. Cofniguración del problema
Definimos el número de ciudades $N$ y una función para mapear las variables binarias $x_{a,b}$ a índices de matriz QUBO.
# Número de ciudades en nuestro problema
N = 9
# Función para calcular el índice en Q para la variable x_(a,b)
def x(a, b):
return (a)*N+(b)
# Listing 8-1b Matriz para TSP_9City_Dwave.ipynb
3. Parámetros sintonizables
Los parámetros clave incluyen los multiplicadores de Lagrange ($A$ o gamma, $B$) y la chainstrength. El número de ejecuciones (numruns) es importante debido a la naturaleza probabilística de las QPU.
# Gamma = A
A = 6500
B = 1
chainstrength = 3000
# Número de ejecuciones
numruns = 100
# Listing 8-1c Parámetros sintonizables TSP_9City_Dwave.ipynb
4. Visualización de ciudades
Generamos un mapa de EE. UU. que destaca las nueve ciudades utilizando los datos de data_9cities.txt.
us_states = us_states.data.copy()
# Eliminar los estados Hawái y Alaska del mapa continental
del us_states["HI"]
del us_states["AK"]
# separar latitud y longitud puntos para las fronteras
# de los estados.
state_xs = [us_states[code]["lons"] for code in us_states]
state_ys = [us_states[code]["lats"] for code in us_states]
with open('data_9cities.txt', "r") as myfile:
city_text = myfile.readlines()
myfile.close()
cities = [',']*N
states = [',']*N
lats=[]
longs=[]
for i in city_text:
index, state, city,lat,lon = i.split(',')
cities[int(index)] = city.rstrip()
states[int(index)] = state
lats.append(float(lat))
longs.append(float(lon))
# inicializar figura
p = figure(title="Encuentra la ruta más corta que visita cada ciudad",
toolbar_location="left", plot_width=550, plot_height=350)
# Dibujar líneas de estado
p.patches(state_xs, state_ys, fill_alpha=0.0,
line_color='blue', line_width=1.5)
# Los marcadores de dispersión
p.circle(longs, lats, size=10, color='red', alpha=1)
show(p)
# Listing 8-1d Mapa de las ciudades TSP_9City_Dwave.ipynb

5. Cargar distancias entre ciudades
Leemos el archivo data_distance9.txt que contiene las distancias interurbanas.
# Archivo de entrada que contiene distancias interurbanas
fn = "data_distance9.txt"
# verificar que el usuario ha proporcionado el archivo de entrada
try:
with open(fn, "r") as myfile:
distance_text = myfile.readlines()
myfile.close()
except IOError:
print("Archivo de distancia de entrada faltante")
exit(1)
# Listing 8-1e Distancias Interurbanas TSP_9City_Dwave.ipynb
Las distancias se almacenan en una matriz $N \times N$, que es simétrica ya que la distancia de A a B es la misma que de B a A.
# Inicializar matriz del tamaño correcto con todos los 0's
D = [[0 for z in range(N)] for y in range(N)]
# Leer valores de distancia e ingresarlos en la matriz
for i in distance_text:
if re.search("^between", i):
m = re.search("^between_(\d+)_(\d+) = (\d+)", i)
citya = int(m.group(1))
cityb = int(m.group(2))
D[citya][cityb] = D[cityb][citya] = int(m.group(3))
# Listing 8-1f Crear la Matriz TSP_9City_Dwave.ipynb
6. Construcción de la matriz QUBO (Q)
Inicializamos la matriz QUBO $Q$ con ceros.
Q = {}
for i in range(N*N):
for j in range(N*N):
Q[(i,j)] = 0
# Listing 8-1g Matriz Q TSP_9City_Dwave.ipynb
7. Incorporar restricciones
La primera restricción garantiza que cada ciudad se visita exactamente una vez. Para cada ciudad $v$, la suma de $x_{v,j}$ para todas las paradas $j$ debe ser 1. En el formato QUBO, esto se implementa como:
$$ \sum_{j=0}^{N-1} x_{v,j} = 1 $$
Esto se traduce en los siguientes términos en la matriz Q:
for v in range(N):
for j in range(N):
Q[(x(v,j), x(v,j))] += -1*A # Penalización por no visitar la ciudad
for k in range(j+1, N):
Q[(x(v,j), x(v,k))] += 2*A # Restricción de visita única
# Listing 8-1h Matriz de Permutación TSP_9City_Dwave.ipynb
8. Incorporar el objetivo de distancia
El objetivo principal es minimizar la distancia total del viaje. Para cada par de ciudades $(u, v)$ y cada par de paradas consecutivas $(j, (j+1)%N)$, añadimos el término de distancia $D_{u,v}$.
for u in range(N):
for v in range(N):
if u!=v:
for j in range(N):
# Penalización por viajar entre ciudades u y v en paradas j y j+1
Q[(x(u,j), x(v,(j+1)%N))] += B*D[u][v]
# Listing 8-1i Añadir objetivo de distancia TSP_9City_Dwave.ipynb
9. Ejecutar qbsolve
Ejecutamos qbsolve para encontrar la solución óptima.
from dwave_qbsolv import QBSolv
resp = QBSolv().sample_qubo(Q)
# Listing 8-1j Ejecutar qbsolve TSP_9City_Dwave.ipynb
10. Postprocesamiento y validación
Analizamos los resultados para encontrar la ruta óptima, la distancia total y realizamos verificaciones de validación.
# La primera solución es la solución de menor energía encontrada
sample = next(iter(resp))
# Mostrar energía para la mejor solución encontrada
print('Energía: ', next(iter(resp.data())).energy)
# Imprimir ruta para la solución encontrada
route = [-1]*N
for node in sample:
if sample[node]>0:
j = node%N
v = (node-j)/N
if route[j]!=-1:
print('Parada '+str(j)+' usada más de una vez.\n')
route[j] = int(v)
# Calcular y mostrar el kilometraje total
mileage = 0
for i in range(N):
mileage+=D[route[i]][route[(i+1)%N]]
print('Kilometraje: ', mileage)
print('\nRuta:\n')
for i in range(N):
if route[i]!=-1:
print(str(i) + ': ' +cities[route[i]]+ ',' + states[route[i]] + '\n')
else:
print(str(i) + ': Ninguna ciudad asignada.\n')
# Listing 8-1k Añadir objetivo de distancia TSP_9City_Dwave.ipynb
alert = 0
# Verificar que cada ciudad se visita una vez
cities_visited = [route[i] for i in range(N) if route[i] != -1]
if len(cities_visited) != N or len(set(cities_visited)) != N:
print('La ruta no es válida: las ciudades no se visitan exactamente una vez.\n')
alert = 1
# Verificar que cada parada tiene una ciudad asignada
for i in range(N):
if route[i] == -1:
print('La parada ' + str(i) + ' no tiene ciudad asignada.')
alert = 1
if alert == 0:
print("Ruta válida.")
# Listing 8-1l Verificar si la ruta es válida TSP_9City_Dwave.ipynb
Este código genera la ruta óptima y la distancia total. Debido a la naturaleza probabilística, el orden de las ciudades puede variar entre ejecuciones. Se anima al lector a experimentar con el parámetro A (gamma).
11. Visualización de la ruta
Visualizamos la ruta óptima encontrada en el mapa de EE. UU.
Path = nx.Graph()
coord={}
coord[route[0]]=(longs[route[0]],lats[route[0]])
Path.add_node(cities[route[0]],pos=coord[route[0]],label=cities[route[0]])
for i in range(N-1):
e=(cities[route[i]],cities[route[i+1]])
Path.add_edge(*e)
coord[route[i+1]]=(longs[route[i+1]],lats[route[i+1]])
Path.add_node(cities[route[i+1]],pos=coord[route[i+1]],label=cities[route[i+1]])
e=(cities[route[N-1]],cities[route[0]])
Path.add_edge(*e)
fig, ax = plt.subplots(figsize=(120,60))
margin=0.15
fig.subplots_adjust(margin, margin, 1.-margin, 1.-margin)
ax.axis('equal')
nx.draw(Path, nx.get_node_attributes(Path, 'pos'), with_labels=True, width=10, edge_color='b', node_size=200,font_size=72,font_weight='bold', ax=ax)
plt.show()
# Listing 8-1m Visualización de la ruta TSP_9City_Dwave.ipynb
Se anima al lector a modificar los archivos de datos para un mayor número de ciudades y explorar el impacto en la visualización y la variabilidad de los resultados.
Para experimentar con QPU reales, D-Wave ofrece acceso gratuito limitado a través de su plataforma en la nube Leap. Para utilizar qbsolve con una QPU, se puede utilizar el siguiente código (requiere un token de API):
from dwave.system.composites import EmbeddingComposite
from dwave.system.samplers import DWaveSampler
# sampler = DWaveSampler(solver={'qpu': True}) # Para algunas cuentas, puede ser necesario usar la siguiente línea:
# sampler = EmbeddingComposite(DWaveSampler(token='MI_TOKEN', solver=dict(name='nombre_solver')))
# print("Conectado a la QPU.")
Redes Neuronales Cuánticas (QNNs)
Las redes neuronales cuánticas (QNNs) son la vanguardia del aprendizaje profundo cuántico. Estas redes, inspiradas en las redes neuronales clásicas, aprovechan los principios cuánticos para procesar información. Los primeros modelos como las máquinas de Boltzmann se han cuantificado para crear QNNs. Una ventaja clave de las QNNs es que no requieren computadoras cuánticas universales a gran escala.
Las QNNs se pueden implementar en varios procesadores cuánticos, incluidos los cuantificadores de recocido y los circuitos fotónicos programables.
Las QNNs se asemejan a las funciones cerebrales, ofreciendo un nuevo paradigma para los sistemas de información. Son particularmente adecuadas para problemas que exigen una capacidad y memoria exponenciales, posicionándose como el siguiente paso evolutivo en el cálculo neuronal. La intersección de la computación cuántica y el aprendizaje automático está produciendo algoritmos nuevos, especialmente adaptados a la era de los dispositivos cuánticos de escala intermedia ruidosa (NISQ).
Aprendizaje Cuántico con Xanadu
Xanadu, una empresa de tecnología cuántica, está desarrollando procesadores cuánticos fotónicos y una plataforma de software de pila completa de código abierto llamada Strawberry Fields. Han desarrollado PennyLane, una biblioteca de Python para programación cuántica diferenciable, que integra bibliotecas clásicas de aprendizaje automático con simuladores y hardware cuánticos.
PennyLane permite entrenar circuitos cuánticos y soporta:
- Modelos híbridos cuántico-clásicos (integración con PyTorch, TensorFlow, NumPy).
- Diferenciación automática de circuitos cuánticos.
- Herramientas de optimización y aprendizaje automático.
- Independencia del hardware, permitiendo la ejecución en diversos backends.
La instalación de PennyLane es sencilla: pip install pennylane --upgrade.
Los procesadores cuánticos fotónicos utilizan estados cuánticos de la luz (amplitud y fase) para codificar información. Esta estructura continua los hace atractivos para las redes neuronales cuánticas. Las QNNs fotónicas utilizan puertas cuánticas fotónicas (interferómetros, puertas de compresión y desplazamiento, transformaciones no lineales) análogas a las capas de redes neuronales clásicas.

PennyLane facilita la creación de modelos híbridos cuántico-clásicos, integrando nodos cuánticos con componentes clásicos.
Ajuste de una función coseno con QNN
Este ejemplo, adaptado de la documentación de PennyLane, demuestra cómo entrenar una red neuronal cuántica para ajustar una función $y = \cos(x)$ ruidosa.
- Crear un dispositivo: Usamos el simulador
default.qubitostrawberryfields.fock. - Definir un QNode: Un circuito cuántico parametrizado que toma una entrada $x$ y pesos entrenables, y produce una salida $y$.
- Generar datos: Creamos datos sintéticos de la función coseno con ruido.
- Seleccionar optimizador: Usamos
AdamOptimizer. - Bucle de optimización: Ajustamos los pesos para minimizar la pérdida.
El circuito variacional utilizado es un modelo de red neuronal cuántica de variables continuas.
# Importar PennyLane, la versión encapsulada de NumPy proporcionada por PennyLane, y un optimizador.
import pennylane as qml
from pennylane import numpy as np
from pennylane.optimize import AdamOptimizer
import matplotlib.pyplot as plt
import pylab
import pickle
# Listing 8-2a Bibliotecas y Optimizador qnn_cosine_pl.ipynb
# Generar ruido para añadir a los valores de la función (valores Y)
noise = np.random.normal(0,0.1,100)
# definir funciones
Xlim = 5
X = np.arange(-Xlim, Xlim, 0.1)
Y = np.cos(X)+noise
# Listing 8-2b Definición de la función Coseno Ruidoso qnn_cosine_pl.ipynb
# escribir los datos en un archivo
cosdata = open('cosdata.md', 'wb')
pickle.dump(Y, cosdata)
cosdata.close()
plt.plot(X[0:200], Y[0:200])
plt.show()
# Listing 8-2c Escribir datos de Coseno Ruidoso en un archivo qnn_cosine_pl.ipynb

Configuramos el dispositivo:
# Nota: Si el plugin Strawberry Fields de PennyLane NO está instalado, obtendrá un error: DeviceError: device not found.
# Asegúrese de que el plugin requerido esté instalado.
# $ pip install pennylane-sf
dev = qml.device("strawberryfields.fock", wires=1, cutoff_dim=10)
# Listing 8-2d Plugin Strawberry Fields qnn_cosine_pl.ipynb
El simulador strawberryfields.fock utiliza estados de Fock para representar el espacio de Hilbert. La simulación es aproximada y su precisión aumenta con cutoff_dim.
Definimos las capas de la QNN y el QNode:
def layer(v):
# Multiplicación de matrices de la capa de entrada
qml.Rotation(v[0], wires=0)
qml.Squeezing(v[1], 0.0, wires=0)
qml.Rotation(v[2], wires=0)
# Sesgo
qml.Displacement(v[3], 0.0, wires=0)
# Transformación no lineal elemento a elemento
qml.Kerr(v[4], wires=0)
@qml.qnode(dev)
def quantum_neural_net(var, x=None):
# Codificar entrada x en el estado cuántico
qml.Displacement(x, 0.0, wires=0)
# Subcircuitos de "capa"
for v in var:
layer(v)
return qml.expval(qml.X(0))
# Listing 8-2e Definir la Capa QNN qnn_cosine_pl.ipynb
# Listing 8-2f Codificar Entrada qnn_cosine_pl.ipynb
Definimos las funciones de pérdida y costo:
def square_loss(labels, predictions):
loss = 0
for l, p in zip(labels, predictions):
loss = loss + (l - p) ** 2
loss = loss / len(labels)
return loss
# Listing 8-2g Modelado de Predicciones qnn_cosine_pl.ipynb
def cost(var, features, labels):
preds = [quantum_neural_net(var, x=x) for x in features]
return square_loss(labels, preds)
# Listing 8-2h Función de Costo qnn_cosine_pl.ipynb
Inicializamos los pesos y ejecutamos el optimizador:
np.random.seed(0)
num_layers = 4
var_init = 0.05 * np.random.randn(num_layers, 5)
print(var_init)
# Listing 8-2j Definir el Número de Capas qnn_cosine_pl.ipynb
opt = AdamOptimizer(0.01, beta1=0.9, beta2=0.999)
var = var_init
for it in range(50):
var = opt.step(lambda v: cost(v, X, Y), var)
print("Iter: {:5d} | Costo: {:0.7f} ".format(it + 1, cost(var, X, Y)))
# Listing 8-2k Ejecutar el Optimizador qnn_cosine_pl.ipynb
Después del entrenamiento, visualizamos los resultados:
x_pred = np.linspace(-3, 3, 50)
predictions = [quantum_neural_net(var, x=x_) for x_ in x_pred]
# graficar la función
plt.figure()
plt.scatter(X, Y)
plt.scatter(x_pred, predictions, color="maroon")
plt.xlabel("x", fontsize=18)
plt.ylabel("f(x)", fontsize=18)
plt.tick_params(axis="both", which="major", labelsize=16)
plt.tick_params(axis="both", which="minor", labelsize=16)
plt.show()
# Listing 8-2l Recopilar Predicciones y Graficar Función qnn_cosine_pl.ipynb

Clasificador Cuántico de PennyLane para el Conjunto de Datos Iris
Comparamos el rendimiento de una QNN con los modelos clásicos (SVM, Red Neuronal Profunda) en el conjunto de datos Iris. Utilizamos la plantilla StronglyEntanglingLayers.
# Importar datos, preprocesamiento, división de datos
# cargar el conjunto de datos
iris = datasets.load_iris ()
# barajar los datos
X, y = shuffle (iris.data , iris.target , random_state = 0)
# seleccionar solo las 2 primeras clases de los datos
X = X[y <=1]
y = y[y <=1]
# normalizar datos
X = minmax_scale (X, feature_range =(0 , np.pi ))
# dividir datos en entrenamiento + validación y prueba
X_train_val , X_test , y_train_val , y_train_val , y_test = train_test_split (X, y, test_size =0.2)
# Listing 8-3b Cargar Datos, Preprocesar y Dividir qml_iris_pl.ipynb
# Número de qubits es igual al número de características
n_qubits = X.shape [1]
# manejador de dispositivo cuántico
dev = DefaultQubit(n_qubits)
# circuito cuántico
@qml.qnode (dev )
def circuit (weights , x= None ):
AngleEmbedding (x, wires = range (n_qubits ))
StronglyEntanglingLayers (weights , wires = range ( n_qubits ))
return qml.expval ( qml.PauliZ (0))
# clasificador cuántico variacional
def variational_classifier (theta, x= None ):
weights = theta [0]
bias = theta [1]
return circuit ( weights , x=x) + bias
def cost (theta , X, expectations ):
e_predicted = \
np.array ([ variational_classifier (theta , x=x) for x in X])
loss = np.mean (( e_predicted - expectations )**2)
return loss
# Listing 8-3c Construcción del Clasificador Cuántico qml_iris_pl.ipynb
# número de capas cuánticas
n_layers = 3
# dividir en entrenamiento y validación
X_train , X_validation , y_train , y_validation = \
train_test_split ( X_train_val , y_train_val , test_size =0.20)
# convertir clases a expectativas: 0 a -1, 1 a +1
e_train = np. empty_like ( y_train )
e_train [ y_train == 0] = -1
e_train [ y_train == 1] = +1
# seleccionar tamaño del lote de aprendizaje
batch_size = 5
# calcular número de lotes
batches = len ( X_train ) // batch_size
# seleccionar número de épocas
n_epochs = 10
# Listing 8-3d Preparar para Entrenar los Datos qml_iris_pl.ipynb
# extraer pesos cuánticos aleatorios del nodo
theta_weights = strong_ent_layers_uniform ( n_layers , n_qubits , seed =42)
theta_bias = 0.0
theta_init = ( theta_weights , theta_bias ) # pesos iniciales
# entrenar el clasificador variacional
theta = theta_init
# inicio del bucle de aprendizaje principal
# construir el objeto optimizador
pennylane_opt = GradientDescentOptimizer ()
# dividir los datos de entrenamiento en lotes
X_batches = np.array_split (np.arange (len ( X_train )), batches )
for it , batch_index in enumerate ( chain (*( n_epochs * [ X_batches ]))):
# Actualizar los pesos con un paso del optimizador
batch_cost = \
lambda theta : cost (theta , X_train [ batch_index ], e_train [ batch_index ])
theta = pennylane_opt.step ( batch_cost , theta )
# usar X_validation e y_validation para decidir cuándo detener
# fin del bucle de aprendizaje
# Listing 8-3e ¡Entrenar los Datos! qml_iris_pl.ipynb
# Inferencia
# convertir expectativas a clases
expectations = np. array ([ variational_classifier (theta , x=x) for x in X_test ])
prob_class_one = ( expectations + 1.0) / 2.0
y_pred = ( prob_class_one >= 0.5)
print ( metrics . accuracy_score ( y_test , y_pred ))
print ("Precisión: {:.2f} %".format(metrics.accuracy_score ( y_test , y_pred )*100))
print ( metrics.confusion_matrix ( y_test , y_pred ))
# Listing 8-3f Inferencia qml_iris_pl.ipynb
Al entrenar durante 10 épocas, logramos una alta precisión. Reducir las épocas a 5 resulta en una menor precisión, lo que demuestra la importancia del número de épocas para el entrenamiento.
TensorFlow Quantum (TFQ) QNN
TensorFlow Quantum (TFQ) es un marco de Python para el aprendizaje automático híbrido cuántico-clásico, que se basa en Cirq. Se utiliza para construir QNNs para la clasificación de imágenes.
Clasificación de Imágenes MNIST Simplificada con TFQ
Este ejemplo utiliza TFQ para construir una QNN que clasifica dígitos '1' y '7' del conjunto de datos MNIST.
- Instalar TensorFlow y TFQ:
!pip install tensorflow==2.3.1
!pip install tensorflow-quantum
- Importar bibliotecas:
import tensorflow as tf
import tensorflow_quantum as tfq
import cirq
import sympy
import numpy as np
import seaborn as sns
import collections
# herramientas de visualización
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
# Listing 8-4c Importar Bibliotecas qnn_mnist_tfq.ipynb
- Cargar y preprocesar datos: Cargamos MNIST, filtramos para '1' y '7', reducimos el tamaño de las imágenes a 4x4 y las convertimos a binario.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# Remodelar las imágenes de [0,255] al rango [0.0,1.0].
x_train, x_test = x_train[..., np.newaxis]/255.0, x_test[..., np.newaxis]/255.0
def filter_17(x, y):
keep = (y == 1) | (y == 7)
x, y = x[keep], y[keep]
y = y == 1 # Convertir a booleano (1 para True, 7 para False)
return x,y
x_train, y_train = filter_17(x_train, y_train)
x_test, y_test = filter_17(x_test, y_test)
# Reducir tamaño de imagen
x_train_small = tf.image.resize(x_train, (4,4)).numpy()
x_test_small = tf.image.resize(x_test, (4,4)).numpy()
THRESHOLD = 0.5
x_train_bin = np.array(x_train_small > THRESHOLD, dtype=np.float32)
x_test_bin = np.array(x_test_small > THRESHOLD, dtype=np.float32)
# Listing 8-4d Cargar los Datos qnn_mnist_tfq.ipynb
# Listing 8-4e Filtrar los Datos qnn_mnist_tfq.ipynb
# Listing 8-4g Ejemplo qnn_mnist_tfq.ipynb (después de redimensionar)
# Listing 8-4h Codificación Binaria qnn_mnist_tfq.ipynb
- Convertir imágenes a circuitos cuánticos: Cada imagen se codifica en un circuito Cirq.
def convert_to_circuit(image):
values = np.ndarray.flatten(image)
qubits = cirq.GridQubit.rect(4, 4)
circuit = cirq.Circuit()
for i, value in enumerate(values):
if value:
circuit.append(cirq.X(qubits[i]))
return circuit
x_train_circ = [convert_to_circuit(x) for x in x_train_bin]
x_test_circ = [convert_to_circuit(x) for x in x_test_bin]
SVGCircuit(x_train_circ[0]) # Visualizar el primer circuito
# Listing 8-4i Rotar Qubits a través de la Puerta X qnn_mnist_tfq.ipynb
- Construir el modelo QNN: Utilizamos
tfq.layers.PQCcon un modelo de circuito escalonado.
def create_quantum_model():
data_qubits = cirq.GridQubit.rect(4, 4)
readout = cirq.GridQubit(-1, -1)
circuit = cirq.Circuit()
circuit.append(cirq.X(readout))
circuit.append(cirq.H(readout))
builder = CircuitLayerBuilder(data_qubits=data_qubits, readout=readout)
builder.add_layer(circuit, cirq.XX, "xx1")
builder.add_layer(circuit, cirq.ZZ, "zz1")
circuit.append(cirq.H(readout))
return circuit, cirq.Z(readout)
model_circuit, model_readout = create_quantum_model()
# Construir el modelo Keras.
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(), dtype=tf.string),
tfq.layers.PQC(model_circuit, model_readout),
])
# Listing 8-4l Construir el Circuito del Modelo QNN qnn_mnist_tfq.ipynb
# Listing 8-4n Construir el Envoltorio Keras qnn_mnist_tfq.ipynb
- Entrenar el modelo: Compilamos el modelo con la pérdida Hinge y lo entrenamos.
y_train_hinge = 2.0*y_train-1.0
y_test_hinge = 2.0*y_test-1.0
def hinge_accuracy(y_true, y_pred):
y_true = tf.squeeze(y_true) > 0.0
y_pred = tf.squeeze(y_pred) > 0.0
result = tf.cast(y_true == y_pred, tf.float32)
return tf.reduce_mean(result)
model.compile(loss=tf.keras.losses.Hinge(),
optimizer=tf.keras.optimizers.Adam(),
metrics=[hinge_accuracy])
EPOCHS = 3
BATCH_SIZE = 32
NUM_EXAMPLES = len(x_train_circ)
x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES]
y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]
qnn_history = model.fit(
x_train_tfcirc_sub, y_train_hinge_sub,
batch_size=BATCH_SIZE,
epochs=EPOCHS,
verbose=1,
validation_data=(x_test_tfcirc, y_test_hinge))
qnn_results = model.evaluate(x_test_tfcirc, y_test_hinge)
# Listing 8-4p Entrenar el Modelo Cuántico qnn_mnist_tfq.ipynb
# Listing 8-4o Argumento de Etiqueta qnn_mnist_tfq.ipynb
- Comparación con modelo clásico: Entrenamos una red neuronal convolucional clásica (CNN) para la misma tarea.
def create_classical_model():
model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(32, [3, 3], activation='relu', input_shape=(28,28,1)))
model.add(tf.keras.layers.Conv2D(64, [3, 3], activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Dropout(0.25))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(1))
return model
classical_model = create_classical_model()
classical_model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])
classical_model.fit(x_train, y_train, batch_size=128, epochs=1, verbose=1, validation_data=(x_test, y_test))
cnn_results = classical_model.evaluate(x_test, y_test)
def create_fair_classical_model():
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(4,4,1)))
model.add(tf.keras.layers.Dense(2, activation='relu'))
model.add(tf.keras.layers.Dense(1))
return model
fair_classical_model = create_fair_classical_model()
fair_classical_model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])
fair_classical_model.fit(x_train_bin, y_train, batch_size=128, epochs=20, verbose=2, validation_data=(x_test_bin, y_test))
fair_nn_results = fair_classical_model.evaluate(x_test_bin, y_test)
# Listing 8-4q Construir el Modelo Clásico qnn_mnist_tfq.ipynb
# Listing 8-4s Modelo de 37 Parámetros qnn_mnist_tfq.ipynb
# Listing 8-4t Entrenar el Modelo de 37 Parámetros qnn_mnist_tfq.ipynb
La comparación de precisión muestra que los modelos clásicos, especialmente el CNN completo, superan a la QNN actual en este problema de datos clásicos. Sin embargo, las QNNs tienen el potencial de superar a los modelos clásicos en problemas inherentemente cuánticos o cuando se aprovecha la superposición y el entrelazamiento.
Redes Neuronales Convolucionales Cuánticas (QCNNs)
Las QCNNs, inspiradas en las CNNs clásicas, utilizan operaciones de convolución y agrupación en circuitos cuánticos. Las convoluciones se aplican a pares de qubits adyacentes, seguidas de capas de agrupación que miden subconjuntos de qubits. Los parámetros de estas operaciones se aprenden durante el entrenamiento.

Las QCNNs se han propuesto para clasificar estados cuánticos y, de manera análoga a las CNNs, para clasificar imágenes. Google ofrece un tutorial práctico de QCNN en su plataforma TensorFlow Quantum.
Resumen
Los datos cuánticos, que surgen de sistemas cuánticos, pueden ofrecer una ventaja cuántica sobre los métodos puramente clásicos en el aprendizaje automático híbrido-clásico. Las propiedades cuánticas como la superposición y el entrelazamiento pueden requerir recursos clásicos exponenciales para su representación.
Esta sección exploró el TSP con D-Wave, las QNNs con PennyLane y TensorFlow Quantum, y las QCNNs. Los avances en el aprendizaje automático cuántico prometen revolucionar campos como la química cuántica, la física y las finanzas.
Capítulo 9: QML: El Camino por Delante
El aprendizaje automático cuántico (QML) es un campo interdisciplinario que fusiona la computación cuántica y el aprendizaje automático clásico. El valor de las computadoras cuánticas en QML radica en abordar problemas que son desafiantes para el hardware clásico, aprovechando la afinidad natural entre los algoritmos de IA y las capacidades cuánticas.
El capítulo continuará explorando áreas avanzadas como el aprendizaje automático cuántico aplicado a la química, caminatas cuánticas, simulación Hamiltoniana, optimización cuántica (QBoost), finanzas cuánticas y comunicaciones cuánticas.
Las nuevas empresas como TQuT Inc., Quacoon y Quantropi están impulsando la innovación en áreas como el descubrimiento de materiales, las cadenas de suministro y la comunicación segura.
Computación Cuántica para la Química
La simulación de sistemas cuánticos, como moléculas y materiales, es una aplicación prometedora para las computadoras cuánticas debido al crecimiento exponencial del espacio de Hilbert con el número de partículas. Los métodos de aprendizaje automático, como las redes neuronales, se están utilizando para predecir propiedades químicas y acelerar las simulaciones de dinámica molecular. El software como OpenFermion facilita la simulación de sistemas de fermiones y bosones en hardware cuántico.
Caminatas Cuánticas
Las caminatas cuánticas son la contraparte cuántica de las caminatas aleatorias clásicas. Ofrecen ventajas algorítmicas en la búsqueda en bases de datos, el análisis de grafos y la simulación cuántica. A diferencia de las caminatas aleatorias clásicas, las caminatas cuánticas exhiben interferencia, lo que lleva a distribuciones de probabilidad distintas. El código pyQuil se utiliza para demostrar una caminata cuántica continua en tiempo.
Simulación Hamiltoniana Polinomial en el Tiempo
La simulación Hamiltoniana es crucial para comprender la evolución temporal de los sistemas cuánticos. Los métodos digitales dsecomponen la evolución temporal en puertas cuánticas. La regla de aproximación de Trotter permite la simulación de Hamiltonianos complejos descomponiéndolos en sumas de Hamiltonianos más simples.
Ensemble y QBoost
QBoost es un algoritmo de entrenamiento iterativo que selecciona subconjuntos de clasificadores débiles resolviendo problemas de optimización. Aprovecha la naturaleza no convexa de las funciones objetivo cuánticas para construir clasificadores fuertes. Se demuestra un ejercicio práctico con qboost_ensemble.ipynb que utiliza Perceptrón, SVM y AdaBoost como clasificadores débiles.
Procesamiento de Imágenes Cuánticas (QIMP)
El QIMP utiliza la computación cuántica para acelerar las tareas de procesamiento de imágenes. Se han propuesto varias representaciones de imágenes en computadoras cuánticas, como la Representación Flexible de Imágenes Cuánticas (FRQI) y la Nueva Representación Cuántica Mejorada de Imágenes Digitales (NEQR). Estas técnicas aprovechan la superposición y el entrelazamiento para el almacenamiento, recuperación y procesamiento de imágenes eficientes.
Redes de Tensores
Las redes de tensores, desarrolladas inicialmente para la simulación de sistemas cuánticos, se están aplicando cada vez más a tareas de aprendizaje automático como el reconocimiento de imágenes. La biblioteca TensorNetwork de Google, Perimeter Institute y X facilita la implementación de algoritmos de redes de tensores.
Finanzas Cuánticas
El campo de las finanzas cuánticas explora las aplicaciones de la computación cuántica y el QML en la ingeniería financiera. Las áreas de interés incluyen la fijación de precios de derivados, la optimización de carteras y el análisis de riesgos. Instituciones como la Chicago Quantum Exchange están investigando activamente estas aplicaciones.
Comunicaciones Cuánticas
Existe un esfuerzo global para construir una red cuántica mundial que admita la transmisión de información cuántica. Las tecnologías como la distribución cuántica de claves (QKD) y la teletransportación cuántica son fundamentales para la comunicación cuántica segura. El aprendizaje automático se está utilizando para optimizar el rendimiento de las redes cuánticas y mitigar los errores.
Los avances en la computación cuántica prometen revolucionar campos como la química, las finanzas y las comunicaciones, abriendo nuevas vías para la investigación y el desarrollo tecnológico.