From 2ca076cf5f41ea4c7056f07b76ca37180600b162 Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Thu, 15 Aug 2024 17:54:48 -0400 Subject: [PATCH 1/6] remove blossom does not compile on mac --- Project.toml | 5 +- examples/simpleswitch/Project.toml | 1 + .../1_interactive_visualization.jl | 82 +++++++++ .../2_wglmakie_interactive.jl | 158 ++++++++++++++++++ .../simpleswitch_randomassign/Project.toml | 10 ++ examples/simpleswitch_randomassign/README.md | 12 ++ examples/simpleswitch_randomassign/setup.jl | 56 +++++++ src/ProtocolZoo/switches.jl | 62 +++---- 8 files changed, 353 insertions(+), 33 deletions(-) create mode 100644 examples/simpleswitch_randomassign/1_interactive_visualization.jl create mode 100644 examples/simpleswitch_randomassign/2_wglmakie_interactive.jl create mode 100644 examples/simpleswitch_randomassign/Project.toml create mode 100644 examples/simpleswitch_randomassign/README.md create mode 100644 examples/simpleswitch_randomassign/setup.jl diff --git a/Project.toml b/Project.toml index 93c13209..e5148356 100644 --- a/Project.toml +++ b/Project.toml @@ -9,10 +9,9 @@ Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" -GraphsMatching = "c3af3a8c-b79e-4b01-bf44-c718d7e0e0d6" IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" @@ -41,9 +40,7 @@ ConcurrentSim = "1.4.1" Distributions = "0.25.90" DocStringExtensions = "0.9" Graphs = "1.9" -GraphsMatching = "0.2" IterTools = "1.4.0" -JuMP = "1.21.1" LinearAlgebra = "1" Makie = "0.20, 0.21" NetworkLayout = "0.4.4" diff --git a/examples/simpleswitch/Project.toml b/examples/simpleswitch/Project.toml index e2bb24c3..07adacf5 100644 --- a/examples/simpleswitch/Project.toml +++ b/examples/simpleswitch/Project.toml @@ -4,6 +4,7 @@ ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079" +QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" diff --git a/examples/simpleswitch_randomassign/1_interactive_visualization.jl b/examples/simpleswitch_randomassign/1_interactive_visualization.jl new file mode 100644 index 00000000..f4674334 --- /dev/null +++ b/examples/simpleswitch_randomassign/1_interactive_visualization.jl @@ -0,0 +1,82 @@ +using GLMakie + +include("setup.jl") + +# Prepare all of the simulation components (while all visualization components are prepared in the rest of this file) +n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale = prepare_simulation() + +# Prepare the main figure +fig = Figure(size=(1600,800)) +fig_plots = fig[1,1] + +# Subfigure for the network visualization +_,ax,_,obs = registernetplot_axis(fig_plots[1:2,1],net) + +# Subfigure for the "backlog over time" +backlog = Observable(Float64[0]) +sim_time = Observable(Float64[0]) +ax_backlog = Axis(fig_plots[1:2,2], xlabel="time", ylabel="average backlog") +stairs!(ax_backlog,sim_time,backlog) + +# Subfigure for the "total successfully established and consumed Bell pairs for a pair of clients" +consumed = Observable(zeros(length(consumers))) +ax_consumed_ticks = ["$i-$j" for (i,j) in client_unordered_pairs] +ax_consumed = Axis(fig_plots[1,3], xlabel="pair", ylabel="consumed pairs", xticks=(1:length(consumers),ax_consumed_ticks)) +barplot!(ax_consumed,1:length(consumers),consumed, color=Cycled(2)) + +# Subfigure for the "backlog for a given pair of clients" +backlog_perpair = Observable(zeros(length(consumers))) +ax_backlog_perpair = Axis(fig_plots[2,3], xlabel="pair", ylabel="backlog", xticks=(1:length(consumers),ax_consumed_ticks)) +barplot!(ax_backlog_perpair,1:length(consumers),backlog_perpair) + +# Sliders with which to control the request rates +sliderfig_ = fig[2,1] +sliderfig = sliderfig_[2,1] +sliders = [] +for ((i,j), rate) in zip(client_pairs, rates) + slider = Slider(sliderfig[i,j], range=0.05:0.05:2, startvalue=1) + push!(sliders, slider) + on(slider.value) do val + rate[] = val*rate_scale + end +end +for i in 1:n + Label(sliderfig[1,i+1], "$(i+1)→", tellwidth=false) + Label(sliderfig[i+1,1], "→$(i+1)", tellwidth=true) +end +sliderfig_override = sliderfig_[3,1] +slider_override = Slider(sliderfig_override[1,2], range=0.05:0.05:2, startvalue=1) +on(slider_override.value) do val + for slider in sliders + @async begin + set_close_to!(slider, val) + end + end +end +Label(sliderfig_override[1,1], "global rate override:") +Label(sliderfig_[1,1], rich("Request Rate Controls:",fontsize=20), tellwidth=false) + +# Display the figure... +display(fig) + +# ... and run the simulation while updating plots as needed +step_ts = range(0, 1000, step=0.1) +for t in step_ts + run(sim, t) + ax.title = "t=$(t)" + push!(sim_time[],t) + push!(backlog[], sum(switch_protocol.backlog)/(n-1)/(n-2)/2) + for (i, consumer) in enumerate(consumers) + consumed[][i] = length(consumer.log) + end + for (l,(i, j)) in enumerate(client_unordered_pairs) + backlog_perpair[][l] = switch_protocol.backlog[i-1,j-1] + end + notify(backlog) + notify(consumed) + notify(backlog_perpair) + notify(obs) + autolimits!(ax_backlog) + autolimits!(ax_consumed) + autolimits!(ax_backlog_perpair) +end diff --git a/examples/simpleswitch_randomassign/2_wglmakie_interactive.jl b/examples/simpleswitch_randomassign/2_wglmakie_interactive.jl new file mode 100644 index 00000000..a4329533 --- /dev/null +++ b/examples/simpleswitch_randomassign/2_wglmakie_interactive.jl @@ -0,0 +1,158 @@ +using WGLMakie +WGLMakie.activate!() +import Bonito +using Markdown + +include("setup.jl") + +const custom_css = Bonito.DOM.style("ul {list-style: circle !important;}") # TODO remove after fix of bug in JSServe https://github.com/SimonDanisch/JSServe.jl/issues/178 + +# Mostly a copy of the 1_interactive_visualization.jl file, encapsulated here for convenient serving from inside a WGLMakie app +function prepare_singlerun() + # Prepare all of the simulation components (while all visualization components are prepared in the rest of this function) + n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale = prepare_simulation() + + # Prepare the main figure + fig = Figure(size=(1600,800)) + fig_plots = fig[1,1] + + # Subfigure for the network visualization + _,ax,_,obs = registernetplot_axis(fig_plots[1:2,1],net) + + # Subfigure for the "backlog over time" + backlog = Observable(Float64[0]) + sim_time = Observable(Float64[0]) + ax_backlog = Axis(fig_plots[1:2,2], xlabel="time", ylabel="average backlog") + stairs!(ax_backlog,sim_time,backlog) + + # Subfigure for the "total successfully established and consumed Bell pairs for a pair of clients" + consumed = Observable(zeros(length(consumers))) + ax_consumed_ticks = ["$i-$j" for (i,j) in client_unordered_pairs] + ax_consumed = Axis(fig_plots[1,3], xlabel="pair", ylabel="consumed pairs", xticks=(1:length(consumers),ax_consumed_ticks)) + barplot!(ax_consumed,1:length(consumers),consumed, color=Cycled(2)) + + # Subfigure for the "backlog for a given pair of clients" + backlog_perpair = Observable(zeros(length(consumers))) + ax_backlog_perpair = Axis(fig_plots[2,3], xlabel="pair", ylabel="backlog", xticks=(1:length(consumers),ax_consumed_ticks)) + barplot!(ax_backlog_perpair,1:length(consumers),backlog_perpair) + + # Sliders with which to control the request rates + sliderfig_ = fig[2,1] + sliderfig = sliderfig_[2,1] + sliders = [] + for ((i,j), rate) in zip(client_pairs, rates) + slider = Makie.Slider(sliderfig[i,j], range=0.05:0.05:2, startvalue=1) + push!(sliders, slider) + on(slider.value) do val + rate[] = val*rate_scale + end + end + for i in 1:n + Label(sliderfig[1,i+1], "$(i+1)→", tellwidth=false) + Label(sliderfig[i+1,1], "→$(i+1)", tellwidth=true) + end + sliderfig_override = sliderfig_[3,1] + slider_override = Makie.Slider(sliderfig_override[1,2], range=0.05:0.05:2, startvalue=1) + on(slider_override.value) do val + for slider in sliders + Threads.@spawn begin + set_close_to!(slider, val) + end + end + end + Label(sliderfig_override[1,1], "global rate override:") + Label(sliderfig_[1,1], rich("Request Rate Controls:",fontsize=20), tellwidth=false) + + axes = (;ax_backlog, ax_consumed, ax_backlog_perpair) + observables = (;backlog, consumed, backlog_perpair, obs) + + return n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale, sim_time, fig, observables, axes +end + +# All the calls that happen in the main event loop of the simulation, +# encapsulated here so that we can conveniently pause the simulation from the WGLMakie app. +function continue_singlerun!(n, fig, sim, sim_time, switch_protocol, client_unordered_pairs, consumers, + observables, axes, running +) + backlog = observables.backlog + consumed = observables.consumed + backlog_perpair = observables.backlog_perpair + step_ts = range(0, 1000, step=0.1) + for t in step_ts + run(sim, t) + #ax.title = "t=$(t)" + push!(sim_time[],t) + push!(backlog[], sum(switch_protocol.backlog)/(n-1)/(n-2)/2) + for (i, consumer) in enumerate(consumers) + consumed[][i] = length(consumer.log) + end + for (l,(i, j)) in enumerate(client_unordered_pairs) + backlog_perpair[][l] = switch_protocol.backlog[i-1,j-1] + end + notify.(tuple(observables...)) + autolimits!.(tuple(axes...)) + end + running[] = nothing +end + +# +landing = Bonito.App() do + + n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale, sim_time, fig, observables, axes = prepare_singlerun() + + running = Observable{Any}(false) + fig[3,1] = buttongrid = GridLayout(tellwidth = false) + buttongrid[1,1] = b = Makie.Button(fig, label = @lift(isnothing($running) ? "Done" : $running ? "Running..." : "Run once"), height=30, tellwidth=false) + + on(b.clicks) do _ + if !running[] + running[] = true + end + end + on(running) do r + if r + Threads.@spawn begin + continue_singlerun!( + n, fig, sim, sim_time, switch_protocol, client_unordered_pairs, consumers, + observables, axes, running) + end + end + end + + + content = md""" + Pick simulation settings and hit run (see below for technical details). + + $(fig.scene) + + # Simulations of a simple entanglement switch + + The switch is in the center of a star network of clients. + Each client can request to be connected to another client. + The rate of requests can be configured for each pair through the sliders. + + After the switch successfully connects two clients, they share an entangled pair which is consumed shortly thereafter -- the total number of consumed pairs is tracked in one of the bar plots. + + The backlog of requests is tracked in the other set of plots. + + The switch runs one of the scheduling algorithms from "Maximizing Entanglement Rates via Efficient Memory Management in Flexible Quantum Switches" by Promponas et al (2024). + + [See and modify the code for this simulation on github.](https://github.com/QuantumSavory/QuantumSavory.jl/tree/master/examples/simpleswitch) + """ + return Bonito.DOM.div(Bonito.MarkdownCSS, Bonito.Styling, custom_css, content) +end; + +# +# Serve the Makie app + +isdefined(Main, :server) && close(server); +port = parse(Int, get(ENV, "QS_SIMPLESWITCH_PORT", "8888")) +interface = get(ENV, "QS_SIMPLESWITCH_IP", "127.0.0.1") +proxy_url = get(ENV, "QS_SIMPLESWITCH_PROXY", "") +server = Bonito.Server(interface, port; proxy_url); +Bonito.HTTPServer.start(server) +Bonito.route!(server, "/" => landing); + +## + +wait(server) diff --git a/examples/simpleswitch_randomassign/Project.toml b/examples/simpleswitch_randomassign/Project.toml new file mode 100644 index 00000000..586a0b3f --- /dev/null +++ b/examples/simpleswitch_randomassign/Project.toml @@ -0,0 +1,10 @@ +[deps] +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" +ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079" +QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" +ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" diff --git a/examples/simpleswitch_randomassign/README.md b/examples/simpleswitch_randomassign/README.md new file mode 100644 index 00000000..270400d6 --- /dev/null +++ b/examples/simpleswitch_randomassign/README.md @@ -0,0 +1,12 @@ +# A Simulation of the Simple Entanglement Switch with random assignment + +The switch is connected to n clients. +The switch has m qubit slots and the clients have a single qubit slot. +The switch initiates m entanglement attempts per clock tick with m clients uniformly at random. +Once entangled links are established among the qubits at the switch and its clients, the switch performs entanglement swaps on the respective links. Note: As the switch randomly selects pairs of clients to entangle, the protocol operates without user requests / backlog priorisation. + + + diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl new file mode 100644 index 00000000..0e0c4589 --- /dev/null +++ b/examples/simpleswitch_randomassign/setup.jl @@ -0,0 +1,56 @@ +using QuantumSavory +using QuantumSavory.ProtocolZoo +using Graphs +using ConcurrentSim +using ResumableFunctions +using Distributions + +"""A helper function to repeatedly make requests from a client to the switch to be connected to another client.""" +@resumable function make_request(sim, net, client, other_client, rate_observable) + while true + wait_time = rand(Exponential(1/rate_observable[])) + @yield timeout(sim, wait_time) + put!(channel(net, client=>1), Tag(SwitchRequest(client, other_client))) + end +end + +function prepare_simulation() + n = 10 # number of clients + m = n-2 # memory slots in switch + + # The graph of network connectivity. Index 1 corresponds to the switch. + graph = star_graph(n+1) + + switch_register = Register(m) + client_registers = [Register(1) for _ in 1:n] + net = RegisterNet(graph, [switch_register, client_registers...]) + sim = get_time_tracker(net) + + # Set up the request-making processes + # between each ordered pair of clients + client_pairs = [(k1,k2) for k1 in 2:n+1 for k2 in 2:n+1 if k2!=k1] + rate_scale = 1/length(client_pairs) + rates = [Ref(rate_scale) for _ in client_pairs] + for ((client1, client2), rate) in zip(client_pairs, rates) + @process make_request(sim, net, client1, client2, rate) + end + + # Set up the entanglement trackers at each client + trackers = [EntanglementTracker(sim, net, k) for k in 2:n+1] + for tracker in trackers + @process tracker() + end + + # Set up an entanglement consumer between each unordered pair of clients + client_unordered_pairs = [(k1,k2) for k1 in 2:n+1 for k2 in 2:n+1 if k2>k1] + consumers = [EntanglementConsumer(net, k1, k2) for (k1,k2) in client_unordered_pairs] + for consumer in consumers + @process consumer() + end + + # Finally, set up the switch + switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(0.4, n)) + @process switch_protocol() + + return n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale +end diff --git a/src/ProtocolZoo/switches.jl b/src/ProtocolZoo/switches.jl index 05fb3e8a..f9616586 100644 --- a/src/ProtocolZoo/switches.jl +++ b/src/ProtocolZoo/switches.jl @@ -4,14 +4,15 @@ using QuantumSavory using QuantumSavory.ProtocolZoo using QuantumSavory.ProtocolZoo: EntanglementCounterpart, AbstractProtocol using Graphs: edges, complete_graph, neighbors -using GraphsMatching: maximum_weight_matching +#using GraphsMatching: maximum_weight_matching using Combinatorics: combinations -using JuMP: MOI, optimizer_with_attributes -import Cbc +#using JuMP: MOI, optimizer_with_attributes +#import Cbc using DocStringExtensions: TYPEDEF, TYPEDFIELDS using ConcurrentSim: @process, timeout, Simulation, Process #using ResumableFunctions: @resumable, @yield # TODO serious bug that makes it not work without full `using` using ResumableFunctions +using Random export SimpleSwitchDiscreteProt, SwitchRequest @@ -65,30 +66,31 @@ julia> let ``` """ function promponas_bruteforce_choice(M,N,backlog,eprobs) # TODO mark as public but unexported - best_weight = 0.0 - best_assignment = zeros(Int, M) - graphs = [complete_graph(i) for i in 1:M] # preallocating them to avoid expensive allocations in the inner loop - weights = [zeros(Int, i, i) for i in 1:M] # preallocating them to avoid expensive allocations in the inner loop - found = false - for assigned_nodes in combinations(1:N, M) - current_weight = 0.0 - for entangled_pattern in combinations(assigned_nodes) - p = prod(@view eprobs[entangled_pattern]) - i = length(entangled_pattern) - g = graphs[i] - w = weights[i] - (;weight, mate) = match_entangled_pattern(backlog, entangled_pattern, g, w) - # TODO above, is this a good choice for optimizer - # TODO above, can we preallocate model objects and optimizer objects to avoid allocations in the inner loop - current_weight += weight*p - end - if current_weight > best_weight - best_weight = current_weight - best_assignment .= assigned_nodes - found = true - end - end - return found ? best_assignment : nothing + return randperm(N)[1:M] + # best_weight = 0.0 + # best_assignment = zeros(Int, M) + # graphs = [complete_graph(i) for i in 1:M] # preallocating them to avoid expensive allocations in the inner loop + # weights = [zeros(Int, i, i) for i in 1:M] # preallocating them to avoid expensive allocations in the inner loop + # found = false + # for assigned_nodes in combinations(1:N, M) + # current_weight = 0.0 + # for entangled_pattern in combinations(assigned_nodes) + # p = prod(@view eprobs[entangled_pattern]) + # i = length(entangled_pattern) + # g = graphs[i] + # w = weights[i] + # (;weight, mate) = match_entangled_pattern(backlog, entangled_pattern, g, w) + # # TODO above, is this a good choice for optimizer + # # TODO above, can we preallocate model objects and optimizer objects to avoid allocations in the inner loop + # current_weight += weight*p + # end + # if current_weight > best_weight + # best_weight = current_weight + # best_assignment .= assigned_nodes + # found = true + # end + # end + # return found ? best_assignment : nothing end """ @@ -298,9 +300,11 @@ function _switch_successful_entanglements_best_match(prot, reverseclientindex) end # get the maximum match for the actually connected nodes ne = length(entangled_clients) + if ne < 2 return nothing end entangled_clients_revindex = [reverseclientindex[k] for k in entangled_clients] @debug "Switch $(prot.switchnode) successfully entangled with clients $entangled_clients" # (indexed as $entangled_clients_revindex)" - (;weight, mate) = match_entangled_pattern(prot.backlog, entangled_clients_revindex, complete_graph(ne), zeros(Int, ne, ne)) + # (;weight, mate) = match_entangled_pattern(prot.backlog, entangled_clients_revindex, complete_graph(ne), zeros(Int, ne, ne)) + mate = collect(zip(entangled_clients_revindex[1:2:end], entangled_clients_revindex[2:2:end])) isempty(mate) && return nothing return mate end @@ -310,7 +314,7 @@ Assuming the pairs in `match` are entangled, perform swaps to connect them and decrement the backlog counter. """ function _switch_run_swaps(prot, match) - @debug "Switch $(prot.switchnode) performs swaps for client pairs $([(prot.clientnodes[i], prot.clientnodes[j]) for (i,j) in match])" + #@info "Switch $(prot.switchnode) performs swaps for client pairs $([(prot.clientnodes[i], prot.clientnodes[j]) for (i,j) in match])" for (i,j) in match swapper = SwapperProt( # TODO be more careful about how much simulated time this takes sim=prot.sim, net=prot.net, node=prot.switchnode, From c73a1e63407fca19ed33a4b38ae81b0f1fe3d67f Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Wed, 21 Aug 2024 17:59:08 +0200 Subject: [PATCH 2/6] All client nodes attempt entanglement with the switch node, which hosts at least as many register slots as clients present (n>=m). Then randomly assigned swaps are performed and consumed. Added fusion protocol and function to return fusable qubits to ProtocolZoo.jl as preparation to generate GHZ states among clients. --- .../1_interactive_visualization.jl | 6 +- .../simpleswitch_randomassign/Project.toml | 1 + examples/simpleswitch_randomassign/setup.jl | 8 +- src/CircuitZoo/CircuitZoo.jl | 15 +++- src/ProtocolZoo/ProtocolZoo.jl | 86 ++++++++++++++++++- src/ProtocolZoo/switches.jl | 76 ++++++++++++++-- 6 files changed, 174 insertions(+), 18 deletions(-) diff --git a/examples/simpleswitch_randomassign/1_interactive_visualization.jl b/examples/simpleswitch_randomassign/1_interactive_visualization.jl index f4674334..cee1b57b 100644 --- a/examples/simpleswitch_randomassign/1_interactive_visualization.jl +++ b/examples/simpleswitch_randomassign/1_interactive_visualization.jl @@ -57,10 +57,10 @@ Label(sliderfig_override[1,1], "global rate override:") Label(sliderfig_[1,1], rich("Request Rate Controls:",fontsize=20), tellwidth=false) # Display the figure... -display(fig) +# display(fig) # ... and run the simulation while updating plots as needed -step_ts = range(0, 1000, step=0.1) +step_ts = range(0, 1, step=0.1) for t in step_ts run(sim, t) ax.title = "t=$(t)" @@ -79,4 +79,4 @@ for t in step_ts autolimits!(ax_backlog) autolimits!(ax_consumed) autolimits!(ax_backlog_perpair) -end +end \ No newline at end of file diff --git a/examples/simpleswitch_randomassign/Project.toml b/examples/simpleswitch_randomassign/Project.toml index 586a0b3f..27f2820e 100644 --- a/examples/simpleswitch_randomassign/Project.toml +++ b/examples/simpleswitch_randomassign/Project.toml @@ -2,6 +2,7 @@ Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079" QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl index 0e0c4589..3dfe6abe 100644 --- a/examples/simpleswitch_randomassign/setup.jl +++ b/examples/simpleswitch_randomassign/setup.jl @@ -16,12 +16,12 @@ end function prepare_simulation() n = 10 # number of clients - m = n-2 # memory slots in switch + m = n+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit # The graph of network connectivity. Index 1 corresponds to the switch. graph = star_graph(n+1) - switch_register = Register(m) + switch_register = Register(m) # the first slot is reserved for the pivot qubit used as fusion qubit client_registers = [Register(1) for _ in 1:n] net = RegisterNet(graph, [switch_register, client_registers...]) sim = get_time_tracker(net) @@ -48,8 +48,8 @@ function prepare_simulation() @process consumer() end - # Finally, set up the switch - switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(0.4, n)) + # Finally, set up the switch without assignments + switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(0.4, n), assignment_algorithm=nothing) @process switch_protocol() return n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale diff --git a/src/CircuitZoo/CircuitZoo.jl b/src/CircuitZoo/CircuitZoo.jl index 9966b4bb..7ad0e0d2 100644 --- a/src/CircuitZoo/CircuitZoo.jl +++ b/src/CircuitZoo/CircuitZoo.jl @@ -3,7 +3,7 @@ module CircuitZoo using QuantumSavory using DocStringExtensions -export EntanglementSwap, LocalEntanglementSwap, +export EntanglementFusion, EntanglementSwap, LocalEntanglementSwap, Purify2to1, Purify2to1Node, Purify3to1, Purify3to1Node, PurifyStringent, PurifyStringentNode, PurifyExpedient, PurifyExpedientNode, SDDecode, SDEncode @@ -46,6 +46,19 @@ end inputqubits(::LocalEntanglementSwap) = 2 +struct EntanglementFusion <: AbstractCircuit +end + +function (::EntanglementFusion)(localq, piecemaker) + apply!((piecemaker, localq), CNOT) + xmeas = project_traceout!(localq, σˣ) + if xmeas==2 + apply!(piecemaker, X) + end + xmeas +end + +inputqubits(::EntanglementFusion) = 2 """ $TYPEDEF diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 8f2f37ab..8754aff7 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -227,6 +227,84 @@ end """ $TYPEDEF +A protocol, running at a given node, that finds fusable entangled pairs and performs entanglement fusion. + +$TYPEDFIELDS +""" +@kwdef struct FusionProt <: AbstractProtocol + """time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """a network graph of registers""" + net::RegisterNet + """the vertex of the node where fusion is happening""" + node::Int + """the query for fusable qubits can return many positive candidates; `choose` picks one of them, defaults to a random pick `arr->rand(keys(arr))`""" + choose = random_index + """the vertex of the remote node for the fusion""" + nodeC::Int + """fixed "busy time" duration immediately before starting entanglement generation attempts""" + local_busy_time::Float64 = 0.0 # TODO the gates should have that busy time built in + """how long to wait before retrying to lock qubits if no qubits are available (`nothing` for queuing up and waiting)""" + retry_lock_time::LT = 0.1 + """how many rounds of this protocol to run (`-1` for infinite))""" + rounds::Int = -1 +end + +#TODO "convenience constructor for the missing things and finish this docstring" +function FusionProt(sim::Simulation, net::RegisterNet, node::Int; kwargs...) + return FusionProt(;sim, net, node, kwargs...) +end + +@resumable function (prot::FusionProt)() + rounds = prot.rounds + round = 1 + while rounds != 0 + reg = prot.net[prot.node] + @show prot.node + fusable_qubit, piecemaker_qubit = findfusablequbit(prot.net, prot.node, prot.nodeC, prot.choose) + if isnothing(fusable_qubit) + 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) + continue + end + + (q, id, tag) = fusable_qubit.slot, fusable_qubit.id, fusable_qubit.tag + (q_pm, id_pm, tag_pm) = piecemaker_qubit.slot, piecemaker_qubit.id, piecemaker_qubit.tag + @yield lock(q) & lock(q_pm) # this should not really need a yield thanks to `findswapablequbits`, but it is better to be defensive + @yield timeout(prot.sim, prot.local_busy_time) + + untag!(q, id) + # store a history of whom we were entangled to: remote_node_idx, remote_slot_idx, fus_node_idx, fus_slot_idx, local_fus_idx + tag!(q, EntanglementHistory, tag[2], tag[3], tag_pm[2], tag_pm[3], q.idx) + + uptotime!((fusable_qubit, piecemaker_qubit), now(prot.sim)) + fuscircuit = EntanglementFusion() + xmeas = fuscircuit(fusable_qubit, piecemaker_qubit) # TODO query piecemaker qubit in the |+> state and use it instead of the last qubit + # send from here to client node + # tag with EntanglementUpdateX past_local_node, past_local_slot_idx, past_remote_slot_idx, new_remote_node, new_remote_slot, correction + msg = Tag(EntanglementUpdateX, prot.node, q_pm.idx, tag_pm[3], tag[2], tag[3], xmeas) + put!(channel(prot.net, prot.node=>tag[2]; permit_forward=true), msg) + @debug "FusionProt @$(prot.node)|round $(round): Send message to $(tag[2]) | message=`$msg`" + unlock(fusable_qubit) + unlock(piecemaker_qubit) + rounds==-1 || (rounds -= 1) + round += 1 + end +end + +function findfusablequbit(net, node, pred_random, choose_random) + reg = net[node] + + nodes = queryall(reg, EntanglementCounterpart, pred_random, ❓; locked=false, assigned=true) + + isempty(nodes) && return nothing + i = choose_random((n.tag[2] for n in nodes)) # TODO make [2] into a nice named property + return nodes[2:end][i], nodes[1] +end + +""" +$TYPEDEF + A protocol, running at a given node, that finds swappable entangled pairs and performs the swap. $TYPEDFIELDS @@ -264,6 +342,7 @@ end round = 1 while rounds != 0 reg = prot.net[prot.node] + @show prot.net qubit_pair = findswapablequbits(prot.net, prot.node, prot.nodeL, prot.nodeH, prot.chooseL, prot.chooseH) if isnothing(qubit_pair) isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO @@ -295,6 +374,8 @@ end # send from here to new entanglement counterpart: # tag with EntanglementUpdateZ past_local_node, past_local_slot_idx past_remote_slot_idx new_remote_node, new_remote_slot, correction msg2 = Tag(EntanglementUpdateZ, prot.node, q2.idx, tag2[3], tag1[2], tag1[3], zmeas) + @show tag2 + @show tag1 put!(channel(prot.net, prot.node=>tag2[2]; permit_forward=true), msg2) @debug "SwapperProt @$(prot.node)|round $(round): Send message to $(tag2[2]) | message=`$msg2`" unlock(q1) @@ -306,10 +387,13 @@ end function findswapablequbits(net, node, pred_low, pred_high, choose_low, choose_high) reg = net[node] + @show pred_low + @show pred_high low_nodes = queryall(reg, EntanglementCounterpart, pred_low, ❓; locked=false, assigned=true) high_nodes = queryall(reg, EntanglementCounterpart, pred_high, ❓; locked=false, assigned=true) - + @show low_nodes + @show high_nodes (isempty(low_nodes) || isempty(high_nodes)) && return nothing il = choose_low((n.tag[2] for n in low_nodes)) # TODO make [2] into a nice named property ih = choose_high((n.tag[2] for n in high_nodes)) diff --git a/src/ProtocolZoo/switches.jl b/src/ProtocolZoo/switches.jl index f9616586..3aeb8eb6 100644 --- a/src/ProtocolZoo/switches.jl +++ b/src/ProtocolZoo/switches.jl @@ -198,21 +198,29 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) _switch_read_backlog(prot, reverseclientindex) # pick a set of client nodes to which to assign local memory slots - assignment = prot.assignment_algorithm(m,n,backlog,prot.success_probs) - if isnothing(assignment) - @debug "Switch $switchnode found no useful memory slot assignments" - @yield timeout(prot.sim, prot.ticktock) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net - continue + if prot.assignment_algorithm == promponas_bruteforce_choice + assignment = prot.assignment_algorithm(m,n,backlog,prot.success_probs) + if isnothing(assignment) + @debug "Switch $switchnode found no useful memory slot assignments" + @yield timeout(prot.sim, prot.ticktock) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net + continue + end + @debug "Switch $switchnode assigns memory slots to clients $([prot.clientnodes[a] for a in assignment])" + + # run entangler + _switch_entangler(prot, assignment) + else + # run entangler without requests (=no assignment) + println("RUN WITH NO ASSIGNMENTS") + _switch_entangler_all(prot) end - @debug "Switch $switchnode assigns memory slots to clients $([prot.clientnodes[a] for a in assignment])" - - # run entangler - _switch_entangler(prot, assignment) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net # read which entanglements were successful # and pick an optimal matching given the backlog of requests + match = _switch_successful_entanglements_best_match(prot, reverseclientindex) + #match = _switch_successful_entanglements(prot, reverseclientindex) if isnothing(match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net continue @@ -220,6 +228,7 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) # perform swaps _switch_run_swaps(prot, match) + #_switch_run_fusions(prot, match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net end end @@ -267,6 +276,22 @@ function _switch_read_backlog(prot, reverseclientindex) end end +""" +Run the entangler protocol between the switch and each client (no assignment). +""" +function _switch_entangler_all(prot) + @assert length(prot.clientnodes) == nsubsystems(prot.net[prot.switchnode])-1 "Number of clientnodes needs to equal the number of switch registers." + for (id, client) in enumerate(prot.clientnodes) + entangler = EntanglerProt( + sim=prot.sim, net=prot.net, + nodeA=prot.switchnode, nodeB=client, + rounds=1, attempts=1, success_prob=prot.success_probs[id], + attempt_time=prot.ticktock/10 # TODO this is a pretty arbitrary value + ) + @process entangler() + end +end + """ Run the entangler protocol between the switch and each client in the assignment. """ @@ -306,9 +331,26 @@ function _switch_successful_entanglements_best_match(prot, reverseclientindex) # (;weight, mate) = match_entangled_pattern(prot.backlog, entangled_clients_revindex, complete_graph(ne), zeros(Int, ne, ne)) mate = collect(zip(entangled_clients_revindex[1:2:end], entangled_clients_revindex[2:2:end])) isempty(mate) && return nothing + # @show mate return mate end +function _switch_successful_entanglements(prot, reverseclientindex) + switch = prot.net[prot.switchnode] + successes = queryall(switch, EntanglementCounterpart, in(prot.clientnodes), ❓) + entangled_clients = [r.tag[2] for r in successes] + if isempty(entangled_clients) + @debug "Switch $(prot.switchnode) failed to entangle with any clients" + return nothing + end + # get the maximum match for the actually connected nodes + ne = length(entangled_clients) + if ne < 1 return nothing end + entangled_clients_revindex = [reverseclientindex[k] for k in entangled_clients] + @debug "Switch $(prot.switchnode) successfully entangled with clients $entangled_clients" + return entangled_clients_revindex +end + """ Assuming the pairs in `match` are entangled, perform swaps to connect them and decrement the backlog counter. @@ -326,6 +368,22 @@ function _switch_run_swaps(prot, match) end end +""" +Assuming the clientnodes are entangled, +perform fusion to connect them with piecemaker qubit (no backlog discounter yet!). +""" +function _switch_run_fusions(prot, match) + @info "Switch $(prot.switchnode) performs fusions for client $([i in match])" + for i in match + fusion = FusionProt( # TODO be more careful about how much simulated time this takes + sim=prot.sim, net=prot.net, node=prot.switchnode, + nodeC=prot.clientnodes[i], + rounds=1 + ) + @process fusion() + end +end + """ $TYPEDEF From d75ac18903874d4fcabe945bf19e13fab059f52b Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Sat, 24 Aug 2024 17:24:46 +0200 Subject: [PATCH 3/6] Added simple run file to run sim without visualization (avoid dependence on GLMakie), added objects FusionProt and FusionSwitchDiscreteProt in preparation for fusion simulation --- .../1_interactive_visualization.jl | 3 +- .../simpleswitch_randomassign/3_simple_run.jl | 17 ++++ examples/simpleswitch_randomassign/setup.jl | 31 +++++++ src/ProtocolZoo/ProtocolZoo.jl | 66 +++++++++++++- src/ProtocolZoo/switches.jl | 86 +++++++++++++++++-- 5 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 examples/simpleswitch_randomassign/3_simple_run.jl diff --git a/examples/simpleswitch_randomassign/1_interactive_visualization.jl b/examples/simpleswitch_randomassign/1_interactive_visualization.jl index cee1b57b..6de980e6 100644 --- a/examples/simpleswitch_randomassign/1_interactive_visualization.jl +++ b/examples/simpleswitch_randomassign/1_interactive_visualization.jl @@ -5,6 +5,7 @@ include("setup.jl") # Prepare all of the simulation components (while all visualization components are prepared in the rest of this file) n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale = prepare_simulation() + # Prepare the main figure fig = Figure(size=(1600,800)) fig_plots = fig[1,1] @@ -60,7 +61,7 @@ Label(sliderfig_[1,1], rich("Request Rate Controls:",fontsize=20), tellwidth=fal # display(fig) # ... and run the simulation while updating plots as needed -step_ts = range(0, 1, step=0.1) +step_ts = range(0, 2, step=0.1) for t in step_ts run(sim, t) ax.title = "t=$(t)" diff --git a/examples/simpleswitch_randomassign/3_simple_run.jl b/examples/simpleswitch_randomassign/3_simple_run.jl new file mode 100644 index 00000000..8457afec --- /dev/null +++ b/examples/simpleswitch_randomassign/3_simple_run.jl @@ -0,0 +1,17 @@ +include("setup.jl") + +# Prepare all of the simulation components +n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale = prepare_simulation() + +step_ts = range(0, 2, step=0.1) +for t in step_ts + run(sim, t) +end + +# Prepare all of the simulation components +# sim = prepare_simulation_fusion() + +# step_ts = range(0, 2, step=0.1) +# for t in step_ts +# run(sim, t) +# end diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl index 3dfe6abe..11ab0b69 100644 --- a/examples/simpleswitch_randomassign/setup.jl +++ b/examples/simpleswitch_randomassign/setup.jl @@ -54,3 +54,34 @@ function prepare_simulation() return n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale end + +function prepare_simulation_fusion() + n = 10 # number of clients + m = n+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit + + # The graph of network connectivity. Index 1 corresponds to the switch. + graph = star_graph(n+1) + + switch_register = Register(m) # the first slot is reserved for the pivot qubit used as fusion qubit = 'piecemaker + client_registers = [Register(1) for _ in 1:n] + net = RegisterNet(graph, [switch_register, client_registers...]) + sim = get_time_tracker(net) + + + # Set up the entanglement trackers at each client + trackers = [EntanglementTracker(sim, net, k) for k in 2:n+1] + for tracker in trackers + @process tracker() + end + + # Set up an GHZ state consumer for fused clients + consumer = GHZConsumer(net, switch_register[1]) + @process consumer() + + + # Finally, set up the switch without assignments + #switch_protocol = FusionSwitchDiscreteProt(get_time_tracker(net))#, net, 1, collect(2:n+1), fill(1., n)) + #@process switch_protocol() + + return sim +end diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 8754aff7..fac4376f 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -16,11 +16,11 @@ import SumTypes export # protocols - EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, + EntanglerProt, SwapperProt, FusionProt, EntanglementTracker, EntanglementConsumer, GHZConsumer, # tags EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, # from Switches - SimpleSwitchDiscreteProt, SwitchRequest + SimpleSwitchDiscreteProt, FusionSwitchDiscreteProt, SwitchRequest abstract type AbstractProtocol end @@ -282,7 +282,7 @@ end xmeas = fuscircuit(fusable_qubit, piecemaker_qubit) # TODO query piecemaker qubit in the |+> state and use it instead of the last qubit # send from here to client node # tag with EntanglementUpdateX past_local_node, past_local_slot_idx, past_remote_slot_idx, new_remote_node, new_remote_slot, correction - msg = Tag(EntanglementUpdateX, prot.node, q_pm.idx, tag_pm[3], tag[2], tag[3], xmeas) + msg = Tag(EntanglementUpdateX, prot.node, q.idx, tag[3], tag_pm[2], tag_pm[3], xmeas) put!(channel(prot.net, prot.node=>tag[2]; permit_forward=true), msg) @debug "FusionProt @$(prot.node)|round $(round): Send message to $(tag[2]) | message=`$msg`" unlock(fusable_qubit) @@ -424,7 +424,7 @@ end workwasdone = true # waiting is not enough because we might have multiple rounds of work to do while workwasdone workwasdone = false - for (updatetagsymbol, updategate) in ((EntanglementUpdateX, Z), (EntanglementUpdateZ, X)) + for (updatetagsymbol, updategate) in ((EntanglementUpdateX, X), (EntanglementUpdateZ, Z)) # look for EntanglementUpdate? past_remote_slot_idx local_slot_idx, new_remote_node, new_remote_slot_idx correction msg = querydelete!(mb, updatetagsymbol, ❓, ❓, ❓, ❓, ❓, ❓) isnothing(msg) && continue @@ -480,6 +480,64 @@ end end end +# """ +# $TYPEDEF + +# A protocol running between two nodes, checking periodically for any entangled states (GHZ states) between all nodes and consuming/emptying the qubit slots. + +# $FIELDS +# """ +# @kwdef struct GHZConsumer{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} +# """time-and-schedule-tracking instance from `ConcurrentSim`""" +# sim::Simulation +# """a network graph of registers""" +# net::RegisterNet +# """the piecemaker qubit slot (RegRef)""" +# piecemaker::RegRef +# """time period between successive queries on the nodes (`nothing` for queuing up and waiting for available pairs)""" +# period::LT = 0.1 +# """stores the time and resulting observable from querying the piecemaker qubit for `EntanglementCounterpart`""" +# log::Vector{Tuple{Float64, Float64, Float64}} = Tuple{Float64, Float64, Float64}[] +# end + +# function GHZConsumer(sim::Simulation, net::RegisterNet, piecemaker::RegRef; kwargs...) +# return GHZConsumer(;sim, net, piecemaker, kwargs...) +# end +# function GHZConsumer(net::RegisterNet, piecemaker::RegRef; kwargs...) +# return GHZConsumer(get_time_tracker(net), net, piecemaker; kwargs...) +# end + +# @resumable function (prot::GHZConsumer)() +# if isnothing(prot.period) +# error("In `GHZConsumer` we do not yet support waiting on register to make qubits available") # TODO +# end +# while true +# qparticipating = queryall(prot.piecemaker, EntanglementCounterpart, ❓, ❓; locked=false, assigned=true) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below +# println(qparticipating) +# if isnothing(qparticipating) +# @debug "GHZConsumer between $(prot.piecemaker): query on piecemaker slot found no entanglement" +# @yield timeout(prot.sim, prot.period) +# return +# end +# break +# # q = query.slot +# # @yield lock(q) + +# # @debug "GHZConsumer of $(prot.node): queries successful, consuming entanglement" +# # untag!(q, query.id) +# # # TODO do we need to add EntanglementHistory and should that be a different EntanglementHistory since the current one is specifically for SwapperProt +# # # TODO currently when calculating the observable we assume that EntanglerProt.pairstate is always (|00⟩ + |11⟩)/√2, make it more general for other states +# # ob1 = real(observable((q1, q2), Z⊗Z)) +# # ob2 = real(observable((q1, q2), X⊗X)) + +# # traceout!(prot.net[prot.nodeA][q1.idx], prot.net[prot.nodeB][q2.idx]) +# # push!(prot.log, (now(prot.sim), ob1, ob2)) +# # unlock(q1) +# # unlock(q2) +# # @yield timeout(prot.sim, prot.period) +# end +# end + """ $TYPEDEF diff --git a/src/ProtocolZoo/switches.jl b/src/ProtocolZoo/switches.jl index 3aeb8eb6..d746c0cd 100644 --- a/src/ProtocolZoo/switches.jl +++ b/src/ProtocolZoo/switches.jl @@ -14,7 +14,7 @@ using ConcurrentSim: @process, timeout, Simulation, Process using ResumableFunctions using Random -export SimpleSwitchDiscreteProt, SwitchRequest +export SimpleSwitchDiscreteProt, FusionSwitchDiscreteProt, SwitchRequest """ A wrapper around a matrix, ensuring that it is symmetric. @@ -124,6 +124,83 @@ function capture_stdout(f) return r end +# """ +# $TYPEDEF + +# A switch "controller", running on a given node, serving neighboring clients by attempting direct raw entanglement +# with the clients and then mediating fusion to connect two clients together. + +# Works on discrete time intervals and does not destroy unused raw entanglement by the end of a ticktock cycle. + +# $TYPEDFIELDS +# """ +# @kwdef struct FusionSwitchDiscreteProt #<: AbstractProtocol +# # """time-and-schedule-tracking instance from `ConcurrentSim`""" +# # sim::Simulation # TODO check that +# """a network graph of registers""" +# net::RegisterNet +# end +# # """the vertex index of the switch""" +# # switchnode::Int64 +# # """the vertex indices of the clients""" +# # clientnodes::Vector{Int64} +# # """best-guess about success of establishing raw entanglement between client and switch""" +# # success_probs::Vector{Float64} +# # """duration of a single full cycle of the switching decision algorithm""" +# # ticktock::Float64 = 1 +# # """how many rounds of this protocol to run (`-1` for infinite)""" +# # rounds::Int = -1 +# # function FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs, ticktock, rounds) +# # length(unique(clientnodes)) == length(clientnodes) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be unique!")) +# # all(in(neighbors(net, switchnode)), clientnodes) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be directly connected to the `switchnode`!")) +# # 0 < ticktock || throw(ArgumentError("In the preparation of `FusioneSwitchDiscreteProt` switch protocol, the requested protocol period `ticktock` must be positive!")) +# # 0 < rounds || rounds == -1 || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested number of rounds `rounds` must be positive or `-1` for infinite!")) +# # length(clientnodes) == length(success_probs) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `success_probs` must have the same length as `clientnodes`!")) +# # all(0 .<= success_probs .<= 1) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `success_probs` must be in the range [0,1]!")) +# # end +# # end +# # FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs; kwrags...) = FusionSwitchDiscreteProt(;sim, net, switchnode, clientnodes=collect(clientnodes), success_probs=collect(success_probs), kwargs...) +# # FusionSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwargs...) = FusionSwitchDiscreteProt(get_time_tracker(net), net, switchnode, clientnodes, success_probs; kwargs...) + +# @resumable function (prot::FusionSwitchDiscreteProt)() +# # rounds = prot.rounds +# # round = 1 +# net = prot.net +# println(net) +# # clientnodes = prot.clientnodes +# # switchnode = prot.switchnode +# # n = length(clientnodes) +# # m = nsubsystems(net[switchnode]) +# # reverseclientindex = Dict{Int,Int}(c=>i for (i,c) in enumerate(clientnodes)) + +# # start a process to delete unused switch-to-node entanglement at the end of each round +# # deleter = _SwitchSynchronizedDelete(prot) # TODO: for fusion, this should not happen! +# # @process deleter() + +# # while rounds != 0 +# # rounds==-1 || (rounds -= 1) + +# # _switch_entangler_all(prot) +# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net + +# # # read which entanglements were successful and pick all possible +# # match = _switch_successful_entanglements(prot, reverseclientindex) +# # if isnothing(match) +# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net +# # continue +# # end + +# # # perform swaps +# # try +# # _switch_run_fusions(prot, match) +# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net +# # catch +# # println("Error occurred. Terminate.") +# # return +# # end +# # end +# end + """ $TYPEDEF @@ -188,7 +265,7 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) reverseclientindex = Dict{Int,Int}(c=>i for (i,c) in enumerate(clientnodes)) # start a process to delete unused switch-to-node entanglement at the end of each round - deleter = _SwitchSynchronizedDelete(prot) + deleter = _SwitchSynchronizedDelete(prot) # TODO: for fusion, this should not happen! @process deleter() while rounds != 0 @@ -228,7 +305,6 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) # perform swaps _switch_run_swaps(prot, match) - #_switch_run_fusions(prot, match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net end end @@ -338,7 +414,7 @@ end function _switch_successful_entanglements(prot, reverseclientindex) switch = prot.net[prot.switchnode] successes = queryall(switch, EntanglementCounterpart, in(prot.clientnodes), ❓) - entangled_clients = [r.tag[2] for r in successes] + entangled_clients = [r.tag[2] for r in successes] # RegRef (qubit slot) if isempty(entangled_clients) @debug "Switch $(prot.switchnode) failed to entangle with any clients" return nothing @@ -373,7 +449,7 @@ Assuming the clientnodes are entangled, perform fusion to connect them with piecemaker qubit (no backlog discounter yet!). """ function _switch_run_fusions(prot, match) - @info "Switch $(prot.switchnode) performs fusions for client $([i in match])" + @info "Switch $(prot.switchnode) performs fusions for clients in $(match)" for i in match fusion = FusionProt( # TODO be more careful about how much simulated time this takes sim=prot.sim, net=prot.net, node=prot.switchnode, From 422984389f40676069f2cac924e8f7c8c8495712 Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Tue, 27 Aug 2024 19:49:04 +0200 Subject: [PATCH 4/6] Added reinit function for piecemaker qubit, updated fusion function, added Tags to signify fusion counterpart and piecemaker, updated fusion protocol, updated GHZConsumer protocol --- Project.toml | 2 +- dev/QuantumSavory | 1 + .../1_interactive_visualization.jl | 2 +- .../simpleswitch_randomassign/3_simple_run.jl | 14 +- examples/simpleswitch_randomassign/setup.jl | 78 +++--- src/CircuitZoo/CircuitZoo.jl | 7 +- src/ProtocolZoo/ProtocolZoo.jl | 251 ++++++++++++------ src/ProtocolZoo/switches.jl | 128 +++++---- test/test_circuitzoo_ent_swap.jl | 2 +- test/test_circuitzoo_purification.jl | 2 +- 10 files changed, 275 insertions(+), 212 deletions(-) create mode 160000 dev/QuantumSavory diff --git a/Project.toml b/Project.toml index e5148356..b602bc2b 100644 --- a/Project.toml +++ b/Project.toml @@ -9,7 +9,6 @@ Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -24,6 +23,7 @@ QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" SumTypes = "8e1ec7a9-0e02-4297-b0fe-6433085c89f2" diff --git a/dev/QuantumSavory b/dev/QuantumSavory new file mode 160000 index 00000000..6e99352a --- /dev/null +++ b/dev/QuantumSavory @@ -0,0 +1 @@ +Subproject commit 6e99352aed1da5ee7067e07e287415f182514e3f diff --git a/examples/simpleswitch_randomassign/1_interactive_visualization.jl b/examples/simpleswitch_randomassign/1_interactive_visualization.jl index 6de980e6..06a0e5dc 100644 --- a/examples/simpleswitch_randomassign/1_interactive_visualization.jl +++ b/examples/simpleswitch_randomassign/1_interactive_visualization.jl @@ -58,7 +58,7 @@ Label(sliderfig_override[1,1], "global rate override:") Label(sliderfig_[1,1], rich("Request Rate Controls:",fontsize=20), tellwidth=false) # Display the figure... -# display(fig) +display(fig) # ... and run the simulation while updating plots as needed step_ts = range(0, 2, step=0.1) diff --git a/examples/simpleswitch_randomassign/3_simple_run.jl b/examples/simpleswitch_randomassign/3_simple_run.jl index 8457afec..75a30046 100644 --- a/examples/simpleswitch_randomassign/3_simple_run.jl +++ b/examples/simpleswitch_randomassign/3_simple_run.jl @@ -1,17 +1,9 @@ include("setup.jl") # Prepare all of the simulation components -n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale = prepare_simulation() +n, sim = prepare_simulation() -step_ts = range(0, 2, step=0.1) +step_ts = range(0, 20, step=0.1) for t in step_ts run(sim, t) -end - -# Prepare all of the simulation components -# sim = prepare_simulation_fusion() - -# step_ts = range(0, 2, step=0.1) -# for t in step_ts -# run(sim, t) -# end +end \ No newline at end of file diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl index 11ab0b69..2e5bb972 100644 --- a/examples/simpleswitch_randomassign/setup.jl +++ b/examples/simpleswitch_randomassign/setup.jl @@ -14,6 +14,25 @@ using Distributions end end +@resumable function init_piecemaker(sim, net, m) + while true + # init piecemaker slot in |+> + @yield lock(net[1][m]) + if !isassigned(net[1][m]) + @info "Init piecemaker!" + initialize!(net[1][m], X1) + tag!(net[1][m], Piecemaker, 1, m) + unlock(net[1][m]) + @yield timeout(sim, 0.1) + else + unlock(net[1][m]) + @yield timeout(sim, 0.1) + end + @yield timeout(sim, 0.1) + end +end + + function prepare_simulation() n = 10 # number of clients m = n+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit @@ -21,19 +40,22 @@ function prepare_simulation() # The graph of network connectivity. Index 1 corresponds to the switch. graph = star_graph(n+1) - switch_register = Register(m) # the first slot is reserved for the pivot qubit used as fusion qubit + switch_register = Register(m) # the first slot is reserved for the 'piecemaker' qubit used as fusion qubit client_registers = [Register(1) for _ in 1:n] net = RegisterNet(graph, [switch_register, client_registers...]) sim = get_time_tracker(net) + + @process init_piecemaker(sim, net, m) + # Set up the request-making processes # between each ordered pair of clients - client_pairs = [(k1,k2) for k1 in 2:n+1 for k2 in 2:n+1 if k2!=k1] - rate_scale = 1/length(client_pairs) - rates = [Ref(rate_scale) for _ in client_pairs] - for ((client1, client2), rate) in zip(client_pairs, rates) - @process make_request(sim, net, client1, client2, rate) - end + # client_pairs = [(k1,k2) for k1 in 2:n+1 for k2 in 2:n+1 if k2!=k1] + # rate_scale = 1/length(client_pairs) + # rates = [Ref(rate_scale) for _ in client_pairs] + # for ((client1, client2), rate) in zip(client_pairs, rates) + # @process make_request(sim, net, client1, client2, rate) + # end # Set up the entanglement trackers at each client trackers = [EntanglementTracker(sim, net, k) for k in 2:n+1] @@ -42,46 +64,12 @@ function prepare_simulation() end # Set up an entanglement consumer between each unordered pair of clients - client_unordered_pairs = [(k1,k2) for k1 in 2:n+1 for k2 in 2:n+1 if k2>k1] - consumers = [EntanglementConsumer(net, k1, k2) for (k1,k2) in client_unordered_pairs] - for consumer in consumers - @process consumer() - end + consumer = GHZConsumer(net, net[1][m]) + @process consumer() # Finally, set up the switch without assignments - switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(0.4, n), assignment_algorithm=nothing) + switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(1, n), assignment_algorithm=nothing) @process switch_protocol() - return n, sim, net, switch_protocol, client_pairs, client_unordered_pairs, consumers, rates, rate_scale -end - -function prepare_simulation_fusion() - n = 10 # number of clients - m = n+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit - - # The graph of network connectivity. Index 1 corresponds to the switch. - graph = star_graph(n+1) - - switch_register = Register(m) # the first slot is reserved for the pivot qubit used as fusion qubit = 'piecemaker - client_registers = [Register(1) for _ in 1:n] - net = RegisterNet(graph, [switch_register, client_registers...]) - sim = get_time_tracker(net) - - - # Set up the entanglement trackers at each client - trackers = [EntanglementTracker(sim, net, k) for k in 2:n+1] - for tracker in trackers - @process tracker() - end - - # Set up an GHZ state consumer for fused clients - consumer = GHZConsumer(net, switch_register[1]) - @process consumer() - - - # Finally, set up the switch without assignments - #switch_protocol = FusionSwitchDiscreteProt(get_time_tracker(net))#, net, 1, collect(2:n+1), fill(1., n)) - #@process switch_protocol() - - return sim + return n, sim end diff --git a/src/CircuitZoo/CircuitZoo.jl b/src/CircuitZoo/CircuitZoo.jl index 7ad0e0d2..71eabea9 100644 --- a/src/CircuitZoo/CircuitZoo.jl +++ b/src/CircuitZoo/CircuitZoo.jl @@ -51,11 +51,8 @@ end function (::EntanglementFusion)(localq, piecemaker) apply!((piecemaker, localq), CNOT) - xmeas = project_traceout!(localq, σˣ) - if xmeas==2 - apply!(piecemaker, X) - end - xmeas + zmeas = project_traceout!(localq, σᶻ) + zmeas end inputqubits(::EntanglementFusion) = 2 diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index fac4376f..8c153c1d 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -3,7 +3,9 @@ module ProtocolZoo using QuantumSavory import QuantumSavory: get_time_tracker, Tag using QuantumSavory: Wildcard -using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap +using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap, EntanglementFusion +# using QuantumClifford +# using QuantumOptics using DocStringExtensions @@ -18,7 +20,7 @@ export # protocols EntanglerProt, SwapperProt, FusionProt, EntanglementTracker, EntanglementConsumer, GHZConsumer, # tags - EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, + EntanglementCounterpart, FusionCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, Piecemaker, # from Switches SimpleSwitchDiscreteProt, FusionSwitchDiscreteProt, SwitchRequest @@ -45,6 +47,23 @@ end Base.show(io::IO, tag::EntanglementCounterpart) = print(io, "Entangled to $(tag.remote_node).$(tag.remote_slot)") Tag(tag::EntanglementCounterpart) = Tag(EntanglementCounterpart, tag.remote_node, tag.remote_slot) +""" +$TYPEDEF + +Indicates the current entanglement status with a remote node's slot. Added when a new qubit is fused into the GHZ state through [`FusionProt`](@ref). + +$TYPEDFIELDS +""" +@kwdef struct FusionCounterpart + "the id of the remote node to which we are entangled" + remote_node::Int + "the slot in the remote node containing the qubit we are entangled to" + remote_slot::Int +end +Base.show(io::IO, tag::FusionCounterpart) = print(io, "GHZ state shared with $(tag.remote_node).$(tag.remote_slot)") +Tag(tag::FusionCounterpart) = Tag(FusionCounterpart, tag.remote_node, tag.remote_slot) + + """ $TYPEDEF @@ -124,6 +143,22 @@ Tag(tag::EntanglementUpdateZ) = Tag(EntanglementUpdateZ, tag.past_local_node, ta """ $TYPEDEF +Indicates the piecemaker responsible for fusions of a remote node's slot. + +$TYPEDFIELDS +""" +@kwdef struct Piecemaker + "the id of the switch node" + node::Int + "the slot in the switch node containing piecemaker qubit" + slot::Int +end +Base.show(io::IO, tag::Piecemaker) = print(io, "Piecemaker slot at $(tag.node).$(tag.slot)") +Tag(tag::Piecemaker) = Tag(Piecemaker, tag.node, tag.slot) + +""" +$TYPEDEF + A protocol that generates entanglement between two nodes. Whenever a pair of empty slots is available, the protocol locks them and starts probabilistic attempts to establish entanglement. @@ -180,8 +215,8 @@ end while rounds != 0 isentangled = !isnothing(query(prot.net[prot.nodeA], EntanglementCounterpart, prot.nodeB, ❓; assigned=true)) margin = isentangled ? prot.margin : prot.hardmargin - a = findfreeslot(prot.net[prot.nodeA]; randomize=prot.randomize, margin=margin) - b = findfreeslot(prot.net[prot.nodeB]; randomize=prot.randomize, margin=margin) + a = prot.net[prot.nodeA][prot.nodeB-1] #findfreeslot(prot.net[prot.nodeA]; randomize=prot.randomize, margin=margin) # + b = prot.net[prot.nodeB][1]#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 @@ -191,7 +226,9 @@ end end @yield lock(a) & lock(b) # this yield is expected to return immediately - + if isassigned(b) + traceout!(b) # TODO: why? + end @yield timeout(prot.sim, prot.local_busy_time_pre) attempts = if isone(prot.success_prob) 1 @@ -231,7 +268,7 @@ A protocol, running at a given node, that finds fusable entangled pairs and perf $TYPEDFIELDS """ -@kwdef struct FusionProt <: AbstractProtocol +@kwdef struct FusionProt{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} """time-and-schedule-tracking instance from `ConcurrentSim`""" sim::Simulation """a network graph of registers""" @@ -259,9 +296,7 @@ end rounds = prot.rounds round = 1 while rounds != 0 - reg = prot.net[prot.node] - @show prot.node - fusable_qubit, piecemaker_qubit = findfusablequbit(prot.net, prot.node, prot.nodeC, prot.choose) + fusable_qubit, piecemaker = findfusablequbit(prot.net, prot.node, prot.nodeC, prot.choose) if isnothing(fusable_qubit) 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) @@ -269,37 +304,37 @@ end end (q, id, tag) = fusable_qubit.slot, fusable_qubit.id, fusable_qubit.tag - (q_pm, id_pm, tag_pm) = piecemaker_qubit.slot, piecemaker_qubit.id, piecemaker_qubit.tag + (q_pm, id_pm, tag_pm) = piecemaker.slot, piecemaker.id, piecemaker.tag @yield lock(q) & lock(q_pm) # this should not really need a yield thanks to `findswapablequbits`, but it is better to be defensive @yield timeout(prot.sim, prot.local_busy_time) untag!(q, id) - # store a history of whom we were entangled to: remote_node_idx, remote_slot_idx, fus_node_idx, fus_slot_idx, local_fus_idx - tag!(q, EntanglementHistory, tag[2], tag[3], tag_pm[2], tag_pm[3], q.idx) + # store a history of whom we were entangled to for both client slot and piecemaker + tag!(q, EntanglementHistory, tag[2], tag[3], prot.node, q_pm.idx, q.idx) + tag!(q_pm, FusionCounterpart, tag[2], tag[3]) - uptotime!((fusable_qubit, piecemaker_qubit), now(prot.sim)) + uptotime!((q, q_pm), now(prot.sim)) fuscircuit = EntanglementFusion() - xmeas = fuscircuit(fusable_qubit, piecemaker_qubit) # TODO query piecemaker qubit in the |+> state and use it instead of the last qubit + zmeas = fuscircuit(q, q_pm) # send from here to client node # tag with EntanglementUpdateX past_local_node, past_local_slot_idx, past_remote_slot_idx, new_remote_node, new_remote_slot, correction - msg = Tag(EntanglementUpdateX, prot.node, q.idx, tag[3], tag_pm[2], tag_pm[3], xmeas) + msg = Tag(EntanglementUpdateZ, prot.node, q.idx, tag[3], prot.node, q_pm.idx, zmeas) put!(channel(prot.net, prot.node=>tag[2]; permit_forward=true), msg) @debug "FusionProt @$(prot.node)|round $(round): Send message to $(tag[2]) | message=`$msg`" - unlock(fusable_qubit) - unlock(piecemaker_qubit) + unlock(q) + unlock(q_pm) rounds==-1 || (rounds -= 1) round += 1 end end -function findfusablequbit(net, node, pred_random, choose_random) +function findfusablequbit(net, node, pred_client, choose_random) reg = net[node] - - nodes = queryall(reg, EntanglementCounterpart, pred_random, ❓; locked=false, assigned=true) - + nodes = queryall(reg, EntanglementCounterpart, pred_client, ❓; locked=false) + piecemaker = query(reg, Piecemaker, ❓, ❓) isempty(nodes) && return nothing - i = choose_random((n.tag[2] for n in nodes)) # TODO make [2] into a nice named property - return nodes[2:end][i], nodes[1] + @assert length(nodes) == 1 "Client seems to be entangled multiple times" + return nodes[1], piecemaker end """ @@ -424,11 +459,11 @@ end workwasdone = true # waiting is not enough because we might have multiple rounds of work to do while workwasdone workwasdone = false - for (updatetagsymbol, updategate) in ((EntanglementUpdateX, X), (EntanglementUpdateZ, Z)) + for (updatetagsymbol, updategate) in ((EntanglementUpdateX, Z), (EntanglementUpdateZ, X)) # look for EntanglementUpdate? past_remote_slot_idx local_slot_idx, new_remote_node, new_remote_slot_idx correction msg = querydelete!(mb, updatetagsymbol, ❓, ❓, ❓, ❓, ❓, ❓) isnothing(msg) && continue - @debug "EntanglementTracker @$(prot.node): Received from $(msg.src).$(msg.tag[3]) | message=`$(msg.tag)`" + @debug "EntanglementTracker @$(prot.node): Received from $(msg.src).$(msg.tag[3]) applying $(updategate) from symbol $(updatetagsymbol) | message=`$(msg.tag)`" workwasdone = true (src, (_, pastremotenode, pastremoteslotid, localslotid, newremotenode, newremoteslotid, correction)) = msg localslot = nodereg[localslotid] @@ -480,63 +515,115 @@ end end end -# """ -# $TYPEDEF - -# A protocol running between two nodes, checking periodically for any entangled states (GHZ states) between all nodes and consuming/emptying the qubit slots. - -# $FIELDS -# """ -# @kwdef struct GHZConsumer{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} -# """time-and-schedule-tracking instance from `ConcurrentSim`""" -# sim::Simulation -# """a network graph of registers""" -# net::RegisterNet -# """the piecemaker qubit slot (RegRef)""" -# piecemaker::RegRef -# """time period between successive queries on the nodes (`nothing` for queuing up and waiting for available pairs)""" -# period::LT = 0.1 -# """stores the time and resulting observable from querying the piecemaker qubit for `EntanglementCounterpart`""" -# log::Vector{Tuple{Float64, Float64, Float64}} = Tuple{Float64, Float64, Float64}[] -# end - -# function GHZConsumer(sim::Simulation, net::RegisterNet, piecemaker::RegRef; kwargs...) -# return GHZConsumer(;sim, net, piecemaker, kwargs...) -# end -# function GHZConsumer(net::RegisterNet, piecemaker::RegRef; kwargs...) -# return GHZConsumer(get_time_tracker(net), net, piecemaker; kwargs...) -# end - -# @resumable function (prot::GHZConsumer)() -# if isnothing(prot.period) -# error("In `GHZConsumer` we do not yet support waiting on register to make qubits available") # TODO -# end -# while true -# qparticipating = queryall(prot.piecemaker, EntanglementCounterpart, ❓, ❓; locked=false, assigned=true) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below -# println(qparticipating) -# if isnothing(qparticipating) -# @debug "GHZConsumer between $(prot.piecemaker): query on piecemaker slot found no entanglement" -# @yield timeout(prot.sim, prot.period) -# return -# end -# break -# # q = query.slot -# # @yield lock(q) - -# # @debug "GHZConsumer of $(prot.node): queries successful, consuming entanglement" -# # untag!(q, query.id) -# # # TODO do we need to add EntanglementHistory and should that be a different EntanglementHistory since the current one is specifically for SwapperProt -# # # TODO currently when calculating the observable we assume that EntanglerProt.pairstate is always (|00⟩ + |11⟩)/√2, make it more general for other states -# # ob1 = real(observable((q1, q2), Z⊗Z)) -# # ob2 = real(observable((q1, q2), X⊗X)) - -# # traceout!(prot.net[prot.nodeA][q1.idx], prot.net[prot.nodeB][q2.idx]) -# # push!(prot.log, (now(prot.sim), ob1, ob2)) -# # unlock(q1) -# # unlock(q2) -# # @yield timeout(prot.sim, prot.period) -# end -# end +""" +$TYPEDEF + +A protocol running between two nodes, checking periodically for any entangled states (GHZ states) between all nodes and consuming/emptying the qubit slots. + +$FIELDS +""" +@kwdef struct GHZConsumer{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} + """time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """a network graph of registers""" + net::RegisterNet + """the piecemaker qubit slot (RegRef)""" + piecemaker::RegRef + """time period between successive queries on the nodes (`nothing` for queuing up and waiting for available pairs)""" + period::LT = 0.1 + """stores the time and resulting observable from querying the piecemaker qubit for `EntanglementCounterpart`""" + log::Vector{Tuple{Float64, Float64, Float64}} = Tuple{Float64, Float64, Float64}[] +end + +function GHZConsumer(sim::Simulation, net::RegisterNet, piecemaker::RegRef; kwargs...) + return GHZConsumer(;sim, net, piecemaker, kwargs...) +end +function GHZConsumer(net::RegisterNet, piecemaker::RegRef; kwargs...) + return GHZConsumer(get_time_tracker(net), net, piecemaker; kwargs...) +end + +@resumable function (prot::GHZConsumer)() + if isnothing(prot.period) + error("In `GHZConsumer` we do not yet support waiting on register to make qubits available") # TODO + end + while true + nclients = nsubsystems(prot.net[1])-1 + @show nclients + qparticipating = queryall(prot.piecemaker, FusionCounterpart, ❓, ❓) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below + if isnothing(qparticipating) + @debug "GHZConsumer between $(prot.piecemaker): query on piecemaker slot found no entanglement" + @yield timeout(prot.sim, prot.period) + return + elseif length(qparticipating) == nclients + @info "All clients are now part of the GHZ state." + client_slots = [prot.net[k][1] for k in 2:nclients+1] + + # Wait for all locks to complete + tasks = [] + for resource in client_slots + push!(tasks, @async lock(resource)) + end + push!(tasks, @async lock(prot.piecemaker)) + for task in tasks + wait(task) + end + + @debug "GHZConsumer of $(prot.piecemaker): queries successful, consuming entanglement" + for q in qparticipating + untag!(prot.piecemaker, q.id) + end + + # when all qubits have arrived, we measure out the central qubit + zmeas = project_traceout!(prot.piecemaker, σˣ) + @show zmeas + if zmeas == 2 + apply!(prot.net[2][1], Z) # apply correction on arbitrary client slot + end + pm = queryall(prot.piecemaker, ❓, ❓, ❓) + @assert length(pm) < 2 "More than one entry for piecemaker in database." + (slot, id, tag) = pm[1] + untag!(prot.piecemaker, id) + @show queryall(prot.piecemaker, ❓, ❓, ❓) + + ob1 = real(observable(client_slots, tensor(collect(fill(Z, nclients))...))) + ob2 = real(observable(client_slots, tensor(collect(fill(X, nclients))...))) + # if nclients-GHZ state achieved both observables equal 1 + @show ob1, ob2 + + # delete tags and free client slots + for k in 2:nclients+1 + queries = queryall(prot.net[k], EntanglementCounterpart, ❓, ❓) + for q in queries + untag!(q.slot, q.id) + end + end + + traceout!([prot.net[k][1] for k in 2:nclients+1]...) + push!(prot.log, (now(prot.sim), ob1, ob2)) + + @show [isassigned(prot.net[k][1]) for k in 2:nclients+1] + + for k in 2:nclients+1 + unlock(prot.net[k][1]) + end + unlock(prot.piecemaker) + @show [islocked(prot.net[k][1]) for k in 2:nclients+1] + @yield timeout(prot.sim, prot.period) + end + + # @debug "GHZConsumer of $(prot.node): queries successful, consuming entanglement" + # untag!(q, query.id) + # # TODO do we need to add EntanglementHistory and should that be a different EntanglementHistory since the current one is specifically for SwapperProt + # # TODO currently when calculating the observable we assume that EntanglerProt.pairstate is always (|00⟩ + |11⟩)/√2, make it more general for other states + # ob1 = real(observable((q1, q2), tensor(collect(fill(Z, nclients))...)) + # ob2 = real(observable((q1, q2), X⊗X)) + + # traceout!(prot.net[prot.nodeA][q1.idx], prot.net[prot.nodeB][q2.idx]) + # push!(prot.log, (now(prot.sim), ob1, ob2)) + @info "CONSUMER DONE" + @yield timeout(prot.sim, prot.period) + end +end """ $TYPEDEF diff --git a/src/ProtocolZoo/switches.jl b/src/ProtocolZoo/switches.jl index d746c0cd..37401256 100644 --- a/src/ProtocolZoo/switches.jl +++ b/src/ProtocolZoo/switches.jl @@ -2,7 +2,7 @@ module Switches using QuantumSavory using QuantumSavory.ProtocolZoo -using QuantumSavory.ProtocolZoo: EntanglementCounterpart, AbstractProtocol +using QuantumSavory.ProtocolZoo: EntanglementCounterpart, FusionCounterpart, AbstractProtocol using Graphs: edges, complete_graph, neighbors #using GraphsMatching: maximum_weight_matching using Combinatorics: combinations @@ -134,74 +134,67 @@ end # $TYPEDFIELDS # """ -# @kwdef struct FusionSwitchDiscreteProt #<: AbstractProtocol -# # """time-and-schedule-tracking instance from `ConcurrentSim`""" -# # sim::Simulation # TODO check that +# @kwdef struct FusionSwitchDiscreteProt{AA} <: AbstractProtocol where {AA} +# """time-and-schedule-tracking instance from `ConcurrentSim`""" +# sim::Simulation # TODO check that # """a network graph of registers""" # net::RegisterNet +# """the vertex index of the switch""" +# switchnode::Int +# """the vertex indices of the clients""" +# clientnodes::Vector{Int} +# """best-guess about success of establishing raw entanglement between client and switch""" +# success_probs::Vector{Float64} +# """duration of a single full cycle of the switching decision algorithm""" +# ticktock::Float64 = 1 +# """how many rounds of this protocol to run (`-1` for infinite)""" +# rounds::Int = -1 +# """the algorithm to use for memory slot assignment, defaulting to `promponas_bruteforce_choice`""" +# assignment_algorithm::AA = promponas_bruteforce_choice +# backlog::SymMatrix{Matrix{Int}} +# function FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs, ticktock, rounds, assignment_algorithm, backlog) +# length(unique(clientnodes)) == length(clientnodes) || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be unique!")) +# all(in(neighbors(net, switchnode)), clientnodes) || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be directly connected to the `switchnode`!")) +# 0 < ticktock || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested protocol period `ticktock` must be positive!")) +# 0 < rounds || rounds == -1 || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested number of rounds `rounds` must be positive or `-1` for infinite!")) +# length(clientnodes) == length(success_probs) || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested `success_probs` must have the same length as `clientnodes`!")) +# all(0 .<= success_probs .<= 1) || throw(ArgumentError("In the preparation of `SimpleSwitchDiscreteProt` switch protocol, the requested `success_probs` must be in the range [0,1]!")) +# new{typeof(assignment_algorithm)}(sim, net, switchnode, clientnodes, success_probs, ticktock, rounds, assignment_algorithm, backlog) +# end # end -# # """the vertex index of the switch""" -# # switchnode::Int64 -# # """the vertex indices of the clients""" -# # clientnodes::Vector{Int64} -# # """best-guess about success of establishing raw entanglement between client and switch""" -# # success_probs::Vector{Float64} -# # """duration of a single full cycle of the switching decision algorithm""" -# # ticktock::Float64 = 1 -# # """how many rounds of this protocol to run (`-1` for infinite)""" -# # rounds::Int = -1 -# # function FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs, ticktock, rounds) -# # length(unique(clientnodes)) == length(clientnodes) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be unique!")) -# # all(in(neighbors(net, switchnode)), clientnodes) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `clientnodes` must be directly connected to the `switchnode`!")) -# # 0 < ticktock || throw(ArgumentError("In the preparation of `FusioneSwitchDiscreteProt` switch protocol, the requested protocol period `ticktock` must be positive!")) -# # 0 < rounds || rounds == -1 || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested number of rounds `rounds` must be positive or `-1` for infinite!")) -# # length(clientnodes) == length(success_probs) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `success_probs` must have the same length as `clientnodes`!")) -# # all(0 .<= success_probs .<= 1) || throw(ArgumentError("In the preparation of `FusionSwitchDiscreteProt` switch protocol, the requested `success_probs` must be in the range [0,1]!")) -# # end -# # end -# # FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs; kwrags...) = FusionSwitchDiscreteProt(;sim, net, switchnode, clientnodes=collect(clientnodes), success_probs=collect(success_probs), kwargs...) -# # FusionSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwargs...) = FusionSwitchDiscreteProt(get_time_tracker(net), net, switchnode, clientnodes, success_probs; kwargs...) + +# function FusionSwitchDiscreteProt(sim, net, switchnode, clientnodes, success_probs; kwrags...) +# n = length(clientnodes) +# backlog = SymMatrix(zeros(Int, n, n)) +# FusionSwitchDiscreteProt(;sim, net, switchnode, clientnodes=collect(clientnodes), success_probs=collect(success_probs), backlog, kwrags...) +# end +# FusionSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) = FusionSwitchDiscreteProt(get_time_tracker(net), net, switchnode, clientnodes, success_probs; kwrags...) # @resumable function (prot::FusionSwitchDiscreteProt)() -# # rounds = prot.rounds -# # round = 1 -# net = prot.net -# println(net) -# # clientnodes = prot.clientnodes -# # switchnode = prot.switchnode -# # n = length(clientnodes) -# # m = nsubsystems(net[switchnode]) -# # reverseclientindex = Dict{Int,Int}(c=>i for (i,c) in enumerate(clientnodes)) +# rounds = prot.rounds +# clientnodes = prot.clientnodes +# reverseclientindex = Dict{Int,Int}(c=>i for (i,c) in enumerate(clientnodes)) # # start a process to delete unused switch-to-node entanglement at the end of each round # # deleter = _SwitchSynchronizedDelete(prot) # TODO: for fusion, this should not happen! # # @process deleter() -# # while rounds != 0 -# # rounds==-1 || (rounds -= 1) - -# # _switch_entangler_all(prot) -# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net - -# # # read which entanglements were successful and pick all possible -# # match = _switch_successful_entanglements(prot, reverseclientindex) -# # if isnothing(match) -# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net -# # continue -# # end - -# # # perform swaps -# # try -# # _switch_run_fusions(prot, match) -# # @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net -# # catch -# # println("Error occurred. Terminate.") -# # return -# # end -# # end +# while rounds != 0 +# rounds==-1 || (rounds -= 1) +# _switch_entangler_all(prot) +# @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net + +# # read which entanglements were successful +# match = _switch_successful_entanglements(prot, reverseclientindex) +# @show match +# # perform swaps +# _switch_run_swaps(prot, match) +# @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net +# end # end + """ $TYPEDEF @@ -259,23 +252,22 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) net = prot.net clientnodes = prot.clientnodes switchnode = prot.switchnode - backlog = prot.backlog n = length(clientnodes) m = nsubsystems(net[switchnode]) reverseclientindex = Dict{Int,Int}(c=>i for (i,c) in enumerate(clientnodes)) # start a process to delete unused switch-to-node entanglement at the end of each round - deleter = _SwitchSynchronizedDelete(prot) # TODO: for fusion, this should not happen! - @process deleter() + # deleter = _SwitchSynchronizedDelete(prot) # TODO: for fusion, this is not necessary + # @process deleter() while rounds != 0 rounds==-1 || (rounds -= 1) - # read the backlog into a weight matrix - _switch_read_backlog(prot, reverseclientindex) - # pick a set of client nodes to which to assign local memory slots if prot.assignment_algorithm == promponas_bruteforce_choice + backlog = prot.backlog + # read the backlog into a weight matrix + _switch_read_backlog(prot, reverseclientindex) assignment = prot.assignment_algorithm(m,n,backlog,prot.success_probs) if isnothing(assignment) @debug "Switch $switchnode found no useful memory slot assignments" @@ -296,15 +288,17 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) # read which entanglements were successful # and pick an optimal matching given the backlog of requests - match = _switch_successful_entanglements_best_match(prot, reverseclientindex) - #match = _switch_successful_entanglements(prot, reverseclientindex) + #match = _switch_successful_entanglements_best_match(prot, reverseclientindex) + match = _switch_successful_entanglements(prot, reverseclientindex) + @show match if isnothing(match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net continue end # perform swaps - _switch_run_swaps(prot, match) + #_switch_run_swaps(prot, match) + _switch_run_fusions(prot, match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net end end @@ -413,7 +407,9 @@ end function _switch_successful_entanglements(prot, reverseclientindex) switch = prot.net[prot.switchnode] + @show prot.clientnodes successes = queryall(switch, EntanglementCounterpart, in(prot.clientnodes), ❓) + @show successes entangled_clients = [r.tag[2] for r in successes] # RegRef (qubit slot) if isempty(entangled_clients) @debug "Switch $(prot.switchnode) failed to entangle with any clients" @@ -451,6 +447,8 @@ perform fusion to connect them with piecemaker qubit (no backlog discounter yet! function _switch_run_fusions(prot, match) @info "Switch $(prot.switchnode) performs fusions for clients in $(match)" for i in match + @show i + @info "Enter fusion protocol with client $(i)" fusion = FusionProt( # TODO be more careful about how much simulated time this takes sim=prot.sim, net=prot.net, node=prot.switchnode, nodeC=prot.clientnodes[i], diff --git a/test/test_circuitzoo_ent_swap.jl b/test/test_circuitzoo_ent_swap.jl index 26b165c7..ac41cc22 100644 --- a/test/test_circuitzoo_ent_swap.jl +++ b/test/test_circuitzoo_ent_swap.jl @@ -1,7 +1,7 @@ using QuantumSavory using QuantumSavory.CircuitZoo using Test -using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap +using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap, EntanglementFusion const perfect_pair_stab = StabilizerState("XX ZZ") const perfect_pair = (Z1⊗Z1 + Z2⊗Z2) / sqrt(2) diff --git a/test/test_circuitzoo_purification.jl b/test/test_circuitzoo_purification.jl index c283a910..4e20b37d 100644 --- a/test/test_circuitzoo_purification.jl +++ b/test/test_circuitzoo_purification.jl @@ -1,7 +1,7 @@ using QuantumSavory using QuantumSavory.CircuitZoo using Test -using QuantumSavory.CircuitZoo: EntanglementSwap, Purify2to1, Purify3to1, Purify3to1Node, Purify2to1Node, PurifyStringent, StringentHead, StringentBody, PurifyExpedient, PurifyStringentNode, PurifyExpedient +using QuantumSavory.CircuitZoo: EntanglementSwap, EntanglementFusion, Purify2to1, Purify3to1, Purify3to1Node, Purify2to1Node, PurifyStringent, StringentHead, StringentBody, PurifyExpedient, PurifyStringentNode, PurifyExpedient const bell = StabilizerState("XX ZZ") From 7db527e35e66a2f0431b43edf369d5bb7e5fa5d8 Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Wed, 28 Aug 2024 15:00:13 +0200 Subject: [PATCH 5/6] Currently first round produces correct fused state, but then mismatch in locks happens --- dev/QuantumSavory | 1 - .../simpleswitch_randomassign/3_simple_run.jl | 2 +- examples/simpleswitch_randomassign/setup.jl | 5 ++- src/ProtocolZoo/ProtocolZoo.jl | 34 +++++++++++++------ src/ProtocolZoo/switches.jl | 11 ++---- 5 files changed, 29 insertions(+), 24 deletions(-) delete mode 160000 dev/QuantumSavory diff --git a/dev/QuantumSavory b/dev/QuantumSavory deleted file mode 160000 index 6e99352a..00000000 --- a/dev/QuantumSavory +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6e99352aed1da5ee7067e07e287415f182514e3f diff --git a/examples/simpleswitch_randomassign/3_simple_run.jl b/examples/simpleswitch_randomassign/3_simple_run.jl index 75a30046..577230b1 100644 --- a/examples/simpleswitch_randomassign/3_simple_run.jl +++ b/examples/simpleswitch_randomassign/3_simple_run.jl @@ -3,7 +3,7 @@ include("setup.jl") # Prepare all of the simulation components n, sim = prepare_simulation() -step_ts = range(0, 20, step=0.1) +step_ts = range(0, 3, step=0.1) for t in step_ts run(sim, t) end \ No newline at end of file diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl index 2e5bb972..b2cad3c2 100644 --- a/examples/simpleswitch_randomassign/setup.jl +++ b/examples/simpleswitch_randomassign/setup.jl @@ -19,7 +19,7 @@ end # init piecemaker slot in |+> @yield lock(net[1][m]) if !isassigned(net[1][m]) - @info "Init piecemaker!" + #@info "Init piecemaker!" initialize!(net[1][m], X1) tag!(net[1][m], Piecemaker, 1, m) unlock(net[1][m]) @@ -28,13 +28,12 @@ end unlock(net[1][m]) @yield timeout(sim, 0.1) end - @yield timeout(sim, 0.1) end end function prepare_simulation() - n = 10 # number of clients + n = 4 # number of clients m = n+1 # memory slots in switch is equal to the number of clients + 1 slot for piecemaker qubit # The graph of network connectivity. Index 1 corresponds to the switch. diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 8c153c1d..56df88f3 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -224,17 +224,31 @@ end @yield timeout(prot.sim, prot.retry_lock_time) continue end - - @yield lock(a) & lock(b) # this yield is expected to return immediately + if isassigned(a) + traceout!(a) # TODO: why? + end if isassigned(b) traceout!(b) # TODO: why? end + + @info "EntanglerProt: Client $(prot.nodeB) STILL HERE 1" + if islocked(a) + @info "switchnode: $(a) is locked!" + end + if islocked(b) + @info "clientnode: $(b) is locked!" + end + @yield lock(a) & lock(b) # this yield is expected to return immediately + + @info "EntanglerProt: Client $(prot.nodeB) STILL HERE 2" + @yield timeout(prot.sim, prot.local_busy_time_pre) attempts = if isone(prot.success_prob) 1 else rand(Geometric(prot.success_prob))+1 end + if prot.attempts == -1 || prot.attempts >= attempts @yield timeout(prot.sim, attempts * prot.attempt_time) initialize!((a,b), prot.pairstate; time=now(prot.sim)) @@ -252,6 +266,7 @@ end end unlock(a) unlock(b) + @yield timeout(prot.sim, prot.retry_lock_time) rounds==-1 || (rounds -= 1) round += 1 end @@ -487,7 +502,7 @@ end apply!(localslot, updategate) end # tag local with updated EntanglementCounterpart new_remote_node new_remote_slot_idx - tag!(localslot, EntanglementCounterpart, newremotenode, newremoteslotid) + # tag!(localslot, EntanglementCounterpart, newremotenode, newremoteslotid) unlock(localslot) continue end @@ -548,7 +563,6 @@ end end while true nclients = nsubsystems(prot.net[1])-1 - @show nclients qparticipating = queryall(prot.piecemaker, FusionCounterpart, ❓, ❓) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below if isnothing(qparticipating) @debug "GHZConsumer between $(prot.piecemaker): query on piecemaker slot found no entanglement" @@ -561,12 +575,11 @@ end # Wait for all locks to complete tasks = [] for resource in client_slots - push!(tasks, @async lock(resource)) - end - push!(tasks, @async lock(prot.piecemaker)) - for task in tasks - wait(task) + push!(tasks, lock(resource)) end + push!(tasks, lock(prot.piecemaker)) + all_locks = reduce(&, tasks) + @yield all_locks @debug "GHZConsumer of $(prot.piecemaker): queries successful, consuming entanglement" for q in qparticipating @@ -588,7 +601,7 @@ end ob1 = real(observable(client_slots, tensor(collect(fill(Z, nclients))...))) ob2 = real(observable(client_slots, tensor(collect(fill(X, nclients))...))) # if nclients-GHZ state achieved both observables equal 1 - @show ob1, ob2 + @info "GHZConsumer: expectation values $(ob1) $(ob2)" # delete tags and free client slots for k in 2:nclients+1 @@ -620,7 +633,6 @@ end # traceout!(prot.net[prot.nodeA][q1.idx], prot.net[prot.nodeB][q2.idx]) # push!(prot.log, (now(prot.sim), ob1, ob2)) - @info "CONSUMER DONE" @yield timeout(prot.sim, prot.period) end end diff --git a/src/ProtocolZoo/switches.jl b/src/ProtocolZoo/switches.jl index 37401256..abc7ee4c 100644 --- a/src/ProtocolZoo/switches.jl +++ b/src/ProtocolZoo/switches.jl @@ -280,7 +280,6 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) _switch_entangler(prot, assignment) else # run entangler without requests (=no assignment) - println("RUN WITH NO ASSIGNMENTS") _switch_entangler_all(prot) end @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net @@ -290,7 +289,6 @@ SimpleSwitchDiscreteProt(net, switchnode, clientnodes, success_probs; kwrags...) #match = _switch_successful_entanglements_best_match(prot, reverseclientindex) match = _switch_successful_entanglements(prot, reverseclientindex) - @show match if isnothing(match) @yield timeout(prot.sim, prot.ticktock/2) # TODO this is a pretty arbitrary value # TODO timeouts should work on prot and on net continue @@ -407,9 +405,7 @@ end function _switch_successful_entanglements(prot, reverseclientindex) switch = prot.net[prot.switchnode] - @show prot.clientnodes successes = queryall(switch, EntanglementCounterpart, in(prot.clientnodes), ❓) - @show successes entangled_clients = [r.tag[2] for r in successes] # RegRef (qubit slot) if isempty(entangled_clients) @debug "Switch $(prot.switchnode) failed to entangle with any clients" @@ -419,7 +415,7 @@ function _switch_successful_entanglements(prot, reverseclientindex) ne = length(entangled_clients) if ne < 1 return nothing end entangled_clients_revindex = [reverseclientindex[k] for k in entangled_clients] - @debug "Switch $(prot.switchnode) successfully entangled with clients $entangled_clients" + @info "Switch $(prot.switchnode) successfully entangled with clients $entangled_clients" return entangled_clients_revindex end @@ -445,10 +441,9 @@ Assuming the clientnodes are entangled, perform fusion to connect them with piecemaker qubit (no backlog discounter yet!). """ function _switch_run_fusions(prot, match) - @info "Switch $(prot.switchnode) performs fusions for clients in $(match)" + @debug "Switch $(prot.switchnode) performs fusions for clients in $(match)" for i in match - @show i - @info "Enter fusion protocol with client $(i)" + @debug "Enter fusion protocol with client $(i)" fusion = FusionProt( # TODO be more careful about how much simulated time this takes sim=prot.sim, net=prot.net, node=prot.switchnode, nodeC=prot.clientnodes[i], From 92548844f7d9a69fc855fe3f608e092a0806a3dd Mon Sep 17 00:00:00 2001 From: Luise Prielinger Date: Wed, 28 Aug 2024 23:55:11 +0200 Subject: [PATCH 6/6] Lock mismatch fixed as well as success prob <1 (bell pairs only get initialized when slots not assigned yet) --- .../simpleswitch_randomassign/3_simple_run.jl | 2 +- examples/simpleswitch_randomassign/setup.jl | 2 +- src/ProtocolZoo/ProtocolZoo.jl | 19 ++----------------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/examples/simpleswitch_randomassign/3_simple_run.jl b/examples/simpleswitch_randomassign/3_simple_run.jl index 577230b1..9e0b4b1f 100644 --- a/examples/simpleswitch_randomassign/3_simple_run.jl +++ b/examples/simpleswitch_randomassign/3_simple_run.jl @@ -3,7 +3,7 @@ include("setup.jl") # Prepare all of the simulation components n, sim = prepare_simulation() -step_ts = range(0, 3, step=0.1) +step_ts = range(0, 12, step=0.1) for t in step_ts run(sim, t) end \ No newline at end of file diff --git a/examples/simpleswitch_randomassign/setup.jl b/examples/simpleswitch_randomassign/setup.jl index b2cad3c2..36985704 100644 --- a/examples/simpleswitch_randomassign/setup.jl +++ b/examples/simpleswitch_randomassign/setup.jl @@ -67,7 +67,7 @@ function prepare_simulation() @process consumer() # Finally, set up the switch without assignments - switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(1, n), assignment_algorithm=nothing) + switch_protocol = SimpleSwitchDiscreteProt(net, 1, 2:n+1, fill(0.7, n), assignment_algorithm=nothing) @process switch_protocol() return n, sim diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 56df88f3..629080b8 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -224,23 +224,8 @@ end @yield timeout(prot.sim, prot.retry_lock_time) continue end - if isassigned(a) - traceout!(a) # TODO: why? - end - if isassigned(b) - traceout!(b) # TODO: why? - end - - @info "EntanglerProt: Client $(prot.nodeB) STILL HERE 1" - if islocked(a) - @info "switchnode: $(a) is locked!" - end - if islocked(b) - @info "clientnode: $(b) is locked!" - end + @yield lock(a) & lock(b) # this yield is expected to return immediately - - @info "EntanglerProt: Client $(prot.nodeB) STILL HERE 2" @yield timeout(prot.sim, prot.local_busy_time_pre) attempts = if isone(prot.success_prob) @@ -249,7 +234,7 @@ end rand(Geometric(prot.success_prob))+1 end - if prot.attempts == -1 || prot.attempts >= attempts + if (prot.attempts == -1 || prot.attempts >= attempts) && !isassigned(b) && !isassigned(a) @yield timeout(prot.sim, attempts * prot.attempt_time) initialize!((a,b), prot.pairstate; time=now(prot.sim)) @yield timeout(prot.sim, prot.local_busy_time_post)