Hola AHS: ejecuta tu primera simulación hamiltoniana analógica - Amazon Braket

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Hola AHS: ejecuta tu primera simulación hamiltoniana analógica

HA

Simulación hamiltoniana analógica(AHS) es un paradigma de la computación cuántica diferente de los circuitos cuánticos: en lugar de una secuencia de puertas, cada una de las cuales actúa solo sobre un par de cúbits a la vez, un programa de AHS se define mediante los parámetros dependientes del tiempo y el espacio del hamiltoniano en cuestión. ElEl hamiltoniano de un sistemacodifica sus niveles de energía y los efectos de las fuerzas externas, que en conjunto rigen la evolución temporal de sus estados. Para un sistema de cúbits N, el hamiltoniano se puede representar con un 2NX2Nmatriz cuadrada de números complejos.

Los dispositivos cuánticos capaces de ejecutar el AHS ajustarán sus parámetros (por ejemplo, la amplitud y la desafinación de un campo conductor coherente) para aproximarse mucho a la evolución temporal del sistema cuántico según el hamiltoniano personalizado. El paradigma AHS es adecuado para simular las propiedades estáticas y dinámicas de los sistemas cuánticos de muchas partículas que interactúan. Las QPUs diseñadas específicamente, como ladispositivo AquiladeQuErapuede simular la evolución temporal de sistemas con tamaños que de otro modo serían imposibles en el hardware clásico.

Cadena de espín interactiva

Como ejemplo canónico de un sistema de muchas partículas que interactúan, consideremos un anillo de ocho espines (cada uno de los cuales puede estar en estados «arriba» y «abajo»). Si bien es pequeño, este sistema modelo ya presenta un puñado de fenómenos interesantes relacionados con materiales magnéticos de origen natural. En este ejemplo, mostraremos cómo preparar el denominado orden antiferromagnético, en el que los espines consecutivos apuntan en direcciones opuestas.


               AntiFerromagnetic

Arreglo

Usaremos un átomo neutro para representar cada giro, y los estados de espín «arriba» y «abajo» se codificarán en el estado de Rydberg excitado y en el estado fundamental de los átomos, respectivamente. En primer lugar, se crea la disposición en 2-D. Podemos programar el anillo de tiradas anterior con el siguiente código.

Requisitos previos: Necesita instalar pip elSDK de.. (Si utilizas una instancia de notebook alojada en Braket, este SDK viene preinstalado con las notebooks). Para reproducir los gráficos, también necesitas instalar matplotlib por separado con el comando shellpip install matplotlib.

import numpy as np import matplotlib.pyplot as plt # required for plotting from braket.ahs.atom_arrangement import AtomArrangement a = 5.7e-6 # nearest-neighbor separation (in meters) register = AtomArrangement() register.add(np.array([0.5, 0.5 + 1/np.sqrt(2)]) * a) register.add(np.array([0.5 + 1/np.sqrt(2), 0.5]) * a) register.add(np.array([0.5 + 1/np.sqrt(2), - 0.5]) * a) register.add(np.array([0.5, - 0.5 - 1/np.sqrt(2)]) * a) register.add(np.array([-0.5, - 0.5 - 1/np.sqrt(2)]) * a) register.add(np.array([-0.5 - 1/np.sqrt(2), - 0.5]) * a) register.add(np.array([-0.5 - 1/np.sqrt(2), 0.5]) * a) register.add(np.array([-0.5, 0.5 + 1/np.sqrt(2)]) * a)

con el que también podemos trazar

fig, ax = plt.subplots(1, 1, figsize=(7,7)) xs, ys = [register.coordinate_list(dim) for dim in (0, 1)] ax.plot(xs, ys, 'r.', ms=15) for idx, (x, y) in enumerate(zip(xs, ys)): ax.text(x, y, f" {idx}", fontsize=12) plt.show() # this will show the plot below in an ipython or jupyter session

               PlotNeutralAtoms

Interacción

