-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ed3ac96
commit 9a819be
Showing
6 changed files
with
959 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
module QuantumObjectUtils | ||
|
||
export operator_from_string | ||
export ket_from_string | ||
export ket_from_bitstring | ||
|
||
export haar_random | ||
export haar_identity | ||
|
||
export create | ||
export annihilate | ||
|
||
using ..Gates | ||
|
||
using LinearAlgebra | ||
using TestItemRunner | ||
|
||
# TODO: | ||
# [ ] Remove need for oscillator operators (used by tests) | ||
# [ ] Allow multi-character symbols for operators_from_string | ||
# [ ] Remove need for otimes symbol or avoid import conflicts with other packages | ||
|
||
|
||
@doc raw""" | ||
operator_from_string(operator::String; lookup::Dict{Symbol, AbstractMatrix}=PAULIS) | ||
Reduce the string (each character is one key) via operators from a dictionary. | ||
""" | ||
function operator_from_string( | ||
operator::String; | ||
lookup::Dict{Symbol, <:AbstractMatrix}=PAULIS | ||
)::Matrix{ComplexF64} | ||
# TODO: allow multi-character keys, ['(', ')'] | ||
|
||
# split string into keys and replace with operators | ||
characters = [Symbol(c) for c ∈ operator] | ||
operators = replace(characters, lookup...) | ||
|
||
return foldr(kron, operators) | ||
end | ||
|
||
function cavity_state(state::Int, levels::Int)::Vector{ComplexF64} | ||
@assert state ≤ levels - 1 "Level $state is not allowed for $levels levels" | ||
ket = zeros(levels) | ||
ket[state + 1] = 1 | ||
return ket | ||
end | ||
|
||
@doc raw""" | ||
ket_from_string( | ||
ket::String, | ||
levels::Vector{Int}; | ||
level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), | ||
return_states=false | ||
) | ||
Construct a quantum state from a string ket representation. | ||
# Example | ||
# TODO: add example | ||
""" | ||
function ket_from_string( | ||
ket::String, | ||
levels::Vector{Int}; | ||
level_dict=Dict(:g => 0, :e => 1, :f => 2, :h => 2), | ||
return_states=false | ||
)::Vector{ComplexF64} | ||
kets = [] | ||
|
||
for x ∈ split(ket, ['(', ')']) | ||
if x == "" | ||
continue | ||
elseif all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) | ||
append!(kets, x) | ||
elseif occursin("+", x) | ||
superposition = split(x, '+') | ||
@assert all(all(Symbol(xᵢ) ∈ keys(level_dict) for xᵢ ∈ x) for x ∈ superposition) "Invalid ket: $x" | ||
@assert length(superposition) == 2 "Only two states can be superposed for now" | ||
push!(kets, x) | ||
else | ||
error("Invalid ket: $x") | ||
end | ||
end | ||
|
||
states = [] | ||
|
||
for (ψᵢ, l) ∈ zip(kets, levels) | ||
if ψᵢ isa AbstractString && occursin("+", ψᵢ) | ||
superposition = split(ψᵢ, '+') | ||
superposition_states = [level_dict[Symbol(x)] for x ∈ superposition] | ||
@assert all(state ≤ l - 1 for state ∈ superposition_states) "Level $ψᵢ is not allowed for $l levels" | ||
superposition_state = sum([ | ||
cavity_state(state, l) for state ∈ superposition_states | ||
]) | ||
normalize!(superposition_state) | ||
push!(states, superposition_state) | ||
else | ||
state = level_dict[Symbol(ψᵢ)] | ||
@assert state ≤ l - 1 "Level $ψᵢ is not allowed for $l levels" | ||
push!(states, cavity_state(state, l)) | ||
end | ||
end | ||
|
||
if return_states | ||
return states | ||
else | ||
return kron([1.0], states...) | ||
end | ||
end | ||
|
||
@doc raw""" | ||
ket_from_bitstring(ket::String) | ||
Get the state vector for a qubit system given a ket string `ket` of 0s and 1s. | ||
""" | ||
function ket_from_bitstring(ket::String)::Vector{ComplexF64} | ||
cs = [c for c ∈ ket] | ||
@assert all(c ∈ "01" for c ∈ cs) | ||
states = [c == '0' ? [1, 0] : [0, 1] for c ∈ cs] | ||
return foldr(kron, states) | ||
end | ||
|
||
### | ||
### Random operators | ||
### | ||
|
||
@doc raw""" | ||
haar_random(n::Int) | ||
Generate a random unitary matrix using the Haar measure for an `n`-dimensional system. | ||
""" | ||
function haar_random(n::Int) | ||
# Ginibre matrix | ||
Z = (randn(n, n) + im * randn(n, n)) / √2 | ||
F = qr(Z) | ||
# QR correction (R main diagonal is real, strictly positive) | ||
Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) | ||
return F.Q * Λ | ||
end | ||
|
||
@doc raw""" | ||
haar_identity(n::Int, radius::Number) | ||
Generate a random unitary matrix close to the identity matrix using the Haar measure for an `n`-dimensional system with a given `radius`. | ||
""" | ||
function haar_identity(n::Int, radius::Number) | ||
# Ginibre matrix | ||
Z = (I + radius * (randn(n, n) + im * randn(n, n)) / √2) / (1 + radius) | ||
F = qr(Z) | ||
# QR correction (R main diagonal is real, strictly positive) | ||
Λ = diagm(diag(F.R) ./ abs.(diag(F.R))) | ||
return F.Q * Λ | ||
end | ||
|
||
### | ||
### Oscillator operators | ||
### | ||
|
||
@doc raw""" | ||
annihilate(levels::Int) | ||
Get the annihilation operator for a system with `levels` levels. | ||
""" | ||
function annihilate(levels::Int)::Matrix{ComplexF64} | ||
return diagm(1 => map(sqrt, 1:levels - 1)) | ||
end | ||
|
||
@doc raw""" | ||
create(levels::Int) | ||
Get the creation operator for a system with `levels` levels. | ||
""" | ||
function create(levels::Int) | ||
return collect(annihilate(levels)') | ||
end | ||
|
||
# ============================================================================= # | ||
|
||
@testitem "Test ket_from_bitstring function" begin | ||
using LinearAlgebra | ||
@test ket_from_bitstring("0") == [1, 0] | ||
@test ket_from_bitstring("1") == [0, 1] | ||
@test ket_from_bitstring("00") == [1, 0, 0, 0] | ||
@test ket_from_bitstring("01") == [0, 1, 0, 0] | ||
@test ket_from_bitstring("10") == [0, 0, 1, 0] | ||
@test ket_from_bitstring("11") == [0, 0, 0, 1] | ||
end | ||
|
||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module QuantumSystemTemplates | ||
|
||
export TransmonSystem | ||
export TransmonDipoleCoupling | ||
export MultiTransmonSystem | ||
export RydbergChainSystem | ||
export QuantumOpticsSystem | ||
|
||
using ..QuantumSystems | ||
using ..QuantumObjectUtils | ||
|
||
using LinearAlgebra | ||
using TestItemRunner | ||
|
||
include("transmons.jl") | ||
include("rydberg.jl") | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
|
||
function generate_pattern(N::Int, i::Int) | ||
# Create an array filled with 'I' | ||
qubits = fill('I', N) | ||
# Insert 'n' at position i and i+1, ensuring it doesn't exceed the array bounds | ||
if i <= N && i+1 <= N | ||
qubits[i] = 'n' | ||
qubits[i+1] = 'n' | ||
end | ||
return join(qubits) | ||
end | ||
function generate_pattern_with_gap(N::Int, i::Int, gap::Int) | ||
# Create an array filled with 'I' | ||
qubits = fill('I', N) | ||
# Insert 'n' at position i and i+gap+1, ensuring it doesn't exceed the array bounds | ||
if i <= N && i+gap+1 <= N | ||
qubits[i] = 'n' | ||
qubits[i+gap+1] = 'n' | ||
end | ||
return join(qubits) | ||
end | ||
|
||
""" | ||
Embed a character into a string at a specific position. | ||
""" | ||
function lift(x::Char,i::Int, N::Int) | ||
qubits = fill('I', N) | ||
qubits[i] = x | ||
return join(qubits) | ||
end | ||
@doc raw""" | ||
RydbergChainSystem(; | ||
N::Int=3, # number of atoms | ||
C::Float64=862690*2π, | ||
distance::Float64=10.0, # μm | ||
cutoff_order::Int=2, # 1 is nearest neighbor, 2 is next-nearest neighbor, etc. | ||
local_detune::Bool=false, # If true, include one local detuning pattern. | ||
all2all::Bool=true, # If true, include all-to-all interactions. | ||
ignore_Y_drive::Bool=false, # If true, ignore the Y drive. (In the experiments, X&Y drives are implemented by Rabi amplitude and its phase.) | ||
) -> QuantumSystem | ||
Returns a `QuantumSystem` object for the Rydberg atom chain in the spin basis | ||
|g⟩ = |0⟩ = [1, 0], |r⟩ = |1⟩ = [0, 1]. | ||
```math | ||
H = \sum_i 0.5*\Omega_i(t)\cos(\phi_i(t)) \sigma_i^x - 0.5*\Omega_i(t)\sin(\phi_i(t)) \sigma_i^y - \sum_i \Delta_i(t)n_i + \sum_{i<j} \frac{C}{|i-j|^6} n_i n_j | ||
``` | ||
# Keyword Arguments | ||
- `N`: Number of atoms. | ||
- `C`: The Rydberg interaction strength in MHz*μm^6. | ||
- `distance`: The distance between atoms in μm. | ||
- `cutoff_order`: Interaction range cutoff, 1 is nearest neighbor, 2 is next nearest neighbor. | ||
- `local_detune`: If true, include one local detuning pattern. | ||
- `all2all`: If true, include all-to-all interactions. | ||
- `ignore_Y_drive`: If true, ignore the Y drive. (In the experiments, X&Y drives are implemented by Rabi amplitude and its phase.) | ||
""" | ||
function RydbergChainSystem(; | ||
N::Int=3, # number of atoms | ||
C::Float64=862690*2π, | ||
distance::Float64=8.7, # μm | ||
cutoff_order::Int=1, # 1 is nearest neighbor, 2 is next-nearest neighbor, etc. | ||
local_detune::Bool=false, | ||
all2all::Bool=true, | ||
ignore_Y_drive::Bool=false, | ||
) | ||
PAULIS = Dict(:I => [1 0; 0 1], :X => [0 1; 1 0], :Y => [0 -im; im 0], :Z => [1 0; 0 -1], :n => [0 0; 0 1]) | ||
|
||
if all2all | ||
H_drift = zeros(ComplexF64, 2^N, 2^N) | ||
for gap in 0:N-2 | ||
for i in 1:N-gap-1 | ||
H_drift += C * operator_from_string( | ||
generate_pattern_with_gap(N, i, gap), | ||
lookup = PAULIS | ||
) / ((gap + 1) * distance)^6 | ||
end | ||
end | ||
else | ||
if cutoff_order == 1 | ||
H_drift = sum([C*operator_from_string(generate_pattern(N,i),lookup=PAULIS)/(distance^6) for i in 1:N-1]) | ||
elseif cutoff_order == 2 | ||
H_drift = sum([C*operator_from_string(generate_pattern(N,i),lookup=PAULIS)/(distance^6) for i in 1:N-1]) | ||
H_drift += sum([C*operator_from_string(generate_pattern_with_gap(N,i,1),lookup=PAULIS)/((2*distance)^6) for i in 1:N-2]) | ||
else | ||
error("Higher cutoff order not supported") | ||
end | ||
end | ||
|
||
H_drives = Matrix{ComplexF64}[] | ||
|
||
# Add global X drive | ||
Hx = sum([0.5*operator_from_string(lift('X',i,N), lookup=PAULIS) for i in 1:N]) | ||
push!(H_drives, Hx) | ||
|
||
if !ignore_Y_drive | ||
# Add global Y drive | ||
Hy = sum([0.5*operator_from_string(lift('Y',i,N), lookup=PAULIS) for i in 1:N]) | ||
push!(H_drives, Hy) | ||
end | ||
|
||
# Add global detuning | ||
H_detune = -sum([operator_from_string(lift('n',i,N), lookup=PAULIS) for i in 1:N]) | ||
push!(H_drives, H_detune) | ||
|
||
params = Dict{Symbol, Any}( | ||
:N => N, | ||
:C => C, | ||
:distance => distance, | ||
:cutoff_order => cutoff_order, | ||
:local_detune => local_detune, | ||
:all2all => all2all, | ||
) | ||
|
||
return QuantumSystem( | ||
H_drift, | ||
H_drives; | ||
constructor=RydbergChainSystem, | ||
params=params, | ||
) | ||
end | ||
|
||
@testitem "Rydberg system test" begin | ||
@test RydbergChainSystem(N=3,cutoff_order=2,all2all=false) isa QuantumSystem | ||
end |
Oops, something went wrong.