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.
HolaAHS: ejecute su primera simulación hamiltoniana analógica
Esta sección proporciona información sobre cómo ejecutar su primera simulación hamiltoniana analógica.
En esta sección:
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 los 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 espín, y los estados de espín «arriba» y «abajo» se codificarán en el estado excitado de Rydberg y en el estado fundamental de los átomos, respectivamente. En primer lugar, creamos la disposición bidimensional. Podemos programar el anillo de tiradas anterior con el siguiente código.
Requisitos previos: Es necesario instalar el Braket mediante pip. SDKpip 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 graficar
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. Para ello utilizamos la interacción de van der Waals
En este caso, nj=↑ j ⟨↑ j es 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 V j,k =C6/(dj,k) 6, donde C 6 es el coeficiente fijo y d es la distancia euclidiana entre los j,k 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 V). j,k Al diseñar cuidadosamente el resto del AHS programa, esta interacción evitará que los giros vecinos estén ambos en estado «positivo», un efecto que se conoce comúnmente como «bloqueo de Rydberg».
Campo de conducción
Al principio del AHS programa, 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 (también conocida como frecuencia de Rabi
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
AHSprograma
El registro, el campo conductor (y las interacciones implícitas de van der Waals) componen el programa de simulación hamiltoniana analógica. ahs_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 tiradas), antes de ejecutarlo en un AHS -compatibleQPU, podemos ejecutarlo en el AHS simulador local que viene con el Braket. SDK Como el simulador local está disponible de forma gratuita con el BraketSDK, esta es una buena 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, aumenta el número de disparos y aumenta el tiempo de ejecución total solo 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
Analizando los resultados del simulador
Podemos sumar 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, ...}
Este 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 y.
"dudududu"
"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.
Corre Aquila QuEra QPU
Requisitos previos: Además de instalar el Braket SDK
nota
Si utiliza una instancia de portátil alojada en Braket, la instancia viene preinstalada con la instancia. SDK
Con todas las dependencias instaladas, podemos conectarnos al Aquila QPU.
from braket.aws import AwsDevice aquila_qpu = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")
Para que nuestro AHS programa sea adecuado para QuEra máquina, necesitamos redondear todos los valores para cumplir con los niveles de precisión permitidos por la Aquila QPU. (Estos requisitos se rigen por los parámetros del dispositivo que llevan la palabra «Resolución» en su nombre. Podemos verlos ejecutándolos aquila_qpu.properties.dict()
en un cuaderno. Para obtener más detalles sobre las capacidades y los requisitos de Aquila, consulte la introducción al cuaderno Aquiladiscretize
discretized_ahs_program = ahs_program.discretize(aquila_qpu)
Ahora podemos ejecutar el programa (por ahora solo se ejecutan 100 disparos) en el Aquila QPU.
nota
Ejecutando este programa en Aquila el procesador conllevará un coste. Amazon Braket SDK incluye un rastreador de costos
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 los intervalos de QPU disponibilidad y el uso), es una buena idea anotar la tarea ARN cuántica para poder 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é COMPLETED (que también se puede comprobar desde la página de tareas cuánticas de la consola
result_aquila = task.result()
Analizando los resultados QPU
Usando las mismas get_counts
funciones que 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 del Aquila QPU. Además, los resultados coinciden con la simulación dentro de la fluctuación estadística esperada debido al reducido número de disparos.
Pasos a seguir a continuación
Enhorabuena, ya ha ejecutado su primera AHS carga de trabajo en Amazon Braket con el AHS simulador local y el Aquila QPU.
Para obtener más información sobre la física de Rydberg, la simulación hamiltoniana analógica y la Aquila dispositivo, consulte nuestros ejemplos de cuadernos.