Para preparar la fase antiferromagnética, necesitamos inducir interacciones entre espines vecinos. Usamos lainteracción de van der Waalspara ello, que se implementa de forma nativa mediante dispositivos de átomos neutros (como elAquiladispositivo deQuEra). Usando la representación del espín, el término hamiltoniano para esta interacción se puede expresar como la suma de todos los pares de espines (j, k).


               Interacción

Aquí, nj=↑j~↑jes un operador que toma el valor de 1 solo si el espín j está en el estado «hacia arriba» y 0 en caso contrario. La fuerza es Vj,k=C6/(d)j,k​)6, donde C6es el coeficiente fijo, y dj,kes la distancia euclidiana entre los espines j y k. El efecto inmediato de este término de interacción es que cualquier estado en el que tanto el espín j como el espín k estén «hacia arriba» tiene una energía elevada (en la cantidad Vj,k). Al diseñar cuidadosamente el resto del programa AHS, esta interacción evitará que los giros vecinos estén en estado «activo», un efecto que se conoce comúnmente como «bloqueo de Rydberg».

Campo de conducción

Al comienzo del programa AHS, todos los giros (por defecto) comienzan en su estado «inactivo», es decir, se encuentran en la denominada fase ferromagnética. Con la vista puesta en nuestro objetivo de preparar la fase antiferromagnética, especificamos un campo impulsor coherente y dependiente del tiempo que hace que los espines pasen sin problemas de este estado a un estado compuesto por varios cuerpos, en el que se prefieren los estados «ascendentes». El hamiltoniano correspondiente se puede escribir como


               Unidad H

donde Ω (t), ω (t) y △ (t) son la amplitud global dependiente del tiempo (también conocida comoFrecuencia de.), la fase y la desafinación del campo de conducción que afectan a todos los giros de manera uniforme. Aquí S−,k=↓k​⟨↑ky S+,k=( S−,k)=↑k​~↓kson los operadores de subida y bajada del espín k, respectivamente, y nk=↑k⟨↑kes el mismo operador que antes. La parte Ω del campo impulsor acopla de manera coherente los estados «hacia abajo» y «hacia arriba» de todos los giros simultáneamente, mientras que la parte delta controla la recompensa de energía para los estados «hacia arriba».

Para programar una transición suave de la fase ferromagnética a la fase antiferromagnética, especificamos el campo impulsor con el siguiente código.

from braket.timings.time_series import TimeSeries from braket.ahs.driving_field import DrivingField # smooth transition from "down" to "up" state time_max = 4e-6 # seconds time_ramp = 1e-7 # seconds omega_max = 6300000.0 # rad / sec delta_start = -5 * omega_max delta_end = 5 * omega_max omega = TimeSeries() omega.put(0.0, 0.0) omega.put(time_ramp, omega_max) omega.put(time_max - time_ramp, omega_max) omega.put(time_max, 0.0) delta = TimeSeries() delta.put(0.0, delta_start) delta.put(time_ramp, delta_start) delta.put(time_max - time_ramp, delta_end) delta.put(time_max, delta_end) phi = TimeSeries().put(0.0, 0.0).put(time_max, 0.0) drive = DrivingField( amplitude=omega, phase=phi, detuning=delta )

Podemos visualizar la serie temporal del campo conductor con el siguiente script.

fig, axes = plt.subplots(3, 1, figsize=(12, 7), sharex=True) ax = axes[0] time_series = drive.amplitude.time_series ax.plot(time_series.times(), time_series.values(), '.-'); ax.grid() ax.set_ylabel('Omega [rad/s]') ax = axes[1] time_series = drive.detuning.time_series ax.plot(time_series.times(), time_series.values(), '.-'); ax.grid() ax.set_ylabel('Delta [rad/s]') ax = axes[2] time_series = drive.phase.time_series # Note: time series of phase is understood as a piecewise constant function ax.step(time_series.times(), time_series.values(), '.-', where='post'); ax.set_ylabel('phi [rad]') ax.grid() ax.set_xlabel('time [s]') plt.show() # this will show the plot below in an ipython or jupyter session

               DrivingTimeSeries

Programa AHS

