Running Analog Programs on QuEra Aquila
Overview
# --- Setup cell added by QCR (not part of the original tutorial) ---
# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.
# Installs the example's dependencies. If a later cell still reports a missing
# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 matplotlib
Introduction to Aquila
In the previous notebook, we have introduced the concept of Analog Hamiltonian Simulation (AHS) and how to run an AHS program on the neutral atom local simulator. In this notebook, we illustrate how to run an AHS program on QuEra's Aquila, a Rydberg based QPU, via Amazon Braket.
QuEra's Aquila
In order to use the Aquila device, let us first connect to it, and query its parameters with its unique Amazon Resource Number (ARN).
# Use Braket SDK Cost Tracking to estimate the cost to run this example
from braket.tracking import Tracker
tracker = Tracker().start()In this notebook, we will use matplotlib package and ahs_utils.py module in the current working directory for visualization purposes
from pprint import pprint as pp
from braket.aws import AwsDevice
from braket.devices import Devices
device = AwsDevice(Devices.QuEra.Aquila)
capabilities = device.properties.paradigm
pp(capabilities.dict()){'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-8'),
'spacingRadialMin': Decimal('0.000004'),
'spacingVerticalMin': Decimal('0.000004')}},
'performance': {'lattice': {'atomCaptureProbabilityTypical': Decimal('0.001'),
'atomCaptureProbabilityWorst': Decimal('0.002'),
'atomDetectionErrorFalseNegativeTypical': Decimal('0.001'),
'atomDetectionErrorFalseNegativeWorst': Decimal('0.005'),
'atomDetectionErrorFalsePositiveTypical': Decimal('0.001'),
'atomDetectionErrorFalsePositiveWorst': Decimal('0.005'),
'atomLossProbabilityTypical': Decimal('0.005'),
'atomLossProbabilityWorst': Decimal('0.01'),
'atomPositionError': Decimal('2E-7'),
'fillingErrorTypical': Decimal('0.008'),
'fillingErrorWorst': Decimal('0.05'),
'positionErrorAbs': Decimal('2.25E-7'),
'sitePositionError': Decimal('1E-7'),
'vacancyErrorTypical': Decimal('0.001'),
'vacancyErrorWorst': Decimal('0.005')},
'rydberg': {'rydbergGlobal': {'T1Ensemble': Decimal('0.000075'),
'T1Single': Decimal('0.000075'),
'T2BlockadedRabiEnsemble': Decimal('0.000007'),
'T2BlockadedRabiSingle': Decimal('0.000008'),
'T2EchoEnsemble': Decimal('0.000007'),
'T2EchoSingle': Decimal('0.000008'),
'T2RabiEnsemble': Decimal('0.000007'),
'T2RabiSingle': Decimal('0.000008'),
'T2StarEnsemble': Decimal('0.00000475'),
'T2StarSingle': Decimal('0.000005'),
'detuningError': Decimal('1000000.0'),
'detuningInhomogeneity': Decimal('1000000.0'),
'groundDetectionError': Decimal('0.05'),
'groundPrepError': Decimal('0.01'),
'rabiAmplitudeRampCorrection': [{'rabiCorrection': Decimal('0.92'),
'rampTime': Decimal('5E-8')},
{'rabiCorrection': Decimal('0.97'),
'rampTime': Decimal('7.5E-8')},
{'rabiCorrection': Decimal('1.0'),
'rampTime': Decimal('1E-7')}],
'rabiFrequencyErrorRel': Decimal('0.03'),
'rabiFrequencyGlobalErrorRel': Decimal('0.02'),
'rabiFrequencyInhomogeneityRel': Decimal('0.02'),
'rydbergDetectionError': Decimal('0.1'),
'rydbergPrepErrorBest': Decimal('0.05'),
'rydbergPrepErrorWorst': Decimal('0.07')},
'rydbergLocal': {'crosstalk': Decimal('0.02'),
'detuningErrorRms': Decimal('0.02'),
'errorRateIncoherentBright': Decimal('36000.0'),
'errorRateIncoherentDark': Decimal('720.0'),
'siteCoefficientErrorRms': Decimal('0.1')}}},
'qubitCount': 256,
'rydberg': {'c6Coefficient': Decimal('5.42E-24'),
'rydbergGlobal': {'detuningRange': (Decimal('-125000000.0'),
Decimal('125000000.0')),
'detuningResolution': Decimal('0.2'),
'detuningSlewRateMax': Decimal('6000000000000000.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('400000000000000.0'),
'timeDeltaMin': Decimal('5E-8'),
'timeMax': Decimal('0.000004'),
'timeMin': Decimal('0.0'),
'timeResolution': Decimal('1E-9')},
'rydbergLocal': {'detuningRange': (Decimal('-125000000.0'),
Decimal('0.0')),
'detuningSlewRateMax': Decimal('2500000000000000.0'),
'numberLocalDetuningSitesMax': Decimal('256'),
'siteCoefficientRange': (Decimal('0.0'),
Decimal('1.0')),
'spacingRadialMin': Decimal('0.000004'),
'timeDeltaMin': Decimal('5E-8'),
'timeResolution': Decimal('1E-9')}}}
The preceding numbers represent numerical capabilities and constraints for which AHS programs can be run on Aquila. In the following sections, we will go through these device capabilities and build an AHS program that complies with these constraints.
Building an AHS program for Aquila
We have seen the basic components of an AHS program in the previous example, including the register, the driving fields and local detuning. In order to run an AHS program on Aquila, however, these components have to meet certain requirements. In this section, we introduce other constraints via building up a valid program for Aquila step by step.
Register
In contrast to the local simulator which can only simulate a handful of atoms (qubits), Aquila can simulate systems with a few hundred. The coordinates of the atoms (qubits), however, have to meet certain constraints. We can check the requirements as follows
lattice_constraints = capabilities.lattice
pp(lattice_constraints.dict()){'area': {'height': Decimal('0.000076'), 'width': Decimal('0.000075')},
'geometry': {'numberSitesMax': 256,
'positionResolution': Decimal('1E-8'),
'spacingRadialMin': Decimal('0.000004'),
'spacingVerticalMin': Decimal('0.000004')}}
The detailed description of these sections can be inspected as follows
print(lattice_constraints.area.__doc__)
print(lattice_constraints.geometry.__doc__) The area of the FOV
Attributes:
width (Decimal): Largest allowed difference between x
coordinates of any two sites (measured in meters)
height (Decimal): Largest allowed difference between y
coordinates of any two sites (measured in meters)
Spacing or number of sites or rows
Attributes:
spacingRadialMin (Decimal): Minimum radial spacing between any
two sites in the lattice (measured in meters)
spacingVerticalMin (Decimal): Minimum spacing between any two
rows in the lattice (measured in meters)
positionResolution (Decimal): Resolution with which site positions
can be specified (measured in meters)
numberSitesMax (int): Maximum number of sites that can be placed
in the lattice
As we can see, the requirements for the setup in an AHS program can be summarized as follows
- The number of sites in the setup cannot be greater than
capabilities.lattice.geometry.numberSitesMax - The atoms have to be separated by at least
capabilities.lattice.geometry.spacingRadialMinmeters - The rows in the setup have to be separated by at least
capabilities.lattice.geometry.spacingVerticalMinmeters - The resolution for the coordinates of the atoms cannot be greater than
capabilities.lattice.geometry.positionResolutionmeters - The setup cannot be wider than
capabilities.lattice.area.widthmeters - The setup cannot be taller than
capabilities.lattice.area.heightmeters
Below, we demonstrate a valid setup that meets these requirements, which has 105 atoms grouped as 35 equilateral triangles that are well separated from each other.
import numpy as np
from ahs_utils import show_register
from braket.ahs.atom_arrangement import AtomArrangement
separation = 5e-6
block_separation = 15e-6
k_max = 5
m_max = 5
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)),
),
)
show_register(register, show_atom_index=False, blockade_radius=1.5 * separation)In the above figure, each blue link connects a pair of atoms that blockade each other. Since the triangles are well separated from each other, and the driving field (see below) is acting on all atoms uniformly, effectively we are repeating the same experiment on the same setup (an equilateral triangle) 35 times in one shot. If the setup of interest is small and contains only a few atoms, we could try to fit in a few identical copies of the setup in the bounding box while avoiding the interference between them. In this way, we are effectively taking more shots for the AHS program of interest.
Driving field
Aquila can simulate the following Hamiltonian
rydbergGlobal = capabilities.rydberg.rydbergGlobal
pp(rydbergGlobal.dict()){'detuningRange': (Decimal('-125000000.0'), Decimal('125000000.0')),
'detuningResolution': Decimal('0.2'),
'detuningSlewRateMax': Decimal('6000000000000000.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('400000000000000.0'),
'timeDeltaMin': Decimal('5E-8'),
'timeMax': Decimal('0.000004'),
'timeMin': Decimal('0.0'),
'timeResolution': Decimal('1E-9')}
The detailed description for each quantity of rydbergGlobal can be inspected as follows
print(rydbergGlobal.__doc__) Constraints for the parameters of the driving field that drives the
ground-to-Rydberg transition uniformly on all atoms
Attributes:
rabiFrequencyRange (Tuple[Decimal,Decimal]): Achievable Rabi frequency range for the global
Rydberg drive waveform (measured in rad/s)
rabiFrequencyResolution (Decimal): Resolution with which global Rabi frequency amplitude
can be specified (measured in rad/s)
rabiFrequencySlewRateMax (Decimal): Maximum slew rate for changing the global Rabi
frequency (measured in (rad/s)/s)
detuningRange(Tuple[Decimal,Decimal]): Achievable detuning range for the global Rydberg
pulse (measured in rad/s)
detuningResolution(Decimal): Resolution with which global detuning can be specified
(measured in rad/s)
detuningSlewRateMax (Decimal): Maximum slew rate for detuning (measured in (rad/s)/s)
phaseRange(Tuple[Decimal,Decimal]): Achievable phase range for the global Rydberg pulse
(measured in rad)
phaseResolution(Decimal): Resolution with which global Rabi frequency phase can be
specified (measured in rad)
timeResolution(Decimal): Resolution with which times for global Rydberg drive parameters
can be specified (measured in s)
timeDeltaMin(Decimal): Minimum time step with which times for global Rydberg drive
parameters can be specified (measured in s)
timeMin (Decimal): Minimum duration of Rydberg drive (measured in s)
timeMax (Decimal): Maximum duration of Rydberg drive (measured in s) Note: This may be
longer than the T2 coherence time.
As we can see, the requirements for the driving field in the AHS program can be summarized as follows
-
The Rabi frequency
has to be within the range rydbergGlobal.rabiFrequencyRange, in the unit of rad/s -
The resolution for the Rabi frequency cannot be greater than
rydbergGlobal.rabiFrequencyResolutionrad/s -
The slew rate for the Rabi frequency cannot be greater than
rydbergGlobal.rabiFrequencySlewRateMax(rad/s)/s -
The phase
has to be within the range rydbergGlobal.phaseRange, in the unit of rad -
The resolution for the phase cannot be greater than
rydbergGlobal.phaseResolutionrad -
The detuning
has to be within the range rydbergGlobal.detuningRange, in the unit of rad -
The resolution for the detuning cannot be greater than
rydbergGlobal.detuningResolutionrad -
The slew rate for the detuning cannot be greater than
rydbergGlobal.detuningSlewRateMaxrad/s -
The duration of the driving field cannot be less than
rydbergGlobal.timeMinseconds -
The duration of the driving field cannot be more than
rydbergGlobal.timeMaxseconds -
The time points have to be separated by at least
rydbergGlobal.timeDeltaMinseconds -
The resolution for the time points cannot be greater than
rydbergGlobal.timeResolutionseconds
Besides, there are a few additional requirements
- The Rabi frequency
has to start with 0 rad/s - The Rabi frequency
has to end with 0 rad/s - The phase
has to start with 0 rad - All the fields in the driving field have to have the same duration
In this example, we are interested in driving the atoms with constant Rabi frequency
However, for the AHS program submitted to Aquila, the Rabi frequency cannot be a constant because of the constraint that it has to start and end with 0 rad/s. Hence we will need to create a time series rabi_pulse below where we demonstrate how to create the desired driving field for Aquila.
from braket.ahs.driving_field import DrivingField
from braket.timings.time_series import TimeSeries
omega_const = 1.5e7 # rad / s
rabi_pulse_area = np.pi / np.sqrt(3) # rad
omega_slew_rate_max = float(rydbergGlobal.rabiFrequencySlewRateMax) # rad/s^2
time_separation_min = float(rydbergGlobal.timeDeltaMin) # s
amplitude = TimeSeries.trapezoidal_signal(
rabi_pulse_area,
omega_const,
0.99 * omega_slew_rate_max,
time_separation_min=time_separation_min,
)
detuning = TimeSeries.constant_like(amplitude, 0.0)
phase = TimeSeries.constant_like(amplitude, 0.0)
drive = DrivingField(amplitude=amplitude, detuning=detuning, phase=phase)We can inspect the driving field in the following way
from ahs_utils import show_global_drive
show_global_drive(drive);Here we used constant-zero phase and detuning, but, for more involved programs, its time series can be customized similarly to how we set the amplitude of the Rabi frequency here.
Local detuning
Apart from the driving field, Aquila also supports the local detuning, the second term in the Hamiltonian
rydbergLocal = capabilities.rydberg.rydbergLocal
if rydbergLocal:
pp(rydbergLocal.dict()){'detuningRange': (Decimal('-125000000.0'), Decimal('0.0')),
'detuningSlewRateMax': Decimal('2500000000000000.0'),
'numberLocalDetuningSitesMax': Decimal('256'),
'siteCoefficientRange': (Decimal('0.0'), Decimal('1.0')),
'spacingRadialMin': Decimal('0.000004'),
'timeDeltaMin': Decimal('5E-8'),
'timeResolution': Decimal('1E-9')}
The detailed description for each quantity of rydbergLocal can be inspected as follows
if rydbergLocal:
print(rydbergLocal.__doc__) Constraints for the parameters of the local detuning
Attributes:
detuningRange(Tuple[Decimal,Decimal]):
Achievable detuning range for the local detuning pattern (measured in rad/s)
detuningSlewRateMax(Decimal):
Maximum slew rate for changing the local detuning (measured in (rad/s)/s)
siteCoefficientRange(Tuple[Decimal,Decimal]):
Achievable site coefficient range for the local detuning pattern (unitless)
numberLocalDetuningSitesMax(Decimal):
Maximum number of sites available for the local detuning pattern
spacingRadialMin(Decimal):
Minimum radial spacing between any two sites with non-zero local detuning
(measured in meter)
timeResolution(Decimal):
Resolution with which times for local detuning time series can be specified
(measured in s)
timeDeltaMin(Decimal):
Minimum step between times for local detuning time series (measured in s)
As we can see, the requirements for the local detuning in the AHS program can be summarized as follows
- The local detuning
has to be within the range rydbergLocal.detuningRange, in the unit of rad - The slew rate for the local detuning cannot be greater than
rydbergLocal.detuningSlewRateMaxrad/s - The atom-dependent pattern
, a dimensionless quantity, has to be within the range rydbergLocal.siteCoefficientRange - The number of sites in the atom-dependent pattern cannot be greater than
rydbergLocal.numberLocalDetuningSitesMax - The atoms with non-zero local detuning have to separated by at least
rydbergLocal.spacingRadialMinmeters - The resolution for the time points cannot be greater than
rydbergLocal.timeResolutionseconds - The time points have to be separated by at least
rydbergLocal.timeDeltaMinseconds
Besides, there are a few additional requirements
- The local detuning
has to start with 0 rad/s - The local detuning
has to end with 0 rad/s - The local detuning has to have to have the same duration as the driving field
For simplicity, we will not use the local detuning in this example. The use cases that require the local detuning will be presented in other notebooks.
AHS program
We can assemble the register and Hamiltonian to an AHS program
from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation
ahs_program = AnalogHamiltonianSimulation(register=register, hamiltonian=drive)Task
Before submitting the AHS program to Aquila, we need to discretize the program to ensure that it complies with resolution-specific validation rules.
discretized_ahs_program = ahs_program.discretize(device)We note that the number of shots has to be within the range specified by device.properties.service.shotsRange.
device.properties.service.shotsRange(1, 1000)
The AHS program can be submitted to the device to create a quantum task on the Braket service.
pre_sequence of each shot with the requested atom filling in the AHS program specification.
n_shots = 100task = device.run(discretized_ahs_program, shots=n_shots)The quantum task metadata can be inspected in the following way
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:260818742045:quantum-task/b5041ea5-83d8-4de6-9aa5-741105177699 status: QUEUED
It is suggested to save the quantum task ARN for retrieving the quantum task result in a later time. For example if the saved quantum task ARN reads arn:aws:braket:us-east-1:545821822555:quantum-task/12345, the quantum task can be retrieved as follows.
from braket.aws import AwsQuantumTask
task = AwsQuantumTask(arn="arn:aws:braket:us-east-1:545821822555:quantum-task/12345")
Alternatively, we can access the quantum tasks through the tasks page of Amazon Braket console.
Analyzing the result from Aquila
The results (once the quantum task is completed) can be downloaded directly into an object in the python session.
result = task.result()The call task.result() is blocking execution until the quantum task is completed and results are loaded from Amazon Braket. After the quantum task is completed, the measurement result is saved in result.measurements which is a list of ShotResult, as shown below.
result.measurements[0]ShotResult(status=<AnalogHamiltonianSimulationShotStatus.SUCCESS: 'Success'>, pre_sequence=array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1]), post_sequence=array([1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1,
0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
0, 1, 1, 0, 1, 1, 0, 1, 1]))ShotResult contains three pieces of information
statusindicates if the shot is successfulpre_sequencecontains the measurement result before running the AHS program. Here0indicates an empty site, while1indicates a filled site with an atom in the ground statepost_sequencecontains the measurement result after running the AHS program. Here0indicates an empty site, or an atom in the Rydberg state, while1indicates an atom in the ground state
pre_sequence of each shot with the requested atom filling in the AHS program specification.
To confirm that at the end of the AHS program, the average Rydberg density for the atoms in the triangles are around 1/3 for each atom, we can first collect the measurement result and aggregate over all triangles
counts = result.get_counts()
average_density = [0, 0, 0]
average_num_triangles = [0, 0, 0, 0]
for key, val in counts.items():
for i in range(0, 3 * k_max * m_max, 3):
short_seq = key[i : i + 3]
for j in range(3):
if short_seq[j] == "r":
average_density[j] += val
average_num_triangles[short_seq.count("r")] += val
average_density = np.array(average_density) / (k_max * m_max * n_shots)
average_num_triangles = np.array(average_num_triangles) / n_shotsNote that although we only perform 100 shots for the given AHS program, since we have made the full usage of the area in the Aquila device, effectively, we have made 3,500 shots for the experiment of interest! We can plot the result as follows
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.bar(range(len(average_density)), average_density)
ax1.set_xticks(range(3))
ax1.set_xticklabels(range(3))
ax1.set_xlabel("Indices of atoms")
ax1.set_ylabel("Average Rydberg density")
ax2.bar(range(len(average_num_triangles)), average_num_triangles)
ax2.set_xlabel("Number of Rydberg atoms")
ax2.set_ylabel("Average number of triangles")
plt.show()In the left panel, we show the average Rydberg density for each atom in the triangles. We see that indeed the average Rydberg density for the atoms in the triangles are around 1/3 as expected. In the right panel, we show the number of triangles with certain number of Rydberg atoms (0, 1, 2 or 3) averaged over the shots. It can be seen that almost no triangle has more than 1 Rydberg atoms since the atoms in the same triangle blockade each other.
In summary, in this notebook we have demonstrated how to connect to QuEra's Aquila device, and define a valid AHS program for the device.
print("Quantum Task Summary")
print(tracker.quantum_tasks_statistics())
print(
"Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).",
)
print(
f"Estimated cost to run this example: {tracker.qpu_tasks_cost() + tracker.simulator_tasks_cost():.2f} USD",
)Quantum Task Summary
{'arn:aws:braket:us-east-1::device/qpu/quera/Aquila': {'shots': 100, 'tasks': {'COMPLETED': 1}}}
Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).
Estimated cost to run this example: 1.30 USD
This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.
Versions
Cite all versions? Use the base QCR ID to always reference the latest version of this entry.
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!