diff --git a/examples/Project.toml b/examples/Project.toml index 98781bc3..c07b10c3 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -5,12 +5,15 @@ ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" +GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a" +QuantumClifford = "0525e862-1e90-11e9-3e4d-1b39d7109de1" QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c" QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079" QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" diff --git a/examples/piecemakerswitch/Project.toml b/examples/piecemakerswitch/Project.toml new file mode 100644 index 00000000..631c595a --- /dev/null +++ b/examples/piecemakerswitch/Project.toml @@ -0,0 +1,17 @@ +[deps] +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +ConcurrentSim = "6ed1e86c-fcaf-46a9-97e0-2b26a2cdb499" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" +GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a" +QuantumClifford = "0525e862-1e90-11e9-3e4d-1b39d7109de1" +QuantumSavory = "2de2e421-972c-4cb5-a0c3-999c85908079" +ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" diff --git a/examples/piecemakerswitch/README.md b/examples/piecemakerswitch/README.md new file mode 100644 index 00000000..4dc50eb2 --- /dev/null +++ b/examples/piecemakerswitch/README.md @@ -0,0 +1,59 @@ +# Distributing a GHZ state with a quantum entanglement switch + +Live version is available at [areweentangledyet.com/piecemakerswitch/](https://areweentangledyet.com/piecemakerswitch/). + +In this setup, multiple nodes (user nodes) connect to a central node (switch node) trough bipartite entangled states. The switch node performs fusion operations on these shared states to create a GHZ state among the user nodes. The goal is to do the latter as memory efficient as possible. Each of the $n$ clients can store one memory qubit in its memory buffer and one qubit at the switch side. In addition the switch's register holds a dedicated 'piecemaker' slot - a qubit in the $|+\rangle$ state, which all successfull clients fuse their switch'side qubits with. We term this protocol the _piecemaker_ protocol, see [Prielinger et al., 2025](https://arxiv.org/abs/2508.14737). + +In each time step, $n$ entanglement generation processes run in parallel. Upon creation of an entanglement link, it is fused with the piecemaker qubit. Once all clients went through this fusion operation, the piecemaker qubit is measured. This projects the state back to the clients, resulting in the desired shared GHZ state. +The fusion operation is performed on the switch node: Let's take a client who just managed to generate a bipartide entangled state -- entangled link -- with its associated qubit at the switch side. The switch executes a `CNOT` gate on the client's qubit (target) and the piecemaker qubit (control). Next, the switch measures the client qubit in the computational basis and sends the outcome to the client (in order to apply the necessary Pauli correction). This procedure merges the bipartide state into the (entangled) state that the piecemaker qubit is currently part of, modulo any required Pauli corrections. + +NOTE: The memories residing in the nodes' `Register`s suffer from depolarizing noise, see [`Depolarization`](https://qs.quantumsavory.org/stable/API/#QuantumSavory.Depolarization). + +The `setup.jl` file implements all necessary base functionality. +The other files run the simulation and generate visuals in a number of different circumstances: +1. `simple_run.jl` A single simulator script convenient for exploratory coding, running a numer of Monte Carlo simulations; +2. `live_visualization_interactive` A web-app version of the simulator; +3. `static_viz.jl` A script running a number of of simulations like the ones in point 1, followed by plotting the fidelity over the protocol's execution time. + +Documentation: +- [The "How To" doc page on setting up this simulation](https://qs.quantumsavory.org/dev/howto/piecemakerswitch/piecemakerswitch) + +### Protocol flow + +```mermaid +sequenceDiagram + participant Client1 + participant ClientN + + participant SwitchNode + participant Log + + Note over Client1,SwitchNode: Round 1 (1 unit time) + par Entanglement Generation + Client1->>+SwitchNode: Try to generate entanglement + ClientN->>+SwitchNode: Try to generate entanglement + end + + SwitchNode->>SwitchNode: Run fusions with successful clients + + par Send Measurement Outcomes + SwitchNode-->>-Client1: Send measurement outcomes + SwitchNode-->>-ClientN: Send measurement outcomes + end + + par Apply Corrections (No time cost) + Client1->>Client1: Apply correction gates + ClientN->>ClientN: Apply correction gates + end + + loop Check Fusion Status (No time cost) + SwitchNode->>SwitchNode: Check if all clients are fused + alt All clients fused + SwitchNode->>SwitchNode: Measure piecemaker + SwitchNode->>SwitchNode: Compute fidelity to GHZ state + SwitchNode->>Log: Log fidelity and time [END OF PROTOCOL] + else + SwitchNode->>SwitchNode: Keep checking + end + end +``` \ No newline at end of file diff --git a/examples/piecemakerswitch/live_visualization_interactive.jl b/examples/piecemakerswitch/live_visualization_interactive.jl new file mode 100644 index 00000000..1bb2d2f6 --- /dev/null +++ b/examples/piecemakerswitch/live_visualization_interactive.jl @@ -0,0 +1,163 @@ +# Live visualization of the piecemaker switch protocol +include("setup.jl") + +using Base.Threads +using WGLMakie +WGLMakie.activate!() + +using Bonito +using Markdown +const custom_css = Bonito.DOM.style("ul {list-style: circle !important;}") + +logging = Observable(Point2f[]) # for plotting + +function push_to_logging!(logging::Observable, t::Float64, fidelity::Float64) + push!(logging[], Point2f(t, fidelity)) +end + +function prepare_sim(fig::Figure, n::Int, link_success_prob::Float64, mem_depolar_prob::Float64) + + repr = QuantumOpticsRepr() + + decoherence_rate = -log(1 - mem_depolar_prob) + noise_model = Depolarization(1 / decoherence_rate) + + switch = Register([Qubit() for _ in 1:(n+1)], [repr for _ in 1:(n+1)], [noise_model for _ in 1:(n+1)]) + clients = [Register([Qubit()], [repr], [noise_model]) for _ in 1:n] + + graph = star_graph(n + 1) + net = RegisterNet(graph, [switch, clients...]) + sim = get_time_tracker(net) + + # Attach the network plot to net and capture its obs + _, ax_net, _, net_obs = registernetplot_axis(fig[1, 2], net) + ax_net.title = "Network of n=5 users (live Δt = 0.1s)" + # Fix the visible ranges + xlims!(ax_net, -15, 15) + ylims!(ax_net, -15, 15) + ax_net.aspect = Makie.DataAspect() # keep aspect ratio + Makie.deregister_interaction!(ax_net, :scrollzoom) # disable zoom and pan interactions + Makie.deregister_interaction!(ax_net, :dragpan) + + @process PiecemakerProt(sim, n, net, link_success_prob, -1) # set rounds=1 + + return sim, net, net_obs +end + +# A helper to add parameter sliders to visualizations +function add_conf_sliders(fig) + conf = Dict( + :link_success_prob => 0.5, + :mem_depolar_prob => 0.1, + ) + conf_obs = Observable(conf) + sg = SliderGrid( + fig, + (label = "link success prob", + range = 0.05:0.05:1.0, format = "{:.2f}", startvalue = conf[:link_success_prob]), + (label = "mem depolar prob", + range = 0.05:0.05:1.0, format = "{:.2f}", startvalue = conf[:mem_depolar_prob]), + width = 300, + ) + + names = [:link_success_prob, :mem_depolar_prob] + for (name,slider) in zip(names,sg.sliders) + on(slider.value) do val + conf_obs[][name] = val + end + end + conf_obs +end + +# Serve the Makie app +landing = Bonito.App() do + + n = 5 # number of clients + + fig = Figure(resolution = (800, 600)) + ax_fid = Axis(fig[1, 1], xlabel="Δt (time steps)", ylabel="Fidelity to GHZₙ", title="Fidelity") + scatter!(ax_fid, logging, markersize = 8) + ylims!(ax_fid, 0, 1.05) + xlims!(ax_fid, 0, 30) + + running = Observable{Union{Bool,Nothing}}(false) + fig[2, 1] = buttongrid = GridLayout(tellwidth = false) + buttongrid[1,1] = b = Makie.Button( + fig, + label = @lift($running ? "Stop" : "Run"), + height = 30, tellwidth = false, + ) + + conf_obs = add_conf_sliders(fig[2, 2]) + + on(b.clicks) do _ + if running[] # if already running, stop it + running[] = false + return + end + running[] = true + @async begin + try # run the sim + sim, net, net_obs = prepare_sim(fig, n, conf_obs[][:link_success_prob], conf_obs[][:mem_depolar_prob]) + t = 0 + while running[] == true + t += 1 + if length(logging[]) > 100 + break + end + run(sim, t) + notify(net_obs) + notify(logging) + sleep(0.1) + end + finally + running[] = false + end + end + end + + content = md""" + Pick simulation settings and hit “Run once”. The left panel plots the running fidelity to the target GHZ state; the right panel shows the network state as it evolves over 10 simulation rounds. + + $(fig.scene) + + # GHZ state distribution with a quantum entanglement switch + + This demo simulates 10 rounds of GHZ-state distribution in a star-shaped network with a central switch node and n client nodes. Each client holds one memory qubit locally and one at the switch. The switch has an extra “piecemaker” qubit (slot n+1) that is initialized in the |+⟩ state; it is used to fuse all successful links into an n-party GHZ state. + + What happens during one run: + - Per time step, the switch attempts to entangle with each client in parallel (success probability set by the slider “link success prob”). + - When a client<>switch entanglement attempt succeeds, the switch immediately fuses the client’s switch-side qubit with the piecemaker via a CNOT, measures the client qubit in Z, and sends the outcome to the client. The client applies necessary corrections. + - After all clients have been fused, the piecemaker is measured in X. The first client receives that outcome and applies a Z correction if needed. + - The current n-qubit state (the clients’ memory qubits) is compared to the ideal GHZₙ target state. The resulting fidelity is plotted as a point on the left over the number of taken time steps Δt. + + Noise model: + - Memory qubits are subject to depolarizing noise ([`Depolarization`](https://qs.quantumsavory.org/stable/API/#QuantumSavory.Depolarization) background). The slider “mem depolar prob” controls the memory depolarization probability. + + UI guide: + - Left: fidelity vs simulation time Δt. Points accumulate across runs so you can compare settings. + - Right: network snapshot. Edges appear when links are established; updates every 0.1s of real time. + - Sliders: tune link success probability and memory depolarization probability before each run. + - Button: starts a single run with the current settings. + + NOTE that this is a simplified simulation for demonstration purposes. In particular, it assumes instantaneous gates as well as classical communication. The only time inducing steps are the attempts for heralded entanglement generation (Δt = 1 time step each). + + [Browse or modify the code for this simulation on GitHub.](https://github.com/QuantumSavory/QuantumSavory.jl/tree/master/examples/piecemakerswitch/live_visualization_interactive.jl) + """ + return Bonito.DOM.div(Bonito.MarkdownCSS, Bonito.Styling, custom_css, content) +end; + + +isdefined(Main, :server) && close(server); +port = parse(Int, get(ENV, "QS_GHZSWITCH_PORT", "3000")) +interface = get(ENV, "QS_GHZSWITCH_IP", "0.0.0.0") # Bind to all interfaces +proxy_url = get(ENV, "QS_GHZSWITCH_PROXY", "") +server = Bonito.Server(interface, port; proxy_url); +Bonito.HTTPServer.start(server) +Bonito.route!(server, "/" => landing); + + +@info "app server is running on http://$(interface):$(port) | proxy_url=`$(proxy_url)`" + + +wait(server) \ No newline at end of file diff --git a/examples/piecemakerswitch/setup.jl b/examples/piecemakerswitch/setup.jl new file mode 100644 index 00000000..1332eb60 --- /dev/null +++ b/examples/piecemakerswitch/setup.jl @@ -0,0 +1,240 @@ +using QuantumSavory +using QuantumSavory.ProtocolZoo +using Graphs +using ConcurrentSim +using ResumableFunctions +using NetworkLayout +using DataFrames +using Random +using QuantumClifford: ghz + +const ghzs = [ghz(n) for n in 1:7] # make const in order to not build new every time + +""" + push_to_logging!(logging::Vector, t::Float64, fidelity::Float64) + +Append a time-fidelity data point to the logging vector. + +# Arguments +- `logging::Vector`: Vector of `Point2f` storing (time, fidelity) pairs +- `t::Float64`: Simulation time at which the measurement was taken +- `fidelity::Float64`: Measured fidelity to the target GHZ state +""" +function push_to_logging!(logging::Vector, t::Float64, fidelity::Float64) + push!(logging, (t, fidelity)) +end + +""" + fusion(piecemaker_slot::RegRef, client_slot::RegRef) + +Perform a fusion operation between a piecemaker qubit and a client qubit. + +Applies a CNOT gate with the piecemaker qubit as control and the client qubit +as target, then measures the client qubit in the Z basis and traces it out. + +# Arguments +- `piecemaker_slot::Int`: Register slot index containing the piecemaker qubit +- `client_slot::Int`: Register slot index containing the client qubit to be fused + +# Returns +- Measurement outcome (1 or 2) from projecting the client qubit onto the Z basis +""" +function fusion(piecemaker_slot::RegRef, client_slot::RegRef) + apply!((piecemaker_slot, client_slot), CNOT) + res = project_traceout!(client_slot, σᶻ) + return res +end + +""" + EntanglementCorrector(sim, net::RegisterNet, node::Int) + +Resumable function that waits for X correction messages and applies them. + +This protocol monitors a client node for `:updateX` tags sent by the switch +after fusion operations. When received, it applies an X gate if needed (when +the measurement outcome is 2) to correct the client's qubit state. + +# Arguments +- `sim`: ConcurrentSim simulation environment +- `net::RegisterNet`: Network containing all nodes +- `node::Int`: Index of the client node to monitor and correct +""" +@resumable function EntanglementCorrector(sim, net::RegisterNet, node::Int) + while true + @yield onchange_tag(net[node][1]) + msg = querydelete!(net[node][1], :updateX, ❓) + if !isnothing(msg) + value = msg[3][2] + @yield lock(net[node][1]) + @debug "X received at node $(node), with value $(value)" + value == 2 && apply!(net[node][1], X) + unlock(net[node][1]) + break + end + end +end + +""" + Logger(sim, net::RegisterNet, node::Int, n::Int, start_of_round) + +Resumable function that applies Z corrections and measures final GHZ fidelity. + +Waits for a `:updateZ` tag from the switch (sent after measuring the piecemaker +qubit in the X basis), applies the necessary Z correction if the measurement +outcome is 2, then measures the fidelity of the resulting n-qubit state to the +target GHZ state and logs it. Pushes a (time, fidelity) data point to the global +`logging` vector via `push_to_logging!` + +# Arguments +- `sim`: ConcurrentSim simulation environment +- `net::RegisterNet`: Network containing all nodes +- `node::Int`: Index of the client node receiving the Z correction +- `n::Int`: Number of clients in the GHZ state +- `start_of_round`: Simulation time when the current round started +""" +@resumable function Logger(sim, net::RegisterNet, node::Int, n::Int, start_of_round) + msg = querydelete!(net[node], :updateZ, ❓) + if isnothing(msg) + error("No message received at node $(node) with tag :updateZ.") + else + value = msg[3][2] + @debug "Z received at node $(node), with value $(value)" + @yield lock(net[node][1]) + value == 2 && apply!(net[node][1], Z) + unlock(net[node][1]) + + # Measure the fidelity to the GHZ state + @yield reduce(&, [lock(q) for q in net[2]]) + obs_proj = SProjector(StabilizerState(ghzs[n])) # GHZ state projector to measure + fidelity = real(observable([net[i+1][1] for i in 1:n], obs_proj; time = now(sim))) + t = now(sim) - start_of_round + @debug "Fidelity: $(fidelity)" + push_to_logging!(logging, t, fidelity) + end +end + +""" + clear_up_qubits!(net::RegisterNet, n::Int) + +Clean up all qubits at the switch and client nodes after a round. + +Traces out and unlocks all storage qubits at the switch (node 1) and the +first qubit at each of the n client nodes, preparing the network for the +next round or simulation end. + +# Arguments +- `net::RegisterNet`: Network containing all nodes +- `n::Int`: Number of client nodes +""" +function clear_up_qubits!(net::RegisterNet, n::Int) + # cleanup qubits + foreach(q -> (traceout!(q); unlock(q)), net[1]) + foreach(q -> (traceout!(q); unlock(q)), [net[1 + i][1] for i in 1:n]) +end + +""" + PiecemakerProt(sim, n::Int, net::RegisterNet, link_success_prob::Float64, rounds::Int) + +Main resumable protocol for the piecemaker quantum switching scheme. + +Orchestrates the generation of n-qubit GHZ states across client nodes using +a central switch node. Each round: +1. Establishes entanglement between the switch and each client in parallel +2. Fuses successful client qubits with a piecemaker qubit at the switch via CNOT +3. Measures the piecemaker qubit in the X basis to project clients into a GHZ state +4. Communicates measurement outcomes for local corrections +5. Logs the fidelity to the target GHZ state and cleans up resources + +# Arguments +- `sim`: ConcurrentSim simulation environment +- `n::Int`: Number of client nodes (GHZ state size) +- `net::RegisterNet`: Network with star topology (switch at center, clients at leaves) +- `link_success_prob::Float64`: Probability of successful entanglement per attempt +- `rounds::Int`: Number of GHZ generation rounds to execute + +# Protocol Overview +The piecemaker protocol generates multipartite entanglement by: +- Creating Bell pairs between switch and each client using `EntanglerProt` +- Fusing these pairs at the switch using a |+⟩ "piecemaker" qubit (slot n+1) +- Measuring the piecemaker in the X basis to project clients into GHZ +- Communicating measurement outcomes via tags for local X and Z corrections +""" +@resumable function PiecemakerProt(sim, n::Int, net::RegisterNet, link_success_prob::Float64, rounds::Int) + while rounds != 0 + @debug "round $(rounds)" + start = now(sim) + + for i in 1:n + entangler = EntanglerProt( + sim = sim, net = net, nodeA = 1, chooseA = i, nodeB = 1 + i, chooseB = 1, + success_prob = link_success_prob, rounds = 1, attempts = -1, attempt_time = 1.0, + ) + @process entangler() + end + + for i in 1:n + @process EntanglementCorrector(sim, net, 1 + i) + end + + while true + # Look for EntanglementCounterpart changed on switch + counter = 0 + while counter < n # until all clients are entangled + @yield onchange_tag(net[1]) + if counter == 0 + # Initialize "piecemaker" qubit in |+> state when first qubit arrived + initialize!(net[1][n+1], X1, time = now(sim)) + end + + while true + counterpart = querydelete!(net[1], EntanglementCounterpart, ❓, ❓) + if !isnothing(counterpart) + slot, _, _ = counterpart + + # fuse the qubit with the piecemaker qubit + @yield lock(net[1][n+1]) & lock(net[1][slot.idx]) + res = fusion(net[1][n+1], net[1][slot.idx]) + unlock(net[1][n+1]); unlock(net[1][slot.idx]) + tag!(net[1 + slot.idx][1], Tag(:updateX, res)) # communicate change to client node + counter += 1 + @debug "Fused client $(slot.idx) with piecemaker qubit" + else + break + end + end + end + + @debug "All clients entangled, measuring piecemaker | time: $(now(sim)-start)" + @yield lock(net[1][n+1]) + res = project_traceout!(net[1][n+1], σˣ) + unlock(net[1][n+1]) + tag!(net[2][1], Tag(:updateZ, res)) # communicate change to client node + break + end + + @yield @process Logger(sim, net, 2, n, start) + + # cleanup qubits + clear_up_qubits!(net, n) + rounds -= 1 + @info "Round $(rounds) finished" + end +end + +# function prepare_sim(n::Int, states_representation::AbstractRepresentation, noise_model::Union{AbstractBackground, Nothing}, +# link_success_prob::Float64, seed::Int, rounds::Int) + +# # Set a random seed +# Random.seed!(seed) + +# switch = Register([Qubit() for _ in 1:(n+1)], [states_representation for _ in 1:(n+1)], [noise_model for _ in 1:(n+1)]) # storage qubits at the switch, first qubit is the "piecemaker" qubit +# clients = [Register([Qubit()], [states_representation], [noise_model]) for _ in 1:n] # client qubits + +# graph = star_graph(n+1) +# net = RegisterNet(graph, [switch, clients...]) +# sim = get_time_tracker(net) + +# # Start the piecemaker protocol +# @process PiecemakerProt(sim, n, net, link_success_prob, rounds) +# return sim +# end \ No newline at end of file diff --git a/examples/piecemakerswitch/simple_run.jl b/examples/piecemakerswitch/simple_run.jl new file mode 100644 index 00000000..287d806d --- /dev/null +++ b/examples/piecemakerswitch/simple_run.jl @@ -0,0 +1,28 @@ +include("setup.jl") + +mem_depolar_prob = 0.1 # memory depolarization probability +decoherence_rate = - log(1 - mem_depolar_prob) # decoherence rates +noise_model = Depolarization(1/decoherence_rate) # noise model applied to the memory qubits +link_success_prob = 0.5 +rounds = 100 # number of rounds to run + +results_per_client = DataFrame(nclients = Int[], Δt = Float64[], fidelity = Float64[], avg_elapsed_time = Float64[]) +for nclients in 2:5 + logging = Tuple[] # for plotting + + # Prepare simulation data storage + distribution_times = Float64[] + fidelities = Float64[] + elapsed_times = Float64[] + + sim = prepare_sim(nclients, QuantumOpticsRepr(), noise_model, link_success_prob, 42, rounds) + elapsed_time = @elapsed run(sim) + + # Add logging data to DataFrame + for point in logging + push!(results_per_client, (nclients = nclients, Δt = point[1], fidelity = point[2], avg_elapsed_time = elapsed_time/rounds)) + end + + @info "Simulation with $(nclients) clients finished in $(elapsed_time) seconds" +end +@info("Results per client:\n", results_per_client) \ No newline at end of file diff --git a/examples/piecemakerswitch/static_viz.jl b/examples/piecemakerswitch/static_viz.jl new file mode 100644 index 00000000..e2dfa4dd --- /dev/null +++ b/examples/piecemakerswitch/static_viz.jl @@ -0,0 +1,38 @@ +include("setup.jl") +using GLMakie +GLMakie.activate!(inline=false) + + +logging = Point2f[] # for plotting + +# Set simulation parameters +nusers = 5 +link_success_prob = 0.5 # probability of successful entanglement per attempt +mem_depolar_prob = 0.1 # memory depolarization probability +decoherence_rate = - log(1 - mem_depolar_prob) # decoherence rates +noise_model = Depolarization(1/decoherence_rate) # noise model applied to the memory qubits +rounds = 100 # number of rounds to run + +sim = prepare_sim( + nusers, QuantumOpticsRepr(), noise_model, link_success_prob, 42, rounds +) + +timed = @elapsed run(sim) +@info("Simulation finished in $(timed) seconds") +@info logging + +function plot_fidelity(logging::Vector{Point2f}) + fig = Figure(resolution = (800, 450)) + ax = Axis(fig[1, 1], xlabel = "Δt (simulation time)", ylabel = "Fidelity to GHZₙ", + title = "Entanglement fidelity over time") + scatter!(ax, logging, markersize = 8) + ylims!(ax, 0, 1) + fig +end + +fig = plot_fidelity(logging) +display(fig) +wait() # keeps REPL open until the figure is closed + +# optional: save it +# save("examples/piecemakerswitch/fidelity.png", fig) \ No newline at end of file diff --git a/test/test_examples.jl b/test/test_examples.jl index cb54912b..0ad91983 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -48,6 +48,14 @@ end include("../examples/simpleswitch/1_interactive_visualization.jl") end +@testitem "Examples - piecemakerswitch 1" tags=[:examples_plotting] begin + include("../examples/piecemakerswitch/static_viz.jl") +end + +@testitem "Examples - piecemakerswitch 2" tags=[:examples_plotting] begin + include("../examples/piecemakerswitch/live_visualization_network.jl") +end + @safetestset "Examples - repeatergrid 1a" tags=[:examples_plotting] begin include("../examples/repeatergrid/1a_async_interactive_visualization.jl") end