Streamplot statemap using JSServe

I’m trying to record a statemap to include an interactive streamplot in a web page. Is there a way to pull out the actual Float64 value from the observable?

Thanks in advance.

using WGLMakie, JSServe
WGLMakie.activate!()

open("index.html", "w") do io
    println(io, """
    <html>
        <head>
        </head>
        <body>
    """)
    show(io, MIME"text/html"(), Page(exportable=true, offline=true))
   
    show(io, MIME"text/html"(),App() do session::Session

    param_slider = Slider(0.1:0.1:1.0)
    
    fig = Figure(resolution = (700, 700), fontsize = 18, font = "sans")
    ax = Axis(fig[1,1],backgroundcolor = :black)
    p = vcat([param_slider.value,1.0]',[1.1,1.0]')
    function f(x,y)
        u = [x,y]
        du = (u .*(1 .-u) .*(p[:,1] .-p[:,2])) ./ ((u .*p[:,1]) .+ ((1 .-u) .*p[:,2])) 
        return Point(du...)
    end

    stplt = streamplot!(ax, f, 0..1, 0..1, colormap = :plasma,
        gridsize= (20,20), arrow_size = 10) 
     slider = DOM.div("z-index: ", param_slider, param_slider.value)
    return JSServe.record_states(session, DOM.div(slider, fig))
end)
    println(io, """
        </body>
    </html>
    """)
end

Produces

ERROR: MethodError: no method matching adjoint(::Observable{Float64})
Closest candidates are:
  adjoint(::StrideArraysCore.PtrArray{S, D, T, 1, C, B, R, X, O}) where {S, D, T, C, B, R, X, O} at C:\Users\arn203\.julia\packages\StrideArraysCore\8rPwH\src\adjoints.jl:45
  adjoint(::LinearAlgebra.Transpose{var"#s155", var"#s154"} where {var"#s155"<:Real, var"#s154"<:Union{StaticArrays.StaticVector{N, T} where {N, T}, StaticArrays.StaticMatrix{N, M, T} where {N, M, T}}}) at C:\Users\arn203\.julia\packages\StaticArrays\0yhGP\src\linalg.jl:73
  adjoint(::LinearAlgebra.Transpose{var"#s832", S} where {var"#s832"<:Real, S}) at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\LinearAlgebra\src\adjtrans.jl:166
  ...
