El algoritmo de Deutsch
El algoritmo de Deutsch resuelve el problema de la paridad para el caso especial en que En el contexto de la computación cuántica, este problema a veces se denomina problema de Deutsch, y seguiremos esa nomenclatura en esta lección.
Para ser precisos, la entrada se representa mediante una función de un bit a un bit. Existen cuatro funciones de este tipo:
La primera y la última de estas funciones son constantes, mientras que las dos del medio son balanceadas, lo que significa que los dos posibles valores de salida de la función aparecen el mismo número de veces al recorrer las entradas. El problema de Deutsch consiste en determinar a cuál de estas dos categorías pertenece la función de entrada: constante o balanceada.
Si vemos la función de entrada del problema de Deutsch como un acceso aleatorio a una cadena, estamos pensando en una cadena de dos bits:
Visto de esta manera, el problema de Deutsch consiste en calcular la paridad (o, de forma equivalente, el OR exclusivo) de los dos bits.
Todo algoritmo de consulta clásico que resuelva correctamente este problema debe consultar ambos bits: y Si aprendemos que por ejemplo, la respuesta podría seguir siendo o , dependiendo de si o , respectivamente. Todos los demás casos son similares; conocer solo uno de los dos bits no proporciona ninguna información sobre su paridad. Por lo tanto, el circuito booleano descrito en la sección anterior es lo mejor que podemos hacer en términos del número de consultas necesarias para resolver este problema.
Descripción del circuito cuántico
El algoritmo de Deutsch resuelve el problema de Deutsch con una sola consulta, lo que proporciona una ventaja cuantificable de la computación cuántica sobre la clásica. Puede parecer una ventaja modesta —una consulta en vez de dos— pero hay que empezar por algún sitio. Los avances científicos a veces tienen orígenes aparentemente humildes.
Aquí se muestra un circuito cuántico que describe el algoritmo de Deutsch:
Análisis
Para analizar el algoritmo de Deutsch, recorreremos la acción del circuito anterior e identificaremos los estados de los qubits en los momentos indicados por esta figura:
El estado inicial es y las dos operaciones Hadamard en el lado izquierdo del circuito transforman este estado en
(Como siempre, seguimos la convención de ordenamiento de qubits de Qiskit, que coloca el qubit superior a la derecha y el qubit inferior a la izquierda.) Puede resultar poco intuitivo escribir este estado producto parcialmente distribuido (dejando los estados del qubit 1 factorizados), pero esto hará que nuestras expresiones posteriores sean más compactas.
A continuación, se aplica la compuerta . De acuerdo con la definición de la compuerta , el valor de la función para el estado clásico del qubit superior/más a la derecha se aplica con XOR al qubit inferior/más a la izquierda, lo que transforma en el estado
Podemos simplificar esta expresión observando que la fórmula
es válida para ambos valores posibles Más explícitamente, los dos casos son los siguientes.
Así, podemos expresar de forma alternativa de la siguiente manera:
¡Algo interesante acaba de ocurrir! Aunque la acción de la compuerta sobre los estados de la base estándar deja intacto el qubit superior/más a la derecha y aplica el XOR del valor de la función al qubit inferior/más a la izquierda, aquí vemos que el estado del qubit superior/más a la derecha ha cambiado (en general) mientras que el estado del qubit inferior/más a la izquierda permanece igual, estando específicamente en el estado antes y después de que se aplique la compuerta . Este fenómeno se conoce como phase kickback (retroceso de fase), y hablaremos más sobre él en breve.
Con una última simplificación, que consiste en extraer el factor fuera de la suma, obtenemos esta expresión del estado :
Observa que en esta expresión tenemos en el exponente de en lugar de que es lo que podríamos esperar desde un punto de vista puramente algebraico, pero obtenemos el mismo resultado de cualquier forma. Esto se debe a que el valor para cualquier entero depende únicamente de si es par o impar.
Aplicar la compuerta Hadamard final al qubit superior nos deja con el estado
lo que conduce al resultado correcto con probabilidad cuando se mide el qubit de la derecha/superior.
Observaciones adicionales sobre el phase kickback
Antes de continuar, veamos el análisis anterior desde un ángulo ligeramente diferente que puede arrojar luz sobre el fenómeno del phase kickback.
Primero, observa que la siguiente fórmula es válida para toda elección de bits
Esto puede verificarse comprobándolo para los dos valores posibles y :
Usando esta fórmula, vemos que
para toda elección de bits Como esta fórmula es válida para y , vemos por linealidad que
para todos los vectores de estado de qubit y por lo tanto
La clave que hace que esto funcione es que En términos matemáticos, el vector es un vector propio de la matriz con valor propio
Discutiremos los vectores propios y valores propios con mayor detalle en la próxima lección sobre Estimación de fase y factorización, donde el fenómeno del phase kickback se generaliza a otras operaciones unitarias.
Teniendo en cuenta que los escalares se propagan libremente a través de los productos tensoriales, encontramos una forma alternativa de razonar cómo la operación transforma en en el análisis anterior:
Implementación en Qiskit
Ahora veamos cómo podemos implementar el algoritmo de Deutsch en Qiskit. Comenzaremos con una verificación de versión y luego realizaremos las importaciones necesarias únicamente para esta implementación. Para las implementaciones de otros algoritmos que siguen, realizaremos las importaciones necesarias por separado en aras de una mayor modularidad.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer
from qiskit import __version__
print(__version__)
2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
Primero definiremos un circuito cuántico que implemente una compuerta de consulta para una de las cuatro funciones o de un bit a un bit descritas anteriormente. Como ya mencionamos, la implementación de las compuertas de consulta no es realmente parte del propio algoritmo de Deutsch; aquí esencialmente solo mostramos una forma de preparar la entrada, en la forma de una implementación en circuito de una compuerta de consulta.
def deutsch_function(case: int):
# This function generates a quantum circuit for one of the 4 functions
# from one bit to one bit
if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")
f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f
Podemos ver cómo luce cada circuito usando el método draw. Aquí está el circuito para la función
display(deutsch_function(3).draw(output="mpl"))
A continuación crearemos el circuito cuántico real para el algoritmo de Deutsch, sustituyendo la compuerta de consulta por una implementación en circuito cuántico dada como argumento. En breve conectaremos uno de los cuatro circuitos definidos por la función deutsch_function que definimos antes.
Se incluyen barreras para mostrar la separación visual entre la implementación de la compuerta de consulta y el resto del circuito.
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in Deutsch's algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.barrier()
qc.compose(function, inplace=True)
qc.barrier()
qc.h(range(n))
qc.measure(range(n), range(n))
return qc
De nuevo podemos ver cómo luce el circuito usando el método draw.
display(compile_circuit(deutsch_function(3)).draw(output="mpl"))
Finalmente, crearemos una función que ejecute el circuito definido anteriormente una sola vez y devuelva el resultado apropiado: "constant" o "balanced."
def deutsch_algorithm(function: QuantumCircuit):
# Determine if a one-bit function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if measurements[0] == "0":
return "constant"
return "balanced"
Ahora podemos ejecutar el algoritmo de Deutsch en cualquiera de las cuatro funciones definidas anteriormente.
f = deutsch_function(3)
display(deutsch_algorithm(f))
'balanced'