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

Precompute all possible slider states #29

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft

Precompute all possible slider states #29

wants to merge 40 commits into from

Conversation

fonsp
Copy link
Member

@fonsp fonsp commented Oct 26, 2021

This draft PR will find all possible combinations of sliders, run those, and save the results to a file. THIS MEANS that you will be able to get most of the coolness of PlutoSliderServer without having to run a server!

These files, and their filenames, exactly match the requests that the sliderserver would get if it was actually running. This means that you can statically serve the folder with generated files, and it respond to the GET requests just like a running slider server.

E.g., currently our sliderserver responds to requests like this:

GET ./staterequest/<notebook hash>/<bond values dict msgpack base64>

which exactly matches the request to access the generated file from our static server!

You need the matching changes to Pluto.jl by @Pangoraw fonsp/Pluto.jl#1600

TODO

Schermafbeelding 2021-11-30 om 18 43 01

Demo video of debugging GUI:

https://youtu.be/gDZ33Lpj8Hw

@sethaxen
Copy link

This seems to have problems with PlutoUI.MultiSelect.

Precomputing this notebook with

PlutoSliderServer.export_notebook("notebook.jl"; Precompute_enabled=true)

gives the warning

┌ Warning: Notebook cannot be (fully) precomputed
│   report =
│      Precomputed state summary
│      ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
│    
│      Total size estimate (based on a radom sample): 0 files, 0 bytes
│    
│      ❌ Group: x
│      ============
│    
│      This group could not be precomputed because:
│    
│        •  The set of possible values for x could not be determined because of an unknown reason:
│           Failed Distributed.RemoteException(1,
│           CapturedException(CompositeException(Any[CapturedException(ErrorException("Error
│           deserializing a remote exception from worker 2\nRemote(original) exception of type
│           MethodError\nRemote stacktrace : "), Any[(#33 at none:0, 1), (iterate at generator.jl:47
│           [inlined], 1), (collect at array.jl:782 [inlined], 1), (make_distributed_serializable at
│           PlutoRunner.jl:1795, 1), (#possible_bond_values#80 at PlutoRunner.jl:1788, 1), (top-level
│           scope at WorkspaceManager.jl:245, 1), (eval at boot.jl:370, 1), (#invokelatest#2 at
│           essentials.jl:816, 1), (invokelatest at essentials.jl:813, 1), (#110 at
│           process_messages.jl:285, 1), (run_work_thunk at process_messages.jl:70, 1), (macro expansion
│           at process_messages.jl:285 [inlined], 1), (#109 at task.jl:514, 1)]),CapturedException(KeyError(PlutoUI [7f904dfe-b85e-4ff6-b463-dae2292396a8]), Any[(getindex at
│           dict.jl:484 [inlined], 1), (macro expansion at lock.jl:267 [inlined], 1),
│           (root_module(key::Base.PkgId) at loading.jl:1688, 1),
│           (deserialize_module(s::Distributed.ClusterSerializer{Sockets.TCPSocket}) at
│           Serialization.jl:996, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:898, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket})
│           at Serialization.jl:816, 1),
│           (deserialize_datatype(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, full::Bool) at
│           Serialization.jl:1384, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:869, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket})
│           at Serialization.jl:816, 1),
│           (deserialize_datatype(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, full::Bool) at
│           Serialization.jl:1409, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:869, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket})
│           at Serialization.jl:816, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:876, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket})
│           at Serialization.jl:816, 1),
│           ((::Serialization.var"#5#6"{Distributed.ClusterSerializer{Sockets.TCPSocket}})(i::Int64) at
│           Serialization.jl:975, 1),
│           (ntupleany(f::Serialization.var"#5#6"{Distributed.ClusterSerializer{Sockets.TCPSocket}},
│           n::Int64) at ntuple.jl:43, 1),
│           (deserialize_tuple(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, len::Int64) at
│           Serialization.jl:975, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:859, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket},
│           t::DataType) at Serialization.jl:1499, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:880, 1), (deserialize at Serialization.jl:816 [inlined], 1),
│           (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket},
│           t::Type{CapturedException}) at clusterserialize.jl:223, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:880, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket},
│           t::DataType) at Serialization.jl:1499, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:880, 1), (deserialize at Serialization.jl:816 [inlined], 1),
│           (deserialize_msg(s::Distributed.ClusterSerializer{Sockets.TCPSocket}) at messages.jl:87, 1),
│           (#invokelatest#2 at essentials.jl:816 [inlined], 1), (invokelatest at essentials.jl:813
│           [inlined], 1), (message_handler_loop(r_stream::Sockets.TCPSocket,
│           w_stream::Sockets.TCPSocket, incoming::Bool) at process_messages.jl:176, 1),
│           (process_tcp_streams(r_stream::Sockets.TCPSocket, w_stream::Sockets.TCPSocket,
│           incoming::Bool) at process_messages.jl:133, 1),
│           ((::Distributed.var"#103#104"{Sockets.TCPSocket, Sockets.TCPSocket, Bool})() at task.jl:514,
│           1)])]), Any[(deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket},
│           t::Type{CapturedException}) at clusterserialize.jl:225, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:880, 1), (deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket},
│           t::DataType) at Serialization.jl:1499, 1),
│           (handle_deserialize(s::Distributed.ClusterSerializer{Sockets.TCPSocket}, b::Int32) at
│           Serialization.jl:880, 1), (deserialize at Serialization.jl:816 [inlined], 1),
│           (deserialize_msg(s::Distributed.ClusterSerializer{Sockets.TCPSocket}) at messages.jl:87, 1),
│           (#invokelatest#2 at essentials.jl:816 [inlined], 1), (invokelatest at essentials.jl:813
│           [inlined], 1), (message_handler_loop(r_stream::Sockets.TCPSocket,
│           w_stream::Sockets.TCPSocket, incoming::Bool) at process_messages.jl:176, 1),
│           (process_tcp_streams(r_stream::Sockets.TCPSocket, w_stream::Sockets.TCPSocket,
│           incoming::Bool) at process_messages.jl:133, 1),
│           ((::Distributed.var"#103#104"{Sockets.TCPSocket, Sockets.TCPSocket, Bool})() at task.jl:514,
│           1)])).
└ @ PlutoSliderServer.var"../precomputed/index.jl" /home/sethaxen/.julia/packages/PlutoSliderServer/X2T6k/src/precomputed/index.jl:156

