# Bloqade

Welcome to the documentation page for Bloqade, a Julia Language package for quantum computation and quantum dynamics based on neutral-atom architectures.

Neutral-atom quantum computers have two major modes of computation: the first mode is a "digital mode" to do universal, digital quantum computation that uses two ground states $|0\rangle$ and $|1\rangle$ to encode the qubit, which has long coherence time, and one Rydberg state $|r\rangle$ to entangle the qubits; the second mode is an "analog mode" as a programmable quantum simulator that uses one ground state $|g\rangle$ and one Rydberg state $|r\rangle$, where the quantum dynamics is governed by a Rydberg Hamiltonian $\hat{\mathcal{H}}$ described below.

Currently, Bloqade enables the easy design and fast execution of quantum dynamics in the analog mode, based on the neutral-atom quantum computing architecture. Besides fast full Hilbert-space simulation on CPUs, the main features include the design of arbitrary-layout quantum registers (Lattices), easy waveform generation (Waveforms), simulation in subspace constrained by the Rydberg blockade (Working with Subspace), faster GPU-accelerated simulation (GPU Acceleration), and more.

## Installation

To install Bloqade,
please open
Julia's interactive session (known as REPL), press `]` key in the REPL to use the package mode, and then add the QuEra Julia registry via:

For the stable release, type:

`pkg> add Bloqade`

Or for the current master:

`pkg> add Bloqade#master`

For a more advanced installation guide, please see the Installation page.

## What does Bloqade Do?

In the analog mode, Bloqade simulates the time evolution of a quantum state under the Schrödinger equation where the Hamiltonian is the interacting Rydberg Hamiltonian $\hat{\mathcal{H}}$,

\[i \hbar \dfrac{\partial}{\partial t} | \psi \rangle = \hat{\mathcal{H}}(t) | \psi \rangle, \\ \frac{\mathcal{H}(t)}{\hbar} = \sum_j \frac{\Omega_j(t)}{2} \left( e^{i \phi_j(t) } | g_j \rangle \langle r_j | + e^{-i \phi_j(t) } | r_j \rangle \langle g_j | \right) - \sum_j \Delta_j(t) \hat{n}_j + \sum_{j < k} V_{jk} \hat{n}_j \hat{n}_k.\]

Following the atomic physics nomenclature, $\Omega_j$, $\phi_j$, and $\Delta_j$ denote the Rabi frequency, laser phase, and the detuning of the driving laser field on atom (qubit) $j$ coupling the two states $| g_j \rangle$ (ground state) and $| r_j \rangle$ (Rydberg state); $\hat{n}_j = |r_j\rangle \langle r_j|$ is the number operator, and $V_{jk} = C_6/|\overrightarrow{\mathbf{r}_j} - \overrightarrow{\mathbf{r}_k}|^6$ describes the Rydberg interaction (van der Waals interaction) between atoms $j$ and $k$ where $\overrightarrow{\mathbf{r}_j}$ denotes the position of the atom $j$; $C_6$ is the Rydberg interaction constant that depends on the particular Rydberg state used. For Bloqade, the default $C_6 = 862690 \times 2\pi \text{ MHz μm}^6$ for $|r \rangle = \lvert 70S_{1/2} \rangle$ of the $^{87}$Rb atoms; $\hbar$ is the reduced Planck's constant. Sometimes, we also refer the states $|g\rangle$ and $|r\rangle$ as $|0\rangle$ and $|1\rangle$ as well in the analog mode.

Starting from an initial quantum state $| \psi_{\text{ini}} \rangle$, Bloqade simulates its time evolution under the Hamiltonian $\hat{\mathcal{H}}(t)$, given the qubit positions and the time-dependent profiles for $\Omega_j$, $\phi_j$, and $\Delta_j$. Bloqade then outputs the real-time-evolved state $| \psi(t) \rangle$, which can then be used for measuring different observables.

More specifically, here are the steps to program neutral-atom quantum computers using Bloqade:

- specify atom positions (see Lattices)
- program waveforms of Hamiltonian parameters (see Waveforms)
- create the Hamiltonian (see Hamiltonians)
- specify the initial state (see Registers and Observables)
- emulation (see Emulation)
- measurements & observables (see Registers and Observables).

The default units for various quantities are:

Quantity | Default Unit |
---|---|

Length | μm |

Time | μs |

$\Omega$ | 2π * MHz |

$\phi$ | rad |

$\Delta$ | 2π * MHz |

## A Simple Example

Let's try a simple example of simulating quantum many-body dynamics governed by the Rydberg Hamiltonian.

We start by loading the Bloqade Module:

`julia> using Bloqade`