El registro, el campo conductor (y las interacciones implícitas de van der Waals) componen el programa de simulación hamiltoniana analógicaahs_program.

from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation ahs_program = AnalogHamiltonianSimulation( register=register, hamiltonian=drive )

Se ejecuta en un simulador local

Como este ejemplo es pequeño (menos de 15 giros), antes de ejecutarlo en una QPU compatible con AHS, podemos ejecutarlo en el simulador AHS local que viene con el SDK de Braket. Como el simulador local está disponible de forma gratuita con el SDK de Braket, esta es la mejor práctica para asegurarnos de que nuestro código se ejecute correctamente.

En este caso, podemos establecer el número de disparos en un valor alto (por ejemplo, 1 millón) porque el simulador local registra la evolución temporal del estado cuántico y extrae muestras del estado final; por lo tanto, el número de disparos aumenta y el tiempo de ejecución total solo se incrementa marginalmente.

from braket.devices import LocalSimulator device = LocalSimulator("braket_ahs") result_simulator = device.run( ahs_program, shots=1_000_000 ).result() # takes about 5 seconds

Análisis de los resultados del simulador

Podemos agregar los resultados de los tiros con la siguiente función que deduce el estado de cada giro (que puede ser «d» para «abajo», «u» para «arriba» o «e» para un sitio vacío) y cuenta cuántas veces se ha producido cada configuración en los tiros.

from collections import Counter def get_counts(result): """Aggregate state counts from AHS shot results A count of strings (of length = # of spins) are returned, where each character denotes the state of a spin (site): e: empty site u: up state spin d: down state spin Args: result (braket.tasks.analog_hamiltonian_simulation_quantum_task_result.AnalogHamiltonianSimulationQuantumTaskResult) Returns dict: number of times each state configuration is measured """ state_counts = Counter() states = ['e', 'u', 'd'] for shot in result.measurements: pre = shot.pre_sequence post = shot.post_sequence state_idx = np.array(pre) * (1 + np.array(post)) state = "".join(map(lambda s_idx: states[s_idx], state_idx)) state_counts.update((state,)) return dict(state_counts) counts_simulator = get_counts(result_simulator) # takes about 5 seconds print(counts_simulator)
{'udududud': 330944, 'dudududu': 329576, 'dududdud': 38033, ...}

Aquícountses un diccionario que cuenta el número de veces que se observa cada configuración de estado en las tomas. También podemos visualizarlos con el siguiente código.

from collections import Counter def has_neighboring_up_states(state): if 'uu' in state: return True if state[0] == 'u' and state[-1] == 'u': return True return False def number_of_up_states(state): return Counter(state)['u'] def plot_counts(counts): non_blockaded = [] blockaded = [] for state, count in counts.items(): if not has_neighboring_up_states(state): collection = non_blockaded else: collection = blockaded collection.append((state, count, number_of_up_states(state))) blockaded.sort(key=lambda _: _[1], reverse=True) non_blockaded.sort(key=lambda _: _[1], reverse=True) for configurations, name in zip((non_blockaded, blockaded), ('no neighboring "up" states', 'some neighboring "up" states')): plt.figure(figsize=(14, 3)) plt.bar(range(len(configurations)), [item[1] for item in configurations]) plt.xticks(range(len(configurations))) plt.gca().set_xticklabels([item[0] for item in configurations], rotation=90) plt.ylabel('shots') plt.grid(axis='y') plt.title(f'{name} configurations') plt.show() plot_counts(counts_simulator)

               AHS cuenta 1

               AHS cuenta 2

A partir de los gráficos, podemos leer las siguientes observaciones para comprobar que hemos preparado con éxito la fase antiferromagnética.

  1. En general, los estados no bloqueados (en los que no hay dos espines vecinos en el estado «hacia arriba») son más comunes que los estados en los que al menos un par de espines vecinos se encuentran ambos en estados «hacia arriba».

  2. En general, se prefieren los estados con más excitaciones «ascendentes», a menos que la configuración esté bloqueada.

  3. Los estados más comunes son, de hecho, los estados antiferromagnéticos perfectos"dudududu"y"udududud".

  4. Los segundos estados más comunes son aquellos en los que solo hay 3 excitaciones «ascendentes» con separaciones consecutivas de 1, 2, 2. Esto demuestra que la interacción de van der Waals también tiene un efecto (aunque mucho menor) en los vecinos más cercanos.

