Skip to content

Commit

Permalink
added QuantumChannel
Browse files Browse the repository at this point in the history
  • Loading branch information
ba2tripleO committed Oct 31, 2023
1 parent 467894c commit 1367b58
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
QuantumSavoryMakie = "Makie"

[compat]
ConcurrentSim = "1.1"
ConcurrentSim = "1.4"
DocStringExtensions = "0.9"
Graphs = "1.7.3"
IterTools = "1.4.0"
Expand Down
6 changes: 6 additions & 0 deletions src/QuantumSavory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export apply!, traceout!, removebackref!
export project_traceout! #TODO should move to QuantumInterface

import ConcurrentSim
using ResumableFunctions

@reexport using QuantumSymbolics
using QuantumSymbolics:
Expand All @@ -29,6 +30,9 @@ export Qubit, Qumode, QuantumStateTrait,
CliffordRepr, QuantumOpticsRepr, QuantumMCRepr,
UseAsState, UseAsObservable, UseAsOperation,
AbstractBackground
export QuantumChannel, take!, put!


#TODO you can not assume you can always in-place modify a state. Have all these functions work on stateref, not stateref[]
# basically all ::QuantumOptics... should be turned into ::Ref{...}... but an abstract ref

Expand Down Expand Up @@ -330,6 +334,8 @@ include("concurrentsim.jl")

include("plots.jl")

include("quantumchannel.jl")

include("CircuitZoo/CircuitZoo.jl")

include("StatesZoo/StatesZoo.jl")
Expand Down
24 changes: 0 additions & 24 deletions src/concurrentsim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,6 @@ end
end
end

mutable struct DelayChannel{T}
delay::Float64
store::Store{T}
end
function DelayChannel(env::Environment, delay::Float64)
return DelayChannel(delay, Store{Any}(env))
end
function DelayChannel{T}(env::Environment, delay::Float64) where T
return DelayChannel(delay, Store{T}(env))
end

@resumable function latency(env::Environment, channel::DelayChannel, value)
@yield timeout(channel.store.env, channel.delay)
put(channel.store, value)
end

function ConcurrentSim.put(channel::DelayChannel, value) # TODO rename to the ones from Base
@process latency(channel.store.env, channel, value) # results in the scheduling of all events generated by latency
end

function ConcurrentSim.get(channel::DelayChannel) # TODO rename to the ones from Base
get(channel.store) # returns an element stored in the cable store
end

##

function get_time_tracker(rn::RegisterNet)
Expand Down
89 changes: 89 additions & 0 deletions src/quantumchannel.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""
QuantumChannel for transmitting qubits as `RegRef`s from one register to another in a quantum protocol simulation,
with a channel delay and under the influence of a background process.
The function `put!` is used to put the `RegRef` containing the qubit in the channel, which can then be received by
the receiving register after a specified delay using the take! method in a synchronous way.
```jldoctest
julia> using QuantumSavory; using ResumableFunctions; using ConcurrentSim
julia> bell = (Z1⊗Z1 + Z2⊗Z2)/sqrt(2.0); regA = Register(2); regB = Register(2); initialize!((regA[1], regB[2]), bell);
julia> sim = Simulation();
julia> queue = DelayQueue{RegRef}(sim, 10.0)
DelayQueue{RegRef}(QueueStore{RegRef, Int64}, 10.0)
julia> qc = QuantumChannel(queue)
QuantumChannel(Qubit(), DelayQueue{RegRef}(QueueStore{RegRef, Int64}, 10.0), nothing)
julia> @resumable function alice_node(env, qc)
println("Putting Alice's qubit in the channel at ", now(env))
put!(qc, regA[1])
end
alice_node (generic function with 1 method)
julia> @resumable function bob_node(env, qc)
@yield @process take!(env, qc, regB[1]) # wait for the process with delay to complete
println("Taking the qubit from alice at ", now(env))
end
bob_node (generic function with 1 method)
julia> @process alice_node(sim, qc); @process bob_node(sim, qc);
julia> run(sim)
Putting Alice's qubit in the channel at 0.0
Taking the qubit from alice at 10.0
julia> regA
Register with 2 slots: [ Qubit | Qubit ]
Slots:
nothing
nothing
julia> regB
Register with 2 slots: [ Qubit | Qubit ]
Slots:
Subsystem 1 of QuantumOpticsBase.Ket 12382959472027850978
Subsystem 2 of QuantumOpticsBase.Ket 12382959472027850978
```
"""
struct QuantumChannel
trait::Qubit
queue::ConcurrentSim.DelayQueue{RegRef}
background::Any
end

function QuantumChannel(queue::ConcurrentSim.DelayQueue{RegRef}, background=nothing)
QuantumChannel(Qubit(), queue, background)
end

function Base.put!(qc::QuantumChannel, rref::RegRef, Δt=nothing)
if xor(isnothing(qc.background), isnothing(Δt))
throw(ArgumentError(lazy"""
Either both background and Δt should be nothing or both should be initialized to appropriate values
"""))
elseif !isnothing(Δt) # if both are not nothing
uptotime!(rref.reg.staterefs[rref.idx], rref.reg.stateindices[rref.idx], qc.background, Δt)
end

put!(qc.queue, rref)
end

# should we mandate that rref_rec.reg is not initialized beforehand
# should other register attributes of the sent regref be copied over like reprs[idx], backgrounds[idx], accesstimes[idx], env and locks[idx]
@resumable function Base.take!(env, qc::QuantumChannel, rref_rec::RegRef)
rref = @yield take!(qc.queue)

rref_rec.reg.staterefs[rref_rec.idx] = rref.reg.staterefs[rref.idx]
rref_rec.reg.stateindices[rref_rec.idx] = rref.reg.stateindices[rref.idx]

# update the stateref
sref = rref.reg.staterefs[rref.idx]
sref.registers[rref.idx] = rref_rec.reg
sref.registerindices[rref.idx] = rref_rec.idx

# erase the state from the sending register
rref.reg.staterefs[rref.idx] = nothing
rref.reg.stateindices[rref.idx] = 0
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ end

println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREADS = $(Sys.CPU_THREADS)`...")

@doset "quantumchannel"
@doset "register_interface"
@doset "project_traceout"
@doset "observable"
Expand Down
34 changes: 34 additions & 0 deletions test/test_quantumchannel.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using QuantumSavory
using ResumableFunctions
using ConcurrentSim
using Test

bell = (Z1Z1 + Z2Z2)/sqrt(2.0)
regA = Register(2)
regB = Register(2)
initialize!((regA[1], regB[2]), bell)

sim = Simulation()

# Delay queue for quantum channel
queue = DelayQueue{RegRef}(sim, 10.0)

qc = QuantumChannel(queue)

@resumable function alice_node(env, qc)
put!(qc, regA[1])
end

@resumable function bob_node(env, qc)
@yield @process take!(env, qc, regB[1])
end

@process alice_node(sim, qc)
@process bob_node(sim, qc)

run(sim)

sref = regB.staterefs[1]

# the above code puts both the qubits of the state in the same register
@test sref.registers[1] == sref.registers[2]

0 comments on commit 1367b58

Please sign in to comment.