Skip to content

Commit

Permalink
initial commit (2?)
Browse files Browse the repository at this point in the history
  • Loading branch information
aarontrowbridge committed Nov 6, 2024
1 parent ed3ac96 commit 9a819be
Show file tree
Hide file tree
Showing 6 changed files with 959 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/PiccoloQuantumObjects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ include("quantum_systems.jl")
include("embedded_operators.jl")
@reexport using .EmbeddedOperators

include("quantum_object_utils.jl")
@reexport using .QuantumObjectUtils

include("quantum_system_utils.jl")
@reexport using .QuantumSystemUtils

include("quantum_system_templates/_quantum_system_templates.jl")
@reexport using .QuantumSystemTemplates

end
192 changes: 192 additions & 0 deletions src/quantum_object_utils.jl
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
18 changes: 18 additions & 0 deletions src/quantum_system_templates/_quantum_system_templates.jl
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
126 changes: 126 additions & 0 deletions src/quantum_system_templates/rydberg.jl
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
Loading

0 comments on commit 9a819be

Please sign in to comment.