Se está ejecutando en QuEraes Aquila QPU

Requisitos previos: Además de la tubería, instalar el soporteSDK, si eres nuevo en Amazon Braket, asegúrate de haber completado los pasos necesariosPasos de inicio.

nota

Si utilizas una instancia de notebook alojada en Braket, el SDK de Braket viene preinstalado con la instancia.

Con todas las dependencias instaladas, podemos conectarnos alAquilaQPU.

from braket.aws import AwsDevice aquila_qpu = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")

Para que nuestro programa AHS sea adecuado para laQuEramáquina, necesitamos redondear todos los valores para cumplir con los niveles de precisión permitidos por elAquilaQPU. (Estos requisitos se rigen por los parámetros del dispositivo que llevan la palabra «Resolución» en su nombre. Podemos verlos ejecutandoaquila_qpu.properties.dict()en una libreta de. Para obtener más información sobre las capacidades y los requisitos de Aquila, consulte laIntroducción a Aquilacuaderno de.) Podemos hacerlo llamando aldiscretizemétodo de.

discretized_ahs_program = ahs_program.discretize(aquila_qpu)

Ahora podemos ejecutar el programa (ejecutando solo 100 disparos por ahora) en elAquilaQPU.

nota

Ejecutando este programa en elAquilael procesador tendrá un coste. El Amazon Braket SDK incluye unSistema de seguimiento de costesque permite a los clientes establecer límites de costos y realizar un seguimiento de sus costos casi en tiempo real.

task = aquila_qpu.run(discretized_ahs_program, shots=100) metadata = task.metadata() task_arn = metadata['quantumTaskArn'] task_status = metadata['status'] print(f"ARN: {task_arn}") print(f"status: {task_status}")
task ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef task status: CREATED

Debido a la gran variación del tiempo que puede tardar en ejecutarse una tarea cuántica (según las ventanas de disponibilidad y el uso de la QPU), es una buena idea anotar el ARN de la tarea cuántica para que podamos comprobar su estado más adelante con el siguiente fragmento de código.

# Optionally, in a new python session from braket.aws import AwsQuantumTask SAVED_TASK_ARN = "arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef" task = AwsQuantumTask(arn=SAVED_TASK_ARN) metadata = task.metadata() task_arn = metadata['quantumTaskArn'] task_status = metadata['status'] print(f"ARN: {task_arn}") print(f"status: {task_status}")
*[Output]* task ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef task status: COMPLETED

Una vez que el estado esté COMPLETADO (que también se puede comprobar desde la página de tareas cuánticas del Amazon Braket)consola), podemos consultar los resultados con:

result_aquila = task.result()

Análisis de los resultados de la QPU

Usando lo mismoget_countsfunciones como antes, podemos calcular los recuentos:

counts_aquila = get_counts(result_aquila) print(counts_aquila)
*[Output]* {'udududud': 24, 'dudududu': 17, 'dududdud': 3, ...}

y graficarlos conplot_counts:

plot_counts(counts_aquila)

               QPUPlotCounts

Tenga en cuenta que una pequeña fracción de las tomas tiene sitios vacíos (marcados con una «e»). Esto se debe a una imperfección del 1 al 2% por átomo en la preparación delAquilaQPU. Además, los resultados coinciden con la simulación dentro de la fluctuación estadística esperada debido al reducido número de disparos.

Siguiente

Enhorabuena, ya ha ejecutado su primera carga de trabajo de AHS en Amazon Braket con el simulador AHS local y elAquilaQPU.

Para obtener más información sobre la física de Rydberg, la simulación hamiltoniana analógica y laAquiladispositivo, consulte nuestracuadernos de ejemplo.