From 0c9f92e5107805732d9e43e2601790536f732bbe Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 23 Sep 2024 15:56:51 -0400 Subject: [PATCH 01/16] Initial changes for different control plane simulations --- CHANGELOG.md | 2 + examples/controlplane/1a_cdd_interactive.jl | 46 +++++ examples/controlplane/1b_cdd_wglmakie.jl | 115 +++++++++++ examples/controlplane/2a_cnc_interactive.jl | 37 ++++ examples/controlplane/2b_cnc_wglmakie.jl | 104 ++++++++++ examples/controlplane/Project.toml | 15 ++ examples/controlplane/Readme.md | 9 + examples/controlplane/setup.jl | 86 +++++++++ src/ProtocolZoo/ProtocolZoo.jl | 199 +++++++++++++++++++- src/ProtocolZoo/controllers.jl | 117 ++++++++++++ src/ProtocolZoo/swapping.jl | 23 --- src/ProtocolZoo/utils.jl | 63 +++++++ test/test_examples.jl | 7 + 13 files changed, 795 insertions(+), 28 deletions(-) create mode 100644 examples/controlplane/1a_cdd_interactive.jl create mode 100644 examples/controlplane/1b_cdd_wglmakie.jl create mode 100644 examples/controlplane/2a_cnc_interactive.jl create mode 100644 examples/controlplane/2b_cnc_wglmakie.jl create mode 100644 examples/controlplane/Project.toml create mode 100644 examples/controlplane/Readme.md create mode 100644 examples/controlplane/setup.jl create mode 100644 src/ProtocolZoo/controllers.jl create mode 100644 src/ProtocolZoo/utils.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f2c680..754b23e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## v0.6.0 - 2024-09-13 - Simplify one of the switch protocols to avoid dependence on GraphMatching.jl which does not install well on non-linux systems. Do not rely on the default `SimpleSwitchDiscreteProt` for the time being. +- Interactive examples for network controller +- Implement a network control protocol, a request generator and a request tracker along with new tags `EntanglementRequest`,`SwapRequest` and `DistributionRequest` ## v0.5.0 - 2024-09-05 diff --git a/examples/controlplane/1a_cdd_interactive.jl b/examples/controlplane/1a_cdd_interactive.jl new file mode 100644 index 00000000..4c3ca8e4 --- /dev/null +++ b/examples/controlplane/1a_cdd_interactive.jl @@ -0,0 +1,46 @@ +include("setup.jl") + +succ_prob = Observable(0.001) +for (;src, dst) in edges(net) + eprot = EntanglerProt(sim, net, src, dst; rounds=-1, randomize=true, success_prob=succ_prob[]) + @process eprot() +end + +local_busy_time = Observable(0.0) +retry_lock_time = Observable(0.1) +for node in 2:7 + swapper = SwapperProt(sim, net, node; nodeL = <(node), nodeH = >(node), chooseL = argmin, chooseH = argmax, rounds=-1, local_busy_time=local_busy_time[], + retry_lock_time=retry_lock_time[]) + @process swapper() +end + +for v in vertices(net) + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +period_cons = Observable(0.1) +consumer = EntanglementConsumer(sim, net, 1, 8; period=period_cons[]) +@process consumer() + +period_dec = Observable(0.1) +for v in vertices(net) + cutoff = CutoffProt(sim, net, v; period=period_dec[]) + @process cutoff() +end +params = [succ_prob, local_busy_time, retry_lock_time, period_cons, period_dec] +sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer, params) + +step_ts = range(0.0, 1000.0, step=0.1) +record(fig, "sim.mp4", step_ts; framerate=10, visible=true) do t + run(sim, t) + notify.((obs,entlog)) + notify.(params) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) +end \ No newline at end of file diff --git a/examples/controlplane/1b_cdd_wglmakie.jl b/examples/controlplane/1b_cdd_wglmakie.jl new file mode 100644 index 00000000..32bc5bc6 --- /dev/null +++ b/examples/controlplane/1b_cdd_wglmakie.jl @@ -0,0 +1,115 @@ +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 + +succ_prob = Observable(0.001) +for (;src, dst) in edges(net) + eprot = EntanglerProt(sim, net, src, dst; rounds=-1, randomize=true, success_prob=succ_prob[]) + @process eprot() +end + +local_busy_time = Observable(0.0) +retry_lock_time = Observable(0.1) +for node in 2:7 + swapper = SwapperProt(sim, net, node; nodeL = <(node), nodeH = >(node), chooseL = argmin, chooseH = argmax, rounds=-1, local_busy_time=local_busy_time[], + retry_lock_time=retry_lock_time[]) + @process swapper() +end + +for v in vertices(net) + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +period_cons = Observable(0.1) +consumer = EntanglementConsumer(sim, net, 1, 8; period=period_cons[]) +@process consumer() + +period_dec = Observable(0.1) +for v in vertices(net) + cutoff = CutoffProt(sim, net, v; period=period_dec[]) + @process cutoff() +end +params = [succ_prob, local_busy_time, retry_lock_time, period_cons, period_dec] + +# 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!(sim, obs, entlog, params, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + step_ts = range(0, 1000, step=0.1) + for t in step_ts + run(sim, t) + notify.((obs,entlog)) + notify.(params) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) + end + running[] = nothing +end + +# +landing = Bonito.App() do + + sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer, params) + + running = Observable{Any}(false) + fig[5,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!( + sim, obs, entlog, params, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + end + end + end + + + content = md""" + Pick simulation settings and hit run (see below for technical details). + + $(fig.scene) + + # Connectionless, Distributed and Decentralized Control Plane for Entanglement Distribution + + The above simulation visualizes entanglement distribution between Alice and Bob on an arbitrary network topology + given by the adjacency matrix of the graph. The control plane architecture used for this simulation is connectionless, + distributed and decentralized. The node representing Alice is the node on the top left and the bottom right is Bob. + The actual connectivity of the physical graph isn't fully captured by the visualization above as we use edges only to + show the virtual graph. + + [See and modify the code for this simulation on github.](https://github.com/QuantumSavory/QuantumSavory.jl/tree/master/examples/controlplane/1b_cdd_wglmakie.jl) + """ + 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, "CDD_PORT", "8888")) +interface = get(ENV, "CDD_IP", "127.0.0.1") +proxy_url = get(ENV, "CDD_PROXY", "") +server = Bonito.Server(interface, port; proxy_url); +Bonito.HTTPServer.start(server) +Bonito.route!(server, "/" => landing); + +## + +wait(server) \ No newline at end of file diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl new file mode 100644 index 00000000..258b19e8 --- /dev/null +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -0,0 +1,37 @@ +include("setup.jl") + +controller = NetController(sim, net, 3, 6, 0.2) +@process controller() + +consumer = EntanglementConsumer(sim, net, 1, 8; period=0.2) +@process consumer() + +for node in 1:7 + tracker = RequestTracker(sim, net, node, 0.3) + @process tracker() +end + +for v in 1:8 + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +for v in 1:8 + c_prot = CutoffProt(sim, net, v) + @process c_prot() +end + +sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer) + +step_ts = range(0.0, 1000.0, step=0.1) +record(fig, "sim.mp4", step_ts; framerate=10, visible=true) do t + run(sim, t) + notify.((obs,entlog)) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) +end \ No newline at end of file diff --git a/examples/controlplane/2b_cnc_wglmakie.jl b/examples/controlplane/2b_cnc_wglmakie.jl new file mode 100644 index 00000000..09528072 --- /dev/null +++ b/examples/controlplane/2b_cnc_wglmakie.jl @@ -0,0 +1,104 @@ +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 + +controller = NetController(sim, net, 3, 6, 0.2) +@process controller() + +consumer = EntanglementConsumer(sim, net, 1, 8; period=0.2) +@process consumer() + +for node in 1:7 + tracker = RequestTracker(sim, net, node, 0.3) + @process tracker() +end + +for v in 1:8 + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +for v in 1:8 + c_prot = CutoffProt(sim, net, v) + @process c_prot() +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!(sim, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + step_ts = range(0, 1000, step=0.1) + for t in step_ts + run(sim, t) + notify.((obs,entlog)) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) + end + running[] = nothing +end + +# +landing = Bonito.App() do + + sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer) + + running = Observable{Any}(false) + fig[5,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!( + sim, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + end + end + end + + + content = md""" + Pick simulation settings and hit run (see below for technical details). + + $(fig.scene) + + # Connection-Oriented, Non-Distributed and Centralized Control Plane for Entanglement Distribution + + The above simulation visualizes entanglement distribution between Alice and Bob on an arbitrary network topology + given by the adjacency matrix of the graph. The control plane architecture used for this simulation is connection-oriented, + non-distributed and centralized. The node representing Alice is the node on the top left and the bottom right is Bob. + The actual connectivity of the physical graph isn't fully captured by the visualization above as we use edges only to + show the virtual graph. + + [See and modify the code for this simulation on github.](https://github.com/QuantumSavory/QuantumSavory.jl/tree/master/examples/controlplane/2b_cnc_wglmakie.jl) + """ + 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, "CNC_PORT", "8888")) +interface = get(ENV, "CNC_IP", "127.0.0.1") +proxy_url = get(ENV, "CNC_PROXY", "") +server = Bonito.Server(interface, port; proxy_url); +Bonito.HTTPServer.start(server) +Bonito.route!(server, "/" => landing); + +## + +wait(server) diff --git a/examples/controlplane/Project.toml b/examples/controlplane/Project.toml new file mode 100644 index 00000000..6e6b0c0e --- /dev/null +++ b/examples/controlplane/Project.toml @@ -0,0 +1,15 @@ +[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" +Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c" +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" + +[compat] +Bonito = "3.1" \ No newline at end of file diff --git a/examples/controlplane/Readme.md b/examples/controlplane/Readme.md new file mode 100644 index 00000000..a4c47a37 --- /dev/null +++ b/examples/controlplane/Readme.md @@ -0,0 +1,9 @@ +Different control plane architectures with arbitrary network topologies can be simulated using QuantumSavory. The `setup.jl` contains basic functionality required by the simulations. The other files are described below: + +1a. A simulation that generates an interactive visualization for a connectionless, distributed and decentralized entanglement distribution network + +1b. An interactive web app with the same simulation as 1a + +2a. A simulation that generates an interactive visualization for a connection-oriented, non-distributed and centralized entanglement distribution network + +2b. An interactive web app with the same simulation as 2a \ No newline at end of file diff --git a/examples/controlplane/setup.jl b/examples/controlplane/setup.jl new file mode 100644 index 00000000..6f8e48c2 --- /dev/null +++ b/examples/controlplane/setup.jl @@ -0,0 +1,86 @@ +using QuantumSavory +using QuantumSavory.ProtocolZoo +using ConcurrentSim +using ResumableFunctions + +using Graphs +using GLMakie +GLMakie.activate!() + +using NetworkLayout +using Random + +adjm = [0 1 0 0 1 0 0 0 + 1 0 1 0 0 0 0 0 + 0 1 0 1 0 1 0 0 + 0 0 1 0 0 0 1 1 + 1 0 0 0 0 1 0 1 + 0 0 1 0 1 0 1 0 + 0 0 0 1 0 1 0 1 + 0 0 0 1 1 0 1 0] +graph = SimpleGraph(adjm) + +regsize = 20 +net = RegisterNet(graph, [Register(regsize, T1Decay(10.0)) for i in 1:8]) +sim = get_time_tracker(net) + +function prepare_vis(consumer::EntanglementConsumer, params=nothing) + ### + fig = Figure(;size=(1200, 1100)) + + # the network part of the visualization + layout = SquareGrid(cols=:auto, dx=30.0, dy=-30.0)(graph) # provided by NetworkLayout, meant to simplify plotting of graphs in 2D + _, ax, _, obs = registernetplot_axis(fig[1:2,1], net; registercoords=layout) + + # the performance log part of the visualization + entlog = Observable(consumer.log) # Observables are used by Makie to update the visualization in real-time in an automated reactive way + ts = @lift [e[1] for e in $entlog] # TODO this needs a better interface, something less cluncky, maybe also a whole Makie recipe + tzzs = @lift [Point2f(e[1],e[2]) for e in $entlog] + txxs = @lift [Point2f(e[1],e[3]) for e in $entlog] + Δts = @lift length($ts)>1 ? $ts[2:end] .- $ts[1:end-1] : [0.0] + entlogaxis = Axis(fig[1,2], xlabel="Time", ylabel="Entanglement", title="Entanglement Successes") + ylims!(entlogaxis, (-1.04,1.04)) + stem!(entlogaxis, txxs) + histaxis = Axis(fig[2,2], xlabel="ΔTime", title="Histogram of Time to Successes") + hist!(histaxis, Δts) + + avg_fids = @lift cumsum([e[3] for e in $entlog])./cumsum(ones(length($entlog))) #avg fidelity per unit time + fid_info = @lift [Point2f(t,f) for (t,f) in zip($ts, $avg_fids)] + fid_axis = Axis(fig[3,1], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") + lines!(fid_axis, fid_info) + + num_epr = @lift cumsum(ones(length($entlog)))./($ts) #avg number of pairs per unit time + num_epr_info = @lift [Point2f(t,n) for (t,n) in zip($ts, $num_epr)] + num_epr_axis = Axis(fig[3,2], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") + lines!(num_epr_axis, num_epr_info) + + if !isnothing(params) + # sliders + sg = SliderGrid( + fig[4,1], + (label="Probability of success of Entanglement generation at each attempt", + range=0.001:0.05:1.0, format="{:.3f}", startvalue=0.001), + (label="Local busy time for swapper", + range=0.001:0.5:10.0, format="{:.3f}", startvalue=0.001), + (label="Wait time after failure to lock qubits for a swap", + range=0.1:0.05:1.0, format="{:.2f}", startvalue=0.1), + (label="Period of time between subsequent queries at the consumer", + range=0.001:0.05:1.0, format="{:.3f}", startvalue=0.001), + (label="Period of time between subsequent queries at the DecoherenceProtocol", + range=0.001:0.05:1.0, format="{:.3f}", startvalue=0.001), + + width = 600, + tellheight = true) + + for (param, slider) in zip(params, sg.sliders) + on(slider.value) do val + param[] = val + end + end + end + + + display(fig) + + return consumer.sim, consumer.net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig +end \ No newline at end of file diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index dee968ac..14796d39 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -7,20 +7,25 @@ using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap using DocStringExtensions -using Distributions: Geometric +using Distributions: Geometric, Exponential using ConcurrentSim: Simulation, @yield, timeout, @process, now import ConcurrentSim: Process import ResumableFunctions using ResumableFunctions: @resumable import SumTypes +using Graphs export # protocols - EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, CutoffProt, + EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, CutoffProt, RequestTracker, RequestGenerator, # tags - EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, + EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, EntanglementRequest, SwapRequest, DistributionRequest, RequestCompletion, # from Switches - SimpleSwitchDiscreteProt, SwitchRequest + SimpleSwitchDiscreteProt, SwitchRequest, + # controllers + NetController, Controller, + #utils + PhysicalGraph, path_selection abstract type AbstractProtocol end @@ -145,6 +150,82 @@ end Base.show(io::IO, tag::EntanglementDelete) = print(io, "Deleted $(tag.send_node).$(tag.send_slot) which was entangled to $(tag.rec_node).$(tag.rec_slot)") Tag(tag::EntanglementDelete) = Tag(EntanglementDelete, tag.send_node, tag.send_slot, tag.rec_node, tag.rec_slot) +""" +$TYPEDEF + +A message sent from a controller to the [`RequestTracker`](@ref) at a node requesting the generation of an entanglement link between the receiving node +and one of its next-hop neighbors on the physical graph, as mentioned in the request + +$TYPEDFIELDS + +See also: [EntanglerProt](@ref), [`EntanglementTracker`](@ref), [`SwapRequest`](@ref) +""" +@kwdef struct EntanglementRequest + "The id of the node receiving the request" + receiver::Int + "The id of the node with which the entanglement link should be established" + neighbor::Int + "The number of rounds the Entangler should run for" + rounds::Int +end +Base.show(io::IO, tag::EntanglementRequest) = print(io, "$(tag.receiver) attempt entanglement generation with $(tag.neighbor)") +Tag(tag::EntanglementRequest) = Tag(EntanglementRequest, tag.receiver, tag.neighbor, tag.rounds) + +""" +$TYPEDEF + +A message sent from a controller to the [`RequestTracker`](@ref) at a node requesting it to perform a swap + +$TYPEDFIELDS + +See also: [`SwapperProt`](@ref), [`EntanglementTracker`](@ref), [`EntanglementRequest`](@ref) +""" +@kwdef struct SwapRequest + """The id of the node instructed to perform a swap""" + swapping_node::Int + """The number of rounds the swapper should run for""" + rounds::Int +end +Base.show(io::IO, tag::SwapRequest) = print(io, "Node $(tag.swapping_node) perform a swap") +Tag(tag::SwapRequest) = Tag(SwapRequest, tag.swapping_node, tag.rounds) + +""" +$TYPEDEF + +A message sent from a node to a control protocol requesting bipartite entanglement with a remote destination node through entanglement distribution. + +$TYPEDFIELDS + +See also: [`EntanglementRequest`](@ref), [`SwapRequest`] +""" +@kwdef struct DistributionRequest + """The node generating the request""" + src::Int + """The node with which entanglement is to be generated""" + dst::Int + """Index of the path to be taken for the entanglement generation""" + path::Int +end +Base.show(io::IO, tag::DistributionRequest) = print(io, "Node $(tag.src) requesting entanglement with $(tag.dst)") +Tag(tag::DistributionRequest) = Tag(DistributionRequest, tag.src, tag.dst, tag.path) + + +""" +$TYPEDEF + +A message sent from the controller to a request generating node after its request has been served. + +$TYPEDFIELDS + +See also: [`EntanglementRequest`](@ref), [`SwapRequest`] +""" +@kwdef struct RequestCompletion + path_id::Int +end +Base.show(io::IO, tag::RequestCompletion) = print(io, "Request on path id $(tag.path_id) served") +Tag(tag::RequestCompletion) = Tag(RequestCompletion, tag.path_id) + + """ $TYPEDEF @@ -372,7 +453,7 @@ $TYPEDEF A protocol running between two nodes, checking periodically for any entangled pairs between the two nodes and consuming/emptying the qubit slots. -$FIELDS +$TYPEDFIELDS """ @kwdef struct EntanglementConsumer{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} """time-and-schedule-tracking instance from `ConcurrentSim`""" @@ -435,9 +516,117 @@ end end end +""" +$TYPEDEF + +A protocol running at a node, listening for incoming entanglement generation and swap requests + +$TYPEDFIELDS +""" +@kwdef struct RequestTracker <: AbstractProtocol + """time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """a network graph of registers""" + net::RegisterNet + """the vertex of the node where the tracker is working""" + node::Int + """duration of request generation and processing""" + ticktock::Float64 +end + +@resumable function (prot::RequestTracker)() + mb = messagebuffer(prot.net, prot.node) + while true + workwasdone = true # waiting is not enough because we might have multiple rounds of work to do + while workwasdone + workwasdone = false # if there is nothing in the mb queue(querydelete returns nothing) we skip to waiting, otherwise we keep querying until the queue is empty + for requesttagsymbol in (EntanglementRequest, SwapRequest) + if requesttagsymbol == EntanglementRequest + msg = querydelete!(mb, requesttagsymbol, ❓, ❓, ❓) + @debug "RequestTracker @$(prot.node): Received $msg" + isnothing(msg) && continue + workwasdone = true + (src, (_, _, neighbor, rounds)) = msg + @debug "RequestTracker @$(prot.node): Generating entanglement with $(neighbor)" + entangler = EntanglerProt(prot.sim, prot.net, prot.node, neighbor; rounds=rounds, randomize=true) + @process entangler() + else + msg = querydelete!(mb, requesttagsymbol, ❓, ❓) + @debug "RequestTracker @$(prot.node): Received $msg" + isnothing(msg) && continue + workwasdone = true + (src, (_, _, rounds)) = msg + @debug "RequestTracker @$(prot.node): Performing a swap" + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) + @process swapper() + end + @yield timeout(prot.sim, prot.ticktock) + end + end + @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @yield wait(mb) + @debug "RequestTracker @$(prot.node): Message wait ends at $(now(prot.sim))" + end +end + +include("utils.jl") + +""" +$TYPEDEF + +Protocol for the simulation of request traffic for a controller in a connection-oriented network for bipartite entanglement distribution. The requests are considered to be arriving according to the Poisson model with rate λ, hence the inter-arrival time is +sampled from an exponential distribution. Physically, the request is generated at the source node(Alice) and is classically communicated to the node where the controller is located. Multiple `RequestGenerator`s can be instantiated for simulation with multiple +user pairs in the same network. + +$TYPEDFIELDS +""" +@kwdef struct RequestGenerator <: AbstractProtocol # TODO Should path_selection be a parameter here, so that it can be customized by the user? + """time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """a network graph of registers""" + net::RegisterNet + """The source node(and the node where this protocol runs) of the user pair, commonly called Alice""" + src::Int + """The destination node, commonly called Bob""" + dst::Int + """The node at which the controller is located""" + controller::Int + """The object containing physical graph metadata for the network""" + phys_graph::PhysicalGraph + """rate of arrival of requests/number of requests sent unit time""" + λ::Int = 4 +end + +function RequestGenerator(sim, net, src, dst, controller, phys_graph; kwargs...) + return RequestGenerator(;sim, net, src, dst, controller, phys_graph, kwargs...) +end + +@resumable function (prot::RequestGenerator)() + d = Exponential(inv(prot.λ)) # Parametrized with the scale which is inverse of the rate + mb = messagebuffer(prot.net, prot.src) + while true + path_index = path_selection(prot.phys_graph) + if isnothing(path_index) + prot.phys_graph.failures[] += 1 + continue + end + msg = Tag(DistributionRequest, prot.src, prot.dst, path_index) + put!(channel(prot.net, prot.src=>prot.controller; permit_forward=true), msg) + @yield timeout(prot.sim, rand(d)) + + # incoming message from the controller after a request has been served + in_msg = querydelete!(mb, RequestCompletion, ❓) + if !isnothing(in_msg) + (src, (_, path_id)) = in_msg + prot.phys_graph.workloads[path_id] -= 1 + end + end +end + include("cutoff.jl") include("swapping.jl") +include("controllers.jl") include("switches.jl") using .Switches diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl new file mode 100644 index 00000000..86ffe331 --- /dev/null +++ b/src/ProtocolZoo/controllers.jl @@ -0,0 +1,117 @@ +""" +$TYPEDEF + +A network control protocol that is connection oriented, non-distributed and centralized. The generation of +random requests is abstracted with picking a random path from all available paths in the arbitrary network +between Alice and Bob. The controller is located at one of the nodes in the network from where it messages all +the other nodes. + +$TYPEDFIELDS + +See also [`RequestTracker`](@ref) +""" +@kwdef struct NetController <: AbstractProtocol + """Time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """A network graph of registers""" + net::RegisterNet + """The number of requests to be generated per cycle""" + n::Int + """The node in the network where the control protocol is physically located, ideally a centrally located node""" + node::Int + """duration of a single full cycle of entanglement generation and swapping along a specific path""" + ticktock::Float64 +end + +@resumable function (prot::NetController)() + paths = collect(Graphs.all_simple_paths(prot.net.graph, 1, 8)) + n_reg = length(prot.net.registers) + mb = messagebuffer(prot.net, prot.node) + while true + draw = (randperm(n_reg))[1:prot.n] + for i in 1:prot.n + path = paths[draw[i]] + @debug "Running Entanglement Distribution on path $(path) @ $(now(prot.sim))" + for i in 1:length(path)-1 + msg = Tag(EntanglementRequest, path[i], path[i+1], 1) + if prot.node == path[i] + put!(mb, msg) + else + put!(channel(prot.net, prot.node=>msg[2]; permit_forward=true), msg) + end + end + + for i in 2:length(path)-1 + msg = Tag(SwapRequest, path[i], 1) + if prot.node == path[i] + put!(mb, msg) + else + put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) + end + end + @yield timeout(prot.sim, prot.ticktock) + end + end +end + +""" +$TYPEDEF + +A network control protocol that is connection oriented, non-distributed and centralized. The controller is located at one of the nodes in the network from where it messages all +the other nodes when it receives an entanglement distribution request from the [`RequestGenerator`](@ref). + +$TYPEDFIELDS + +See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) +""" +@kwdef struct Controller <: AbstractProtocol + """Time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """A network graph of registers""" + net::RegisterNet + """The node in the network where the control protocol is physically located, ideally a centrally located node""" + node::Int + """The object containing physical graph metadata for the network""" + phys_graph::PhysicalGraph + """duration of a single full cycle of entanglement generation and swapping along a specific path""" + ticktock::Float64 +end + +@resumable function (prot::Controller)() + mb = messagebuffer(prot.net, prot.node) + while true + workwasdone = true + while workwasdone + workwasdone = false + msg = querydelete!(mb, DistributionRequest, ❓, ❓, ❓) + if !isnothing(msg) + (msg_src, (_, src, dst, path_ind)) = msg + path = prot.phys_graph.paths[path_ind] + @debug "Running Entanglement Distribution on path $(path) @ $(now(prot.sim))" + for i in 1:length(path)-1 + msg = Tag(EntanglementRequest, path[i], path[i+1], 1) + if prot.node == path[i] + put!(mb, msg) + else + put!(channel(prot.net, prot.node=>msg[2]; permit_forward=true), msg) + end + end + + for i in 2:length(path)-1 + msg = Tag(SwapRequest, path[i], 1) + if prot.node == path[i] + put!(mb, msg) + else + put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) + end + end + out_msg = Tag(RequestCompletion, path_ind) + put!(channel(prot.net, prot.node=>src;permit_forward=true), out_msg) + @yield timeout(prot.sim, prot.ticktock) + end + @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @yield wait(mb) + @debug "Controller @$(prot.node): Message wait ends at $(now(prot.sim))" + end + end +end \ No newline at end of file diff --git a/src/ProtocolZoo/swapping.jl b/src/ProtocolZoo/swapping.jl index e7330604..6cae7185 100644 --- a/src/ProtocolZoo/swapping.jl +++ b/src/ProtocolZoo/swapping.jl @@ -1,26 +1,3 @@ -function random_index(arr) - return rand(keys(arr)) -end - - -function findswapablequbits(net, node, pred_low, pred_high, choose_low, choose_high; agelimit=nothing) - reg = net[node] - low_nodes = [ - n for n in queryall(reg, EntanglementCounterpart, pred_low, ❓; locked=false, assigned=true) - if isnothing(agelimit) || !isolderthan(n.slot, agelimit) # TODO add age limit to query and queryall - ] - high_nodes = [ - n for n in queryall(reg, EntanglementCounterpart, pred_high, ❓; locked=false, assigned=true) - if isnothing(agelimit) || !isolderthan(n.slot, agelimit) # TODO add age limit to query and queryall - ] - - (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)) - return (low_nodes[il], high_nodes[ih]) -end - - """ $TYPEDEF diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl new file mode 100644 index 00000000..d4bd7c9e --- /dev/null +++ b/src/ProtocolZoo/utils.jl @@ -0,0 +1,63 @@ +""" +$TYPEDEF + +A struct containing the physical graph metadata for a network. The latest workload data is only available +at the node where the [`RequestGenerator`](@ref) runs, but every node has access to a copy for referencing paths based on indices. + +$TYPEDFIELDS +""" +@kwdef struct PhysicalGraph + """The vector of paths between the user pair""" + paths::Vector{Vector{Int}} + """The vector containing the workload information of a path""" + workloads::Vector{Int} + """The number of slots available at each node. Scalar if all are same, vector otherwise.""" + capacity::Union{Vector{Int}, Int} + """Number of failed requests due to high request traffic""" + failures::Ref{Int} +end + +function PhysicalGraph(graph::SimpleGraph{Int64}, src::Int, dst::Int, caps::Union{Vector{Int}, Int}; failures=Ref{Int}(0)) + paths = sort(collect(all_simple_paths(graph, src, dst)); by = x->length(x)) + workloads = zeros(length(paths)) + PhysicalGraph(paths, workloads, caps, failures) +end + + +""" +A simple path selection algorithm for connection oriented networks. +""" +function path_selection(phys_graph::PhysicalGraph) + for i in 1:length(phys_graph.paths) + capacity = isa(phys_graph.capacity, Number) ? phys_graph.capacity : phys_graph.capacity[i] + if phys_graph.workloads[i] Date: Mon, 23 Sep 2024 16:23:06 -0400 Subject: [PATCH 02/16] update the examples to use the new protocols --- examples/controlplane/2a_cnc_interactive.jl | 10 +++++++--- examples/controlplane/2b_cnc_wglmakie.jl | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl index 258b19e8..f173eb54 100644 --- a/examples/controlplane/2a_cnc_interactive.jl +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -1,13 +1,17 @@ include("setup.jl") -controller = NetController(sim, net, 3, 6, 0.2) +phys_graph = PhysicalGraph(graph, 1, 8, regsize) +controller = Controller(sim, net, 6, phys_graph, 0.1) @process controller() -consumer = EntanglementConsumer(sim, net, 1, 8; period=0.2) +req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +@process req_gen() + +consumer = EntanglementConsumer(sim, net, 1, 8) @process consumer() for node in 1:7 - tracker = RequestTracker(sim, net, node, 0.3) + tracker = RequestTracker(sim, net, node, 0.1) @process tracker() end diff --git a/examples/controlplane/2b_cnc_wglmakie.jl b/examples/controlplane/2b_cnc_wglmakie.jl index 09528072..025de838 100644 --- a/examples/controlplane/2b_cnc_wglmakie.jl +++ b/examples/controlplane/2b_cnc_wglmakie.jl @@ -7,14 +7,18 @@ 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 -controller = NetController(sim, net, 3, 6, 0.2) +phys_graph = PhysicalGraph(graph, 1, 8, regsize) +controller = Controller(sim, net, 6, phys_graph, 0.1) @process controller() -consumer = EntanglementConsumer(sim, net, 1, 8; period=0.2) +req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +@process req_gen() + +consumer = EntanglementConsumer(sim, net, 1, 8) @process consumer() for node in 1:7 - tracker = RequestTracker(sim, net, node, 0.3) + tracker = RequestTracker(sim, net, node, 0.1) @process tracker() end From 33d4b209470db63a90b6c6cca90480043be4e6d6 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:28:23 -0400 Subject: [PATCH 03/16] fixup --- examples/controlplane/2a_cnc_interactive.jl | 4 ++-- src/ProtocolZoo/ProtocolZoo.jl | 24 ++++++++++++++------- src/ProtocolZoo/controllers.jl | 8 ++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl index f173eb54..09ece2e3 100644 --- a/examples/controlplane/2a_cnc_interactive.jl +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -1,7 +1,7 @@ include("setup.jl") phys_graph = PhysicalGraph(graph, 1, 8, regsize) -controller = Controller(sim, net, 6, phys_graph, 0.1) +controller = Controller(sim, net, 6, phys_graph) @process controller() req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) @@ -11,7 +11,7 @@ consumer = EntanglementConsumer(sim, net, 1, 8) @process consumer() for node in 1:7 - tracker = RequestTracker(sim, net, node, 0.1) + tracker = RequestTracker(sim, net, node) @process tracker() end diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 14796d39..4dc7ca60 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -185,9 +185,15 @@ See also: [`SwapperProt`](@ref), [`EntanglementTracker`](@ref), [`EntanglementRe swapping_node::Int """The number of rounds the swapper should run for""" rounds::Int + """Whether the swap request is the final request on a path, hence signaling the request being served""" + last::Int + """path id for which the request was generated""" + id::Int + """the source node that generated the request""" + src::Int end Base.show(io::IO, tag::SwapRequest) = print(io, "Node $(tag.swapping_node) perform a swap") -Tag(tag::SwapRequest) = Tag(SwapRequest, tag.swapping_node, tag.rounds) +Tag(tag::SwapRequest) = Tag(SwapRequest, tag.swapping_node, tag.rounds, tag.last, tag.id, tag.src) """ $TYPEDEF @@ -530,8 +536,6 @@ $TYPEDFIELDS net::RegisterNet """the vertex of the node where the tracker is working""" node::Int - """duration of request generation and processing""" - ticktock::Float64 end @resumable function (prot::RequestTracker)() @@ -551,16 +555,19 @@ end entangler = EntanglerProt(prot.sim, prot.net, prot.node, neighbor; rounds=rounds, randomize=true) @process entangler() else - msg = querydelete!(mb, requesttagsymbol, ❓, ❓) + msg = querydelete!(mb, requesttagsymbol, ❓, ❓, ❓, ❓, ❓) @debug "RequestTracker @$(prot.node): Received $msg" isnothing(msg) && continue workwasdone = true - (src, (_, _, rounds)) = msg + (msg_src, (_, _, rounds, last, path_id, src)) = msg @debug "RequestTracker @$(prot.node): Performing a swap" swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) @process swapper() + if last == 1 + comp_msg = Tag(RequestCompletion, path_id) + put!(channel(prot.net, prot.node=>src; permit_forward=true), comp_msg) + end end - @yield timeout(prot.sim, prot.ticktock) end end @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @@ -594,7 +601,7 @@ $TYPEDFIELDS """The object containing physical graph metadata for the network""" phys_graph::PhysicalGraph """rate of arrival of requests/number of requests sent unit time""" - λ::Int = 4 + λ::Int = 3 end function RequestGenerator(sim, net, src, dst, controller, phys_graph; kwargs...) @@ -608,11 +615,11 @@ end path_index = path_selection(prot.phys_graph) if isnothing(path_index) prot.phys_graph.failures[] += 1 + @yield timeout(prot.sim, rand(d)) continue end msg = Tag(DistributionRequest, prot.src, prot.dst, path_index) put!(channel(prot.net, prot.src=>prot.controller; permit_forward=true), msg) - @yield timeout(prot.sim, rand(d)) # incoming message from the controller after a request has been served in_msg = querydelete!(mb, RequestCompletion, ❓) @@ -620,6 +627,7 @@ end (src, (_, path_id)) = in_msg prot.phys_graph.workloads[path_id] -= 1 end + @yield timeout(prot.sim, rand(d)) end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 86ffe331..7d7def2d 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -73,8 +73,6 @@ See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) node::Int """The object containing physical graph metadata for the network""" phys_graph::PhysicalGraph - """duration of a single full cycle of entanglement generation and swapping along a specific path""" - ticktock::Float64 end @resumable function (prot::Controller)() @@ -98,16 +96,14 @@ end end for i in 2:length(path)-1 - msg = Tag(SwapRequest, path[i], 1) + last = i == length(path) - 1 ? 1 : 0 + msg = Tag(SwapRequest, path[i], 1, last, path_ind, src) if prot.node == path[i] put!(mb, msg) else put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) end end - out_msg = Tag(RequestCompletion, path_ind) - put!(channel(prot.net, prot.node=>src;permit_forward=true), out_msg) - @yield timeout(prot.sim, prot.ticktock) end @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) From 12bf5f50b83bd6224095816f42f39e4179e547bf Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:53:19 -0400 Subject: [PATCH 04/16] update docstrings and changelog --- CHANGELOG.md | 6 ++++-- src/ProtocolZoo/ProtocolZoo.jl | 5 +++-- src/ProtocolZoo/controllers.jl | 2 +- src/ProtocolZoo/utils.jl | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 754b23e4..acaed58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ ## v0.6.0 - 2024-09-13 - Simplify one of the switch protocols to avoid dependence on GraphMatching.jl which does not install well on non-linux systems. Do not rely on the default `SimpleSwitchDiscreteProt` for the time being. -- Interactive examples for network controller -- Implement a network control protocol, a request generator and a request tracker along with new tags `EntanglementRequest`,`SwapRequest` and `DistributionRequest` +- Implement a network control protocol that is connection-oriented, centralized and non-distributed +- Implement protocols: request generator and request tracker for simulation with the above control protocol in an asynchronous way. +- Add `PhysicalGraph` struct for storing network metadata as the simulation evolves. +- New tags: `EntanglementRequest`,`SwapRequest`, `DistributionRequest` and `RequestCompletion` ## v0.5.0 - 2024-09-05 diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 4dc7ca60..871e2a4a 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -525,7 +525,8 @@ end """ $TYPEDEF -A protocol running at a node, listening for incoming entanglement generation and swap requests +A protocol running at a node, listening for incoming entanglement generation and swap requests and serving +them in an asynchronous way, without waiting for the completion of the instantiated entanglement generation or swapping processes to complete $TYPEDFIELDS """ @@ -581,7 +582,7 @@ include("utils.jl") """ $TYPEDEF -Protocol for the simulation of request traffic for a controller in a connection-oriented network for bipartite entanglement distribution. The requests are considered to be arriving according to the Poisson model with rate λ, hence the inter-arrival time is +Protocol for the simulation of request traffic for a controller in a connection-oriented network for bipartite entanglement distribution. The requests are considered to be generated according to the Poisson model with rate λ, hence the inter-arrival time is sampled from an exponential distribution. Physically, the request is generated at the source node(Alice) and is classically communicated to the node where the controller is located. Multiple `RequestGenerator`s can be instantiated for simulation with multiple user pairs in the same network. diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 7d7def2d..2f565178 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -58,7 +58,7 @@ end $TYPEDEF A network control protocol that is connection oriented, non-distributed and centralized. The controller is located at one of the nodes in the network from where it messages all -the other nodes when it receives an entanglement distribution request from the [`RequestGenerator`](@ref). +the other nodes' [`RequestTracker`](@ref) protocols when it receives [`DistributionRequest`](@ref) from the [`RequestGenerator`](@ref). $TYPEDFIELDS diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index d4bd7c9e..50061a53 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -2,7 +2,8 @@ $TYPEDEF A struct containing the physical graph metadata for a network. The latest workload data is only available -at the node where the [`RequestGenerator`](@ref) runs, but every node has access to a copy for referencing paths based on indices. +at the node where the [`RequestGenerator`](@ref) runs, but every node has access to a copy for referencing paths based on indices +passed through the `DistributionRequest` tag/message. $TYPEDFIELDS """ From 7026fd5207f616c78adcc52ad3335294fc9e1731 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Sat, 5 Oct 2024 15:24:39 -0400 Subject: [PATCH 05/16] add tests --- test/test_controlplane.jl | 63 +++++++++++++++++++++++++++++++++++++++ test/test_examples.jl | 4 +-- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 test/test_controlplane.jl diff --git a/test/test_controlplane.jl b/test/test_controlplane.jl new file mode 100644 index 00000000..0d4f2acf --- /dev/null +++ b/test/test_controlplane.jl @@ -0,0 +1,63 @@ +@testitem "Control Protocol" tags=[:controlplane] begin + +using QuantumSavory +using QuantumSavory.ProtocolZoo +using ConcurrentSim +using ResumableFunctions + +using Graphs + +if isinteractive() + using Logging + logger = ConsoleLogger(Logging.Warn; meta_formatter=(args...)->(:black,"","")) + global_logger(logger) + println("Logger set to debug") +end + +adjm = [0 1 0 0 1 0 0 0 + 1 0 1 0 0 0 0 0 + 0 1 0 1 0 1 0 0 + 0 0 1 0 0 0 1 1 + 1 0 0 0 0 1 0 1 + 0 0 1 0 1 0 1 0 + 0 0 0 1 0 1 0 1 + 0 0 0 1 1 0 1 0] +graph = SimpleGraph(adjm) + +regsize = 20 +net = RegisterNet(graph, [Register(regsize, CliffordRepr()) for i in 1:8]) +sim = get_time_tracker(net) + +# PhysicalGraph +phys_graph = PhysicalGraph(graph, 1, 8, regsize) + +# controller +controller = Controller(sim, net, 6, phys_graph) +@process controller() + +# RequestGenerator for the user pair (1,8) +req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +@process req_gen() + +# consumer +consumer = EntanglementConsumer(sim, net, 1, 8) +@process consumer() + +# entanglement and request trackers, cutoff protocol +for v in 1:8 + etracker = EntanglementTracker(sim, net, v) + rtracker = RequestTracker(sim, net, v) + cutoff = CutoffProt(sim, net, v) + @process etracker() + @process rtracker() + @process cutoff() +end + +run(sim, 1000) + +for i in 1:length(consumer.log) + @test consumer.log[i][2] ≈ 1.0 + @test consumer.log[i][3] ≈ 1.0 +end + +end \ No newline at end of file diff --git a/test/test_examples.jl b/test/test_examples.jl index f2d92234..260a9828 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -41,7 +41,7 @@ end end end -@safetestset "repeatergrid" begin +@testitem "Examples - repeatergrid" tags=[:examples] begin if get(ENV, "QUANTUMSAVORY_PLOT_TEST","")=="true" include("setup_plotting.jl") include("../examples/repeatergrid/1a_async_interactive_visualization.jl") @@ -49,7 +49,7 @@ end end end -@safetestset "controlplane" begin +@testitem "Examples - controlplane" tags=[:examples] begin if get(ENV, "QUANTUMSAVORY_PLOT_TEST","")=="true" include("../examples/controlplane/1a_cdd_interactive.jl") include("../examples/controlplane/2a_cnc_interactive.jl") From 0290be6ed5c35ed3bab976b2bc2bfb57493a43b0 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 7 Oct 2024 14:57:39 -0400 Subject: [PATCH 06/16] avoid using CliffordRepr() in tests --- test/test_controlplane.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_controlplane.jl b/test/test_controlplane.jl index 0d4f2acf..76c865a9 100644 --- a/test/test_controlplane.jl +++ b/test/test_controlplane.jl @@ -25,7 +25,7 @@ adjm = [0 1 0 0 1 0 0 0 graph = SimpleGraph(adjm) regsize = 20 -net = RegisterNet(graph, [Register(regsize, CliffordRepr()) for i in 1:8]) +net = RegisterNet(graph, [Register(regsize) for i in 1:8]) sim = get_time_tracker(net) # PhysicalGraph From a2d2e8c4ecad4712af7bc960987eadd27dbba969 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 10 Oct 2024 12:30:28 -0400 Subject: [PATCH 07/16] Send RequestCompletion from controller instead of RequestTracker --- src/ProtocolZoo/ProtocolZoo.jl | 16 +++------------- src/ProtocolZoo/controllers.jl | 4 +++- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 871e2a4a..f52d71ef 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -185,15 +185,9 @@ See also: [`SwapperProt`](@ref), [`EntanglementTracker`](@ref), [`EntanglementRe swapping_node::Int """The number of rounds the swapper should run for""" rounds::Int - """Whether the swap request is the final request on a path, hence signaling the request being served""" - last::Int - """path id for which the request was generated""" - id::Int - """the source node that generated the request""" - src::Int end Base.show(io::IO, tag::SwapRequest) = print(io, "Node $(tag.swapping_node) perform a swap") -Tag(tag::SwapRequest) = Tag(SwapRequest, tag.swapping_node, tag.rounds, tag.last, tag.id, tag.src) +Tag(tag::SwapRequest) = Tag(SwapRequest, tag.swapping_node, tag.rounds) """ $TYPEDEF @@ -556,18 +550,14 @@ end entangler = EntanglerProt(prot.sim, prot.net, prot.node, neighbor; rounds=rounds, randomize=true) @process entangler() else - msg = querydelete!(mb, requesttagsymbol, ❓, ❓, ❓, ❓, ❓) + msg = querydelete!(mb, requesttagsymbol, ❓, ❓) @debug "RequestTracker @$(prot.node): Received $msg" isnothing(msg) && continue workwasdone = true - (msg_src, (_, _, rounds, last, path_id, src)) = msg + (msg_src, (_, _, rounds)) = msg @debug "RequestTracker @$(prot.node): Performing a swap" swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) @process swapper() - if last == 1 - comp_msg = Tag(RequestCompletion, path_id) - put!(channel(prot.net, prot.node=>src; permit_forward=true), comp_msg) - end end end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 2f565178..650e5fcf 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -97,13 +97,15 @@ end for i in 2:length(path)-1 last = i == length(path) - 1 ? 1 : 0 - msg = Tag(SwapRequest, path[i], 1, last, path_ind, src) + msg = Tag(SwapRequest, path[i], 1) if prot.node == path[i] put!(mb, msg) else put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) end end + comp_msg = Tag(RequestCompletion, path_ind) + put!(channel(prot.net, prot.node=>src; permit_forward=true), comp_msg) end @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) From a63f28030e6fae2c9390791dd73efd6da3503d6b Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:19:17 -0400 Subject: [PATCH 08/16] Try to add a sequence diagram to the readme and fix a typo in wgl app --- examples/controlplane/2b_cnc_wglmakie.jl | 4 ++-- examples/controlplane/Readme.md | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/examples/controlplane/2b_cnc_wglmakie.jl b/examples/controlplane/2b_cnc_wglmakie.jl index 025de838..72db04cc 100644 --- a/examples/controlplane/2b_cnc_wglmakie.jl +++ b/examples/controlplane/2b_cnc_wglmakie.jl @@ -8,7 +8,7 @@ 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 phys_graph = PhysicalGraph(graph, 1, 8, regsize) -controller = Controller(sim, net, 6, phys_graph, 0.1) +controller = Controller(sim, net, 6, phys_graph) @process controller() req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) @@ -18,7 +18,7 @@ consumer = EntanglementConsumer(sim, net, 1, 8) @process consumer() for node in 1:7 - tracker = RequestTracker(sim, net, node, 0.1) + tracker = RequestTracker(sim, net, node) @process tracker() end diff --git a/examples/controlplane/Readme.md b/examples/controlplane/Readme.md index a4c47a37..9fd71bd8 100644 --- a/examples/controlplane/Readme.md +++ b/examples/controlplane/Readme.md @@ -6,4 +6,20 @@ Different control plane architectures with arbitrary network topologies can be s 2a. A simulation that generates an interactive visualization for a connection-oriented, non-distributed and centralized entanglement distribution network -2b. An interactive web app with the same simulation as 2a \ No newline at end of file +2b. An interactive web app with the same simulation as 2a + +The control protocol is illustrated at a high level by the sequence diagram below: + +```mermaid sequenceDiagram +Alice(Request Generator)->>+Controller: DistributionRequest(path_id) +Controller-->>+RequestTracker1: EntanglementRequest +Controller-->>+RequestTracker2: EntanglementRequest +Controller-->>+RequestTrackerN: EntanglementRequest +Controller-->>+RequestTracker1: SwapRequest +Controller-->>+RequestTracker2: SwapRequest +Controller-->>+RequestTrackerN: SwapRequest +Controller-->>-Alice(Request Generator): RequestCompletion(path_id) +``` + +The `RequestGenerator` (Alice) sends a message to the controller, requesting entanglement generation with an end node (Bob). +The message contains index of the path selected by Alice and the controller sends `EntanglementRequest`s to the nodes on the path followed by `SwapRequest`s \ No newline at end of file From 563dfebf8894e433b2303ccd4962dc747033fffd Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:21:24 -0400 Subject: [PATCH 09/16] fix typo in the sequence diagram --- examples/controlplane/Readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/controlplane/Readme.md b/examples/controlplane/Readme.md index 9fd71bd8..5356e174 100644 --- a/examples/controlplane/Readme.md +++ b/examples/controlplane/Readme.md @@ -10,7 +10,8 @@ Different control plane architectures with arbitrary network topologies can be s The control protocol is illustrated at a high level by the sequence diagram below: -```mermaid sequenceDiagram +```mermaid +sequenceDiagram Alice(Request Generator)->>+Controller: DistributionRequest(path_id) Controller-->>+RequestTracker1: EntanglementRequest Controller-->>+RequestTracker2: EntanglementRequest From 88ec49dbc889e1c15ed83e1c67ba5d6f162fc8c0 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 11:34:37 -0500 Subject: [PATCH 10/16] move path selection to controller and related modifications --- examples/controlplane/2a_cnc_interactive.jl | 5 +-- examples/controlplane/2b_cnc_wglmakie.jl | 5 +-- src/ProtocolZoo/ProtocolZoo.jl | 46 ++++----------------- src/ProtocolZoo/controllers.jl | 22 ++++++---- src/ProtocolZoo/utils.jl | 25 +++++++---- 5 files changed, 42 insertions(+), 61 deletions(-) diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl index 09ece2e3..43595067 100644 --- a/examples/controlplane/2a_cnc_interactive.jl +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -1,10 +1,9 @@ include("setup.jl") -phys_graph = PhysicalGraph(graph, 1, 8, regsize) -controller = Controller(sim, net, 6, phys_graph) +controller = Controller(sim, net, 6, zeros(8,8)) @process controller() -req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +req_gen = RequestGenerator(sim, net, 1, 8, 6) @process req_gen() consumer = EntanglementConsumer(sim, net, 1, 8) diff --git a/examples/controlplane/2b_cnc_wglmakie.jl b/examples/controlplane/2b_cnc_wglmakie.jl index 72db04cc..99d840f5 100644 --- a/examples/controlplane/2b_cnc_wglmakie.jl +++ b/examples/controlplane/2b_cnc_wglmakie.jl @@ -7,11 +7,10 @@ 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 -phys_graph = PhysicalGraph(graph, 1, 8, regsize) -controller = Controller(sim, net, 6, phys_graph) +controller = Controller(sim, net, 6, zeros(8,8)) @process controller() -req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +req_gen = RequestGenerator(sim, net, 1, 8, 6) @process req_gen() consumer = EntanglementConsumer(sim, net, 1, 8) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index f52d71ef..7eb70988 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -25,7 +25,7 @@ export # controllers NetController, Controller, #utils - PhysicalGraph, path_selection + PathMetadata, path_selection abstract type AbstractProtocol end @@ -203,27 +203,9 @@ See also: [`EntanglementRequest`](@ref), [`SwapRequest`] src::Int """The node with which entanglement is to be generated""" dst::Int - """Index of the path to be taken for the entanglement generation""" - path::Int end Base.show(io::IO, tag::DistributionRequest) = print(io, "Node $(tag.src) requesting entanglement with $(tag.dst)") -Tag(tag::DistributionRequest) = Tag(DistributionRequest, tag.src, tag.dst, tag.path) - - -""" -$TYPEDEF - -A message sent from the controller to a request generating node after its request has been served. - -$TYPEDFIELDS - -See also: [`EntanglementRequest`](@ref), [`SwapRequest`] -""" -@kwdef struct RequestCompletion - path_id::Int -end -Base.show(io::IO, tag::RequestCompletion) = print(io, "Request on path id $(tag.path_id) served") -Tag(tag::RequestCompletion) = Tag(RequestCompletion, tag.path_id) +Tag(tag::DistributionRequest) = Tag(DistributionRequest, tag.src, tag.dst) """ @@ -442,7 +424,7 @@ end error("`EntanglementTracker` on node $(prot.node) received a message $(msg) that it does not know how to handle (due to the absence of corresponding `EntanglementCounterpart` or `EntanglementHistory` or `EntanglementDelete` tags). This might have happened due to `CutoffProt` deleting qubits while swaps are happening. Make sure that the retention times in `CutoffProt` are sufficiently larger than the `agelimit` in `SwapperProt`. Otherwise, this is a bug in the protocol and should not happen -- please report an issue at QuantumSavory's repository.") end end - @debug "EntanglementTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + # @debug "EntanglementTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "EntanglementTracker @$(prot.node): Message wait ends at $(now(prot.sim))" end @@ -561,7 +543,7 @@ end end end end - @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + # @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "RequestTracker @$(prot.node): Message wait ends at $(now(prot.sim))" end @@ -589,35 +571,21 @@ $TYPEDFIELDS dst::Int """The node at which the controller is located""" controller::Int - """The object containing physical graph metadata for the network""" - phys_graph::PhysicalGraph """rate of arrival of requests/number of requests sent unit time""" λ::Int = 3 end -function RequestGenerator(sim, net, src, dst, controller, phys_graph; kwargs...) - return RequestGenerator(;sim, net, src, dst, controller, phys_graph, kwargs...) +function RequestGenerator(sim, net, src, dst, controller; kwargs...) + return RequestGenerator(;sim, net, src, dst, controller, kwargs...) end @resumable function (prot::RequestGenerator)() d = Exponential(inv(prot.λ)) # Parametrized with the scale which is inverse of the rate mb = messagebuffer(prot.net, prot.src) while true - path_index = path_selection(prot.phys_graph) - if isnothing(path_index) - prot.phys_graph.failures[] += 1 - @yield timeout(prot.sim, rand(d)) - continue - end - msg = Tag(DistributionRequest, prot.src, prot.dst, path_index) + msg = Tag(DistributionRequest, prot.src, prot.dst) put!(channel(prot.net, prot.src=>prot.controller; permit_forward=true), msg) - # incoming message from the controller after a request has been served - in_msg = querydelete!(mb, RequestCompletion, ❓) - if !isnothing(in_msg) - (src, (_, path_id)) = in_msg - prot.phys_graph.workloads[path_id] -= 1 - end @yield timeout(prot.sim, rand(d)) end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 650e5fcf..95b4e901 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -71,8 +71,8 @@ See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) net::RegisterNet """The node in the network where the control protocol is physically located, ideally a centrally located node""" node::Int - """The object containing physical graph metadata for the network""" - phys_graph::PhysicalGraph + """A matrix for the object containing physical graph metadata for the network""" + path_mat::Matrix{Union{Float64, PathMetadata}} end @resumable function (prot::Controller)() @@ -81,10 +81,18 @@ end workwasdone = true while workwasdone workwasdone = false - msg = querydelete!(mb, DistributionRequest, ❓, ❓, ❓) + msg = querydelete!(mb, DistributionRequest, ❓, ❓) if !isnothing(msg) - (msg_src, (_, src, dst, path_ind)) = msg - path = prot.phys_graph.paths[path_ind] + (msg_src, (_, src, dst)) = msg + if typeof(prot.path_mat[src, dst]) <: Number + prot.path_mat[src, dst] = PathMetadata(prot.net.graph, src, dst, Int(length(prot.net[1].staterefs)/2)) + end + path_id = path_selection(prot.sim, prot.path_mat[src, dst]) + path = prot.path_mat[src, dst].paths[path_id] + if isnothing(path_id) + @debug "Request failed, all paths reserved" + end + @debug "Running Entanglement Distribution on path $(path) @ $(now(prot.sim))" for i in 1:length(path)-1 msg = Tag(EntanglementRequest, path[i], path[i+1], 1) @@ -104,10 +112,8 @@ end put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) end end - comp_msg = Tag(RequestCompletion, path_ind) - put!(channel(prot.net, prot.node=>src; permit_forward=true), comp_msg) end - @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + # @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "Controller @$(prot.node): Message wait ends at $(now(prot.sim))" end diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index 50061a53..2366daeb 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -7,7 +7,7 @@ passed through the `DistributionRequest` tag/message. $TYPEDFIELDS """ -@kwdef struct PhysicalGraph +@kwdef struct PathMetadata """The vector of paths between the user pair""" paths::Vector{Vector{Int}} """The vector containing the workload information of a path""" @@ -18,24 +18,33 @@ $TYPEDFIELDS failures::Ref{Int} end -function PhysicalGraph(graph::SimpleGraph{Int64}, src::Int, dst::Int, caps::Union{Vector{Int}, Int}; failures=Ref{Int}(0)) +function PathMetadata(graph::SimpleGraph{Int64}, src::Int, dst::Int, caps::Union{Vector{Int}, Int}; failures=Ref{Int}(0)) paths = sort(collect(all_simple_paths(graph, src, dst)); by = x->length(x)) workloads = zeros(length(paths)) - PhysicalGraph(paths, workloads, caps, failures) + PathMetadata(paths, workloads, caps, failures) end """ A simple path selection algorithm for connection oriented networks. """ -function path_selection(phys_graph::PhysicalGraph) - for i in 1:length(phys_graph.paths) - capacity = isa(phys_graph.capacity, Number) ? phys_graph.capacity : phys_graph.capacity[i] - if phys_graph.workloads[i] Date: Mon, 4 Nov 2024 14:10:25 -0500 Subject: [PATCH 11/16] update controlplane test --- test/test_controlplane.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/test_controlplane.jl b/test/test_controlplane.jl index 76c865a9..db61b572 100644 --- a/test/test_controlplane.jl +++ b/test/test_controlplane.jl @@ -28,15 +28,12 @@ regsize = 20 net = RegisterNet(graph, [Register(regsize) for i in 1:8]) sim = get_time_tracker(net) -# PhysicalGraph -phys_graph = PhysicalGraph(graph, 1, 8, regsize) - # controller -controller = Controller(sim, net, 6, phys_graph) +controller = Controller(sim, net, 6, zeros(8,8)) @process controller() # RequestGenerator for the user pair (1,8) -req_gen = RequestGenerator(sim, net, 1, 8, 6, phys_graph) +req_gen = RequestGenerator(sim, net, 1, 8, 6) @process req_gen() # consumer From 3b6e721afe5128ec1536ea7e9ed0b27cc94a631c Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 14:58:21 -0500 Subject: [PATCH 12/16] update ProtocolZoo/ProtocolZoo.jl --- src/ProtocolZoo/ProtocolZoo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 7eb70988..cf25996b 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -19,7 +19,7 @@ export # protocols EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, CutoffProt, RequestTracker, RequestGenerator, # tags - EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, EntanglementRequest, SwapRequest, DistributionRequest, RequestCompletion, + EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, EntanglementRequest, SwapRequest, DistributionRequest, # from Switches SimpleSwitchDiscreteProt, SwitchRequest, # controllers From 6c425fbd098f54d073c79f3a373ef0ca98229df1 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 15:30:33 -0500 Subject: [PATCH 13/16] fix for congestionchain example --- examples/congestionchain/1_visualization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/congestionchain/1_visualization.jl b/examples/congestionchain/1_visualization.jl index c1df5d8d..2e1790dd 100644 --- a/examples/congestionchain/1_visualization.jl +++ b/examples/congestionchain/1_visualization.jl @@ -44,7 +44,7 @@ scatter!(ax_fidZZ,ts,fidZZ,label="ZZ",color=(c2,0.1)) display(fig) -step_ts = range(0, 1000, step=0.1) +step_ts = range(0.0, 1000.0, step=0.1) record(fig, "congestionchain.mp4", step_ts; framerate=50, visible=true) do t run(sim, t) From c3098065032790114279009787b631fc6da5f7b5 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 5 Nov 2024 23:29:11 -0500 Subject: [PATCH 14/16] Add a connection-less and centralized protocol --- examples/controlplane/3a_cl_interactive.jl | 45 +++++++++ examples/controlplane/3b_cl_wglmakie.jl | 112 +++++++++++++++++++++ src/ProtocolZoo/ProtocolZoo.jl | 6 +- src/ProtocolZoo/controllers.jl | 50 ++++++++- 4 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 examples/controlplane/3a_cl_interactive.jl create mode 100644 examples/controlplane/3b_cl_wglmakie.jl diff --git a/examples/controlplane/3a_cl_interactive.jl b/examples/controlplane/3a_cl_interactive.jl new file mode 100644 index 00000000..b2f3779f --- /dev/null +++ b/examples/controlplane/3a_cl_interactive.jl @@ -0,0 +1,45 @@ +include("setup.jl") + +for (;src, dst) in edges(net) + entangler = EntanglerProt(sim, net, src, dst; rounds=-1, randomize=true) + @process entangler() +end + +controller = CLController(sim, net, 6) +@process controller() + +req_gen = RequestGenerator(sim, net, 1, 8, 6) +@process req_gen() + +consumer = EntanglementConsumer(sim, net, 1, 8) +@process consumer() + +for node in 1:7 + tracker = RequestTracker(sim, net, node) + @process tracker() +end + +for v in 1:8 + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +for v in 1:8 + c_prot = CutoffProt(sim, net, v) + @process c_prot() +end + +sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer) + +step_ts = range(0.0, 1000.0, step=0.1) +record(fig, "sim.mp4", step_ts; framerate=10, visible=true) do t + run(sim, t) + notify.((obs,entlog)) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) +end \ No newline at end of file diff --git a/examples/controlplane/3b_cl_wglmakie.jl b/examples/controlplane/3b_cl_wglmakie.jl new file mode 100644 index 00000000..dead673d --- /dev/null +++ b/examples/controlplane/3b_cl_wglmakie.jl @@ -0,0 +1,112 @@ +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 + +for (;src, dst) in edges(net) + entangler = EntanglerProt(sim, net, src, dst; rounds=-1, randomize=true) + @process entangler() +end + +controller = CLController(sim, net, 6) +@process controller() + +req_gen = RequestGenerator(sim, net, 1, 8, 6) +@process req_gen() + +consumer = EntanglementConsumer(sim, net, 1, 8) +@process consumer() + +for node in 1:7 + tracker = RequestTracker(sim, net, node) + @process tracker() +end + +for v in 1:8 + tracker = EntanglementTracker(sim, net, v) + @process tracker() +end + +for v in 1:8 + c_prot = CutoffProt(sim, net, v) + @process c_prot() +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!(sim, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + step_ts = range(0, 1000, step=0.1) + for t in step_ts + run(sim, t) + notify.((obs,entlog)) + ylims!(entlogaxis, (-1.04,1.04)) + xlims!(entlogaxis, max(0,t-50), 1+t) + ylims!(fid_axis, (0, 1.04)) + xlims!(fid_axis, max(0, t-50), 1+t) + autolimits!(histaxis) + ylims!(num_epr_axis, (0, 4)) + xlims!(num_epr_axis, max(0, t-50), 1+t) + end + running[] = nothing +end + +# +landing = Bonito.App() do + + sim, net, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, fig = prepare_vis(consumer) + + running = Observable{Any}(false) + fig[5,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!( + sim, obs, entlog, entlogaxis, fid_axis, histaxis, num_epr_axis, running) + end + end + end + + + content = md""" + Pick simulation settings and hit run (see below for technical details). + + $(fig.scene) + + # Connection-Less, Non-Distributed and Centralized Control Plane for Entanglement Distribution + + The above simulation visualizes entanglement distribution between Alice and Bob on an arbitrary network topology + given by the adjacency matrix of the graph. The control plane architecture used for this simulation is connection-less, + non-distributed and centralized. The node representing Alice is the node on the top left and the bottom right is Bob. + The actual connectivity of the physical graph isn't fully captured by the visualization above as we use edges only to + show the virtual graph. + + [See and modify the code for this simulation on github.](https://github.com/QuantumSavory/QuantumSavory.jl/tree/master/examples/controlplane/3b_cl_wglmakie.jl) + """ + 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, "CNC_PORT", "8888")) +interface = get(ENV, "CNC_IP", "127.0.0.1") +proxy_url = get(ENV, "CNC_PROXY", "") +server = Bonito.Server(interface, port; proxy_url); +Bonito.HTTPServer.start(server) +Bonito.route!(server, "/" => landing); + +## + +wait(server) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index cf25996b..7d1c749b 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -23,7 +23,7 @@ export # from Switches SimpleSwitchDiscreteProt, SwitchRequest, # controllers - NetController, Controller, + NetController, Controller, CLController, #utils PathMetadata, path_selection @@ -424,7 +424,7 @@ end error("`EntanglementTracker` on node $(prot.node) received a message $(msg) that it does not know how to handle (due to the absence of corresponding `EntanglementCounterpart` or `EntanglementHistory` or `EntanglementDelete` tags). This might have happened due to `CutoffProt` deleting qubits while swaps are happening. Make sure that the retention times in `CutoffProt` are sufficiently larger than the `agelimit` in `SwapperProt`. Otherwise, this is a bug in the protocol and should not happen -- please report an issue at QuantumSavory's repository.") end end - # @debug "EntanglementTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @debug "EntanglementTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "EntanglementTracker @$(prot.node): Message wait ends at $(now(prot.sim))" end @@ -543,7 +543,7 @@ end end end end - # @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @debug "RequestTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "RequestTracker @$(prot.node): Message wait ends at $(now(prot.sim))" end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 95b4e901..260b078a 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -104,7 +104,6 @@ end end for i in 2:length(path)-1 - last = i == length(path) - 1 ? 1 : 0 msg = Tag(SwapRequest, path[i], 1) if prot.node == path[i] put!(mb, msg) @@ -113,7 +112,54 @@ end end end end - # @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" + @yield wait(mb) + @debug "Controller @$(prot.node): Message wait ends at $(now(prot.sim))" + end + end +end + + +""" +$TYPEDEF + +A network control protocol that is connection less, non-distributed and centralized. The controller is located at one of the nodes in the network from where it messages all +the other nodes' [`RequestTracker`](@ref) protocols when it receives [`DistributionRequest`](@ref) from the [`RequestGenerator`](@ref). + +$TYPEDFIELDS + +See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) +""" +@kwdef struct CLController <: AbstractProtocol + """Time-and-schedule-tracking instance from `ConcurrentSim`""" + sim::Simulation + """A network graph of registers""" + net::RegisterNet + """The node in the network where the control protocol is physically located, ideally a centrally located node""" + node::Int +end + +@resumable function (prot::CLController)() + mb = messagebuffer(prot.net, prot.node) + while true + workwasdone = true + while workwasdone + workwasdone = false + msg = querydelete!(mb, DistributionRequest, ❓, ❓) + if !isnothing(msg) + (msg_src, (_, req_src, req_dst)) = msg + for v in vertices(prot.net) + if v != req_src && v != req_dst + msg = Tag(SwapRequest, v, 1) + if prot.node == v + put!(mb, msg) + else + put!(channel(prot.net, prot.node=>msg[2];permit_forward=true), msg) + end + end + end + end + @debug "Controller @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)" @yield wait(mb) @debug "Controller @$(prot.node): Message wait ends at $(now(prot.sim))" end From 66ae3a470fae03260ae6eb682b69a20bb7e29272 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 5 Nov 2024 23:33:13 -0500 Subject: [PATCH 15/16] add the new example to tests --- test/test_examples.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_examples.jl b/test/test_examples.jl index 260a9828..53bc12d3 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -53,6 +53,7 @@ end if get(ENV, "QUANTUMSAVORY_PLOT_TEST","")=="true" include("../examples/controlplane/1a_cdd_interactive.jl") include("../examples/controlplane/2a_cnc_interactive.jl") + include("../examples/controlplane/3a_cl_interactive.jl") end end From f344058ece4eec2dc957783f0552d7c7b410f95d Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Wed, 6 Nov 2024 11:22:38 -0500 Subject: [PATCH 16/16] add new protocol to tests --- test/test_controlplane.jl | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/test_controlplane.jl b/test/test_controlplane.jl index db61b572..6c43bd91 100644 --- a/test/test_controlplane.jl +++ b/test/test_controlplane.jl @@ -1,4 +1,4 @@ -@testitem "Control Protocol" tags=[:controlplane] begin +@testitem "Control Protocols" tags=[:controlplane] begin using QuantumSavory using QuantumSavory.ProtocolZoo @@ -57,4 +57,38 @@ for i in 1:length(consumer.log) @test consumer.log[i][3] ≈ 1.0 end +#### Connection less controller + +net = RegisterNet(graph, [Register(regsize) for i in 1:8]) +sim = get_time_tracker(net) + +# controller +controller = CLController(sim, net, 6) +@process controller() + +# RequestGenerator for the user pair (1,8) +req_gen = RequestGenerator(sim, net, 1, 8, 6) +@process req_gen() + +# consumer +consumer = EntanglementConsumer(sim, net, 1, 8) +@process consumer() + +# entanglement and request trackers, cutoff protocol +for v in 1:8 + etracker = EntanglementTracker(sim, net, v) + rtracker = RequestTracker(sim, net, v) + cutoff = CutoffProt(sim, net, v) + @process etracker() + @process rtracker() + @process cutoff() +end + +run(sim, 1000) + +for i in 1:length(consumer.log) + @test consumer.log[i][2] ≈ 1.0 + @test consumer.log[i][3] ≈ 1.0 +end + end \ No newline at end of file