Skip to the content.

Neutral atom / AHS integration (Bloqade · Aquila)

qpubench models neutral-atom Analog Hamiltonian Simulation (AHS) in src/qpubench/schemas/mirrors/quera_bloqade.py.

Component Details
SDK Bloqade (QuEra)
Hardware Aquila 256-qubit Rydberg QPU via AWS Braket
Computing model ComputingModel.ADIABATIC
Qubit modality QubitModality.NEUTRAL_ATOM
Result field QuantumResult.vendor_results["ahs_result"]
Backend factories BackendSpec.aquila(), BackendSpec.bloqade_emulator()

In AHS, neutral Rubidium-87 atoms are trapped at programmable 2-D positions by optical tweezers. A global laser applies a time-dependent Rabi drive (Ω, φ) and detuning (Δ), evolving the system through the Rydberg blockade Hamiltonian:

H(t) = Ω(t)/2 · Σᵢ (e^{iφ(t)} |gᵢ⟩⟨rᵢ| + h.c.) - Δ(t) · Σᵢ nᵢ + Σᵢ<ⱼ C₆/rᵢⱼ⁶ · nᵢnⱼ

Measurement collapses each atom to ground (1) or Rydberg (0) state.


Quick start

from qpubench.schemas.mirrors.quera_bloqade import (
    AtomicSite, AtomArrangement, LatticeGeometryType,
    AHSDrivingField, AHSTimeSeries, AHSProgramSpec,
    AHSShotResult, AHSShotStatus, AHSTaskResult,
    NeutralAtomCoupling,
)
from qpubench.schemas.backend import BackendSpec
from qpubench.schemas.result import QuantumResult
from qpubench.schemas.primitives import ComputingModel, QubitModality

# 1-D chain of 5 atoms at 6 µm spacing
sites = [AtomicSite(x=i * 6.0, y=0.0) for i in range(5)]
arrangement = AtomArrangement(
    sites=sites,
    lattice_type=LatticeGeometryType.CHAIN,
    lattice_spacing_um=6.0,
)

# Rabi amplitude: ramp up, hold, ramp down (π-pulse shape)
rabi = AHSTimeSeries(
    times_us=[0.0, 0.1, 0.9, 1.0],
    values=[0.0, 15.7, 15.7, 0.0],     # rad/µs
)
# Detuning: linear sweep from -30 to +30 rad/µs
detuning = AHSTimeSeries(
    times_us=[0.0, 1.0],
    values=[-30.0, 30.0],              # rad/µs
)
# Phase: constant at 0
phase = AHSTimeSeries(times_us=[0.0, 1.0], values=[0.0, 0.0])

field = AHSDrivingField(
    coupling=NeutralAtomCoupling.RYDBERG,
    rabi_amplitude=rabi,
    rabi_phase=phase,
    detuning=detuning,
)

program = AHSProgramSpec(
    atom_arrangement=arrangement,
    driving_fields=[field],
    total_duration_us=1.0,
    description="Z2 antiferromagnetic order preparation",
)
print(f"Effective qubits: {program.num_qubits}")   # 5

Atom arrangements

Pre-defined lattices

from qpubench.schemas.mirrors.quera_bloqade import AtomicSite, AtomArrangement, LatticeGeometryType

# Square lattice 4×4
sites_sq = [
    AtomicSite(x=i * 6.0, y=j * 6.0)
    for i in range(4) for j in range(4)
]
sq = AtomArrangement(
    sites=sites_sq,
    lattice_type=LatticeGeometryType.SQUARE,
    lattice_spacing_um=6.0,
)
print(sq.num_sites)           # 16
print(sq.fill_fraction)       # 1.0

# Chain with a defect (empty site)
chain_sites = [AtomicSite(x=i * 5.0, y=0.0) for i in range(6)]
chain = AtomArrangement(
    sites=chain_sites,
    filling=[1, 1, 0, 1, 1, 1],   # site 2 missing
    lattice_type=LatticeGeometryType.CHAIN,
    lattice_spacing_um=5.0,
)
print(chain.num_filled_sites)  # 5