Stacktrace:
  [1] getindex
    @ C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\LinearAlgebra\src\adjtrans.jl:202 [inlined]
  [2] iterate(A::LinearAlgebra.Adjoint{Any, Vector{Any}}, state::Tuple{Base.OneTo{Int64}})
    @ Base .\abstractarray.jl:1096
  [3] iterate
    @ .\abstractarray.jl:1094 [inlined]
  [4] _unsafe_setindex!(::IndexLinear, ::Matrix{Any}, ::LinearAlgebra.Adjoint{Any, Vector{Any}}, ::UnitRange{Int64}, ::Base.Slice{Base.OneTo{Int64}})
    @ Base .\multidimensional.jl:897
  [5] _setindex!
    @ .\multidimensional.jl:887 [inlined]
  [6] setindex!
    @ .\abstractarray.jl:1267 [inlined]
  [7] _typed_vcat(#unused#::Type{Any}, A::Tuple{LinearAlgebra.Adjoint{Any, Vector{Any}}, LinearAlgebra.Adjoint{Float64, Vector{Float64}}})
    @ Base .\abstractarray.jl:1561
  [8] typed_vcat
    @ .\abstractarray.jl:1567 [inlined]
  [9] vcat
    @ C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\SparseArrays\src\sparsevector.jl:1114 [inlined]
 [10] (::var"#53#55")(session::Session)
    @ Main .\REPL[70]:18
 [11] (::JSServe.var"#1#5"{var"#53#55"})(session::Session, request::NamedTuple{(:show,), Tuple{String}})    @ JSServe C:\Users\arn203\.julia\packages\JSServe\E7QrV\src\types.jl:15
 [12] #invokelatest#2
    @ .\essentials.jl:708 [inlined]
 [13] invokelatest
    @ .\essentials.jl:706 [inlined]
 [14] show_in_page(page::Page, app::App)
    @ JSServe C:\Users\arn203\.julia\packages\JSServe\E7QrV\src\display.jl:288
 [15] show(io::IOStream, m::MIME{Symbol("text/html")}, app::App)
    @ JSServe C:\Users\arn203\.julia\packages\JSServe\E7QrV\src\display.jl:377
 [16] (::var"#52#54")(io::IOStream)
    @ Main .\REPL[70]:10
 [17] open(::var"#52#54", ::String, ::Vararg{String, N} where N; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Base .\io.jl:330
 [18] open(::Function, ::String, ::String)
    @ Base .\io.jl:328
 [19] top-level scope
    @ REPL[70]:1

Wait, who is taking a derivative here? streamplot only evaluates f. Perhaps check if p = vcat([param_slider.value,1.0]',[1.1,1.0]') does something funny

Edit: Nobody takes a derivative, the issue is the use of a transpose/adjoint.

My mistake! I often see adjoints wrt AD in Julia. Completely glossed over the transposition in my code. Will edit original post, in case it misleads people.

@oxinabox water on your mill

1 Like

Removing the transpose

p = [param_slider.value 1.0; 1.1 1.0]

I now get

ERROR: MethodError: no method matching -(::Observable{Float64}, ::Float64)

So I guess it’s still the issue of getting an actual Float64 from the observable. But I may be misunderstanding something fundamental about how JSServe works.

You should read: http://makie.juliaplots.org/stable/interaction/nodes.html
To see how to actually work with observables :wink:
Or: Home · Observables.jl
For your example it should be:

p = map(val-> [val 1.0; 1.1 1.0], param_slider.value) # returns a new observable with that value
1 Like

Thanks! that seems like a pretty sensible thing to do…

I’m still a bit lost though to be honest. I woud expect the following to create an interactive plot, where both the marker size and the slope change with a slider. but only the marker size changes.


open("index10.html", "w") do io
    println(io, """
    <html>
        <head>
        </head>
        <body>
    """)
    show(io, MIME"text/html"(), Page(exportable=true, offline=true))
    show(io, MIME"text/html"(),App() do session::Session
    param_slider = Slider(1:10)
    size = map(val-> val, param_slider.value) 
    
    x = collect(1:0.1:10)
    fig = Figure(resolution = (700, 700), fontsize = 18, font = "sans")
    ax = Axis(fig[1,1])  
    sca = scatter!(ax, x, x .* to_value(size), markersize =size) 
    
    slider = DOM.div("size: ", param_slider, param_slider.value)
    return JSServe.record_states(session, DOM.div(slider, fig))
    end)
    println(io, """
        </body>
    </html>
    """)
end

How can makie know to update the value when size changes, when it’s not an observable anymore?

to_value(size), removes the observable, and therefore any updates…
For your mental model, to_value is the same as doing something:

data = [33]
x  = data[1] # same as to_value(obs)
data[1] = 22
x !== 22 # would you expect x to change?!

Which is also why obs[] == to_value(obs)
You should really read the docs…

Sorry if I’m just being dim. I read the docs you sent on Makie and Observables.jl. the only reason I used to_value was that I can’t do mathematical functions on observable/nodes. For example, in the line of code you provided before

p = map(val-> [val 1.0; 1.1 1.0], param_slider.value) 

p is still of type observable. So I couldn’t use it in my function f(x,y).

so you need to move f into the map?

Yes. Sorry, I’m slowly figuring it out. thanks for your patience!

1 Like

As in, I’ve got the output i’m looking for for a simple line plot. Should be straight forward to do the stream plot now.

I spoke too soon… I thought I had it sussed by moving the the whole streamplot into the map

fig = map(param_slider) do v
    return streamplot((x,y) -> Point(x - v,y/v), 0..1.0, 0..1.0)
end

This produces a plot, but once the slider is moved the plot vanishes, never to be seen again.

I tried putting just the function f(x,y) in the map but that also lead to out of bounds errors.

So I ended up pulling out the parts of the streamplot as observables aand putting them back together, like so

params = []
   for i in 1:5
   push!(params,map(index_slider) do idx    
        f(x,y) = Point(x- idx,1/y)
        impl = Makie.streamplot_impl(StreamPlot,f, Makie.HyperRectangle(0.0,0.0,1.0,1.0),(20,20), 0.01, 500, 1.0)
        return i ==2 ? impl[i] ./1000 : impl[i] 
        end)
    end

   ax = Axis(fig[1,1])
   lines!(ax, params[3])
   arrows!(ax,params[1],params[2])

This works fine! but notice I’ve not used params 4 & 5 (the colors of the arrows and streams), since it turns out this is where the errors were coming from when I tried just putting f(x,y) in the map. That is, if I try

lines!(ax, params[3], color = params[4])
arrows!(ax,params[1],params[2], color = params[5])

I get

BoundsError: attempt to access 6698-element Array{RGBA{Float32},1} with eltype ColorTypes.RGBA{Float32} at index [6699]┌ Warning: ERROR: MethodError occurred

which is the same error I get from

 f = map(index_slider) do idx    
        return (x,y) -> Point(x- idx,1/y)
   end
streamplot(fig[1,1]f,0..1.0,0..1.0)