Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background Map for Plotting #170

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v0.5.1-dev

- Add `classical_delay` and `quantum_delay` as keyword arguments to the `RegisterNet` constructor to set a default global network edge latency.
- Plots of networks can now overlay real-world maps (see `generate_map`).

## v0.5.0 - 2024-10-16

Expand Down
7 changes: 5 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumSavory"
uuid = "2de2e421-972c-4cb5-a0c3-999c85908079"
authors = ["Stefan Krastanov <[email protected]>"]
version = "0.5.1-dev"
version = "0.5.2"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand All @@ -27,19 +27,22 @@ SumTypes = "8e1ec7a9-0e02-4297-b0fe-6433085c89f2"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"

[extensions]
QuantumSavoryGeoMakie = "GeoMakie"
QuantumSavoryMakie = "Makie"

[compat]
Combinatorics = "1"
ConcurrentSim = "1.4.1"
Distributions = "0.25.90"
DocStringExtensions = "0.9"
GeoMakie = "0.7.8"
Graphs = "1.9"
IterTools = "1.4.0"
LinearAlgebra = "1"
Makie = "0.20, 0.21"
Makie = "0.21"
NetworkLayout = "0.4.4"
PrecompileTools = "1"
Printf = "1"
Expand Down
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Expand Down
19 changes: 19 additions & 0 deletions docs/src/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,26 @@ Other configuration options are available as well (the ones ending on `plot` let
```@example vis
propertynames(plt)
```
## Plotting Registers on a Background Map
If your registers have latitude and longitude coordinates (ranging from -180 to 180), you can plot them directly on a map. One way is to use `generate_map` function to create the map as a plotting axis using the package 'GeoMakie'. Here's how you can do this with the registers defined earlier:

```@example vis
using GLMakie # hide
GLMakie.activate!() # hide
net = RegisterNet([Register(2),Register(3),Register(2),Register(5)]) # hide
initialize!(net[1,1]) # hide
initialize!(net[2,3], X₁) # hide
initialize!((net[3,1],net[4,2]), X₁⊗Z₂) # hide
apply!((net[2,3],net[3,1]), CNOT) # hide
using GeoMakie
fig = Figure(size=(800,400))
ax = generate_map(fig[1, 1])
_, ax, plt, obs = registernetplot_axis(ax, net, registercoords=[Point2f(-118, 34), Point2f(-71, 42), Point2f(-111, 34), Point2f(-96, 32)], state_linecolor=:black)
xlims!(ax, -125, -65)
ylims!(ax, 25, 50)
fig
```
In general, if you have a custom background axis, you can use it as the axis parameter in `registerplot_axis`.
## State and tag metadata in interactive visualizations

When working with interactive plots, you can also hover over different parts of the visualization to see the registers, what is stored in them, and potentially whether they contain any [tagged metadata in use by simulated networking protocols](@ref tagging-and-querying).
Expand Down
38 changes: 38 additions & 0 deletions ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module QuantumSavoryGeoMakie

using QuantumSavory
using GeoMakie
import GeoMakie: GeoAxis, image!, poly!, naturalearth, Makie
import QuantumSavory: generate_map

"""
Generates a default map with country and state boundaries and returns a GeoAxis. The returned GeoAxis can be used as an input for `registernetplot_axis`.

For borders, the optional `scale` parameter can be set to 10, 50, or 110, corresponding to Natural Earth resolutions of 1:10m, 1:50m, and 1:110m.
"""
function generate_map(subfig::Makie.GridPosition, scale::Int=110)
if scale ∉ (10, 50, 110)
error("Invalid scale value: scale must be 10, 50, or 110.")

Check warning on line 15 in ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl#L13-L15

Added lines #L13 - L15 were not covered by tests
end

ax = GeoAxis(subfig; limits=((-180, 180), (-90, 90)), dest="+proj=longlat +datum=WGS84")
countries = naturalearth("admin_0_countries", scale)
states = naturalearth("admin_1_states_provinces_lakes", scale)
image!(ax, (-180, 180), (-90, 90), GeoMakie.earth() |> rotr90; interpolate=false, inspectable=false)
poly!(ax, GeoMakie.land(); color=:lightyellow, strokecolor=:transparent, inspectable=false)
poly!(ax, GeoMakie.to_multipoly.(countries.geometry), color=:transparent, strokecolor=(:black, 0.5), strokewidth=0.7, inspectable=false)
poly!(ax, GeoMakie.to_multipoly.(states.geometry); color=:transparent, strokecolor=(:grey, 0.5), strokewidth=0.5, inspectable=false)

Check warning on line 24 in ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl#L18-L24

Added lines #L18 - L24 were not covered by tests

#ax.scene.paddings[] = 0
#Makie.hidemargins!(ax)
tightlimits!(ax)

Check warning on line 28 in ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl#L28

Added line #L28 was not covered by tests

return ax

Check warning on line 30 in ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl#L30

Added line #L30 was not covered by tests
end

function generate_map()
fig = Makie.Figure()
generate_map(fig[1, 1])

Check warning on line 35 in ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryGeoMakie/QuantumSavoryGeoMakie.jl#L33-L35

Added lines #L33 - L35 were not covered by tests
end

end
35 changes: 26 additions & 9 deletions ext/QuantumSavoryMakie/QuantumSavoryMakie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -280,27 +280,44 @@

##

"""Draw the given registers on a given Makie axis.
Krastanov marked this conversation as resolved.
Show resolved Hide resolved
"""Draw the given registers on a given Makie axis or a subfigure.

It returns a tuple of (subfigure, axis, plot, observable).
The observable can be used to issue a `notify` call that updates
the plot with the current state of the network."""
function registernetplot_axis(subfig, registersobservable; infocli=true, datainspector=true, kwargs...)
ax = Makie.Axis(subfig)
function registernetplot_axis end

function registernetplot_axis(ax::Makie.AbstractAxis, registersobservable; infocli=true, datainspector=true, map=false, kwargs...)

Check warning on line 290 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L290

Added line #L290 was not covered by tests
p = registernetplot!(ax, registersobservable; kwargs...)
ax.aspect = Makie.DataAspect()
Makie.hidedecorations!(ax)
Makie.hidespines!(ax)
if hasmethod(Makie.hidedecorations!, Tuple{typeof(ax)})
Makie.hidedecorations!(ax)

Check warning on line 294 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L293-L294

Added lines #L293 - L294 were not covered by tests
end
if hasmethod(Makie.hidespines!, Tuple{typeof(ax)})
Makie.hidespines!(ax)

Check warning on line 297 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L296-L297

Added lines #L296 - L297 were not covered by tests
end
Makie.deregister_interaction!(ax, :rectanglezoom)
if infocli
rnh = RNHandler(p)
Makie.register_interaction!(ax, :registernet, rnh)
end
if datainspector
DataInspector(subfig)
DataInspector(ax.parent)

Check warning on line 305 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L305

Added line #L305 was not covered by tests
end
Makie.autolimits!(ax)
subfig, ax, p, p[1]
if hasmethod(Makie.autolimits!, Tuple{typeof(ax)})
Makie.autolimits!(ax)

Check warning on line 308 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L307-L308

Added lines #L307 - L308 were not covered by tests
end
ax.parent, ax, p, p[1]

Check warning on line 310 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L310

Added line #L310 was not covered by tests
end

function registernetplot_axis(subfig::Makie.GridPosition, registersobservable; infocli=true, datainspector=true, kwargs...)
registernetplot_axis(Makie.Axis(subfig), registersobservable; infocli, datainspector, kwargs...)

Check warning on line 314 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L313-L314

Added lines #L313 - L314 were not covered by tests
end

function registernetplot_axis(registersobservable; infocli=true, datainspector=true, kwargs...)
fig = Figure()
ax = Axis(fig[1, 1])
registernetplot_axis(ax, registersobservable; infocli, datainspector, kwargs...)

Check warning on line 320 in ext/QuantumSavoryMakie/QuantumSavoryMakie.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumSavoryMakie/QuantumSavoryMakie.jl#L317-L320

Added lines #L317 - L320 were not covered by tests
end

##
Expand Down Expand Up @@ -358,4 +375,4 @@
tuple(Makie.shift_project(ax.scene, p.registercoords[][reg].+(0,slot-1))...);
end

end
end
21 changes: 20 additions & 1 deletion src/QuantumSavory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,31 @@ export
# noninstant.jl
AbstractNoninstantOperation, NonInstantGate, ConstantHamiltonianEvolution,
# plots.jl
registernetplot, registernetplot!, registernetplot_axis, resourceplot_axis
registernetplot, registernetplot!, registernetplot_axis, resourceplot_axis, generate_map


#TODO you can not assume you can always in-place modify a state. Have all these functions work on stateref, not stateref[]
# basically all ::QuantumOptics... should be turned into ::Ref{...}... but an abstract ref

# warnings for
function __init__()
if isdefined(Base.Experimental, :register_error_hint)
Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs
if exc.f === registernetplot
println(io, "\n`registernetplot!` requires the package `Makie`; please make sure `Makie` is installed and imported first.")
elseif exc.f === registernetplot!
println(io, "\n`registernetplot!` requires the package `Makie`; please make sure `Makie` is installed and imported first.")
elseif exc.f === registernetplot_axis
println(io, "\n`registernetplot_axis` requires the package `Makie`; please make sure `Makie` is installed and imported first.")
elseif exc.f === resourceplot_axis
println(io, "\n`resourceplot_axis` requires the package `Makie`; please make sure `Makie` is installed and imported first.")
elseif exc.f === generate_map
println(io, "\n`generate_map` requires the package `GeoMakie`; please make sure `GeoMakie` is installed and imported first.")
end
end
end
end

include("traits_and_defaults.jl")

include("tags.jl")
Expand Down
8 changes: 7 additions & 1 deletion src/plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function registernetplot end
Requires a Makie backend be already imported."""
function registernetplot! end

"""Draw the given register network on a given Makie subfigure and modify the axis with numerous visualization enhancements.
"""Draw the given register network on a given Makie axis or subfigure and modify the axis with numerous visualization enhancements.

Requires a Makie backend be already imported."""
function registernetplot_axis end
Expand All @@ -22,3 +22,9 @@ function resourceplot_axis end
function showmetadata end

function showonplot end

"""Generates a default map with country and state boundaries and returns a GeoAxis. The returned GeoAxis can be used as an input for registernetplot_axis.

The `GeoMakie` package must be installed and imported."""
function generate_map end

1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
Expand Down
32 changes: 32 additions & 0 deletions test/test_plotting_3_maps.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using FileIO
using GeoMakie
using QuantumSavory

sizes = [2,3,5]
registers = Register[]
for s in sizes
traits = [Qubit() for _ in 1:s]
bg = [T2Dephasing(1.0) for _ in 1:s]
push!(registers, Register(traits,bg))
end
network = RegisterNet(registers)
map_axis = generate_map()
coords = [Point2f(-71, 42), Point2f(-111, 34), Point2f(-122, 37)]
_, _, plt, netobs = registernetplot_axis(map_axis, network, registercoords=coords)
save(File{format"PNG"}(mktemp()[1]), fig)

initialize!(network[1,1])
initialize!(network[2,1])
notify(netobs)
save(File{format"PNG"}(mktemp()[1]), fig)

apply!([network[1,1],network[2,1]], CNOT)
notify(netobs)
save(File{format"PNG"}(mktemp()[1]), fig)

display(fig)

fig = Figure()
map_axis = generate_map(fig[1, 1])
_, _, plt, netobs = registernetplot_axis(map_axis, network, registercoords=coords)
save(File{format"PNG"}(mktemp()[1]), fig)
3 changes: 3 additions & 0 deletions test/test_plotting_cairo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ end
@testset "arguments and observables and tags" begin
include("test_plotting_2_tags_observables.jl")
end
@testset "background map" begin
include("test_plotting_3_maps.jl")
end
end
3 changes: 3 additions & 0 deletions test/test_plotting_gl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ end
@testset "arguments and observables and tags" begin
include("test_plotting_2_tags_observables.jl")
end
@testset "background map" begin
include("test_plotting_3_maps.jl")
end

@testset "data inspectors" begin # only available in GLMakie
# create a network of qubit registers
Expand Down
Loading