Lattices
We can use Bloqade to simulate the quantum evolution of information stored in neutral atoms. Present-day neutral-atom hardware permits the arrangement of atoms in a regular lattice structure and even in nearly arbitrary geometries in 1D, 2D, and 3D. This makes neutral atom platform a natural playground for quantum simulation of statistical models and quantum matters. With Bloqade, we support several built-in lattice structures and also allow the users to specify atom positions by inputting coordinates. Please refer to the Rydberg Blockade page for recommendations on how to set the lattice constants for different lattices.
Types of Lattices
A crystal lattice is completely determined by a set of Bravais lattice vectors (in unit of μm) plus the locations of atoms within a unit cell. A Bravais lattice is an infinite array of discrete points generated by a set of discrete translation operations described by
\[\mathbf{R} = n_1 \mathbf{a}_1 + n_2 \mathbf{a}_2 + \ldots + n_d \mathbf{a}_d,\]
where $d$ is the dimension of space, $n_1, \ldots, n_d \in Z$ are integers. The unit cell of a Bravais lattice is defined by specifying its lattice vectors $(\mathbf{a}_1, \mathbf{a}_2, \ldots, \mathbf{a}_d)$. To create a simple lattice, we first specify the locations of the atoms within a unit cell and then specify the lattice vectors of the Bravais lattice. For example, for a triangular lattice, we need just one site (atom) at the location (0.0, 0.0)
in the unit cell and then lattice vectors (1.0, 0.0)
and (0.5, 0.5*sqrt(3))
:
julia> using Bloqade
julia> triangular = GeneralLattice([(1.0, 0.0), (0.5, 0.5*sqrt(3))], [(0.0, 0.0)])
GeneralLattice{2, 1, Float64}(((1.0, 0.0), (0.5, 0.8660254037844386)), ((0.0, 0.0),))
For composite lattices, one should provide multiple sites as the second argument to specify their locations in a unit cell. For example, the honeycomb lattice can be defined by:
julia> honeycomb = GeneralLattice([(1.0, 0.0), (0.5, 0.5*sqrt(3))], [(0.0, 0.0), (0.5, 0.5/sqrt(3))])
GeneralLattice{2, 2, Float64}(((1.0, 0.0), (0.5, 0.8660254037844386)), ((0.0, 0.0), (0.5, 0.2886751345948129)))
We provide a few shorthands for several useful lattices, including the ChainLattice
, SquareLattice
, HoneycombLattice
, TriangularLattice
, LiebLattice
, and KagomeLattice
shown below. One can use lattice_vectors
and lattice_sites
to access the lattice vectors and site locations in a unit cell as described in the above section.
ChainLattice
using Bloqade
chain = ChainLattice()
ChainLattice()
# to make the plot look good in both light and dark backgrounds.
BloqadeLattices.DEFAULT_LINE_COLOR[] = "#0085FF"
# to show the lattice vectors (rescaled a bit to shrink the head).
unitvectors(lattice::AbstractLattice{2}, scale=0.9) = [((0.0, 0.0), v .* scale) for v in lattice_vectors(lattice)]
Bloqade.plot(generate_sites(chain, 10); vectors=[((0.0, 0.0), (0.9, 0.0))], bond_linewidth=0.015)
lattice_vectors(chain)
((1.0,),)
lattice_sites(chain)
((0.0,),)
Once we have defined certain lattice shapes (which have fixed lattice vectors and site positions in the unit cell), we can generate the atom positons by specifying the number of atoms and the scale size of the lattice. This is done by using the function generate_sites
, which will return a AtomList
instance containing the coordinates of each atom, e.g.:
atoms = generate_sites(HoneycombLattice(), 3, 5; scale = 4.5)
where scale
defines the unit distance in the unit μm of the lattice, and 3, 5
specifies the repetitions of unit cells in each lattice vector direction. The default scale
is 1 μm.
Here are some examples of other lattices:
SquareLattice
square = SquareLattice()
Bloqade.plot(generate_sites(square, 10, 10); vectors=unitvectors(square), bond_linewidth=0.015)
Note that the indices showing on the sites are consistent with the indices of the qubits for performing computation. In other words, if we want to do measurement or apply operations on individual sites (qubits), we can refer to the numbering on the atoms for convenience. For more details on how to generate Hamiltonians by using the lattice as an argument, please see the section Hamiltonians.
lattice_vectors(square)
((1.0, 0.0), (0.0, 1.0))
lattice_sites(square)
((0.0, 0.0),)
HoneycombLattice
honeycomb = HoneycombLattice()
Bloqade.plot(generate_sites(honeycomb, 5, 5); vectors=unitvectors(honeycomb), bond_linewidth=0.015)
TriangularLattice
triangular = TriangularLattice()
Bloqade.plot(generate_sites(triangular, 8, 8); vectors=unitvectors(triangular), bond_linewidth=0.015)
LiebLattice
lieb = LiebLattice()
Bloqade.plot(generate_sites(lieb, 5, 5); vectors=unitvectors(lieb), bond_linewidth=0.015)
KagomeLattice
kagome = KagomeLattice()
Bloqade.plot(generate_sites(kagome, 5, 5); vectors=unitvectors(kagome), bond_linewidth=0.015)
Sort Sites and Other Operations on Lattices
We also support different operations on the generated lattices. For instance, one can apply some predefined filters, e.g. rescale_axes
, clip_axes
, offset_axes
, to manipulate atom locations:
atoms = generate_sites(HoneycombLattice(), 3, 5; scale = 4.5)
rescale_axes(atoms, 0.8)
where the above operation rescales the coordinates of the original sites
by a factor of 0.8
.
The code below restricts the atoms sitting in the window (0.0, 5.0), (0.0, 6.0)
and throw away those outside this area:
clip_axes(atoms, (0.0, 5.0), (0.0, 6.0))
Furthermore, we can shift the origin of the atoms
by some vector (5.0, 5.0)
simply by typing the code:
offset_axes(atoms, 5.0, 5.0)
To sort the atoms by their x-coordinates, one can convert these locations to a MaskedGrid
representation of the atoms:
atoms_in_grid = make_grid(atoms)
Then one can get the sorted atoms by typing:
sorted_atoms = collect_atoms(atoms_in_grid)
Note that the sorting has changed the index numbering of the atoms.
User-Defined Arbitrary Geometries
One can also generate atoms located at arbitrary positions by directly inputting the coordinates of the atoms:
julia> atom_coordinate = AtomList([(0.0, 0.0), (0, 5), (0, 8), (5, 2), (6, 7), (9, 6)])
6-element AtomList{2, Real}: (0.0, 0.0) (0, 5) (0, 8) (5, 2) (6, 7) (9, 6)
Query Neighbors
One can use make_kdtree
to generate a k-d tree data type for the efficient querying of neighborhoods in a low-dimensional space.
tree = make_kdtree(sorted_atoms)
NearestNeighbors.KDTree{StaticArrays.SVector{2, Float64}, Distances.Euclidean, Float64}
Number of points: 30
Dimensions: 2
Metric: Distances.Euclidean(0.0)
Reordered: true
The return value is a KDTree
instance, which is defined in the package NearestNeigbors
. One can use it to query the neighbors of an atom: e.g. one can find the 20 nearest neighbors of the 5-th site by typing:
neighbors = grouped_nearest(tree, 5, 20)
DistanceGroup([5, 8, 2, 3, 10, 6, 11, 4, 7, 9, 14, 13, 1, 16, 12, 17, 15, 19, 20, 22], [1, 2, 5, 9, 11, 14, 16, 17, 18, 19, 20, 21])
The return value is a DistanceGroup
instance, and the indices of the second nearest neighbors are:
neighbors[2]
4-element Vector{Int64}:
10
6
11
4
One can select and display these atoms with the correct labeling by typing:
Bloqade.plot(sorted_atoms[neighbors[2]]; texts=string.(neighbors[2]))
It shows the correct second nearest neigbors of the site 5. One can check the docstring of Bloqade.plot
to know more about how to customize lattice visualization.
References
BloqadeLattices.AtomList
— TypeAtomList{D, T} <: AbstractVector{NTuple{D, T}}
AtomList(atoms::Vector{<:NTuple})
A list of atoms in D
dimensional space.
BloqadeLattices.ByDensity
— MethodByDensity(values; colormap="Grays", vmin=minimum(values), vmax=maximum(values))
For specifying the colors for density plots, where values
are densities.
Keyword arguments
colormap
is a string for specifying the color map, check the documentation of [Colors
] package for the detailed description.vmin
andvmax
are the color range.
BloqadeLattices.ChainLattice
— TypeBloqadeLattices.ChainLattice <: AbstractLattice{1}
BloqadeLattices.ChainLattice()
BloqadeLattices.ChainLattice
is a 1 dimensional lattice with:
- Lattice vectors = ((1.0,),)
- Lattice sites = ((0.0,),)
BloqadeLattices.DistanceGroup
— TypeDistanceGroup
The vertices grouped by distances. One can use distancegroup[n]
to get n
-th nearest neighbors.
BloqadeLattices.GeneralLattice
— TypeGeneralLattice{D,K,T} <: AbstractLattice{D}
GeneralLattice(vectors, sites)
The general lattice type for tiling the space. Type parameter D
is the dimension, K
is the number of sites in a unit cell and T
is the data type for coordinates, e.g. Float64
. Input arguments are
vectors
is a vector/tuple of D-tuple. Its length is D, it specifies the Bravais lattice vectors.sites
is a vector/tuple of D-tuple. Its length is K, it specifies the sites inside a Bravais cell.
BloqadeLattices.HoneycombLattice
— TypeBloqadeLattices.HoneycombLattice <: AbstractLattice{2}
BloqadeLattices.HoneycombLattice()
BloqadeLattices.HoneycombLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.5, 0.8660254037844386))
- Lattice sites = ((0.0, 0.0), (0.5, 0.2886751345948129))
BloqadeLattices.KagomeLattice
— TypeBloqadeLattices.KagomeLattice <: AbstractLattice{2}
BloqadeLattices.KagomeLattice()
BloqadeLattices.KagomeLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.5, 0.8660254037844386))
- Lattice sites = ((0.0, 0.0), (0.25, 0.4330127018922193), (0.75, 0.4330127018922193))
BloqadeLattices.LiebLattice
— TypeBloqadeLattices.LiebLattice <: AbstractLattice{2}
BloqadeLattices.LiebLattice()
BloqadeLattices.LiebLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.0, 1.0))
- Lattice sites = ((0.0, 0.0), (0.5, 0.0), (0.0, 0.5))
BloqadeLattices.MaskedGrid
— TypeMaskedGrid{T}
MaskedGrid(xs, ys, mask)
Masked square lattice contains 3 fields, the x-coordinates, y-coordinates and a mask. e.g. MaskedGrid([0.0, 1.0, 3.0], [0.0, 2.0,6.0], Bool[1 0 0; 0 1 1; 0 1 0])
specifies the following lattice:
y₁ y₂ y₃
↓ ↓ ↓
x₁ → ● ⋅ ●
x₂ → ⋅ ● ●
x₃ → ⋅ ● ⋅
BloqadeLattices.RectangularLattice
— TypeRectangularLattice <: AbstractLattice{2}
RectangularLattice(aspect_ratio::Real)
RectangularLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.0,
aspect_ratio
) - Lattice sites = ((0.0, 0.0),)
BloqadeLattices.SquareLattice
— TypeBloqadeLattices.SquareLattice <: AbstractLattice{2}
BloqadeLattices.SquareLattice()
BloqadeLattices.SquareLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.0, 1.0))
- Lattice sites = ((0.0, 0.0),)
BloqadeLattices.TriangularLattice
— TypeBloqadeLattices.TriangularLattice <: AbstractLattice{2}
BloqadeLattices.TriangularLattice()
BloqadeLattices.TriangularLattice
is a 2 dimensional lattice with:
- Lattice vectors = ((1.0, 0.0), (0.5, 0.8660254037844386))
- Lattice sites = ((0.0, 0.0),)
BloqadeLattices.clip_axes
— Methodclip_axes(sites::AtomList{D, T}, bounds::Vararg{Tuple{T,T},D}) where {D, T}
clip_axes(bounds...)
Remove sites out of bounds
, where bounds
is specified by D D-tuples.
julia> sites = AtomList([(1.0, 2.0), (10.0, 3.0), (1.0, 12.0), (3.0, 5.0)])
4-element AtomList{2, Float64}:
(1.0, 2.0)
(10.0, 3.0)
(1.0, 12.0)
(3.0, 5.0)
julia> clip_axes(sites, (-5.0, 5.0), (-5.0, 5.0))
2-element AtomList{2, Float64}:
(1.0, 2.0)
(3.0, 5.0)
BloqadeLattices.collect_atoms
— Methodcollect_atoms(maskedgrid::MaskedGrid)
Returns an list of atoms in the maskedgrid
in order.
BloqadeLattices.dimension
— Methoddimension(lattice)
Returns the space dimension of target lattice. e.g. ChainLattice
is a 1D lattice, hence returns 1.
BloqadeLattices.generate_sites
— Methodgenerate_sites(lattice::AbstractLattice{D}, repeats::Vararg{Int,D}; scale=1.0)
Returns an AtomList
instance by tiling the specified lattice
. The tiling repeat the sites
of the lattice m
times along the first dimension, n
times along the second dimension, and so on. scale
is a real number that re-scales the lattice constant and atom locations.
BloqadeLattices.grouped_nearest
— Methodgrouped_nearest(tree::KDTree, siteindex::Int, nsites::Int; atol=1e-8)
Find the nsites
closest vertices to siteindex
, and group them by distance. Difference of the distances smaller than the atol
(default is 1e-8
) are treated as the same Returns a DistanceGroup
instance.
julia> atoms = generate_sites(HoneycombLattice(), 5, 5);
julia> tree = make_kdtree(atoms)
NearestNeighbors.KDTree{StaticArrays.SVector{2, Float64}, Distances.Euclidean, Float64}
Number of points: 50
Dimensions: 2
Metric: Distances.Euclidean(0.0)
Reordered: true
julia> gn = grouped_nearest(tree, 23, 20)
DistanceGroup([23, 14, 22, 24, 15, 13, 21, 25, 33, 31, 12, 16, 32, 4, 6, 34, 26, 17, 5, 41], [1, 2, 5, 11, 14, 18, 21])
julia> gn[0] # the 0-th nearest neighbor is defined by vertex itself
1-element Vector{Int64}:
23
julia> gn[1] # nearest neighbors
3-element Vector{Int64}:
14
22
24
julia> gn[2] # second nearest neighbors
6-element Vector{Int64}:
15
13
21
25
33
31
BloqadeLattices.img_atoms
— Methodimg_atoms(atoms::AtomList;
colors=[DEFAULT_LINE_COLOR[], ...],
blockade_radius=0,
texts=["1", "2", ...],
vectors = [],
format=SVG,
io=nothing,
kwargs...
)
Plots atoms
with colors specified by colors
and texts specified by texts
. Extra vectors can be specified by the vectors
keyword argument, which is a vector of (startloc, endloc) pair. You will need a VSCode
, Pluto
notebook or Jupyter
notebook to show the image. If you want to write this image to the disk without displaying it in a frontend, please try
julia> using Compose
julia> open("test.png", "w") do f
img_atoms(generate_sites(SquareLattice(), 5, 5); io=f, format=Compose.PNG)
end
The format
keyword argument can also be Compose.SVG
or Compose.PDF
. Atoms within blockade_radius
will be connected by bonds.
Other Keyword Arguments
# overall scaling
scale::Float64 = 1.0
# padding space
pad::Float64 = 1.5
# axes
axes_text_color::String = DEFAULT_LINE_COLOR[]
axes_text_fontsize::Float64 = 11.0
axes_num_of_xticks = 5
axes_num_of_yticks = 5
axes_x_offset::Float64 = 0.1
axes_y_offset::Float64 = 0.06
axes_unit::String = "μm"
# node
node_text_fontsize::Float64 = 5.0
node_text_color::String = DEFAULT_LINE_COLOR[]
node_stroke_color = DEFAULT_LINE_COLOR[]
node_stroke_linewidth = 0.03
node_fill_color = "white"
# bond
bond_color::String = DEFAULT_LINE_COLOR[]
bond_linewidth::Float64 = 0.03
# blockade
blockade_style::String = "none"
blockade_stroke_color::String = DEFAULT_LINE_COLOR[]
blockade_fill_color::String = "transparent"
blockade_fill_opacity::Float64 = 0.5
blockade_stroke_linewidth = 0.03
# image size in cm
image_size::Float64 = 12
BloqadeLattices.img_maskedgrid
— Methodimg_maskedgrid(maskedgrid::MaskedGrid;
format=SVG,
io=nothing,
colors=[DEFAULT_LINE_COLOR[], ...],
texts=["1", "2", ...],
vectors=[],
blockade_radius = 0,
kwargs...
)
Draw a maskedgrid
with colors specified by colors
and texts specified by texts
. You will need a VSCode
, Pluto
notebook or Jupyter
notebook to show the image.
See also the docstring of img_atoms
for explanations of other keyword arguments.
BloqadeLattices.lattice_sites
— Methodlattice_sites(lattice::AbstractLattice)
Returns sites in a Bravais lattice unit cell as a Tuple of D-Tuple, where D is the space dimension.
BloqadeLattices.lattice_vectors
— Methodlattice_vectors(lattice::AbstractLattice)
Returns Bravais lattice vectors as a D-Tuple of D-Tuple, where D is the space dimension.
BloqadeLattices.make_grid
— Methodmake_grid(sites::AtomList; atol=...)
Create a MaskedGrid
from the sites. It is required by lattice preparation of Rydberg array. Because the grid will sort the sites by rows, we need atol
(default value is 10 time sit data precision) determines up to what level of round off error, two atoms belong to the same row.
BloqadeLattices.make_kdtree
— Methodmake_kdtree(atoms::AtomList{D,T}) where {T, D}
Returns a KDTree
instance defined in package NearestNeighbors from input atoms
.
BloqadeLattices.offset_axes
— Methodoffset_axes(sites::AtomList{D, T}, offsets::Vararg{T,D}) where {D, T}
offset_axes(offsets...)
Offset the sites
by distance specified by offsets
.
julia> sites = AtomList([(1.0, 2.0), (10.0, 3.0), (1.0, 12.0), (3.0, 5.0)])
4-element AtomList{2, Float64}:
(1.0, 2.0)
(10.0, 3.0)
(1.0, 12.0)
(3.0, 5.0)
julia> offset_axes(sites, 1.0, 3.0)
4-element AtomList{2, Float64}:
(2.0, 5.0)
(11.0, 6.0)
(2.0, 15.0)
(4.0, 8.0)
BloqadeLattices.random_dropout
— Methodrandom_dropout(sites::AtomList{D, T}, ratio::Real) where {D, T}
random_dropout(ratio)
Randomly drop out ratio * number of sites
atoms from sites
, where ratio
∈ [0, 1].
BloqadeLattices.rescale_axes
— Methodrescale_axes(sites::AtomList{D, T}, scale::Real) where {D, T}
rescale_axes(scale)
Rescale the sites
by a constant scale
.
julia> sites = AtomList([(1.0, 2.0), (10.0, 3.0), (1.0, 12.0), (3.0, 5.0)])
4-element AtomList{2, Float64}:
(1.0, 2.0)
(10.0, 3.0)
(1.0, 12.0)
(3.0, 5.0)
julia> rescale_axes(sites, 2.0)
4-element AtomList{2, Float64}:
(2.0, 4.0)
(20.0, 6.0)
(2.0, 24.0)
(6.0, 10.0)