El equivalente de 'Hello World' en el mundo D-Wave es el ejemplo del tablero de ajedrez 2D. En este ejemplo, se le da el siguiente gráfico cuadrado con 4 nodos:
Definamos que vértice negro si y blanco si . El objetivo es crear un patrón de tablero de ajedrez con los cuatro vértices en el gráfico. Hay varias formas de definir
y para lograr este resultado. En primer lugar, hay dos posibles soluciones a este problema: σ i = - 1 σ i = + 1 h Jσyoσyo= - 1σyo= + 1hJ
El anillador cuántico D-Wave minimiza el Ising Hamiltoniano que definimos y es importante comprender el efecto de los diferentes ajustes del acoplador. Considere, por ejemplo, el acoplador :J0 , 1
Si lo establecemos en , el Hamiltoniano se minimiza si ambos qubits toman el mismo valor. Decimos que los acopladores negativos se correlacionan . Mientras que si lo establecemos en , el hamiltoniano se minimiza si los dos qubits toman valores opuestos. Por lo tanto, los acopladores positivos anti-correlacionan .J 0 , 1 = + 1J0 , 1= - 1J0 , 1= + 1
En el ejemplo del tablero de ajedrez, queremos anti-correlacionar cada par de qubits vecinos que da lugar al siguiente hamiltoniano:
H= σ0 0σ1+ σ0 0σ2+ σ1σ3+ σ2σ3
En aras de la demostración, añadimos también un término de sesgo en el qubit -ésimo de tal manera que sólo tenemos la solución # 1. Esta solución requiere y, por lo tanto, establecemos su sesgo . El Hamiltoniano final es ahora:σ 0 = - 1 h 0 = 10 0σ0 0= - 1h0 0= 1
H= σ0 0+ σ0 0σ1+σ0 0σ2+σ1σ3+σ2σ3
¡Así que codifíquelo!
NOTA: NECESITA acceso al servicio en la nube de D-Wave para que cualquier cosa funcione.
En primer lugar, asegúrese de tener instalado el paquete de Python dwave_sapi2
( https://cloud.dwavesys.com/qubist/downloads/ ). Todo será Python 2.7 ya que D-Wave actualmente no es compatible con ninguna versión superior de Python. Dicho esto, importemos lo esencial:
from dwave_sapi2.core import solve_ising
from dwave_sapi2.embedding import find_embedding, embed_problem, unembed_answer
from dwave_sapi2.util import get_hardware_adjacency
from dwave_sapi2.remote import RemoteConnection
Para conectarse a la API de D-Wave Solver, necesitará un token de API válido para su solucionador de SAPI, la URL de SAPI y deberá decidir qué procesador cuántico desea utilizar:
DWAVE_SAPI_URL = 'https://cloud.dwavesys.com/sapi'
DWAVE_TOKEN = [your D-Wave API token]
DWAVE_SOLVER = 'DW_2000Q_VFYC_1'
¡Recomiendo usar la D-Wave 2000Q Virtual Full Yield Chimera (VFYC), que es un chip completamente funcional sin qubits muertos! Aquí está el diseño del chip Chimera:
En este punto estoy dividiendo el tutorial en dos partes distintas. En la primera sección, estamos incrustando manualmente el problema en el gráfico de hardware de Chimera y en la segunda sección estamos usando la heurística de incrustación de D-Wave para encontrar una incrustación de hardware.
Inclusión manual
La celda de la unidad en la esquina superior izquierda en el diseño del chip D-Wave 2000Q anterior se ve así:
Tenga en cuenta que no todos los acopladores se visualizan en esta imagen. Como puede ver, no hay un acoplador entre qubit y qubit , que necesitaríamos para implementar directamente nuestro gráfico cuadrado anterior. Es por eso que ahora estamos redefiniendo , , y . Luego continuamos y definimos como una lista y como un diccionario:0 010 → 01 → 42 → 73 → 3hJ
J = {(0,4): 1, (4,3): 1, (3,7): 1, (7,0): 1}
h = [-1,0,0,0,0,0,0,0,0]
h tiene 8 entradas ya que usamos qubits de 0 a 7. Ahora establecemos conexión con la API de Solver y solicitamos el solucionador D-Wave 2000Q VFYC:
connection = RemoteConnection(DWAVE_SAPI_URL, DWAVE_TOKEN)
solver = connection.get_solver(DWAVE_SOLVER)
Ahora, podemos definir el número de lecturas y elegir answer_mode
ser "histograma" que ya ordena los resultados por el número de ocurrencias para nosotros. Ahora estamos listos para resolver la instancia de Ising con el anillador cuántico D-Wave:
params = {"answer_mode": 'histogram', "num_reads": 10000}
results = solve_ising(solver, h, J, **params)
print results
Deberías obtener el siguiente resultado:
{
'timing': {
'total_real_time': 1655206,
'anneal_time_per_run': 20,
'post_processing_overhead_time': 13588,
'qpu_sampling_time': 1640000,
'readout_time_per_run': 123,
'qpu_delay_time_per_sample': 21,
'qpu_anneal_time_per_sample': 20,
'total_post_processing_time': 97081,
'qpu_programming_time': 8748,
'run_time_chip': 1640000,
'qpu_access_time': 1655206,
'qpu_readout_time_per_sample': 123
},
'energies': [-5.0],
'num_occurrences': [10000],
'solutions': [
[1, 3, 3, 1, -1, 3, 3, -1, {
lots of 3 's that I am omitting}]]}
Como puede ver, obtuvimos la energía correcta del estado fundamental ( energies
) de . La cadena de solución está llena de , que es el resultado predeterminado para qubits no utilizados / no medidos y si aplicamos las transformaciones inversas - , , y - obtenemos la cadena de solución correcta . ¡Hecho!- 5.030 → 04 → 17 → 23 → 3[ 1 , - 1 , - 1 , 1 ]
Incrustación heurística
Si comienza a crear instancias de Ising cada vez más grandes, no podrá realizar la incrustación manual. Supongamos que no podemos incrustar manualmente nuestro ejemplo de tablero de ajedrez 2D. y luego permanecen sin cambios desde nuestras definiciones iniciales:Jh
J = {(0,1): 1, (0,2): 1, (1,3): 1, (2,3): 1}
h = [-1,0,0,0]
Nuevamente establecemos la conexión remota y obtenemos la instancia del solucionador D-Wave 2000Q VFYC:
connection = RemoteConnection(DWAVE_SAPI_URL, DWAVE_TOKEN)
solver = connection.get_solver(DWAVE_SOLVER)
Para encontrar una incrustación de nuestro problema, primero debemos obtener la matriz de adyacencia del gráfico de hardware actual:
adjacency = get_hardware_adjacency(solver)
Ahora intentemos encontrar una incrustación de nuestro problema:
embedding = find_embedding(J.keys(), adjacency)
Si se trata de grandes instancias de Ising, es posible que desee buscar incrustaciones en varios subprocesos (en paralelo en varias CPU) y luego seleccionar la incrustación con la longitud de cadena más pequeña. Una cadena es cuando múltiples qubits se ven obligados a actuar como un solo qubit para aumentar el grado de conectividad. Sin embargo, cuanto más larga sea la cadena, más probable es que se rompa. ¡Y las cadenas rotas dan malos resultados!
Ahora estamos listos para insertar nuestro problema en el gráfico:
[h, j0, jc, embeddings] = embed_problem(h, J, embedding, adjacency)
j0
jc
J
J = j0.copy()
J.update(jc)
Ahora, estamos listos para resolver el problema incrustado:
params = {"answer_mode": 'histogram', "num_reads": 10000}
raw_results = solve_ising(solver, h, J, **params)
print 'Lowest energy found: {}'.format(raw_results['energies'])
print 'Number of occurences: {}'.format(raw_results['num_occurrences'])
No raw_results
tendrá sentido para nosotros a menos que hayamos descifrado el problema. En caso de que algunas cadenas se rompan, las estamos arreglando a través de un voto mayoritario según lo define el argumento opcional broken_chains
:
unembedded_results = unembed_answer(raw_results['solutions'],
embedding, broken_chains='vote')
print 'Solution string: {}'.format(unembedded_results)
Si ejecuta esto, debería obtener el resultado correcto en todas las lecturas:
Lowest energy found: [-5.0]
Number of occurences: [10000]
Solution string: [[1, -1, -1, 1]]
Espero que esto haya respondido a su pregunta y le recomiendo que consulte todos los parámetros adicionales que puede pasar a la solve_ising
función para mejorar la calidad de sus soluciones, como num_spin_reversal_transforms
o postprocess
.