El algoritmo Deutsch-Jozsa
El algoritmo de Deutsch supera a todos los algoritmos clásicos para un problema de consulta, pero la ventaja es bastante modesta: una consulta frente a dos. El algoritmo Deutsch-Jozsa amplía esta ventaja y, de hecho, puede utilizarse para resolver un par de problemas de consulta diferentes.
Aquí tienes una descripción del circuito cuántico del algoritmo Deutsch-Jozsa. También puede ser necesario un paso clásico adicional de postprocesamiento, que no se muestra en la figura, dependiendo del problema específico que se esté resolviendo.
Por supuesto, no hemos discutido qué problemas resuelve este algoritmo; esto se hace en las dos secciones siguientes.
El problema Deutsch-Jozsa
Empezaremos con el problema de consulta que el algoritmo Deutsch-Jozsa pretendía resolver originalmente, que se conoce como el problema Deutsch-Jozsa.
La función de entrada para este problema tiene la forma para un entero positivo arbitrario Al igual que en el problema de Deutsch, la tarea consiste en obtener si es constante y si es equilibrado, lo que de nuevo significa que el número de cadenas de entrada en las que la función toma el valor es igual al número de cadenas de entrada en las que la función toma el valor .
Obsérvese que, cuando es mayor que existen funciones de la forma que no son ni constantes ni equilibradas. Por ejemplo, la función definida como
no pertenece a ninguna de estas dos categorías. Para el problema Deutsch-Jozsa, simplemente no nos preocupamos por funciones como ésta, ya que se consideran entradas "no importantes". Es decir, para este problema tenemos la promesa de que es constante o equilibrado.
Entrada: una función \ Promesa: es constante o equilibrada \ Salida: si es constante, si es equilibrado
El algoritmo de Deutsch-Jozsa, con su única consulta, resuelve este problema en el siguiente sentido: si todos y cada uno de los resultados de la medición son , entonces la función es constante; y, en caso contrario, si al menos uno de los resultados de la medición es , entonces la función es equilibrada. Otra forma de expresarlo es que al circuito descrito anteriormente le sigue una etapa clásica de posprocesamiento en la que se calcula la operación OR de los resultados de las mediciones para obtener el bit de salida del problema de Deutsch-Jozsa.
Análisis de algoritmos
Para analizar el rendimiento del algoritmo Deutsch-Jozsa para el problema Deutsch-Jozsa, es útil empezar pensando en la acción de una sola capa de puertas Hadamard. Una operación Hadamard puede expresarse como una matriz de la forma habitual,
pero también podemos expresar esta operación en términos de su acción sobre los estados base estándar:
Estas dos ecuaciones pueden combinarse en una sola fórmula,
lo que es cierto para ambas opciones de
Supongamos ahora que en lugar de un único qubit tenemos qubits, y que se realiza una operación Hadamard en cada uno de ellos. La operación combinada en los qubits se describe mediante el producto tensorial ( veces), que escribimos como para mayor concisión y claridad. Utilizando la fórmula anterior, expandiéndola y simplificándola, podemos expresar la acción de esta operación combinada sobre los estados base estándar de los qubits de de la siguiente manera:
Aquí, por cierto, estamos escribiendo cadenas binarias de longitud como y siguiendo la convención de indexación de Qiskit.
Esta fórmula nos proporciona una herramienta útil para analizar el circuito cuántico anterior. Una vez realizada la primera capa de puertas Hadamard, el estado de los qubits de (incluido el qubit más a la izquierda/abajo, que se trata por separado del resto) es
Cuando se realiza la operación , este estado se transforma en
mediante exactamente el mismo fenómeno de retroceso de fase que vimos en el análisis del algoritmo de Deutsch.
A continuación, se ejecuta la segunda capa de puertas Hadamard, que (mediante la fórmula anterior) transforma este estado en
Esta expresión parece algo complicada, y no se puede concluir demasiado sobre las probabilidades de obtener diferentes resultados de medición sin saber más sobre la función
Afortunadamente, todo lo que necesitamos saber es la probabilidad de que cada uno de los resultados de la medición sea - porque esa es la probabilidad de que el algoritmo determine que es constante. Esta probabilidad tiene una fórmula sencilla.
Ten en cuenta que estos valores corresponden a la probabilidad de medir el estado , y no directamente al bit de salida clásico final del problema de Deutsch-Jozsa. El algoritmo devuelve « » cuando todos los resultados de las mediciones son « » (lo que indica que « » es constante) y, en caso contrario, devuelve « » (lo que indica que « » está en equilibrio).
Más en detalle, si es constante, entonces o bien para cada cadena en cuyo caso el valor de la suma es o para cada cadena en cuyo caso el valor de la suma es Dividiendo por y tomando el cuadrado del valor absoluto se obtiene
Si, por el contrario, está equilibrada, entonces toma el valor en la mitad de las cadenas y el valor en la otra mitad, por lo que los términos y de la suma se cancelan y nos quedamos con el valor
Concluimos que el algoritmo funciona correctamente siempre que se cumpla la promesa.
Dificultad clásica
El algoritmo Deutsch-Jozsa funciona siempre, nos da siempre la respuesta correcta cuando se cumple la promesa y requiere una única consulta. ¿Qué diferencia hay con los algoritmos de consulta clásicos para el problema Deutsch-Jozsa?
En primer lugar, cualquier algoritmo clásico determinista que resuelva correctamente el problema Deutsch-Jozsa debe realizar exponencialmente muchas consultas: en el peor de los casos. El razonamiento es que, si un algoritmo determinista consulta en o menos cadenas diferentes, y obtiene el mismo valor de función cada vez, entonces ambas respuestas siguen siendo posibles. La función puede ser constante o equilibrada, pero por mala suerte todas las consultas devuelven el mismo valor de función.
La segunda posibilidad puede parecer improbable, pero en los algoritmos deterministas no hay aleatoriedad ni incertidumbre, por lo que fallarán sistemáticamente en determinadas funciones. En este sentido, los algoritmos cuánticos tienen una ventaja significativa sobre los clásicos.
Sin embargo, hay un inconveniente: los algoritmos probabilísticos clásicos pueden resolver el problema Deutsch-Jozsa con una probabilidad muy alta utilizando sólo unas pocas consultas. En concreto, si simplemente elegimos unas cuantas cadenas diferentes de longitud al azar, y consultamos sobre esas cadenas, es poco probable que obtengamos el mismo valor de función para todas ellas cuando esté equilibrado.
En concreto, si elegimos cadenas de entrada uniformemente al azar, evaluamos y respondemos si los valores de la función son todos iguales, y en caso contrario, entonces siempre acertaremos cuando sea constante, y nos equivocaremos en el caso de que esté equilibrada con probabilidad justo Si tomamos por ejemplo, este algoritmo responderá correctamente con probabilidad mayor que %.
Por esta razón, seguimos teniendo una ventaja bastante modesta de los algoritmos cuánticos sobre los clásicos, pero no deja de ser una ventaja cuantificable que representa una mejora con respecto al algoritmo de Deutsch.
Deutsch-Jozsa con Qiskit
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as npPara implementar el algoritmo Deutsch-Jozsa en Qiskit, empezaremos definiendo una función dj_query que genera un circuito cuántico que implementa una puerta de consulta, para una función seleccionada aleatoriamente que satisface la promesa para el problema Deutsch-Jozsa.
Con un 50% de probabilidad, la función es constante, y con un 50% de cambio, la función está equilibrada.
Para cada una de esas dos posibilidades, la función se selecciona uniformemente entre las funciones de ese tipo.
El argumento es el número de bits de entrada de la función.
def dj_query(num_qubits):
# Create a circuit implementing for a query gate for a random function
# satisfying the promise for the Deutsch-Jozsa problem.
qc = QuantumCircuit(num_qubits + 1)
if np.random.randint(0, 2):
# Flip output qubit with 50% chance
qc.x(num_qubits)
if np.random.randint(0, 2):
# return constant circuit with 50% chance
return qc
# Choose half the possible input strings
on_states = np.random.choice(
range(2**num_qubits), # numbers to sample from
2**num_qubits // 2, # number of samples
replace=False, # makes sure states are only sampled once
)
def add_cx(qc, bit_string):
for qubit, bit in enumerate(reversed(bit_string)):
if bit == "1":
qc.x(qubit)
return qc
for state in on_states:
qc.barrier() # Barriers are added to help visualize how the functions are created.
qc = add_cx(qc, f"{state:0b}")
qc.mcx(list(range(num_qubits)), num_qubits)
qc = add_cx(qc, f"{state:0b}")
qc.barrier()
return qcPodemos mostrar la implementación del circuito cuántico de la puerta de consulta utilizando el método draw como de costumbre.
display(dj_query(3).draw(output="mpl"))Output:
A continuación definimos una función que crea el circuito Deutsch-Jozsa, tomando como argumento una implementación de circuito cuántico de una puerta de consulta.
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in the Deutsch-Jozsa algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.compose(function, inplace=True)
qc.h(range(n))
qc.measure(range(n), range(n))
return qcPor último, se define una función que ejecuta una vez el circuito Deutsch-Jozsa.
def dj_algorithm(function: QuantumCircuit):
# Determine if a function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if "1" in measurements[0]:
return "balanced"
return "constant"Podemos probar nuestra implementación eligiendo una función al azar, mostrando la implementación del circuito cuántico de una puerta de consulta para esta función y, a continuación, ejecutando el algoritmo Deutsch-Jozsa en esa función.
f = dj_query(3)
display(f.draw("mpl"))
display(dj_algorithm(f))Output:
'balanced'
El problema de Bernstein-Vazirani
A continuación, discutiremos un problema conocido como el problema Bernstein-Vazirani. También se denomina problema de muestreo de Fourier, aunque existen formulaciones más generales de este problema que también reciben ese nombre.
En primer lugar, introduzcamos algo de notación. Para dos cadenas binarias cualesquiera y de longitud definimos
Nos referiremos a esta operación como el producto punto binario. Una forma alternativa de definirlo es así.
Observe que se trata de una operación simétrica, lo que significa que el resultado no cambia si intercambiamos y , por lo que somos libres de hacerlo cuando nos convenga. A veces es útil pensar en el producto binario por puntos como la paridad de los bits de en las posiciones en las que la cadena tiene un o, equivalentemente, la paridad de los bits de en las posiciones en las que la cadena tiene un
Con esta notación podemos definir el problema de Bernstein-Vazirani.
Entrada: una función \ Promesa: existe una cadena binaria para la cual para todas \ Salida: la cadena
En realidad no necesitamos un nuevo algoritmo cuántico para este problema; el algoritmo Deutsch-Jozsa lo resuelve. En aras de la claridad, vamos a referirnos al circuito cuántico de arriba, que no incluye el paso de post-procesamiento clásico de calcular el OR, como el circuito Deutsch-Jozsa.
Análisis de algoritmos
Para analizar cómo funciona el circuito Deutsch-Jozsa para una función que satisface la promesa para el problema Bernstein-Vazirani, empezaremos con una rápida observación. Utilizando el producto punto binario, podemos describir alternativamente la acción de puertas Hadamard sobre los estados base estándar de qubits como sigue.
De forma similar a lo que vimos al analizar el algoritmo de Deutsch, esto se debe a que el valor para cualquier entero sólo depende de si es par o impar.
Volviendo al circuito Deutsch-Jozsa, una vez realizada la primera capa de compuertas Hadamard, el estado de los qubits de es
A continuación, se ejecuta la puerta de consulta, que (a través del fenómeno de retroceso de fase) transforma el estado en
Utilizando nuestra fórmula para la acción de una capa de puertas Hadamard, vemos que la segunda capa de puertas Hadamard transforma entonces este estado en
Ahora podemos hacer algunas simplificaciones, en el exponente de dentro de la suma. Se nos promete que para alguna cadena por lo que podemos expresar el estado como
Dado que y son valores binarios, podemos sustituir la suma por el OR exclusivo, de nuevo porque lo único que importa para un número entero en el exponente de es si es par o impar. Haciendo uso de la simetría del producto punto binario, obtenemos esta expresión para el estado:
(Se han añadido paréntesis para mayor claridad, aunque en realidad no son necesarios porque es convencional tratar el producto binario por puntos como si tuviera mayor precedencia que el exclusivo-OR)
En este punto utilizaremos la siguiente fórmula.
Podemos obtener la fórmula mediante una fórmula similar para los bits,
junto con una expansión del producto binario por puntos y de la función bitwise exclusive-OR:
Esto nos permite expresar así el estado del circuito inmediatamente antes de las mediciones:
El último paso consiste en utilizar otra fórmula, que funciona para todas las cadenas binarias
Aquí estamos utilizando una notación simple para cadenas que utilizaremos varias veces más en la lección: es la cadena completamente nula de longitud
Una forma sencilla de argumentar que esta fórmula funciona es considerar los dos casos por separado. Si entonces para cada cadena por lo que el valor de cada término en la suma es y obtenemos sumando y dividiendo por Por otra parte, si cualquiera de los bits de es igual a entonces el producto binario de puntos es igual a para exactamente la mitad de las posibles opciones para y para la otra mitad - porque el valor del producto binario de puntos se voltea (de a o de a ) si volteamos cualquier bit de en una posición donde tiene un
Si ahora aplicamos esta fórmula para simplificar el estado del circuito antes de las medidas, obtenemos
debido a que si y sólo si Así pues, las mediciones revelan precisamente la cadena que buscamos.
Dificultad clásica
Mientras que el circuito Deutsch-Jozsa resuelve el problema Bernstein-Vazirani con una sola consulta, cualquier algoritmo de consulta clásico debe realizar al menos consultas para resolver este problema.
Esto puede razonarse mediante el llamado argumento de la teoría de la información, que es muy sencillo en este caso. Cada consulta clásica revela un único bit de información sobre la solución, y hay bits de información que deben ser descubiertos - por lo que se necesitan al menos consultas.
De hecho, es posible resolver el problema Bernstein-Vazirani de forma clásica consultando la función en cada una de las cadenas que tienen un único en cada posición posible, y para todos los demás bits, lo que revela los bits de de uno en uno. Por lo tanto, la ventaja de los algoritmos cuánticos sobre los clásicos para este problema es consultas frente a consultas.
Bernstein-Vazirani con Qiskit
Ya hemos implementado el circuito Deutsch-Jozsa anteriormente, y aquí lo utilizaremos para resolver el problema Bernstein-Vazirani. Primero definiremos una función que implemente una puerta de consulta para el problema Bernstein-Vazirani dada cualquier cadena binaria
def bv_query(s):
# Create a quantum circuit implementing a query gate for the
# Bernstein-Vazirani problem.
qc = QuantumCircuit(len(s) + 1)
for index, bit in enumerate(reversed(s)):
if bit == "1":
qc.cx(index, len(s))
return qc
display(bv_query("1011").draw(output="mpl"))Output:
Ahora podemos crear una función que ejecute el circuito Deutsch-Jozsa en la función, utilizando la función compile_circuit que se definió anteriormente.
def bv_algorithm(function: QuantumCircuit):
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
return result.get_memory()[0]
display(bv_algorithm(bv_query("1011")))Output:
'1011'
Observación sobre la nomenclatura
En el contexto del problema Bernstein-Vazirani, es habitual que se haga referencia al algoritmo Deutsch-Jozsa como "algoritmo Bernstein-Vazirani" Esto es ligeramente engañoso, porque el algoritmo es el algoritmo Deutsch-Jozsa, como Bernstein y Vazirani dejaron muy claro en su trabajo.
Lo que hicieron Bernstein y Vazirani después de demostrar que el algoritmo Deutsch-Jozsa resuelve el problema Bernstein-Vazirani (como se ha indicado anteriormente) fue definir un problema mucho más complicado, conocido como el problema de muestreo recursivo de Fourier. Se trata de un problema muy elaborado en el que las soluciones a diferentes instancias del problema desbloquean nuevos niveles del problema dispuestos en una estructura arborescente. El problema Bernstein-Vazirani es esencialmente sólo el caso base de este problema más complicado.
El problema de muestreo recursivo de Fourier fue el primer ejemplo conocido de problema de consulta en el que los algoritmos cuánticos tienen una ventaja denominada superpolinómica sobre los algoritmos probabilísticos, superando así la ventaja de la cuántica sobre la clásica ofrecida por el algoritmo Deutsch-Jozsa. Intuitivamente hablando, la versión recursiva del problema amplifica la ventaja de frente a de los algoritmos cuánticos a algo mucho mayor.
El aspecto más difícil del análisis matemático que establece esta ventaja es demostrar que los algoritmos de consulta clásicos no pueden resolver el problema sin hacer muchas consultas. Esto es bastante típico; para muchos problemas puede ser muy difícil descartar enfoques clásicos creativos que los resuelvan eficientemente.
El problema de Simon, y el algoritmo para él descrito en la siguiente sección, proporciona un ejemplo mucho más simple de una ventaja superpolinómica (y, de hecho, exponencial) de los algoritmos cuánticos sobre los clásicos, y por esta razón el problema de muestreo recursivo de Fourier se discute con menos frecuencia. No obstante, es un problema computacional interesante por derecho propio.