@sethaxen
Copy link

@fonsp, any chance the issue in #29 (comment) might be fixed within the next month? I'm hoping to use this feature to bring this static notebook to life for a workshop.

@fonsp
Copy link
Member Author

fonsp commented May 31, 2023

Hey Seth! Are you happy with using a fork of PlutoUI.jl? In that case, you can change

https://github.com/JuliaPluto/PlutoUI.jl/blob/f74d58ca5f67ab9606a34b8a7ede3e73baf77344/src/MultiCheckBox.jl#L298

to

collect(subarrays((string(i) for i in 1:length(select.options))))

I can't just put this workaround in a PlutoUI release because of the large size of the array when you have lots of options.

Otherwise, this probably won't be fixed until autumn at the earliest :(

@Pangoraw
Copy link
Member

@fonsp I think the issue is the input of subarrays being an iterator (which is not indexable) not the actual output of subarrays being an iterator. I was just looking at this too (JuliaPluto/PlutoUI.jl#260)!

@sethaxen
Copy link

@fonsp and @Pangoraw, I have tried both of your solutions but still get the same error.

Two remaining issues I'm seeing (from mlcolab/IntroML.jl#5 (comment)):

@sethaxen
Copy link

Here is the line from my branch of PlutoUI that is being used: https://github.com/sethaxen/PlutoUI.jl/blob/10e03038a9caa9159de5cc749561997e4c3b05d3/src/MultiCheckBox.jl#L298

@lucaferranti
Copy link
Member

lucaferranti commented Jun 18, 2023

I was trying out this branch, but I am not getting it to work even for the following simple case.

after having generated the html with

julia> PlutoSliderServer.export_directory(; Precompute_enabled=true, Precompute_max_filesize_per_group=1e9)

I get

www_screencapture_com_2023-6-18_15_16.mp4

so it does update the value next to the slider, but the output of the other cell using n remains unchanged.

Am I doing something wrong?

For reference

julia> versioninfo()
Julia Version 1.9.1
Commit 147bdf428cd (2023-06-07 08:27 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 12 × 12th Gen Intel(R) Core(TM) i7-1255U
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-14.0.6 (ORCJIT, alderlake)
  Threads: 1 on 12 virtual cores
Environment:
  JULIA_EDITOR = code
  JULIA_IMAGE_THREADS = 1

(pluto_sandbox) pkg> st
Status `~/work/teaching/pluto_sandbox/Project.toml`
  [c3e4b0f8] Pluto v0.19.26
  [2fc8631c] PlutoSliderServer v0.3.25 `https://github.com/JuliaPluto/PlutoSliderServer.jl.git#static-export-1`
  [7f904dfe] PlutoUI v0.7.51

@sethaxen
Copy link

@lucaferranti at least for me, when I loaded the generated notebook on my machine it was static, but when I deployed it with GitHub Pages, it became dynamic.

@fonsp
Copy link
Member Author

fonsp commented Jun 20, 2023

@sethaxen Are you sure that you are using your PlutoUI fork? The manifest still refers to the latest version: https://github.com/mlcolab/IntroML.jl/pull/5/files#diff-de5aebd2f07be72d79aa0280756882a9be1d2711d41daeaa375da1e8792dd60fR2717

Use Pluto.activate_notebook_environment to fix this, and use AbstractPlutoDingetjes.possible_values inside your notebook to verify that it works.

@fonsp
Copy link
Member Author

fonsp commented Jun 20, 2023

@lucaferranti It only works when hosted on a web server, like LiveServer.jl or deno run --allow-net --allow-read https://deno.land/[email protected]/http/file_server.ts or GitHub Pages (like Seth suggested), nginx, netlify, etc. Try to get familiar with the Chrome Dev Tools (Network tab) to be able to diagnose network issues! You should see that the requests are being blocked (because you are not allowed to request a file:// URL dynamically).

@sethaxen
Copy link

@sethaxen Are you sure that you are using your PlutoUI fork? The manifest still refers to the latest version: https://github.com/mlcolab/IntroML.jl/pull/5/files#diff-de5aebd2f07be72d79aa0280756882a9be1d2711d41daeaa375da1e8792dd60fR2717

Use Pluto.activate_notebook_environment to fix this, and use AbstractPlutoDingetjes.possible_values inside your notebook to verify that it works.

Thanks, that worked perfectly! I still see the other issue where the plots are not responsive to the first and last values in the scrubbable, see e.g. https://mlcolab.github.io/IntroML.jl/previews/PR6/supervised_learning.html#cd49e0a5-4120-481a-965e-72e7bdaf867c

@lucaferranti
Copy link
Member

lucaferranti commented Jun 25, 2023

This currently errors if some notebooks are excluded for PlutoSliderServer.

Steps to reproduce

  1. grab a simple pluto notebook, e.g. this one

  2. add the following PlutoDeployment.toml

[SliderServer]
exclude = ["pluto_sandbox.jl"] # or whatever your notebook is called
  1. Run
PlutoSliderServer.export_directory(; Precompute_enabled=true)

This will produce the error

┌ Info: 
│   ◐ [1/1] Launching...
└   s.path = "pluto_sandbox.jl"

┌ Info: Shutting down notebook process
└   s.path = "pluto_sandbox.jl"

ERROR: type FinishedNotebook has no field bond_connections
Stacktrace:
  [1] getproperty
    @ ./Base.jl:37 [inlined]
  [2] generate_precomputed_staterequests(notebook_session::PlutoSliderServer.var"../Types.jl".NotebookSession{String, String, PlutoSliderServer.var"../Types.jl".FinishedNotebook}; settings::PlutoSliderServer.var"../Configuration.jl".PlutoDeploySettings, pluto_session::Pluto.ServerSession, output_dir::String)
    @ PlutoSliderServer.var"../precomputed/index.jl" ~/.julia/packages/PlutoSliderServer/vYCzf/src/precomputed/index.jl:130
  [3] process(s::PlutoSliderServer.var"../Types.jl".NotebookSession{Nothing, String, Nothing}; server_session::Pluto.ServerSession, settings::PlutoSliderServer.var"../Configuration.jl".PlutoDeploySettings, output_dir::String, start_dir::String, progress::String)


sesh = notebook_session
run = sesh.run
connections = run.bond_connections
Copy link
Member

@lucaferranti lucaferranti Jun 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this causes problems if I have some notebooks under PlutoSliderServer_exclude (see this comment), because in that case run will be FinishedNotebook.

Myabe one dirty fix could be to have before this

run isa FinishedNotebook && return

?

cleaner might be not to call this in the first place for notebooks under PlutoSliderServer_exclude.

@gdalle
Copy link

gdalle commented Jun 29, 2023

Hey Fons and friends! Not sure I can help, just wanted to cheer from the sidelines because this looks awesome

@eguar11011
Copy link

eguar11011 commented Oct 24, 2024

I don’t quite understand how to precompute my sliders. What I did was run:

using PlutoSliderServer
PlutoSliderServer.export_notebook("path/to/notebook.jl";  Precompute_enabled=true)

And I had these errors:

ERROR: InvalidKeyError: invalid key Precompute_enabled, possible keys are: SliderServer_enabled, SliderServer_exclude, SliderServer_port, SliderServer_host, SliderServer_watch_dir, SliderServer_serve_static_export_folder, SliderServer_simulated_lag, SliderServer_cache_control... please check documentation for other valid keys

@behinger
Copy link

behinger commented Oct 25, 2024

did you check out the correct branch?
e.g. via

]add PlutoSliderServer#static-export-1

@eguar11011
Copy link

@behinger
I don't understand, would I have to download sliderserver but from another branch?
Meaning, not with using PlutoSliderServer?

@behinger
Copy link

using is fine, but you first need to install it from a different branch, e.g. https://stackoverflow.com/questions/62837941/julia-how-to-change-a-packages-branch

hope that helps!

@eguar11011
Copy link

@behinger
Thank you very much; I now understand how to download it from a different branch and generate the precomputed files.

I already have the files, but when I upload them to the site, there’s still no interactivity. Is there anything else I should keep in mind?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Precompute all possible binder states