From 9816e2046417077bca90fc0d594eb71e92309841 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 23 Sep 2024 15:56:51 -0400 Subject: [PATCH 01/26] 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/26] 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 7a8d4c497d1e69afa07eb0d213aad28f8aa04cbe Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:28:23 -0400 Subject: [PATCH 03/26] 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 b9b726a4b230a36d32af2760cc5d105d503809aa Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:53:19 -0400 Subject: [PATCH 04/26] 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 98454b055bfe6935ebce63932cfe53c2975dc2ac Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Sat, 5 Oct 2024 15:24:39 -0400 Subject: [PATCH 05/26] 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 057541c634497e86f6f7bbdd981f1eac0fb5884f Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 7 Oct 2024 14:57:39 -0400 Subject: [PATCH 06/26] 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 70dc3345801b7ecfdc6014e623a81aac84708d50 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 10 Oct 2024 12:30:28 -0400 Subject: [PATCH 07/26] 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 20e501eefd662e6d07c6b6af0ca6de2712390f69 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:19:17 -0400 Subject: [PATCH 08/26] 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 0fe7fc775844325786a479ccb06663cc6e4d21fd Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:21:24 -0400 Subject: [PATCH 09/26] 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 46f5d3f0dc81a94ffbb8a2c9f14a41e1db0557d0 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 11:34:37 -0500 Subject: [PATCH 10/26] 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/26] 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 49ab5e6ea8647d396bdfb989bab742d919d96cc8 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 14:58:21 -0500 Subject: [PATCH 12/26] 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 1a7bddbf63dd27eabefc70bb80999c56d7b2666a Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 15:30:33 -0500 Subject: [PATCH 13/26] 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 859292ef171dfd819d0426450e87f74e8c2e0c74 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 5 Nov 2024 23:29:11 -0500 Subject: [PATCH 14/26] 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 c965b4c38c3a00b3fe124841756a8da0e28c5522 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 5 Nov 2024 23:33:13 -0500 Subject: [PATCH 15/26] 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 a1fa6bed2c9e175f5b00d54e572653aa346a154a Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Wed, 6 Nov 2024 11:22:38 -0500 Subject: [PATCH 16/26] 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 From 656679f184f82f733c3907d81b5e34ff02a099f4 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt <46929125+ba2tro@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:29:42 -0400 Subject: [PATCH 17/26] =?UTF-8?q?Make=20classical=20and=20quantum=20channe?= =?UTF-8?q?l=20delays=20customizable=20when=20initializ=E2=80=A6=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: Stefan Krastanov Co-authored-by: Stefan Krastanov --- CHANGELOG.md | 1 + src/networks.jl | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acaed58c..4a2fba7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - 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` +- Add `classical_delay` and `quantum_delay` as keyword arguments to the `RegisterNet` constructor to set a default global network edge latency. ## v0.5.0 - 2024-09-05 diff --git a/src/networks.jl b/src/networks.jl index a10dc369..620030b8 100644 --- a/src/networks.jl +++ b/src/networks.jl @@ -13,7 +13,7 @@ struct RegisterNet reverse_lookup::IdDict{Register,Int} end -function RegisterNet(graph::SimpleGraph, registers, vertex_metadata, edge_metadata, directed_edge_metadata) +function RegisterNet(graph::SimpleGraph, registers, vertex_metadata, edge_metadata, directed_edge_metadata; classical_delay=0, quantum_delay=0) env = get_time_tracker(registers[1]) all_are_at_zero = all(iszero(ConcurrentSim.now(get_time_tracker(r))) && isempty(get_time_tracker(r).heap) && isnothing(get_time_tracker(r).active_proc) for r in registers) @@ -42,10 +42,10 @@ function RegisterNet(graph::SimpleGraph, registers, vertex_metadata, edge_metada end for (;src,dst) in edges(graph) - cchannels[src=>dst] = DelayQueue{Tag}(env, 0) - qchannels[src=>dst] = QuantumChannel(env, 0) - cchannels[dst=>src] = DelayQueue{Tag}(env, 0) - qchannels[dst=>src] = QuantumChannel(env, 0) + cchannels[src=>dst] = DelayQueue{Tag}(env, classical_delay) + qchannels[src=>dst] = QuantumChannel(env, quantum_delay) + cchannels[dst=>src] = DelayQueue{Tag}(env, classical_delay) + qchannels[dst=>src] = QuantumChannel(env, quantum_delay) end for (v,r) in zip(vertices(graph), registers) channels = [(;src=w, channel=cchannels[w=>v]) for w in neighbors(graph, v)] @@ -92,9 +92,9 @@ julia> neighbors(net, 1) # from Graphs.jl 3 ``` """ -function RegisterNet(graph::SimpleGraph, registers) +function RegisterNet(graph::SimpleGraph, registers; classical_delay=0, quantum_delay=0) size(graph, 1) == length(registers) || ArgumentError(lazy"You attempted to construct a `RegisterNet` with a graph of $(size(graph, 1)) vertices but provided a list of $(length(registers)) `Registers`. These two numbers have to match.") - RegisterNet(graph, registers, empty_vmd(length(registers)), empty_emd(), empty_demd()) + RegisterNet(graph, registers, empty_vmd(length(registers)), empty_emd(), empty_demd(); classical_delay, quantum_delay) end empty_vmd(n) = [Dict{Symbol,Any}() for _ in 1:n] @@ -113,9 +113,9 @@ julia> neighbors(net,2) # from Graphs.jl 3 ``` """ -function RegisterNet(registers::Vector{Register}) +function RegisterNet(registers::Vector{Register}; classical_delay=0, quantum_delay=0) graph = grid([length(registers)]) - RegisterNet(graph, registers) + RegisterNet(graph, registers; classical_delay, quantum_delay) end function add_register!(net::RegisterNet, r::Register) From a3842db79a7b5ea4e40b42eab6aa3a99bc349306 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Wed, 13 Nov 2024 22:41:11 -0500 Subject: [PATCH 18/26] a more general predicate and choosing function --- src/ProtocolZoo/ProtocolZoo.jl | 21 ++++++++++++++++----- src/ProtocolZoo/controllers.jl | 14 +++++++------- src/ProtocolZoo/utils.jl | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 7d1c749b..6483ec69 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -185,9 +185,13 @@ See also: [`SwapperProt`](@ref), [`EntanglementTracker`](@ref), [`EntanglementRe swapping_node::Int """The number of rounds the swapper should run for""" rounds::Int + """source node for the `DistributionRequest`""" + src::Int + """destination node for the `DistributionRequest`""" + dst::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.src, tag.dst) """ $TYPEDEF @@ -532,14 +536,21 @@ 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)) = msg + (msg_src, (_, _, rounds, req_src, req_dst)) = 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 req_src == 0 + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) + @process swapper() + else + ###instantiate predicate + ### instantiate choosing function + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) + @process swapper() + end end end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 260b078a..a62a1a10 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -83,12 +83,12 @@ end workwasdone = false msg = querydelete!(mb, DistributionRequest, ❓, ❓) if !isnothing(msg) - (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)) + (msg_src, (_, req_src, req_dst)) = msg + if typeof(prot.path_mat[req_src, req_dst]) <: Number + prot.path_mat[req_src, req_dst] = PathMetadata(prot.net.graph, req_src, req_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] + path_id = path_selection(prot.sim, prot.path_mat[req_src, req_dst]) + path = prot.path_mat[req_src, req_dst].paths[path_id] if isnothing(path_id) @debug "Request failed, all paths reserved" end @@ -104,7 +104,7 @@ end end for i in 2:length(path)-1 - msg = Tag(SwapRequest, path[i], 1) + msg = Tag(SwapRequest, path[i], 1, 0, 0) if prot.node == path[i] put!(mb, msg) else @@ -150,7 +150,7 @@ end (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) + msg = Tag(SwapRequest, v, 1, req_src, req_dst) if prot.node == v put!(mb, msg) else diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index 2366daeb..2d75d149 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -70,4 +70,20 @@ function findswapablequbits(net, node, pred_low, pred_high, choose_low, choose_h 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 + +""" +A generic predicate function for any arbitrary topology and entanglement flow +""" +function predicate(graph, src, dst, node; low=true) + d_src = length(a_star(graph, src, node)) + d_dst = length(a_star(graph, dst, node)) + return low ? d_src <= d_dst : d_src > d_dst +end + +""" +A generic choosing function for any arbitrary topology and entanglement flow +""" +function choose(graph, target_node, arr) + return argmin(length.([a_star(graph, node, target_node) for node in arr])) end \ No newline at end of file From bed4af2589e3bb949a15380f0dbbace10ea7f15f Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Fri, 22 Nov 2024 16:24:25 -0500 Subject: [PATCH 19/26] Improve swap predicate with memoization and add condition for swaps only with closer nodes --- Project.toml | 1 + examples/controlplane/3a_cl_interactive.jl | 2 +- src/ProtocolZoo/ProtocolZoo.jl | 23 +++++++++++-------- src/ProtocolZoo/controllers.jl | 4 ++-- src/ProtocolZoo/utils.jl | 26 +++++++++++++++------- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Project.toml b/Project.toml index 2789bda5..75bf3392 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Memoize = "c03570c3-d221-55d1-a50c-7939bbd78826" NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/examples/controlplane/3a_cl_interactive.jl b/examples/controlplane/3a_cl_interactive.jl index b2f3779f..18f8171f 100644 --- a/examples/controlplane/3a_cl_interactive.jl +++ b/examples/controlplane/3a_cl_interactive.jl @@ -32,7 +32,7 @@ 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 +record(fig, "sim2.mp4", step_ts; framerate=10, visible=true) do t run(sim, t) notify.((obs,entlog)) ylims!(entlogaxis, (-1.04,1.04)) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 6483ec69..07af87b9 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -14,6 +14,7 @@ import ResumableFunctions using ResumableFunctions: @resumable import SumTypes using Graphs +using Memoize export # protocols @@ -25,7 +26,7 @@ export # controllers NetController, Controller, CLController, #utils - PathMetadata, path_selection + PathMetadata, path_selection, swap_predicate, swap_choose abstract type AbstractProtocol end @@ -189,6 +190,8 @@ See also: [`SwapperProt`](@ref), [`EntanglementTracker`](@ref), [`EntanglementRe src::Int """destination node for the `DistributionRequest`""" dst::Int + """whether the request is from a connection-oriented(0) or connection-less controller(1)""" + conn::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.src, tag.dst) @@ -536,19 +539,21 @@ 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, req_src, req_dst)) = msg + (msg_src, (_, _, rounds, req_src, req_dst, conn)) = msg @debug "RequestTracker @$(prot.node): Performing a swap" - if req_src == 0 - swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) + if conn == 0 # connection-oriented + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = req_src, nodeH = req_dst, rounds=rounds) @process swapper() - else - ###instantiate predicate - ### instantiate choosing function - swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = <(prot.node), nodeH = >(prot.node), chooseL=argmin, chooseH=argmax, rounds=rounds) + else # connection-less + pred_low = swap_predicate(prot.net.graph, req_src, req_dst, prot.node) + pred_high = swap_predicate(prot.net.graph, req_src, req_dst, prot.node; low=false) + choose_low = swap_choose(prot.net.graph, req_src) + choose_high = swap_choose(prot.net.graph, req_dst) + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = pred_low, nodeH = pred_high, chooseL=choose_low, chooseH=choose_high, rounds=rounds) @process swapper() end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index a62a1a10..b37aeb40 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -104,7 +104,7 @@ end end for i in 2:length(path)-1 - msg = Tag(SwapRequest, path[i], 1, 0, 0) + msg = Tag(SwapRequest, path[i], 1, path[i-1], path[i+1], 0) if prot.node == path[i] put!(mb, msg) else @@ -150,7 +150,7 @@ end (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, req_src, req_dst) + msg = Tag(SwapRequest, v, 1, req_src, req_dst, 1) if prot.node == v put!(mb, msg) else diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index 2d75d149..d1bac7b7 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -73,17 +73,27 @@ function findswapablequbits(net, node, pred_low, pred_high, choose_low, choose_h end """ -A generic predicate function for any arbitrary topology and entanglement flow +A generic predicate function for any arbitrary topology and entanglement flow used by [`SwapperProt`](@ref) and `findswapablequbits` +Returns a predicate function indicating whether a node is closer to Alice(source) or Bob(destination) """ -function predicate(graph, src, dst, node; low=true) - d_src = length(a_star(graph, src, node)) - d_dst = length(a_star(graph, dst, node)) - return low ? d_src <= d_dst : d_src > d_dst +function swap_predicate(graph, src, dst, curr_node; low=true) + return node -> begin + d_src = get_distance(graph, src, node) + d_dst = get_distance(graph, dst, node) + is_closer = low ? d_src < get_distance(graph, src, curr_node) : d_dst < get_distance(graph, dst, curr_node) + res = d_src <= d_dst + low ? res & is_closer : !res & is_closer + end end """ -A generic choosing function for any arbitrary topology and entanglement flow +A generic choosing function for any arbitrary topology and entanglement flow. Returns the index of the node closest to `target_node` +for performing a swap. """ -function choose(graph, target_node, arr) - return argmin(length.([a_star(graph, node, target_node) for node in arr])) +function swap_choose(graph, target_node) + return arr -> argmin([get_distance(graph, target_node, node) for node in arr]) +end + +@memoize function get_distance(graph, nodeA, nodeB) + return length(a_star(graph, nodeA, nodeB)) end \ No newline at end of file From 37ffab2b611255f51d5cf944d784a91be4f37cad Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 25 Nov 2024 14:54:28 -0500 Subject: [PATCH 20/26] Add compat entry for Memoize.jl --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index c4e9afa8..c18fd1fe 100644 --- a/Project.toml +++ b/Project.toml @@ -41,6 +41,7 @@ Graphs = "1.9" IterTools = "1.4.0" LinearAlgebra = "1" Makie = "0.20, 0.21" +Memoize = "0.4.4" NetworkLayout = "0.4.4" PrecompileTools = "1" Printf = "1" From 65a7d9f02b7bb093b9ecfa5bc7d79fe8a947bed3 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 5 Dec 2024 18:42:37 -0500 Subject: [PATCH 21/26] infinite rounds for swapper in connection-less controller and separate request generators for the two controllers --- examples/controlplane/2a_cnc_interactive.jl | 2 +- examples/controlplane/3a_cl_interactive.jl | 2 +- src/ProtocolZoo/ProtocolZoo.jl | 51 ++++++++++++++++++--- src/ProtocolZoo/controllers.jl | 12 ++--- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl index 43595067..ee4dfe83 100644 --- a/examples/controlplane/2a_cnc_interactive.jl +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -3,7 +3,7 @@ include("setup.jl") controller = Controller(sim, net, 6, zeros(8,8)) @process controller() -req_gen = RequestGenerator(sim, net, 1, 8, 6) +req_gen = RequestGeneratorCO(sim, net, 1, 8, 6) @process req_gen() consumer = EntanglementConsumer(sim, net, 1, 8) diff --git a/examples/controlplane/3a_cl_interactive.jl b/examples/controlplane/3a_cl_interactive.jl index 18f8171f..30b90d96 100644 --- a/examples/controlplane/3a_cl_interactive.jl +++ b/examples/controlplane/3a_cl_interactive.jl @@ -8,7 +8,7 @@ end controller = CLController(sim, net, 6) @process controller() -req_gen = RequestGenerator(sim, net, 1, 8, 6) +req_gen = RequestGeneratorCL(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 07af87b9..d6105c6a 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -18,7 +18,7 @@ using Memoize export # protocols - EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, CutoffProt, RequestTracker, RequestGenerator, + EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer, CutoffProt, RequestTracker, RequestGeneratorCO, RequestGeneratorCL, # tags EntanglementCounterpart, EntanglementHistory, EntanglementUpdateX, EntanglementUpdateZ, EntanglementRequest, SwapRequest, DistributionRequest, # from Switches @@ -210,9 +210,11 @@ See also: [`EntanglementRequest`](@ref), [`SwapRequest`] src::Int """The node with which entanglement is to be generated""" dst::Int + """The number of rounds of swaps requested""" + rounds::Int = 1 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(tag::DistributionRequest) = Tag(DistributionRequest, tag.src, tag.dst, tag.rounds) """ @@ -576,7 +578,7 @@ 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? +@kwdef struct RequestGeneratorCO <: AbstractProtocol """time-and-schedule-tracking instance from `ConcurrentSim`""" sim::Simulation """a network graph of registers""" @@ -591,15 +593,15 @@ $TYPEDFIELDS λ::Int = 3 end -function RequestGenerator(sim, net, src, dst, controller; kwargs...) - return RequestGenerator(;sim, net, src, dst, controller, kwargs...) +function RequestGeneratorCO(sim, net, src, dst, controller; kwargs...) + return RequestGeneratorCO(;sim, net, src, dst, controller, kwargs...) end -@resumable function (prot::RequestGenerator)() +@resumable function (prot::RequestGeneratorCO)() d = Exponential(inv(prot.λ)) # Parametrized with the scale which is inverse of the rate mb = messagebuffer(prot.net, prot.src) while true - msg = Tag(DistributionRequest, prot.src, prot.dst) + msg = Tag(DistributionRequest, prot.src, prot.dst, 1) put!(channel(prot.net, prot.src=>prot.controller; permit_forward=true), msg) @yield timeout(prot.sim, rand(d)) @@ -607,6 +609,41 @@ end end +""" +$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 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. + +$TYPEDFIELDS +""" +@kwdef struct RequestGeneratorCL <: AbstractProtocol + """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 number of rounds of swaps to be requested, -1 for infinite""" + rounds::Int = -1 +end + +function RequestGeneratorCL(sim, net, src, dst, controller; kwargs...) + return RequestGeneratorCL(;sim, net, src, dst, controller, kwargs...) +end + +@resumable function (prot::RequestGeneratorCL)() + mb = messagebuffer(prot.net, prot.src) + msg = Tag(DistributionRequest, prot.src, prot.dst, prot.rounds) + put!(channel(prot.net, prot.src=>prot.controller; permit_forward=true), msg) +end + + include("cutoff.jl") include("swapping.jl") include("controllers.jl") diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index b37aeb40..9945c15d 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -81,9 +81,9 @@ end workwasdone = true while workwasdone workwasdone = false - msg = querydelete!(mb, DistributionRequest, ❓, ❓) + msg = querydelete!(mb, DistributionRequest, ❓, ❓, ❓) if !isnothing(msg) - (msg_src, (_, req_src, req_dst)) = msg + (msg_src, (_, req_src, req_dst, rounds)) = msg if typeof(prot.path_mat[req_src, req_dst]) <: Number prot.path_mat[req_src, req_dst] = PathMetadata(prot.net.graph, req_src, req_dst, Int(length(prot.net[1].staterefs)/2)) end @@ -104,7 +104,7 @@ end end for i in 2:length(path)-1 - msg = Tag(SwapRequest, path[i], 1, path[i-1], path[i+1], 0) + msg = Tag(SwapRequest, path[i], rounds, path[i-1], path[i+1], 0) if prot.node == path[i] put!(mb, msg) else @@ -145,12 +145,12 @@ end workwasdone = true while workwasdone workwasdone = false - msg = querydelete!(mb, DistributionRequest, ❓, ❓) + msg = querydelete!(mb, DistributionRequest, ❓, ❓, ❓) if !isnothing(msg) - (msg_src, (_, req_src, req_dst)) = msg + (msg_src, (_, req_src, req_dst, rounds)) = msg for v in vertices(prot.net) if v != req_src && v != req_dst - msg = Tag(SwapRequest, v, 1, req_src, req_dst, 1) + msg = Tag(SwapRequest, v, rounds, req_src, req_dst, 1) if prot.node == v put!(mb, msg) else From 3dbd8aa0187cf6a876fc98660b08ca3fcb56f062 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Fri, 6 Dec 2024 11:37:35 -0500 Subject: [PATCH 22/26] update test_controlplane.jl --- test/test_controlplane.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_controlplane.jl b/test/test_controlplane.jl index 6c43bd91..2b6bc0c8 100644 --- a/test/test_controlplane.jl +++ b/test/test_controlplane.jl @@ -33,7 +33,7 @@ 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) +req_gen = RequestGeneratorCO(sim, net, 1, 8, 6) @process req_gen() # consumer @@ -67,7 +67,7 @@ controller = CLController(sim, net, 6) @process controller() # RequestGenerator for the user pair (1,8) -req_gen = RequestGenerator(sim, net, 1, 8, 6) +req_gen = RequestGeneratorCL(sim, net, 1, 8, 6) @process req_gen() # consumer From b61b6b6b5504f21b0c8e809e1bb65f70859a1f76 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Fri, 6 Dec 2024 14:07:41 -0500 Subject: [PATCH 23/26] update docstrings --- src/ProtocolZoo/controllers.jl | 8 ++++---- src/ProtocolZoo/utils.jl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 9945c15d..ebd86ac9 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -58,11 +58,11 @@ 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' [`RequestTracker`](@ref) protocols when it receives [`DistributionRequest`](@ref) from the [`RequestGenerator`](@ref). +the other nodes' [`RequestTracker`](@ref) protocols when it receives [`DistributionRequest`](@ref) from [`RequestGeneratorCO`](@ref). $TYPEDFIELDS -See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) +See also [`RequestGeneratorCO`](@ref), [`RequestGeneratorCL`](@ref), [`RequestTracker`](@ref) """ @kwdef struct Controller <: AbstractProtocol """Time-and-schedule-tracking instance from `ConcurrentSim`""" @@ -124,11 +124,11 @@ 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). +the other nodes' [`RequestTracker`](@ref) protocols when it receives [`DistributionRequest`](@ref) from the [`RequestGeneratorCL`](@ref). $TYPEDFIELDS -See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) +See also [`RequestGeneratorCO`](@ref), [`RequestGeneratorCL`](@ref), [`RequestTracker`](@ref) """ @kwdef struct CLController <: AbstractProtocol """Time-and-schedule-tracking instance from `ConcurrentSim`""" diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index d1bac7b7..76750b93 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -2,7 +2,7 @@ $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 [`Controller`](@ref) runs, but every node has access to a copy for referencing paths based on indices passed through the `DistributionRequest` tag/message. $TYPEDFIELDS From c86b7d7a1e7beee3f058b82579669d2709ab084b Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Wed, 11 Dec 2024 15:14:15 -0500 Subject: [PATCH 24/26] fix --- src/ProtocolZoo/ProtocolZoo.jl | 7 +++++++ src/ProtocolZoo/cutoff.jl | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index d6105c6a..b4e8f838 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -526,6 +526,7 @@ end @resumable function (prot::RequestTracker)() mb = messagebuffer(prot.net, prot.node) + isinf = false while true workwasdone = true # waiting is not enough because we might have multiple rounds of work to do while workwasdone @@ -551,6 +552,11 @@ end swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = req_src, nodeH = req_dst, rounds=rounds) @process swapper() else # connection-less + if rounds == -1 + isinf = true + else + isinf = false + end pred_low = swap_predicate(prot.net.graph, req_src, req_dst, prot.node) pred_high = swap_predicate(prot.net.graph, req_src, req_dst, prot.node; low=false) choose_low = swap_choose(prot.net.graph, req_src) @@ -561,6 +567,7 @@ end end end end + if isinf @yield timeout(prot.sim, 5.0) 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))" diff --git a/src/ProtocolZoo/cutoff.jl b/src/ProtocolZoo/cutoff.jl index da17356c..5e1bcc6e 100644 --- a/src/ProtocolZoo/cutoff.jl +++ b/src/ProtocolZoo/cutoff.jl @@ -34,7 +34,7 @@ end @resumable function (prot::CutoffProt)() if isnothing(prot.period) - error("In `CutoffProt` we do not yet support quing up and waiting on register") # TODO + error("In `CutoffProt` we do not yet support queuing up and waiting on register") # TODO end reg = prot.net[prot.node] while true From 36737502c47b732bcfa3c104f7b961414265e0d6 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 12 Dec 2024 10:05:02 -0500 Subject: [PATCH 25/26] Update sequence diagrams --- examples/controlplane/Readme.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/controlplane/Readme.md b/examples/controlplane/Readme.md index 5356e174..100b50f7 100644 --- a/examples/controlplane/Readme.md +++ b/examples/controlplane/Readme.md @@ -8,7 +8,7 @@ Different control plane architectures with arbitrary network topologies can be s 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: +The connection-oriented control protocol is illustrated at a high level by the sequence diagram below: ```mermaid sequenceDiagram @@ -19,7 +19,16 @@ Controller-->>+RequestTrackerN: EntanglementRequest Controller-->>+RequestTracker1: SwapRequest Controller-->>+RequestTracker2: SwapRequest Controller-->>+RequestTrackerN: SwapRequest -Controller-->>-Alice(Request Generator): RequestCompletion(path_id) +``` + +For the connection-less protocol we have pre=emptive entanglement generation and swaps are performed when a request is received at the controller + +```mermaid +sequenceDiagram +Alice(Request Generator)->>+Controller: DistributionRequest(path_id) +Controller-->>+RequestTracker1: SwapRequest +Controller-->>+RequestTracker2: SwapRequest +Controller-->>+RequestTrackerN: SwapRequest ``` The `RequestGenerator` (Alice) sends a message to the controller, requesting entanglement generation with an end node (Bob). From 52975175a27b2a71aaadbf1d1ac2177cb1ffe839 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 24 Dec 2024 17:25:50 -0500 Subject: [PATCH 26/26] documentation and performance visualization --- examples/controlplane/4_exp.jl | 137 +++++++++++++++++++++++++++++ examples/controlplane/5_exp.jl | 149 ++++++++++++++++++++++++++++++++ examples/controlplane/Readme.md | 16 +++- 3 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 examples/controlplane/4_exp.jl create mode 100644 examples/controlplane/5_exp.jl diff --git a/examples/controlplane/4_exp.jl b/examples/controlplane/4_exp.jl new file mode 100644 index 00000000..30f971f1 --- /dev/null +++ b/examples/controlplane/4_exp.jl @@ -0,0 +1,137 @@ +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) + + +controller = Controller(sim, net, 6, zeros(8,8)) +@process controller() + +req_gen = RequestGeneratorCO(sim, net, 1, 8, 6) +@process req_gen() + +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 + +consumerCO = EntanglementConsumer(sim, net, 1, 8) +@process consumerCO() + +run(sim, 500) +###################################### + +net = RegisterNet(graph, [Register(regsize, T1Decay(10.0)) for i in 1:8]) +sim = get_time_tracker(net) + + +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 = RequestGeneratorCL(sim, net, 1, 8, 6) +@process req_gen() + +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 + +consumerCL = EntanglementConsumer(sim, net, 1, 8) +@process consumerCL() + +run(sim, 500) + +####return 3 metrics: Time to success, Time evolution of average fidelity, avg number of pairs per unit time +function metrics(consumer) + entlog = consumer.log + ts = [e[1] for e in entlog] + txxs = [Point2f(e[1],e[3]) for e in entlog] + Δts = length(ts)>1 ? ts[2:end] .- ts[1:end-1] : [0.0] #Times to success + + ### + avg_fids = cumsum([e[3] for e in entlog])./cumsum(ones(length(entlog))) #avg fidelity per unit time + fid_info = [Point2f(t,f) for (t,f) in zip(ts, avg_fids)] + + ## + num_epr = cumsum(ones(length(entlog)))./(ts) #avg number of pairs per unit time + num_epr_info = [Point2f(t,n) for (t,n) in zip(ts, num_epr)] + + return Δts, fid_info, num_epr_info +end + +Δts_co, fid_co, num_epr_co = metrics(consumerCO) +Δts_cl, fid_cl, num_epr_cl = metrics(consumerCL) + + +fig = Figure(;size=(1200, 1500)) + +histaxis1 = Axis(fig[1,1], xlabel="ΔTime", title="Histogram of Time to Successes(Connection-Oriented)") +hist!(histaxis1, Δts_co) + +histaxis2 = Axis(fig[1,2], xlabel="ΔTime", title="Histogram of Time to Successes(Connection-Less)") +hist!(histaxis2, Δts_cl) + +fid_axis1 = Axis(fig[2,1], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") +ylims!(fid_axis1, (0.0, 1.0)) +lines!(fid_axis1, fid_co) + +fid_axis2 = Axis(fig[2,2], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") +ylims!(fid_axis2, (0.0, 1.0)) +lines!(fid_axis2, fid_cl) + +num_epr_axis1 = Axis(fig[3,1], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") +lines!(num_epr_axis1, num_epr_co) + +num_epr_axis2 = Axis(fig[3,2], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") +lines!(num_epr_axis2, num_epr_cl) + +display(fig) +save("fig.png", fig) \ No newline at end of file diff --git a/examples/controlplane/5_exp.jl b/examples/controlplane/5_exp.jl new file mode 100644 index 00000000..67e629bb --- /dev/null +++ b/examples/controlplane/5_exp.jl @@ -0,0 +1,149 @@ +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) + + +controller = Controller(sim, net, 6, zeros(8,8)) +@process controller() + +req_gen18 = RequestGeneratorCO(sim, net, 1, 8, 6) +@process req_gen18() + +req_gen27 = RequestGeneratorCO(sim, net, 2, 7, 6) +@process req_gen27() + +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 + +consumerCO18 = EntanglementConsumer(sim, net, 1, 8) +@process consumerCO18() + +consumerCO27 = EntanglementConsumer(sim, net, 2, 7) +@process consumerCO27() + +run(sim, 500) + +######################################################################## +net = RegisterNet(graph, [Register(regsize, T1Decay(10.0)) for i in 1:8]) +sim = get_time_tracker(net) + + +controller = Controller(sim, net, 6, zeros(8,8)) +@process controller() + +req_gen = RequestGeneratorCO(sim, net, 1, 8, 6) +@process req_gen() + +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 + +consumerCOsin = EntanglementConsumer(sim, net, 1, 8) +@process consumerCOsin() + + +run(sim, 500) + +####return 3 metrics: Time to success, Time evolution of average fidelity, avg number of pairs per unit time +function metrics(consumer) + entlog = consumer.log + ts = [e[1] for e in entlog] + txxs = [Point2f(e[1],e[3]) for e in entlog] + Δts = length(ts)>1 ? ts[2:end] .- ts[1:end-1] : [0.0] #Times to success + + ### + avg_fids = cumsum([e[3] for e in entlog])./cumsum(ones(length(entlog))) #avg fidelity per unit time + fid_info = [Point2f(t,f) for (t,f) in zip(ts, avg_fids)] + + ## + num_epr = cumsum(ones(length(entlog)))./(ts) #avg number of pairs per unit time + num_epr_info = [Point2f(t,n) for (t,n) in zip(ts, num_epr)] + + return Δts, fid_info, num_epr_info +end + +Δts_co, fid_co, num_epr_co = metrics(consumerCOsin) +Δts_co18, fid_co18, num_epr_co18 = metrics(consumerCO18) +Δts_co27, fid_co27, num_epr_co27 = metrics(consumerCO27) + + +fig = Figure(;size=(1200, 1500)) + +histaxis = Axis(fig[1,1], xlabel="ΔTime", title="Histogram of Time to Successes(Single User Pair 1-8)") +hist!(histaxis, Δts_co) + +histaxis1 = Axis(fig[1,2], xlabel="ΔTime", title="Histogram of Time to Successes(Pair 1-8)") +hist!(histaxis1, Δts_co18) + +histaxis2 = Axis(fig[1,3], xlabel="ΔTime", title="Histogram of Time to Successes(Pair 2-7)") +hist!(histaxis2, Δts_co27) + +fid_axis = Axis(fig[2,1], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") +ylims!(fid_axis, (0.0, 1.0)) +lines!(fid_axis, fid_co) + +fid_axis1 = Axis(fig[2,2], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") +ylims!(fid_axis1, (0.0, 1.0)) +lines!(fid_axis1, fid_co18) + +fid_axis2 = Axis(fig[2,3], xlabel="Time", ylabel="Avg. Fidelity", title="Time evolution of Average Fidelity") +ylims!(fid_axis2, (0.0, 1.0)) +lines!(fid_axis2, fid_co27) + +num_epr_axis = Axis(fig[3,1], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") +lines!(num_epr_axis, num_epr_co) + +num_epr_axis1 = Axis(fig[3,2], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") +lines!(num_epr_axis1, num_epr_co18) + +num_epr_axis2 = Axis(fig[3,3], xlabel="Time", title="Avg. Number of Entangled Pairs between Alice and Bob") +lines!(num_epr_axis2, num_epr_co27) + +display(fig) +save("fig.png", fig) \ No newline at end of file diff --git a/examples/controlplane/Readme.md b/examples/controlplane/Readme.md index 100b50f7..3b709b24 100644 --- a/examples/controlplane/Readme.md +++ b/examples/controlplane/Readme.md @@ -8,6 +8,14 @@ Different control plane architectures with arbitrary network topologies can be s 2b. An interactive web app with the same simulation as 2a +3a. A simulation that generates an interactive visualization for a connection-less, non-distributed and centralized entanglement distribution network + +3b. An interactive web app with the same simulation as 3a + +4 A script that generates a visualization for side-by-side comparison of the connection-oriented and connection-less approaches with a single user-pair. + +5 A script that generates a visualization for the connection-oriented approach comparing performance in case of single user pair vs two user pairs. + The connection-oriented control protocol is illustrated at a high level by the sequence diagram below: ```mermaid @@ -20,8 +28,10 @@ Controller-->>+RequestTracker1: SwapRequest Controller-->>+RequestTracker2: SwapRequest Controller-->>+RequestTrackerN: SwapRequest ``` +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 -For the connection-less protocol we have pre=emptive entanglement generation and swaps are performed when a request is received at the controller +For the connection-less protocol we have pre-emptive entanglement generation and swaps are performed at all the nodes except the user pair when a request is received at the controller. ```mermaid sequenceDiagram @@ -30,6 +40,4 @@ Controller-->>+RequestTracker1: SwapRequest Controller-->>+RequestTracker2: SwapRequest Controller-->>+RequestTrackerN: SwapRequest ``` - -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 +Note: The connection-less controller is currently limited to only one user pair since having multiple distinct user pairs run swaps at the user pair of other requests, which leads to cyclic swaps(two qubits of the same node getting entangled to each other through swaps due to different swap predicate priorities for different user pairs) \ No newline at end of file