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
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 Aquila
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.

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.pip 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

Interacción
Para preparar la fase antiferromagnética, necesitamos inducir interacciones entre espines vecinos. Usamos lainteracción de van der Waals

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

donde Ω (t), ω (t) y △ (t) son la amplitud global dependiente del tiempo (también conocida comoFrecuencia de.
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

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ícounts
es 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)


A partir de los gráficos, podemos leer las siguientes observaciones para comprobar que hemos preparado con éxito la fase antiferromagnética.
-
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».
-
En general, se prefieren los estados con más excitaciones «ascendentes», a menos que la configuración esté bloqueada.
-
Los estados más comunes son, de hecho, los estados antiferromagnéticos perfectos
"dudududu"
y"udududud"
. -
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
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 Aquiladiscretize
mé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 costes
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
result_aquila = task.result()
Análisis de los resultados de la QPU
Usando lo mismoget_counts
funciones 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)

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