Skip to content

Commit

Permalink
Merge branch 'master' into hana_map
Browse files Browse the repository at this point in the history
  • Loading branch information
hanakl authored Feb 25, 2025
2 parents 7b8cb04 + 50b80a2 commit 02589c0
Show file tree
Hide file tree
Showing 27 changed files with 415 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@v4

# restore records from the artifacts
- uses: dawidd6/action-download-artifact@v6
- uses: dawidd6/action-download-artifact@v8
with:
workflow: benchmark.yml
name: performance-tracking
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/ci-julia-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ on:
branches: [master, main]
tags: ["*"]
pull_request:

concurrency:
# group by workflow and ref; the last slightly strange component ensures that for pull
# requests, we limit to 1 concurrent job, but for the master branch we don't
group: ${{ github.workflow }}-${{ github.ref }}-${{ (github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main') || github.run_number }}
# Cancel intermediate builds, but only if it is a pull request build.
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}

env:
PYTHON: ~
jobs:
Expand Down Expand Up @@ -36,7 +44,7 @@ jobs:
JULIA_NUM_THREADS: ${{ matrix.threads }}
JET_TEST: ${{ matrix.jet }}
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
file: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
15 changes: 12 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ on:
branches: [master, main]
tags: ["*"]
pull_request:

concurrency:
# group by workflow and ref; the last slightly strange component ensures that for pull
# requests, we limit to 1 concurrent job, but for the master branch we don't
group: ${{ github.workflow }}-${{ github.ref }}-${{ (github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main') || github.run_number }}
# Cancel intermediate builds, but only if it is a pull request build.
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}

env:
PYTHON: ~
jobs:
Expand All @@ -19,7 +27,8 @@ jobs:
os:
- ubuntu-latest
threads:
- '2'
- '1'
- '5'
arch:
- x64
include:
Expand All @@ -34,7 +43,7 @@ jobs:
- arch: x64
os: windows-latest
version: '1'
threads: '1'
threads: '1'
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
Expand All @@ -47,7 +56,7 @@ jobs:
env:
JULIA_NUM_THREADS: ${{ matrix.threads }}
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
file: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ Manifest.toml
build
.gitignore
ROADMAP.md
coverage
coverage
*.cov
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v0.5.1-dev

- Add `classical_delay` and `quantum_delay` as keyword arguments to the `RegisterNet` constructor to set a default global network edge latency.
- `onchange_tag` now permits a protocol to wait for any change to the tag metadata. Implemented thanks to the new `AsymmetricSemaphore`, a resource object that allows multiple processes to wait for an update.
- Plots of networks can now overlay real-world maps (see `generate_map`).

## v0.5.0 - 2024-10-16
Expand Down
8 changes: 4 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumSavory"
uuid = "2de2e421-972c-4cb5-a0c3-999c85908079"
authors = ["Stefan Krastanov <stefan@krastanov.org>"]
version = "0.5.2"
version = "0.5.1-dev"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand Down Expand Up @@ -35,14 +35,14 @@ QuantumSavoryMakie = "Makie"

[compat]
Combinatorics = "1"
ConcurrentSim = "1.4.1"
ConcurrentSim = "1.5"
Distributions = "0.25.90"
DocStringExtensions = "0.9"
GeoMakie = "0.7.8"
Graphs = "1.9"
IterTools = "1.4.0"
LinearAlgebra = "1"
Makie = "0.21"
Makie = "0.20, 0.21, 0.22"
NetworkLayout = "0.4.4"
PrecompileTools = "1"
Printf = "1"
Expand All @@ -53,7 +53,7 @@ QuantumOpticsBase = "0.5.3"
QuantumSymbolics = "0.4.3"
Random = "1"
Reexport = "1.2.2"
ResumableFunctions = "0.6.9"
ResumableFunctions = "1"
Statistics = "1"
SumTypes = "0.5.5"
julia = "1.10"
4 changes: 2 additions & 2 deletions examples/colorcentermodularcluster/1_time_to_connected.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ end

# Run a quick check that the simulation works.
# The first run will be slow as the code has to first compile.
@time run_until_connected(root_conf)
run_until_connected(root_conf)

##
# Run a hundred simulations in multiple parallel threads
# and store the results in a dataframe.

@time r = tmap((_)->run_until_connected(root_conf), 1:100);
r = tmap((_)->run_until_connected(root_conf), 1:100);
df = rename(DataFrame(r), [:time,:fid])

##
Expand Down
4 changes: 2 additions & 2 deletions examples/firstgenrepeater/6.1_compare_formalisms_noplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ nologging = ConsoleLogger(stderr, Logging.Warn)
# Run sims
replicates = 100
sampled_times = 0.:0.4:25.
@time qo_res = with_logger(nologging) do
qo_res = with_logger(nologging) do
[monte_carlo_trajectory(; sampled_times) for _ in 1:replicates]
end;
@time qc_res = with_logger(nologging) do
qc_res = with_logger(nologging) do
[monte_carlo_trajectory(; sampled_times, representation=CliffordRepr) for _ in 1:replicates]
end;

Expand Down
4 changes: 2 additions & 2 deletions examples/firstgenrepeater/6_compare_formalisms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ nologging = ConsoleLogger(stderr, Logging.Warn)
# Run sims
replicates = 100
sampled_times = 0.:0.2:25.
@time qo_res = with_logger(nologging) do
qo_res = with_logger(nologging) do
[monte_carlo_trajectory(; sampled_times) for _ in 1:replicates]
end;
@time qc_res = with_logger(nologging) do
qc_res = with_logger(nologging) do
[monte_carlo_trajectory(; sampled_times, representation=CliffordRepr) for _ in 1:replicates]
end;

Expand Down
37 changes: 25 additions & 12 deletions src/ProtocolZoo/ProtocolZoo.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module ProtocolZoo

using QuantumSavory
import QuantumSavory: get_time_tracker, Tag, isolderthan
import QuantumSavory: get_time_tracker, Tag, isolderthan, onchange_tag
using QuantumSavory: Wildcard
using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap

Expand Down Expand Up @@ -208,9 +208,13 @@ end
b_ = findfreeslot(prot.net[prot.nodeB]; randomize=prot.randomize, margin=margin)

if isnothing(a_) || isnothing(b_)
isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO
@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|round $(round): Failed to find free slots. \nGot:\n1. \t $a_ \n2.\t $b_ \n retrying..."
@yield timeout(prot.sim, prot.retry_lock_time)
if isnothing(prot.retry_lock_time)
@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|round $(round): Failed to find free slots. \nGot:\n1. \t $a_ \n2.\t $b_ \n waiting for changes to tags..."
@yield onchange_tag(prot.net[prot.nodeA]) | onchange_tag(prot.net[prot.nodeB])
else
@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|round $(round): Failed to find free slots. \nGot:\n1. \t $a_ \n2.\t $b_ \n waiting a fixed amount of time..."
@yield timeout(prot.sim, prot.retry_lock_time)
end
continue
end
# we are now certain that a_ and b_ are not nothing. The compiler is not smart enough to figure this out
Expand Down Expand Up @@ -397,20 +401,27 @@ function EntanglementConsumer(net::RegisterNet, nodeA::Int, nodeB::Int; kwargs..
end

@resumable function (prot::EntanglementConsumer)()
if isnothing(prot.period)
error("In `EntanglementConsumer` we do not yet support waiting on register to make qubits available") # TODO
end
while true
query1 = query(prot.net[prot.nodeA], EntanglementCounterpart, prot.nodeB, ❓; locked=false, assigned=true) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below
if isnothing(query1)
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on first node found no entanglement"
@yield timeout(prot.sim, prot.period)
if isnothing(prot.period)
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on first node found no entanglement. Waiting on tag updates in $(prot.nodeA)."
@yield onchange_tag(prot.net[prot.nodeA])
else
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on first node found no entanglement. Waiting a fixed amount of time."
@yield timeout(prot.sim, prot.period)
end
continue
else
query2 = query(prot.net[prot.nodeB], EntanglementCounterpart, prot.nodeA, query1.slot.idx; locked=false, assigned=true)
if isnothing(query2) # in case EntanglementUpdate hasn't reached the second node yet, but the first node has the EntanglementCounterpart
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on second node found no entanglement (yet...)"
@yield timeout(prot.sim, prot.period)
if isnothing(prot.period)
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on second node found no entanglement (yet...). Waiting on tag updates in $(prot.nodeB)."
@yield onchange_tag(prot.net[prot.nodeB])
else
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query on second node found no entanglement (yet...). Waiting a fixed amount of time."
@yield timeout(prot.sim, prot.period)
end
continue
end
end
Expand All @@ -431,7 +442,9 @@ end
push!(prot.log, (now(prot.sim), ob1, ob2))
unlock(q1)
unlock(q2)
@yield timeout(prot.sim, prot.period)
if !isnothing(prot.period)
@yield timeout(prot.sim, prot.period)
end
end
end

Expand Down
9 changes: 5 additions & 4 deletions src/ProtocolZoo/cutoff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ function CutoffProt(sim::Simulation, net::RegisterNet, node::Int; kwargs...)
end

@resumable function (prot::CutoffProt)()
if isnothing(prot.period)
error("In `CutoffProt` we do not yet support quing up and waiting on register") # TODO
end
reg = prot.net[prot.node]
while true
for slot in reg # TODO these should be done in parallel, otherwise we will be waiting on each slot, greatly slowing down the cutoffs
Expand Down Expand Up @@ -72,6 +69,10 @@ end

unlock(slot)
end
@yield timeout(prot.sim, prot.period)
if isnothing(prot.period)
@yield onchange_tag(reg)
else
@yield timeout(prot.sim, prot.period)
end
end
end
9 changes: 7 additions & 2 deletions src/ProtocolZoo/swapping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ end
while rounds != 0
qubit_pair_ = findswapablequbits(prot.net, prot.node, prot.nodeL, prot.nodeH, prot.chooseL, prot.chooseH; agelimit=prot.agelimit)
if isnothing(qubit_pair_)
isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO
@yield timeout(prot.sim, prot.retry_lock_time)
if isnothing(prot.retry_lock_time)
@debug "SwapperProt: no swappable qubits found. Waiting for tag change..."
@yield onchange_tag(prot.net[prot.node])
else
@debug "SwapperProt: no swappable qubits found. Waiting a fixed amount of time..."
@yield timeout(prot.sim, prot.retry_lock_time)
end
continue
end
# The compiler is not smart enough to figure out that qubit_pair_ is not nothing, so we need to tell it explicitly. A new variable name is needed due to @resumable.
Expand Down
3 changes: 3 additions & 0 deletions src/QuantumSavory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export
CliffordRepr, QuantumOpticsRepr, QuantumMCRepr,
UseAsState, UseAsObservable, UseAsOperation,
AbstractBackground,
onchange_tag,
# networks.jl
RegisterNet, channel, qchannel, messagebuffer,
# initialize.jl
Expand Down Expand Up @@ -98,6 +99,8 @@ include("traits_and_defaults.jl")

include("tags.jl")

include("semaphore.jl")

include("states_registers.jl")
include("quantumchannel.jl")
include("messagebuffer.jl")
Expand Down
2 changes: 2 additions & 0 deletions src/concurrentsim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ end
##

function get_time_tracker(rn::RegisterNet)
# TODO assert they are all the same
return get_time_tracker(rn.registers[1])
end
function get_time_tracker(r::Register)
# TODO assert all locks and tag_waiters and similar have the same env
r.locks[1].env::Simulation
end
function get_time_tracker(r::RegRef)
Expand Down
1 change: 1 addition & 0 deletions src/networks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function RegisterNet(graph::SimpleGraph, registers, vertex_metadata, edge_metada
if !all_are_same
if all_are_at_zero
for r in registers
r.tag_waiter[] = AsymmetricSemaphore(env)
for i in eachindex(r.locks)
r.locks[i] = ConcurrentSim.Resource(env,1)
end
Expand Down
2 changes: 2 additions & 0 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function tag!(ref::RegRef, tag)
id = guid()
push!(ref.reg.guids, id)
ref.reg.tag_info[id] = (;tag, slot=ref.idx, time=now(get_time_tracker(ref)))
unlock(ref.reg.tag_waiter[])
return id
end

Expand All @@ -41,6 +42,7 @@ function untag!(ref::RegOrRegRef, id::Integer)
isnothing(i) ? throw(QueryError("Attempted to delete a nonexistent tag id", untag!, id)) : deleteat!(reg.guids, i) # TODO make sure there is a clear error message
to_be_deleted = reg.tag_info[id]
delete!(reg.tag_info, id)
unlock(reg.tag_waiter[])
return to_be_deleted
end

Expand Down
33 changes: 33 additions & 0 deletions src/semaphore.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using ConcurrentSim
using ResumableFunctions
import Base: unlock, lock, islocked

"""Multiple processes can wait on this semaphore for a permission to run given by another process"""
struct AsymmetricSemaphore
nbwaiters::Ref{Int}
lock::Resource
end
AsymmetricSemaphore(sim) = AsymmetricSemaphore(Ref(0), Resource(sim,1,level=1)) # start locked

function Base.lock(s::AsymmetricSemaphore)
return @process _lock(s.lock.env, s)
end

@resumable function _lock(sim, s::AsymmetricSemaphore)
s.nbwaiters[] += 1
@yield lock(s.lock)
s.nbwaiters[] -= 1
if s.nbwaiters[] > 0
unlock(s.lock)
end
end

function unlock(s::AsymmetricSemaphore)
if s.nbwaiters[] > 0
unlock(s.lock)
end
end

function islocked(s::AsymmetricSemaphore)
return islocked(s.lock)
end
8 changes: 7 additions & 1 deletion src/states_registers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ struct Register # TODO better type description
tag_info::Dict{Int128, @NamedTuple{tag::Tag, slot::Int, time::Float64}}
guids::Vector{Int128}
netparent::Ref{Any}
tag_waiter::Ref{AsymmetricSemaphore} # TODO This being a ref is a bit of code smell... but we also want to be able to have registers that are not linked to a net so we need to be able to have this field un-initialized
end

function Register(traits, reprs, bg, sr, si, at)
env = ConcurrentSim.Simulation()
Register(traits, reprs, bg, sr, si, at, [ConcurrentSim.Resource(env) for _ in traits], Dict{Int128, Tuple{Tag, Int64, Float64}}(), [], nothing)
Register(traits, reprs, bg, sr, si, at, [ConcurrentSim.Resource(env) for _ in traits], Dict{Int128, Tuple{Tag, Int64, Float64}}(), [], nothing, Ref(AsymmetricSemaphore(env)))
end

Register(traits,reprs,bg,sr,si) = Register(traits,reprs,bg,sr,si,zeros(length(traits)))
Expand Down Expand Up @@ -60,3 +61,8 @@ get_register(r::RegRef) = r.reg
get_register(r::Register) = r

#Base.:(==)(r1::Register, r2::Register) =

function onchange_tag(r::RegOrRegRef)
register = get_register(r)
return lock(register.tag_waiter[])
end
Loading

0 comments on commit 02589c0

Please sign in to comment.