We will start using the newly released neutral atom quantum computer from the tools. This time we will use braket, which is provided by amazon and is not currently supported by the blueqatSDK.
The neutral atom uses an analog quantum processor, which uses knowledge of the Rydberg Brocade and so on. The calculation is based on an equation called the Hamiltonian, which can be found in various articles on blueqat.com.
First, bring your amazon braket up to date. To use the actual device, you will need to register an account again, but we will skip that for this article.
The example is used from here.
Translated with www.DeepL.com/Translator (free version)
Sometimes I also include original codes.
!pip install -U amazon-braket-sdk
Requirement already satisfied: amazon-braket-sdk in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (1.31.0)
Collecting amazon-braket-sdk
Downloading amazon_braket_sdk-1.33.0-py3-none-any.whl (241 kB)
[K |████████████████████████████████| 241 kB 27.3 MB/s eta 0:00:01
[?25hRequirement already satisfied: boltons in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (20.2.1)
Requirement already satisfied: numpy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.21.6)
Requirement already satisfied: sympy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.7.1)
Requirement already satisfied: networkx in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (2.4)
Requirement already satisfied: backoff in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.10.0)
Requirement already satisfied: nest-asyncio in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.5.1)
Collecting oqpy==0.1.0
Downloading oqpy-0.1.0-py3-none-any.whl (26 kB)
Requirement already satisfied: typing-extensions<5.0.0,>=4.3.0 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from oqpy==0.1.0->amazon-braket-sdk) (4.3.0)
Collecting openpulse
Downloading openpulse-0.4.0-py3-none-any.whl (376 kB)
[K |████████████████████████████████| 376 kB 72.9 MB/s eta 0:00:01
[?25hRequirement already satisfied: importlib-metadata in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from openpulse->amazon-braket-sdk) (2.0.0)
Requirement already satisfied: antlr4-python3-runtime<4.12,>=4.7 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from openpulse->amazon-braket-sdk) (4.8)
Collecting openqasm3
Downloading openqasm3-0.4.0-py3-none-any.whl (385 kB)
[K |████████████████████████████████| 385 kB 40.0 MB/s eta 0:00:01
[?25hCollecting amazon-braket-default-simulator>=1.10.0
Downloading amazon_braket_default_simulator-1.10.0-py3-none-any.whl (206 kB)
[K |████████████████████████████████| 206 kB 71.9 MB/s eta 0:00:01
[?25hRequirement already satisfied: scipy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-default-simulator>=1.10.0->amazon-braket-sdk) (1.5.2)
Requirement already satisfied: opt-einsum in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-default-simulator>=1.10.0->amazon-braket-sdk) (3.1.0)
Collecting antlr4-python3-runtime<4.12,>=4.7
Using cached antlr4-python3-runtime-4.9.2.tar.gz (117 kB)
Collecting pydantic==1.9.0
Using cached pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.9 MB)
Collecting amazon-braket-schemas>=1.12.0
Downloading amazon_braket_schemas-1.12.0-py3-none-any.whl (109 kB)
[K |████████████████████████████████| 109 kB 73.2 MB/s eta 0:00:01
[?25hCollecting boto3>=1.22.3
Downloading boto3-1.25.5-py3-none-any.whl (132 kB)
[K |████████████████████████████████| 132 kB 66.0 MB/s eta 0:00:01
[?25hRequirement already satisfied: jmespath<2.0.0,>=0.7.1 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from boto3>=1.22.3->amazon-braket-sdk) (0.10.0)
Collecting botocore<1.29.0,>=1.28.5
Downloading botocore-1.28.5-py3-none-any.whl (9.3 MB)
[K |████████████████████████████████| 9.3 MB 63.0 MB/s eta 0:00:01
[?25hRequirement already satisfied: python-dateutil<3.0.0,>=2.1 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (2.8.1)
Requirement already satisfied: urllib3<1.27,>=1.25.4 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (1.25.11)
Collecting mypy-extensions<0.5.0,>=0.4.3
Downloading mypy_extensions-0.4.3-py2.py3-none-any.whl (4.5 kB)
Requirement already satisfied: six>=1.5 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from python-dateutil<3.0.0,>=2.1->botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (1.15.0)
Collecting s3transfer<0.7.0,>=0.6.0
Downloading s3transfer-0.6.0-py3-none-any.whl (79 kB)
[K |████████████████████████████████| 79 kB 10.7 MB/s eta 0:00:01
[?25hRequirement already satisfied: zipp>=0.5 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from importlib-metadata->openpulse->amazon-braket-sdk) (3.4.0)
Requirement already satisfied: decorator>=4.3.0 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from networkx->amazon-braket-sdk) (4.4.2)
Requirement already satisfied: mpmath>=0.19 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from sympy->amazon-braket-sdk) (1.2.1)
Building wheels for collected packages: antlr4-python3-runtime
Building wheel for antlr4-python3-runtime (setup.py) ... [?25ldone
[?25h Created wheel for antlr4-python3-runtime: filename=antlr4_python3_runtime-4.9.2-py3-none-any.whl size=144566 sha256=93a2d2ba99501db149d0bd31e57327f71039a9732da618116340f16db8a86422
Stored in directory: /home/ec2-user/.cache/pip/wheels/14/4c/18/1dbbc9875a2547d2063400ea9f404da4af3331965a71061029
Successfully built antlr4-python3-runtime
Installing collected packages: openqasm3, antlr4-python3-runtime, pydantic, botocore, s3transfer, openpulse, mypy-extensions, amazon-braket-schemas, oqpy, boto3, amazon-braket-default-simulator, amazon-braket-sdk
Attempting uninstall: antlr4-python3-runtime
Found existing installation: antlr4-python3-runtime 4.8
Uninstalling antlr4-python3-runtime-4.8:
Successfully uninstalled antlr4-python3-runtime-4.8
Attempting uninstall: pydantic
Found existing installation: pydantic 1.10.2
Uninstalling pydantic-1.10.2:
Successfully uninstalled pydantic-1.10.2
Attempting uninstall: botocore
Found existing installation: botocore 1.17.44
Uninstalling botocore-1.17.44:
Successfully uninstalled botocore-1.17.44
Attempting uninstall: s3transfer
Found existing installation: s3transfer 0.3.4
Uninstalling s3transfer-0.3.4:
Successfully uninstalled s3transfer-0.3.4
Attempting uninstall: amazon-braket-schemas
Found existing installation: amazon-braket-schemas 1.10.2
Uninstalling amazon-braket-schemas-1.10.2:
Successfully uninstalled amazon-braket-schemas-1.10.2
Attempting uninstall: boto3
Found existing installation: boto3 1.14.43
Uninstalling boto3-1.14.43:
Successfully uninstalled boto3-1.14.43
Attempting uninstall: amazon-braket-default-simulator
Found existing installation: amazon-braket-default-simulator 1.8.1
Uninstalling amazon-braket-default-simulator-1.8.1:
Successfully uninstalled amazon-braket-default-simulator-1.8.1
Attempting uninstall: amazon-braket-sdk
Found existing installation: amazon-braket-sdk 1.31.0
Uninstalling amazon-braket-sdk-1.31.0:
Successfully uninstalled amazon-braket-sdk-1.31.0
[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
quantum-blackbird 0.4.0 requires antlr4-python3-runtime==4.8, but you have antlr4-python3-runtime 4.9.2 which is incompatible.
awscli 1.18.121 requires botocore==1.17.44, but you have botocore 1.28.5 which is incompatible.
awscli 1.18.121 requires s3transfer<0.4.0,>=0.3.0, but you have s3transfer 0.6.0 which is incompatible.[0m
Successfully installed amazon-braket-default-simulator-1.10.0 amazon-braket-schemas-1.12.0 amazon-braket-sdk-1.33.0 antlr4-python3-runtime-4.9.2 boto3-1.25.5 botocore-1.28.5 mypy-extensions-0.4.3 openpulse-0.4.0 openqasm3-0.4.0 oqpy-0.1.0 pydantic-1.9.0 s3transfer-0.6.0
Before we begin, as of November 6, 2022, we did not have all the libraries yet, so we manually installed the following library here. If you use it, please copy and paste and use it.
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from braket.ahs.atom_arrangement import SiteType
from braket.timings.time_series import TimeSeries
from braket.ahs.driving_field import DrivingField
from braket.ahs.shifting_field import ShiftingField
from braket.ahs.field import Field
from braket.ahs.pattern import Pattern
from collections import Counter
from typing import Dict, List, Tuple
from braket.tasks.analog_hamiltonian_simulation_quantum_task_result import AnalogHamiltonianSimulationQuantumTaskResult
from braket.ahs.atom_arrangement import AtomArrangement
def show_register(
register: AtomArrangement,
blockade_radius: float=0.0,
what_to_draw: str="bond",
show_atom_index:bool=True
):
"""Plot the given register
Args:
register (AtomArrangement): A given register
blockade_radius (float): The blockade radius for the register. Default is 0
what_to_draw (str): Either "bond" or "circle" to indicate the blockade region.
Default is "bond"
show_atom_index (bool): Whether showing the indices of the atoms. Default is True
"""
filled_sites = [site.coordinate for site in register if site.site_type == SiteType.FILLED]
empty_sites = [site.coordinate for site in register if site.site_type == SiteType.VACANT]
fig = plt.figure(figsize=(7, 7))
if filled_sites:
plt.plot(np.array(filled_sites)[:, 0], np.array(filled_sites)[:, 1], 'r.', ms=15, label='filled')
if empty_sites:
plt.plot(np.array(empty_sites)[:, 0], np.array(empty_sites)[:, 1], 'k.', ms=5, label='empty')
plt.legend(bbox_to_anchor=(1.1, 1.05))
if show_atom_index:
for idx, site in enumerate(register):
plt.text(*site.coordinate, f" {idx}", fontsize=12)
if blockade_radius > 0 and what_to_draw=="bond":
for i in range(len(filled_sites)):
for j in range(i+1, len(filled_sites)):
dist = np.linalg.norm(np.array(filled_sites[i]) - np.array(filled_sites[j]))
if dist <= blockade_radius:
plt.plot([filled_sites[i][0], filled_sites[j][0]], [filled_sites[i][1], filled_sites[j][1]], 'b')
if blockade_radius > 0 and what_to_draw=="circle":
for site in filled_sites:
plt.gca().add_patch( plt.Circle((site[0],site[1]), blockade_radius/2, color="b", alpha=0.3) )
plt.gca().set_aspect(1)
plt.show()
def rabi_pulse(
rabi_pulse_area: float,
omega_max: float,
omega_slew_rate_max: float
) -> Tuple[List[float], List[float]]:
"""Get a time series for Rabi frequency with specified Rabi phase, maximum amplitude
and maximum slew rate
Args:
rabi_pulse_area (float): Total area under the Rabi frequency time series
omega_max (float): The maximum amplitude
omega_slew_rate_max (float): The maximum slew rate
Returns:
Tuple[List[float], List[float]]: A tuple containing the time points and values
of the time series for the time dependent Rabi frequency
Notes: By Rabi phase, it means the integral of the amplitude of a time-dependent
Rabi frequency, \int_0^T\Omega(t)dt, where T is the duration.
"""
phase_threshold = omega_max**2 / omega_slew_rate_max
if rabi_pulse_area <= phase_threshold:
t_ramp = np.sqrt(rabi_pulse_area / omega_slew_rate_max)
t_plateau = 0
else:
t_ramp = omega_max / omega_slew_rate_max
t_plateau = (rabi_pulse_area / omega_max) - t_ramp
t_pules = 2 * t_ramp + t_plateau
time_points = [0, t_ramp, t_ramp + t_plateau, t_pules]
amplitude_values = [0, t_ramp * omega_slew_rate_max, t_ramp * omega_slew_rate_max, 0]
return time_points, amplitude_values
def get_counts(result: AnalogHamiltonianSimulationQuantumTaskResult) -> Dict[str, int]:
"""Aggregate state counts from AHS shot results
Args:
result (AnalogHamiltonianSimulationQuantumTaskResult): The result
from which the aggregated state counts are obtained
Returns:
Dict[str, int]: number of times each state configuration is measured
Notes: We use the following convention to denote the state of an atom (site):
e: empty site
r: Rydberg state atom
g: ground state atom
"""
state_counts = Counter()
states = ['e', 'r', 'g']
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)
def get_drive(
times: List[float],
amplitude_values: List[float],
detuning_values: List[float],
phase_values: List[float]
) -> DrivingField:
"""Get the driving field from a set of time points and values of the fields
Args:
times (List[float]): The time points of the driving field
amplitude_values (List[float]): The values of the amplitude
detuning_values (List[float]): The values of the detuning
phase_values (List[float]): The values of the phase
Returns:
DrivingField: The driving field obtained
"""
assert len(times) == len(amplitude_values)
assert len(times) == len(detuning_values)
assert len(times) == len(phase_values)
amplitude = TimeSeries()
detuning = TimeSeries()
phase = TimeSeries()
for t, amplitude_value, detuning_value, phase_value in zip(times, amplitude_values, detuning_values, phase_values):
amplitude.put(t, amplitude_value)
detuning.put(t, detuning_value)
phase.put(t, phase_value)
drive = DrivingField(
amplitude=amplitude,
detuning=detuning,
phase=phase
)
return drive
def get_shift(times: List[float], values: List[float], pattern: List[float]) -> ShiftingField:
"""Get the shifting field from a set of time points, values and pattern
Args:
times (List[float]): The time points of the shifting field
values (List[float]): The values of the shifting field
pattern (List[float]): The pattern of the shifting field
Returns:
ShiftingField: The shifting field obtained
"""
assert len(times) == len(values)
magnitude = TimeSeries()
for t, v in zip(times, values):
magnitude.put(t, v)
shift = ShiftingField(Field(magnitude, Pattern(pattern)))
return shift
def show_global_drive(drive, axes=None, **plot_ops):
"""Plot the driving field
Args:
drive (DrivingField): The driving field to be plot
axes: matplotlib axis to draw on
**plot_ops: options passed to matplitlib.pyplot.plot
"""
data = {
'amplitude [rad/s]': drive.amplitude.time_series,
'detuning [rad/s]': drive.detuning.time_series,
'phase [rad]': drive.phase.time_series,
}
if axes is None:
fig, axes = plt.subplots(3, 1, figsize=(7, 7), sharex=True)
for ax, data_name in zip(axes, data.keys()):
if data_name == 'phase [rad]':
ax.step(data[data_name].times(), data[data_name].values(), '.-', where='post',**plot_ops)
else:
ax.plot(data[data_name].times(), data[data_name].values(), '.-',**plot_ops)
ax.set_ylabel(data_name)
ax.grid(ls=':')
axes[-1].set_xlabel('time [s]')
plt.tight_layout()
plt.show()
def show_local_shift(shift:ShiftingField):
"""Plot the shifting field
Args:
shift (ShiftingField): The shifting field to be plot
"""
data = shift.magnitude.time_series
pattern = shift.magnitude.pattern.series
plt.plot(data.times(), data.values(), '.-', label="pattern: " + str(pattern))
plt.xlabel('time [s]')
plt.ylabel('shift [rad/s]')
plt.legend()
plt.tight_layout()
plt.show()
def show_drive_and_shift(drive: DrivingField, shift: ShiftingField):
"""Plot the driving and shifting fields
Args:
drive (DrivingField): The driving field to be plot
shift (ShiftingField): The shifting field to be plot
"""
drive_data = {
'amplitude [rad/s]': drive.amplitude.time_series,
'detuning [rad/s]': drive.detuning.time_series,
'phase [rad]': drive.phase.time_series,
}
fig, axes = plt.subplots(4, 1, figsize=(7, 7), sharex=True)
for ax, data_name in zip(axes, drive_data.keys()):
if data_name == 'phase [rad]':
ax.step(drive_data[data_name].times(), drive_data[data_name].values(), '.-', where='post')
else:
ax.plot(drive_data[data_name].times(), drive_data[data_name].values(), '.-')
ax.set_ylabel(data_name)
ax.grid(ls=':')
shift_data = shift.magnitude.time_series
pattern = shift.magnitude.pattern.series
axes[-1].plot(shift_data.times(), shift_data.values(), '.-', label="pattern: " + str(pattern))
axes[-1].set_ylabel('shift [rad/s]')
axes[-1].set_xlabel('time [s]')
axes[-1].legend()
axes[-1].grid()
plt.tight_layout()
plt.show()
def get_avg_density(result: AnalogHamiltonianSimulationQuantumTaskResult) -> np.ndarray:
"""Get the average Rydberg densities from the result
Args:
result (AnalogHamiltonianSimulationQuantumTaskResult): The result
from which the aggregated state counts are obtained
Returns:
ndarray: The average densities from the result
"""
measurements = result.measurements
postSeqs = [measurement.post_sequence for measurement in measurements]
postSeqs = 1 - np.array(postSeqs) # change the notation such 1 for rydberg state, and 0 for ground state
avg_density = np.sum(postSeqs, axis=0)/len(postSeqs)
return avg_density
def show_final_avg_density(result: AnalogHamiltonianSimulationQuantumTaskResult):
"""Showing a bar plot for the average Rydberg densities from the result
Args:
result (AnalogHamiltonianSimulationQuantumTaskResult): The result
from which the aggregated state counts are obtained
"""
avg_density = get_avg_density(result)
plt.bar(range(len(avg_density)), avg_density)
plt.xlabel("Indices of atoms")
plt.ylabel("Average Rydberg density")
plt.show()
def constant_time_series(other_time_series: TimeSeries, constant: float=0.0) -> TimeSeries:
"""Obtain a constant time series with the same time points as the given time series
Args:
other_time_series (TimeSeries): The given time series
Returns:
TimeSeries: A constant time series with the same time points as the given time series
"""
ts = TimeSeries()
for t in other_time_series.times():
ts.put(t, constant)
return ts
def concatenate_time_series(time_series_1: TimeSeries, time_series_2: TimeSeries) -> TimeSeries:
"""Concatenate two time series to a single time series
Args:
time_series_1 (TimeSeries): The first time series to be concatenated
time_series_2 (TimeSeries): The second time series to be concatenated
Returns:
TimeSeries: The concatenated time series
"""
assert time_series_1.values()[-1] == time_series_2.values()[0]
duration_1 = time_series_1.times()[-1] - time_series_1.times()[0]
new_time_series = TimeSeries()
new_times = time_series_1.times() + [t + duration_1 - time_series_2.times()[0] for t in time_series_2.times()[1:]]
new_values = time_series_1.values() + time_series_2.values()[1:]
for t, v in zip(new_times, new_values):
new_time_series.put(t, v)
return new_time_series
def concatenate_drives(drive_1: DrivingField, drive_2: DrivingField) -> DrivingField:
"""Concatenate two driving fields to a single driving field
Args:
drive_1 (DrivingField): The first driving field to be concatenated
drive_2 (DrivingField): The second driving field to be concatenated
Returns:
DrivingField: The concatenated driving field
"""
return DrivingField(
amplitude=concatenate_time_series(drive_1.amplitude.time_series, drive_2.amplitude.time_series),
detuning=concatenate_time_series(drive_1.detuning.time_series, drive_2.detuning.time_series),
phase=concatenate_time_series(drive_1.phase.time_series, drive_2.phase.time_series)
)
def concatenate_shifts(shift_1: ShiftingField, shift_2: ShiftingField) -> ShiftingField:
"""Concatenate two driving fields to a single driving field
Args:
shift_1 (ShiftingField): The first shifting field to be concatenated
shift_2 (ShiftingField): The second shifting field to be concatenated
Returns:
ShiftingField: The concatenated shifting field
"""
assert shift_1.magnitude.pattern.series == shift_2.magnitude.pattern.series
new_magnitude = concatenate_time_series(shift_1.magnitude.time_series, shift_2.magnitude.time_series)
return ShiftingField(Field(new_magnitude, shift_1.magnitude.pattern))
def concatenate_drive_list(drive_list: List[DrivingField]) -> DrivingField:
"""Concatenate a list of driving fields to a single driving field
Args:
drive_list (List[DrivingField]): The list of driving fields to be concatenated
Returns:
DrivingField: The concatenated driving field
"""
drive = drive_list[0]
for dr in drive_list[1:]:
drive = concatenate_drives(drive, dr)
return drive
def concatenate_shift_list(shift_list: List[ShiftingField]) -> ShiftingField:
"""Concatenate a list of shifting fields to a single driving field
Args:
shift_list (List[ShiftingField]): The list of shifting fields to be concatenated
Returns:
ShiftingField: The concatenated shifting field
"""
shift = shift_list[0]
for sf in shift_list[1:]:
shift = concatenate_shifts(shift, sf)
return shift
def plot_avg_density_2D(densities, register, with_labels = True, batch_index = None, batch_mapping = None, custom_axes = None):
# get atom coordinates
atom_coords = list(zip(register.coordinate_list(0), register.coordinate_list(1)))
# convert all to micrometers
atom_coords = [(atom_coord[0] * 10**6, atom_coord[1] * 10**6) for atom_coord in atom_coords]
plot_avg_of_avgs = False
plot_single_batch = False
if batch_index is not None:
if batch_mapping is not None:
plot_single_batch = True
# provided both batch and batch_mapping, show averages of single batch
batch_subindices = batch_mapping[batch_index]
batch_labels = {i:label for i,label in enumerate(batch_subindices)}
# get proper positions
pos = {i:tuple(coord) for i,coord in enumerate(list(np.array(atom_coords)[batch_subindices]))}
# narrow down densities
densities = np.array(densities)[batch_subindices]
else:
raise Exception("batch_mapping required to index into")
else:
if batch_mapping is not None:
plot_avg_of_avgs = True
# just need the coordinates for first batch_mapping
subcoordinates = np.array(atom_coords)[batch_mapping[(0,0)]]
pos = {i:coord for i,coord in enumerate(subcoordinates)}
else:
# If both not provided do standard FOV
# handle 1D case
pos = {i:coord for i,coord in enumerate(atom_coords)}
# get colors
vmin = 0
vmax = 1
cmap = plt.cm.Blues
# construct graph
g = nx.Graph()
g.add_nodes_from(list(range(len(densities))))
# construct plot
if custom_axes is None:
fig, ax = plt.subplots()
else:
ax = custom_axes
nx.draw(g,
pos,
node_color=densities,
cmap=cmap,
node_shape="o",
vmin=vmin,
vmax=vmax,
font_size=9,
with_labels=with_labels,
labels= batch_labels if plot_single_batch else None,
ax = custom_axes if custom_axes is not None else ax)
## Set axes
ax.set_axis_on()
ax.tick_params(left=True,
bottom=True,
top=True,
right=True,
labelleft=True,
labelbottom=True,
# labeltop=True,
# labelright=True,
direction="in")
## Set colorbar
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=vmin, vmax=vmax))
sm.set_array([])
ax.ticklabel_format(style="sci", useOffset=False)
# set titles on x and y axes
plt.xlabel("x [μm]")
plt.ylabel("y [μm]")
if plot_avg_of_avgs:
cbar_label = "Averaged Rydberg Density"
else:
cbar_label = "Rydberg Density"
plt.colorbar(sm, ax=ax, label=cbar_label)
First, try to get device information.
from braket.aws import AwsDevice
device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")
print(device.properties)
service=DeviceServiceProperties(braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.device_service_properties', version='1'), executionWindows=[DeviceExecutionWindow(executionDay=<ExecutionDay.TUESDAY: 'Tuesday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(20, 0)), DeviceExecutionWindow(executionDay=<ExecutionDay.WEDNESDAY: 'Wednesday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(20, 0)), DeviceExecutionWindow(executionDay=<ExecutionDay.THURSDAY: 'Thursday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(18, 0))], shotsRange=(1, 1000), deviceCost=DeviceCost(price=0.01, unit='shot'), deviceDocumentation=DeviceDocumentation(imageUrl='https://a.b.cdn.console.awsstatic.com/59534b58c709fc239521ef866db9ea3f1aba73ad3ebcf60c23914ad8c5c5c878/a6cfc6fca26cf1c2e1c6.png', summary='Analog quantum processor based on neutral atom arrays', externalDocumentationUrl='https://www.quera.com/aquila'), deviceLocation='Boston, USA', updatedAt=datetime.datetime(2022, 10, 31, 21, 30, tzinfo=datetime.timezone.utc)) action={<DeviceActionType.AHS: 'braket.ir.ahs.program'>: DeviceActionProperties(version=['1'], actionType=<DeviceActionType.AHS: 'braket.ir.ahs.program'>)} deviceParameters={} braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.quera.quera_device_capabilities', version='1') paradigm=QueraAhsParadigmProperties(braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.quera.quera_ahs_paradigm_properties', version='1'), qubitCount=256, lattice=Lattice(area=Area(width=Decimal('0.000075'), height=Decimal('0.000076')), geometry=Geometry(spacingRadialMin=Decimal('0.000004'), spacingVerticalMin=Decimal('0.000004'), positionResolution=Decimal('1E-7'), numberSitesMax=256)), rydberg=Rydberg(c6Coefficient=Decimal('5.42E-24'), rydbergGlobal=RydbergGlobal(rabiFrequencyRange=(Decimal('0.0'), Decimal('15800000.0')), rabiFrequencyResolution=Decimal('400.0'), rabiFrequencySlewRateMax=Decimal('250000000000000.0'), detuningRange=(Decimal('-125000000.0'), Decimal('125000000.0')), detuningResolution=Decimal('0.2'), detuningSlewRateMax=Decimal('2500000000000000.0'), phaseRange=(Decimal('-99.0'), Decimal('99.0')), phaseResolution=Decimal('5E-7'), timeResolution=Decimal('1E-9'), timeDeltaMin=Decimal('5E-8'), timeMin=Decimal('0.0'), timeMax=Decimal('0.000004'))), performance=Performance(lattice=PerformanceLattice(positionErrorAbs=Decimal('1E-7')), rydberg=PerformanceRydberg(rydbergGlobal=PerformanceRydbergGlobal(rabiFrequencyErrorRel=Decimal('0.02')))))
The machine appears to be registered. Machine properties have been taken.
Atomic configuration
Let's take a look at what's going on. First, let's arrange the atoms, preparing 11 qubits and spacing them 6.1 micrometers apart.
from braket.ahs.atom_arrangement import AtomArrangement
import numpy as np
#distance
a = 6.1e-6
#num of qubits
N_atoms = 11
#arrange
register = AtomArrangement()
for i in range(N_atoms):
register.add([0.0, i*a])
vars(register)
{'_sites': [AtomArrangementItem(coordinate=(0.0, 0.0), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 6.1e-06), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 1.22e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 1.83e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 2.44e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 3.05e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 3.66e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 4.27e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 4.88e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 5.49e-05), site_type=<SiteType.FILLED: 'Filled'>),
AtomArrangementItem(coordinate=(0.0, 6.1e-05), site_type=<SiteType.FILLED: 'Filled'>)]}
You got it. It's a new way to arrange atoms in a position you specify yourself.
show_register(register)
<Figure size 504x504 with 1 Axes>
Next is the triangle. The base is prepared and then the vertices are specified with functions.
#an edge of triangle
a = 5.5e-6
#arrange
register = AtomArrangement()
register.add([0, 0])
register.add([a, 0.0])
register.add([0.5 * a, np.sqrt(3)/2 * a]);
show_register(register)
<Figure size 504x504 with 1 Axes>
Next, let's increase the number a little more. We will prepare 25 groups of three atoms. This time, we will also display atoms that span the Rydberg radius.
#edge of triangle
separation = 5e-6
#distance between units
block_separation = 15e-6
#vertical and horizontal numbe of units
k_max = 5
m_max = 5
#arrange
register = AtomArrangement()
for k in range(k_max):
for m in range(m_max):
register.add((block_separation*m, block_separation*k + separation/np.sqrt(3)))
register.add((block_separation*m-separation/2, block_separation*k - separation/(2*np.sqrt(3))))
register.add((block_separation*m+separation/2, block_separation*k - separation/(2*np.sqrt(3))))
#now we specified the blockade radius and effected nodes are connected each other with blue line.
show_register(register, show_atom_index=False, blockade_radius= 1.5 * separation)
<Figure size 504x504 with 1 Axes>
Time Evolution
Next we will look at a Hamiltonian simulation of time evolution. Schedule with omega and delta.
from braket.timings.time_series import TimeSeries
from braket.ahs.driving_field import DrivingField
from pprint import pprint as pp
capabilities = device.properties.paradigm
pp(capabilities.dict())
rydberg = capabilities.rydberg
pp(rydberg.dict())
omega_const = 1.5e7 # rad / s
rabi_pulse_area = np.pi/np.sqrt(3) # rad
omega_slew_rate_max = float(rydberg.rydbergGlobal.rabiFrequencySlewRateMax) # rad/s^2
time_points, amplitude_values = rabi_pulse(rabi_pulse_area, omega_const, 0.99 * omega_slew_rate_max)
amplitude = TimeSeries()
for t, v in zip(time_points, amplitude_values):
amplitude.put(t, v)
detuning = constant_time_series(amplitude, 0.0)
phase = constant_time_series(amplitude, 0.0)
drive = DrivingField(
amplitude=amplitude,
detuning=detuning,
phase=phase
)
{'braketSchemaHeader': {'name': 'braket.device_schema.quera.quera_ahs_paradigm_properties',
'version': '1'},
'lattice': {'area': {'height': Decimal('0.000076'),
'width': Decimal('0.000075')},
'geometry': {'numberSitesMax': 256,
'positionResolution': Decimal('1E-7'),
'spacingRadialMin': Decimal('0.000004'),
'spacingVerticalMin': Decimal('0.000004')}},
'performance': {'lattice': {'positionErrorAbs': Decimal('1E-7')},
'rydberg': {'rydbergGlobal': {'rabiFrequencyErrorRel': Decimal('0.02')}}},
'qubitCount': 256,
'rydberg': {'c6Coefficient': Decimal('5.42E-24'),
'rydbergGlobal': {'detuningRange': (Decimal('-125000000.0'),
Decimal('125000000.0')),
'detuningResolution': Decimal('0.2'),
'detuningSlewRateMax': Decimal('2500000000000000.0'),
'phaseRange': (Decimal('-99.0'),
Decimal('99.0')),
'phaseResolution': Decimal('5E-7'),
'rabiFrequencyRange': (Decimal('0.0'),
Decimal('15800000.0')),
'rabiFrequencyResolution': Decimal('400.0'),
'rabiFrequencySlewRateMax': Decimal('250000000000000.0'),
'timeDeltaMin': Decimal('5E-8'),
'timeMax': Decimal('0.000004'),
'timeMin': Decimal('0.0'),
'timeResolution': Decimal('1E-9')}}}
{'c6Coefficient': Decimal('5.42E-24'),
'rydbergGlobal': {'detuningRange': (Decimal('-125000000.0'),
Decimal('125000000.0')),
'detuningResolution': Decimal('0.2'),
'detuningSlewRateMax': Decimal('2500000000000000.0'),
'phaseRange': (Decimal('-99.0'), Decimal('99.0')),
'phaseResolution': Decimal('5E-7'),
'rabiFrequencyRange': (Decimal('0.0'),
Decimal('15800000.0')),
'rabiFrequencyResolution': Decimal('400.0'),
'rabiFrequencySlewRateMax': Decimal('250000000000000.0'),
'timeDeltaMin': Decimal('5E-8'),
'timeMax': Decimal('0.000004'),
'timeMin': Decimal('0.0'),
'timeResolution': Decimal('1E-9')}}
show_global_drive(drive)
<Figure size 504x504 with 3 Axes>
Execute the actual device!
Let's take a quick look at how to throw to the actual machine.
First, we store the placement and time evalotion information.
from braket.ahs.hamiltonian import Hamiltonian
from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation
ahs_program = AnalogHamiltonianSimulation(
register=register,
hamiltonian=drive
)
And the next step is to convert the problem to throw in a task
discretized_ahs_program = ahs_program.discretize(device)
The example checks the number of times a task can be thrown here; it can be thrown up to 1000 times, so specify 100.
device.properties.service.shotsRange
(1, 1000)
n_shots = 100
Let's execute and done.
task = device.run(discretized_ahs_program, shots=n_shots)
Check the ID of the task and the status. At the time of article submission, it is Sunday, so the task is just created because it is not yet running.
metadata = task.metadata()
task_arn = metadata['quantumTaskArn']
task_status = metadata['status']
print(f"ARN: {task_arn}")
print(f"status: {task_status}")
ARN: arn:aws:braket:us-east-1:722034924650:quantum-task/75a1751d-f99c-45b9-8181-f304c0ce4df2
status: CREATED
If it is after-hours, the job will have to wait a few days before it can be checked again, so that the task ID can be used to get the job.
from braket.aws import AwsQuantumTask
task = AwsQuantumTask(arn="arn:aws:braket:us-east-1:722034924650:quantum-task/75a1751d-f99c-45b9-8181-f304c0ce4df2")
task_status = metadata['status']
print(f"status: {task_status}")
status: CREATED
The job does not seem to be thrown yet, so it is retrieved when finished.
Next, check the retrieval of the calculation results.
I would like to write an article about the processing of the calculation results, but since the calculation results cannot be retrieved, I will do so next time.