As one can see from the Rydberg Hamiltonian, the interactions between Rydberg atoms depend on their positions. Bloqade provides several built-in Lattices structures for specifying the atom positions. For instance, we can use the following codes to quickly generate a chain of 10 atoms in 1D:

`julia> nsites = 10;`

`julia> atoms = generate_sites(ChainLattice(), nsites, scale = 5.74)`

`10-element AtomList{1, Float64}: (0.0,) (5.74,) (11.48,) (17.22,) (22.96,) (28.700000000000003,) (34.44,) (40.18,) (45.92,) (51.660000000000004,)`

We have set the distance between nearest-neighbor atoms to be 5.74 μm. Note that the default unit of length is μm as shown in the table above.

Let's set both $\Omega$ and $\Delta$ to be constants (and $\phi = 0$). Since all the variable parameters in the Hamiltonian are specified, we can now create an interacting Rydberg Hamiltonian by using `rydberg_h`

:

`julia> h = rydberg_h(atoms; Ω = 4 * 2π, Δ = 0)`

`nqubits: 10 + ├─ [+] ∑ 2π ⋅ 8.627e5.0/|r_i-r_j|^6 n_i n_j ├─ [+] 2π ⋅ 2.0 ⋅ ∑ σ^x_i └─ [-] 2π ⋅ 0.0 ⋅ ∑ n_i`

To create more complicated waveforms for $\Omega$ and $\Delta$ and find the supported utilities, please refer to the Waveforms page.

Let's create an initial state with all the atoms in the ground state by using `zero_state`

.

`julia> reg = zero_state(10)`

`ArrayReg{2, ComplexF64, Array...} active qubits: 10/10 nlevel: 2`

We are interested in measuring observables of the final quantum state of the Rydberg system starting from the initial state and evolving under the Rydberg Hamiltonian over some time duration. We can first create the problem and then directly simulate the time evolution.

`julia> prob = SchrodingerProblem(reg, 1.6, h)`

`SchrodingerProblem: register info: type: ArrayReg{2, ComplexF64, Matrix{ComplexF64}} storage size: 8 bytes time span (μs): (0.0, 1.6) equation: storage size: 183.836 KiB expression: nqubits: 10 + ├─ [+] ∑ 2π ⋅ 8.627e5.0/|r_i-r_j|^6 n_i n_j ├─ [+] 2π ⋅ 2.0 ⋅ ∑ σ^x_i └─ [-] 2π ⋅ 0.0 ⋅ ∑ n_i algorithm: CompositeAlgorithm{Tuple{Vern9, Rodas5{0, false, Nothing, typeof(OrdinaryDiffEq.DEFAULT_PRECS), Val{:forward}, true, nothing}}, AutoSwitch{Vern9, Rodas5{0, false, Nothing, typeof(OrdinaryDiffEq.DEFAULT_PRECS), Val{:forward}, true, nothing}, Rational{Int64}, Int64}}((Vern9(true), Rodas5{0, false, Nothing, typeof(OrdinaryDiffEq.DEFAULT_PRECS), Val{:forward}, true, nothing}(nothing, OrdinaryDiffEq.DEFAULT_PRECS)), AutoSwitch{Vern9, Rodas5{0, false, Nothing, typeof(OrdinaryDiffEq.DEFAULT_PRECS), Val{:forward}, true, nothing}, Rational{Int64}, Int64}(Vern9(true), Rodas5{0, false, Nothing, typeof(OrdinaryDiffEq.DEFAULT_PRECS), Val{:forward}, true, nothing}(nothing, OrdinaryDiffEq.DEFAULT_PRECS), 10, 3, 9//10, 9//10, 2, false, 5)) options: save_everystep: false save_start: false save_on: false dense: false`

`julia> integrator = init(prob, Vern8());`

`julia> emulate!(prob);`

Here, we have chosen the ODE-based solver (`Vern8()`

) by using `SchrodingerProblem`

and set the total evolution time to be 1.6 μs.

After simulating the time evolution and get the final state, we can measure the Rydberg population at each site for the final state:

`julia> rydberg_populations = map(1:nsites) do i rydberg_density(prob.reg, i) end`

`10-element Vector{Float64}: 0.4219218363122977 0.17593447277832852 0.28413947232948267 0.20000108257741664 0.3057048351222039 0.30570483512220425 0.20000108257741708 0.2841394723294831 0.17593447277832852 0.42192183631229796`

`prob.reg`

is the register storing the final state after the time evolution.

## Benchmarks

## Have Suggestions or Interested in Contributing?

- File an issue to report a bug or request a feature.
- Check out the Contributing to Bloqade guide if you would like to make your own changes and make a Pull Request when you would like your work to be reviewed