Aquila constraints


Waveforms

AHSTimeSeries (hardware format)

from qpubench.schemas.mirrors.quera_bloqade import AHSTimeSeries

# Rabi amplitude: trapezoidal π-pulse over 1 µs
rabi = AHSTimeSeries(
    times_us=[0.0, 0.3, 0.7, 1.0],
    values=[0.0, 15.7, 15.7, 0.0],   # rad/µs
)
print(rabi.duration_us)    # 1.0
print(rabi.num_points)     # 4

# Detuning ramp: -30 → +30 rad/µs over 2 µs
det = AHSTimeSeries(
    times_us=[0.0, 2.0],
    values=[-30.0, 30.0],
)

AHSWaveform (Bloqade builder format)

The compact form stores segment durations + boundary values before discretization.

from qpubench.schemas.mirrors.quera_bloqade import AHSWaveform, AHSWaveformType

# Piecewise linear Rabi (required for Ω on hardware)
wf_rabi = AHSWaveform(
    waveform_type=AHSWaveformType.PIECEWISE_LINEAR,
    durations_us=[0.3, 0.4, 0.3],          # 3 segments → 1.0 µs total
    values=[0.0, 15.7, 15.7, 0.0],         # 4 boundary values
)
print(wf_rabi.total_duration_us)   # 1.0

# Piecewise constant phase (required for φ on hardware)
wf_phase = AHSWaveform(
    waveform_type=AHSWaveformType.PIECEWISE_CONSTANT,
    durations_us=[1.0],
    values=[0.0],
)

# Polynomial detuning: Δ(t) = -30 + 60t  (linear sweep)
wf_poly = AHSWaveform(
    waveform_type=AHSWaveformType.POLY,
    duration_us=1.0,
    values=[-30.0, 60.0],   # c₀ + c₁·t
)

Aquila waveform constraints

Field Allowed shape Range
Rabi amplitude Ω(t) Piecewise linear [0, 15.8] rad/µs
Phase φ(t) Piecewise constant [-99, 99] rad
Detuning Δ(t) Piecewise linear [-125, 125] rad/µs
Max slope dΩ/dt 250 rad/µs²
Max slope dΔ/dt 2500 rad/µs²
Time resolution 0.001 µs
Max duration 4.0 µs

Driving fields and programs

from qpubench.schemas.mirrors.quera_bloqade import (
    AHSDrivingField, AHSProgramSpec, AHSLocalDetuning,
    NeutralAtomCoupling, SpatialModulationType,
)

# Global Rydberg drive
field = AHSDrivingField(
    coupling=NeutralAtomCoupling.RYDBERG,
    rabi_amplitude=rabi,
    rabi_phase=phase,
    detuning=detuning,
    spatial_modulation=SpatialModulationType.UNIFORM,
)

# Optional local detuning (experimental feature on Aquila)
# Effective detuning at site k: h_k × Δ_local(t)
local_det = AHSLocalDetuning(
    time_series=AHSTimeSeries(times_us=[0.0, 1.0], values=[0.0, 100.0]),
    site_coefficients=[0.0, 1.0, 0.0, 1.0, 0.0],  # alternating sites
)

program = AHSProgramSpec(
    atom_arrangement=arrangement,
    driving_fields=[field],
    local_detunings=[local_det],
    total_duration_us=1.0,
)

Parametric sweeps

from qpubench.schemas.mirrors.quera_bloqade import AHSBatchSpec

# Sweep detuning endpoint and Rabi max across 4 parameter sets
batch = AHSBatchSpec(
    variable_names=["detuning_end", "rabi_max"],
    parameter_values=[
        [-20.0, -10.0,   0.0,  10.0],   # detuning_end
        [ 10.0,  12.0,  14.0,  15.7],   # rabi_max
    ],
    num_shots_per_batch=100,
)
print(batch.batch_size)   # 4

Hardware specification

from qpubench.schemas.mirrors.quera_bloqade import AquilaDeviceSpec

