From 8af6e754d25ac5c287ec86809a43492417073596 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 23 Sep 2024 15:56:51 -0400 Subject: [PATCH 01/20] 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/20] 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 03b19cde190c390a40e310346ad560c20b225279 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:28:23 -0400 Subject: [PATCH 03/20] 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 e11906f1dd078be69875be3ca58cda2784df62bc Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 3 Oct 2024 21:53:19 -0400 Subject: [PATCH 04/20] 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 7cbe56f19df981c2a19e2b76f9f855c6d9c2cb6f Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Sat, 5 Oct 2024 15:24:39 -0400 Subject: [PATCH 05/20] 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 328cc1005394d2d2cb92b40fef19e74c1e359aba Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 7 Oct 2024 14:57:39 -0400 Subject: [PATCH 06/20] 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 48c263cd26a0f0b44a5948581888940e1aca33e3 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Thu, 10 Oct 2024 12:30:28 -0400 Subject: [PATCH 07/20] 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 089a40a71facf749aaa1826ce860520d1ed58976 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:19:17 -0400 Subject: [PATCH 08/20] 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 808b5d090d349934bf6c900a5c2af9bec2f345ef Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 14 Oct 2024 12:21:24 -0400 Subject: [PATCH 09/20] 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 26e36cec7c52dce54180aa7f2cb0c28a9ecfc683 Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 16 Oct 2024 09:34:04 -0400 Subject: [PATCH 10/20] fix incorrect version number (#163) --- CHANGELOG.md | 8 +++++--- Project.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acaed58c..d5b63356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # News -## v0.6.0 - 2024-09-13 +## v0.5.1-dev - 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. - Implement a network control protocol that is connection-oriented, centralized and non-distributed @@ -8,13 +8,15 @@ - Add `PhysicalGraph` struct for storing network metadata as the simulation evolves. - New tags: `EntanglementRequest`,`SwapRequest`, `DistributionRequest` and `RequestCompletion` -## v0.5.0 - 2024-09-05 +## v0.5.0 - 2024-10-16 - Develop `CutoffProt` to deal with deadlocks in a simulation - Expand `SwapperProt` with `agelimit` to permit cutoff policies (with `CutoffProt`) - Tutorial and interactive examples for entanglement distribution on a grid with local-only knowledge - **(breaking)** `observable` now takes a default value as a kwarg, i.e., you need to make the substitution `observable(regs, obs, 0.0; time)` ↦ `observable(regs, obs; something=0.0, time)` - Bump QuantumSymbolics and QuantumOpticsBase compat bound and bump julia compat to 1.10. +- Implement a simple switch protocol. + - 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. ## v0.4.2 - 2024-08-13 @@ -31,4 +33,4 @@ - Establishing the symbolic expression capabilities - Establishing plotting and visualization capabilities -## older versions were not tracked \ No newline at end of file +## older versions were not tracked diff --git a/Project.toml b/Project.toml index 716f233b..2789bda5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumSavory" uuid = "2de2e421-972c-4cb5-a0c3-999c85908079" authors = ["Stefan Krastanov "] -version = "0.6" +version = "0.5.1-dev" [deps] Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" From 18a51d9a9756048521b33f2f26d408607b318046 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt <46929125+ba2tro@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:32:46 -0400 Subject: [PATCH 11/20] convert zmeas and xmeas in swapper to Int explicitly (#161) --- src/ProtocolZoo/swapping.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProtocolZoo/swapping.jl b/src/ProtocolZoo/swapping.jl index 6cae7185..978fabe3 100644 --- a/src/ProtocolZoo/swapping.jl +++ b/src/ProtocolZoo/swapping.jl @@ -69,12 +69,12 @@ end xmeas, zmeas = swapcircuit(q1, q2) # send from here to new entanglement counterpart: # tag with EntanglementUpdateX past_local_node, past_local_slot_idx past_remote_slot_idx new_remote_node, new_remote_slot, correction - msg1 = Tag(EntanglementUpdateX, prot.node, q1.idx, tag1[3], tag2[2], tag2[3], xmeas) + msg1 = Tag(EntanglementUpdateX, prot.node, q1.idx, tag1[3], tag2[2], tag2[3], Int(xmeas)) put!(channel(prot.net, prot.node=>tag1[2]; permit_forward=true), msg1) @debug "SwapperProt @$(prot.node)|round $(round): Send message to $(tag1[2]) | message=`$msg1` | time = $(now(prot.sim))" # send from here to new entanglement counterpart: # tag with EntanglementUpdateZ past_local_node, past_local_slot_idx past_remote_slot_idx new_remote_node, new_remote_slot, correction - msg2 = Tag(EntanglementUpdateZ, prot.node, q2.idx, tag2[3], tag1[2], tag1[3], zmeas) + msg2 = Tag(EntanglementUpdateZ, prot.node, q2.idx, tag2[3], tag1[2], tag1[3], Int(zmeas)) put!(channel(prot.net, prot.node=>tag2[2]; permit_forward=true), msg2) @debug "SwapperProt @$(prot.node)|round $(round): Send message to $(tag2[2]) | message=`$msg2` | time = $(now(prot.sim))" @yield timeout(prot.sim, prot.local_busy_time) From cf7c707737dc6295205e1ee8bf3e64a17658c02d 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 12/20] =?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 d5b63356..8ba9107f 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-10-16 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 41b0271c63c6914b5ec9d3f7c3aa0ed23cd20223 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 11:34:37 -0500 Subject: [PATCH 13/20] 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 14/20] 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 1593425ba051e8b70f6f5cdb728cd741288bfb7b Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 14:58:21 -0500 Subject: [PATCH 15/20] 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 0a9cc0ce11794ac60789e13fea001658769a59da Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Mon, 4 Nov 2024 15:30:33 -0500 Subject: [PATCH 16/20] 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 c63ebe43b5b14af194dad85f7aee890416d086cc Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Fri, 22 Nov 2024 16:48:31 -0500 Subject: [PATCH 17/20] Simplify the swapping predicate, since the path is already decided --- src/ProtocolZoo/ProtocolZoo.jl | 10 +++++++--- src/ProtocolZoo/controllers.jl | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index cf25996b..5d35d1b3 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -185,6 +185,10 @@ 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) @@ -532,13 +536,13 @@ 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) + swapper = SwapperProt(prot.sim, prot.net, prot.node; nodeL = req_src, nodeH = req_dst, rounds=rounds) @process swapper() end end diff --git a/src/ProtocolZoo/controllers.jl b/src/ProtocolZoo/controllers.jl index 95b4e901..2f0d9e2c 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -105,7 +105,7 @@ end for i in 2:length(path)-1 last = i == length(path) - 1 ? 1 : 0 - msg = Tag(SwapRequest, path[i], 1) + msg = Tag(SwapRequest, path[i], 1, path[i-1], path[i+1]) if prot.node == path[i] put!(mb, msg) else From 62a420c8bdbb9275a1aab74cc7dee5743cca0020 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 26 Nov 2024 11:09:06 -0500 Subject: [PATCH 18/20] Use nothing for initializing path matrix in the controller --- examples/controlplane/2a_cnc_interactive.jl | 2 +- src/ProtocolZoo/ProtocolZoo.jl | 4 ++-- src/ProtocolZoo/controllers.jl | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/controlplane/2a_cnc_interactive.jl b/examples/controlplane/2a_cnc_interactive.jl index 43595067..19f14c35 100644 --- a/examples/controlplane/2a_cnc_interactive.jl +++ b/examples/controlplane/2a_cnc_interactive.jl @@ -1,6 +1,6 @@ include("setup.jl") -controller = Controller(sim, net, 6, zeros(8,8)) +controller = Controller(sim, net, 6, fill(nothing, 8, 8)) @process controller() req_gen = RequestGenerator(sim, net, 1, 8, 6) diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 5d35d1b3..a450b040 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -428,7 +428,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 @@ -547,7 +547,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 2f0d9e2c..cf73f7a2 100644 --- a/src/ProtocolZoo/controllers.jl +++ b/src/ProtocolZoo/controllers.jl @@ -72,7 +72,7 @@ See also [`RequestGenerator`](@ref), [`RequestTracker`](@ref) """The node in the network where the control protocol is physically located, ideally a centrally located node""" node::Int """A matrix for the object containing physical graph metadata for the network""" - path_mat::Matrix{Union{Float64, PathMetadata}} + path_mat::Matrix{Union{Nothing, PathMetadata}} end @resumable function (prot::Controller)() @@ -84,7 +84,7 @@ end msg = querydelete!(mb, DistributionRequest, ❓, ❓) if !isnothing(msg) (msg_src, (_, src, dst)) = msg - if typeof(prot.path_mat[src, dst]) <: Number + if isnothing(prot.path_mat[src, dst]) 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]) @@ -113,7 +113,7 @@ 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 From 7c9f176fab79eae9c0bb08d0120411ef06b09946 Mon Sep 17 00:00:00 2001 From: Abhishek Bhatt Date: Tue, 26 Nov 2024 17:30:11 -0500 Subject: [PATCH 19/20] keep record of node wise workloads(requests being served) --- src/ProtocolZoo/utils.jl | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/ProtocolZoo/utils.jl b/src/ProtocolZoo/utils.jl index 2366daeb..c1e618b2 100644 --- a/src/ProtocolZoo/utils.jl +++ b/src/ProtocolZoo/utils.jl @@ -11,17 +11,28 @@ $TYPEDFIELDS """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} + workloads::Dict{Int, Int} + """The number of slots available at each node. Scalar if all are same, otherwise a dictionary.""" + capacity::Union{Dict{Int, Int}} """Number of failed requests due to high request traffic""" failures::Ref{Int} end -function PathMetadata(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{Dict{Int, Int}, Int}; failures=Ref{Int}(0)) paths = sort(collect(all_simple_paths(graph, src, dst)); by = x->length(x)) - workloads = zeros(length(paths)) - PathMetadata(paths, workloads, caps, failures) + src = paths[1][1] + dst = paths[1][end] + workloads = Dict{Int, Int}() + capacity = isa(caps, Number) ? Dict{Int, Int}() : caps + for node in 1:size(graph)[1] + if !(node == src || node == dst) + workloads[node] = 0 + if isa(caps, Number) + capacity[node] = caps + end + end + end + PathMetadata(paths, workloads, capacity, failures) end @@ -29,12 +40,13 @@ end A simple path selection algorithm for connection oriented networks. """ function path_selection(sim, pathobj::PathMetadata) - for i in 1:length(pathobj.paths) - capacity = isa(pathobj.capacity, Number) ? pathobj.capacity : pathobj.capacity[i] - if pathobj.workloads[i] Date: Tue, 26 Nov 2024 19:25:46 -0500 Subject: [PATCH 20/20] update test --- 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 db61b572..68064bc9 100644 --- a/test/test_controlplane.jl +++ b/test/test_controlplane.jl @@ -29,7 +29,7 @@ net = RegisterNet(graph, [Register(regsize) for i in 1:8]) sim = get_time_tracker(net) # controller -controller = Controller(sim, net, 6, zeros(8,8)) +controller = Controller(sim, net, 6, fill(nothing, 8, 8)) @process controller() # RequestGenerator for the user pair (1,8)