¿Cómo explicaría el algoritmo de Grover a un programador profesional?

Aquí hay una respuesta autónoma usando la notación de bra-ket. Lo que hace Grover es encontrar la función inversa sobre los enteros. Específicamente si y = f (x) encuentra x para un y dado. Después de reflexionar, debería poder convencerse de que esto es equivalente a buscar en una base de datos, pero es mucho más general. Ahora mostraremos cómo funciona Grover para el caso de 4 bits.
Primero, tenemos un cuadro negro que calcula f (x), y en particular tomamos el caso cuando [math] f (| 0001 \ rangle) = 1, f (.) = 0
[/ matemáticas] de lo contrario. Comenzamos con 4 qubits,
[matemáticas] | 0000 \ rangle, | 0001 \ rangle,… .., | 1111 \ rangle. [/ math] Lo importante en estos algoritmos es que los aplicaremos a billones de bits o más, por lo que es engañoso para diga, por ejemplo, “¡oh, acabo de ver qué valor es el correcto y qué es tan difícil”? Bueno, el peor caso para n qubits debe formar f () [matemática] 2 ^ n [/ matemática] veces clásicamente. ¿Qué tal en cuanto?

Crearemos una superposición para que todos los estados sean igualmente probables. Recuerde que el cuadrado de la amplitud es la probabilidad, así que aquí los pesos son [matemática] 1/4. [/ Matemática] Entonces tenemos al principio: [matemática]
[| 0000 \ rangle + | 0001 \ rangle +… .. + | 1111 \ rangle] / 4. [/ math] Ahora aplicamos la función a las amplitudes de la superposición y negamos la amplitud para el estado especial que elegimos arbitrariamente arriba para ser [matemáticas] | 0001 \ rangle
[/mates]. Luego reflejamos las amplitudes sobre su media y negación.
Entonces, en la primera iteración, obtenemos una media de 14 / (4 × 16), por lo que los estados ordinarios producen -12 / (4 × 16) y el estado especial está por debajo de la media en -14 / 64-16 / 64 = – 30/64. Entonces ahora se convierte en -44/64. Se puede verificar que el resultado sigue siendo la probabilidad unitaria. Vemos que el valor verdadero ha obtenido más amplitud. Después de algunas iteraciones de esto, casi toda la energía (probabilidad) estará en el valor correcto. El costo aquí es la raíz cuadrada de clásica o [matemática] \ sqrt {2} ^ n [/ matemática]. Esto se ve por enumeración directa.

Creo que esta es una implementación correcta en Python:

La clase Qubits representa un vector de qubits enredados.

importar colecciones
matemáticas de importación
importar al azar

def bit_set (bits, bit):
return ((bits >> bit) & 1) == 1

def bits_to_list (bits, n):
return [bit_set (bits, i) para i en el rango (n)]

def list_to_bits (arr):
ans = 0
para i en rango (len (arr)):
si arr [i]:
ans + = pow (2, i)
volver ans

flip def (bits, bit):
bits de retorno ^ (1 << bit)

def Op (fn):
def Envuelto (self, * args):
fn (self, * args)
autochequeo()
auto.cost + = 1
volver envuelto

clase Qubits (objeto):

def __init __ (self, vals):
vals = mapa (flotante, vals)
length = math.sqrt (sum (val * val para val en vals))
self.P = [val / longitud para val en vals]

lg_len = math.log (len (vals)) / math.log (2)
self.n = int (round (lg_len))

self.cost = 0

verificación de def (auto):
total = sum (val * val para val en self.P)
ok = abs (total – 1.0) <1e-9
afirmar (ok)

@staticmethod
def Cero (n):
P = [0 para _ en el rango (1 << n)]
P [0] = 1
devolver Qubits (P)

medida de def (auto):
hi = sum (val * val para val en self.P)
x = aleatorio.uniforme (0, hola)
para i, p en enumerate (self.P):
si x

return bits_to_list (i, self.n)
más:
x – = p * p

@Op
def rotar (auto, bit, ángulo):
P2 = rango (len (self.P))
para i, p en enumerate (self.P):
si no bit_set (i, bit):
i0 = i
i1 = voltear (i, bit)
P2 [i0] = self.P [i0] * math.cos (ángulo) – self.P [i1] * math.sin (ángulo)
P2 [i1] = self.P [i0] * math.sin (ángulo) + self.P [i1] * math.cos (ángulo)
self.P = P2

@Op
flip def (auto, bit):
para i, p en enumerate (self.P):
si no bit_set (i, bit):
i0 = i ^ (1 << bit)
i1 = i
self.P [i0], self.P [i1] = self.P [i1], self.P [i0]