hw = AquilaDeviceSpec()   # all Aquila defaults
print(hw.max_qubits)               # 256
print(hw.c6_rad_us_um6)            # 5420000.0  (van der Waals C₆)
print(hw.cost_per_shot_usd)        # 0.01

# Custom / future device
custom = AquilaDeviceSpec(max_qubits=512, max_pulse_duration_us=8.0)

Results

Shot structure

from qpubench.schemas.mirrors.quera_bloqade import AHSShotResult, AHSShotStatus

shot = AHSShotResult(
    status=AHSShotStatus.SUCCESS,
    pre_sequence=[1, 1, 1, 1, 1],   # all atoms loaded
    post_sequence=[1, 0, 1, 0, 1],  # alternating Rydberg excitations (Z2 order)
)
print(shot.is_perfect_fill)   # True — usable for analysis

# Imperfect fill: site 2 missing before evolution
bad = AHSShotResult(
    status=AHSShotStatus.SUCCESS,
    pre_sequence=[1, 1, 0, 1, 1],
    post_sequence=[1, 0, 0, 0, 1],
)
print(bad.is_perfect_fill)   # False — excluded from default analysis

Task result analysis

from qpubench.schemas.mirrors.quera_bloqade import AHSTaskResult, AHSExecutionMetadata

task = AHSTaskResult(
    metadata=AHSExecutionMetadata(
        task_id="arn:aws:braket:us-east-1::task/abc123",
        device_id="arn:aws:braket:us-east-1::device/qpu/quera/Aquila",
        status="COMPLETED",
        cost_usd=0.10,
    ),
    num_shots_requested=10,
    shot_results=[...],   # list of AHSShotResult
)

# Analysis (perfect-fill shots only, matching Bloqade's filter_perfect_filling=True)
print(task.perfect_fill_shots)   # shots with all pre_sequence == 1 and SUCCESS
print(task.bitstrings)           # [[1,0,1,0,1], [0,1,0,1,0], ...]
print(task.counts)               # {"10101": 47, "01010": 43, ...}
print(task.rydberg_densities)    # [0.48, 0.51, 0.47, 0.52, 0.49] — P(Rydberg) per site

Attach to QuantumResult

result = QuantumResult(
    computing_model=ComputingModel.ADIABATIC,
    qubit_modality=QubitModality.NEUTRAL_ATOM,
    vendor_results={"ahs_result": task},
)

Backends

from qpubench.schemas.backend import BackendSpec

# QuEra Aquila QPU via AWS Braket (hardware)
aquila = BackendSpec.aquila(aws_region="us-east-1")
# name="aquila", provider="quera", simulator=False

# Bloqade Python local emulator (exact statevector, no credentials)
emulator = BackendSpec.bloqade_emulator(num_qubits=12)
# name="bloqade_python", provider="bloqade", simulator=True

Bloqade builder → qpubench mapping

Bloqade uses a fluent builder API to construct programs. The mapping to qpubench types:

Bloqade builder call qpubench type
start.add_position([(x,y), ...]) AtomArrangement(sites=[AtomicSite(x,y), ...])
Square(n, lattice_spacing=6.0) AtomArrangement(lattice_type=SQUARE, lattice_spacing_um=6.0)
.rydberg.detuning.uniform.piecewise_linear(durations, values) AHSDrivingField(coupling=RYDBERG, detuning=AHSTimeSeries(...))
.rydberg.amplitude.uniform.piecewise_linear(...) AHSDrivingField(rabi_amplitude=AHSTimeSeries(...))
.rydberg.phase.uniform.piecewise_constant(...) AHSDrivingField(rabi_phase=AHSTimeSeries(...))
.batch_assign(var=[v1, v2, ...]) AHSBatchSpec(variable_names=["var"], parameter_values=[[v1, v2, ...]])
result.report.bitstrings() AHSTaskResult.bitstrings
result.report.counts() AHSTaskResult.counts
result.report.rydberg_densities() AHSTaskResult.rydberg_densities
shot.pre_sequence AHSShotResult.pre_sequence
shot.post_sequence AHSShotResult.post_sequence