@Op
cambio de def (self, b1, b2):
para i, p en enumerate (self.P):
si no es bit_set (i, b1) y bit_set (i, b2):
i2 = flip (flip (i, b1), b2)
self.P [i], self.P [i2] = self.P [i2], self.P [i]

@Op
def cnot (self, b1, b2):
# (0, x) -> (0, x)
# (1, x) -> (1, 1-x)
para i, p en enumerate (self.P):
si bit_set (i, b1):
i2 = voltear (i, b2)
self.P [i], self.P [i2] = self.P [i2], self.P [i]

@Op
def Y (self, b1, b2, b3):
para i, p en enumerate (self.P):
si bit_set (i, b1) y bit_set (i, b2):
i2 = voltear (i, b3)
self.P [i], self.P [i2] = self.P [i2], self.P [i]

@Op
def hadamard (self, bit):
para i, p en enumerate (self.P):
si no bit_set (i, bit):
i0 = i
i1 = voltear (i, bit)
p0 = self.P [i0]
p1 = self.P [i1]
self.P [i0] = (p0 + p1) / math.sqrt (2)
self.P [i1] = (p0 – p1) / math.sqrt (2)

# xb -> x (b xor fn (x))
# donde | x | = n y fn: {0,1} ^ n -> {0,1}
@Op
def apply_fn (self, fn, n):
para i, p en enumerate (self.P):
si fn (i):
i2 = voltear (i, n)
self.P [i], self.P [i2] = self.P [i2], self.P [i]

@Op
def negate_if_set (self, bit):
para i, p en enumerate (self.P):
si bit_set (i, bit):
self.P [i] = -self.P [i]

def Grover (f, n):
“” “Con probabilidad constante, calcula algo x tal que f (x) es verdadero.

f: {0,1} ^ n -> {0, 1}

Se ejecuta en operaciones cuánticas O (2 ^ (n / 2))

Si existe alguna entrada x tal que f (x) = 1, devolvemos dicha entrada
con alta probabilidad De lo contrario, devolvemos una entrada aleatoria.
“” ”
u = Qubits.Zeroes (n + 1)
para i en rango (n):
u.hadamard (i)

def OneRound ():
u.apply_fn (f, n)
u.negate_if_set (n)
u.apply_fn (f, n)

para i en rango (n):
u.hadamard (i)
u.apply_fn (lambda x: 1 si x == 0 más 0, n)
u.negate_if_set (n)
u.apply_fn (lambda x: 1 si x == 0 más 0, n)
para i en rango (n):
u.hadamard (i)

para i en rango (pow (2, n / 2)):
Una vuelta()

x = u.medida ()
return list_to_bits (x [: n]), u.cost

M = collections.defaultdict (
lambda: collections.defaultdict (
lambda: collections.defaultdict (int)))

def EPR_quantum ():
x = random.randint (0, 1)
y = random.randint (0, 1)

S = Qubits ([1, 0, 0, 1])
si x == 1:
S.rotate (0, math.pi / 8)
si y == 1:
S.rotate (1, -math.pi / 8)
a, b = S.medida ()
win = a ^ b == (x e y)
M [x] [y] [ganar] + = 1
volver ganar

def testGrover ():
n = 5
x = random.randint (0, pow (2, n) -1)
fn = lambda y: y == x

ans, costo = Grover (fn, n)
volver ans == x

def count_good (fn):
PRUEBAS = 1000
ok = 0
para _ en rango (PRUEBAS):
si fn ():
ok + = 1
flotador de retorno (ok) / PRUEBAS

def main ():
print count_good (testGrover)
principal()

Wikipedia se esfuerza por ser una enciclopedia, no una colección de material introductorio o tutorial. La notación que están utilizando es la de la computación cuántica, modelada a partir de la de la mecánica cuántica, incluso imitando la notación de sujetador y ket de Dirac. Entonces, si realmente desea confiar en Wikipedia para su material tutorial, debe buscar sus artículos sobre algoritmos cuánticos, comenzando con el algoritmo Quantum. En ese artículo, encontrará referencias a otros artículos, como la amplificación de amplitud, que explican parte de la notación.

Pero encontrar todas esas referencias para encontrar explicaciones de toda la notación es tedioso; es mejor que busque una introducción más completa en ArchiveX.

Para lo que vale, aquí hay una explicación muy intuitiva de otro algoritmo cuántico, el algoritmo de Shor: Shor, lo haré. No utiliza ningún tipo de matemática más allá de la aritmética básica. Tal vez después de leerlo pueda entender un poco mejor el otro algoritmo (y los algoritmos cuánticos en general).