Recommended way for self-triggering computation / semi-infinite loop?

Is there a recommended way to trigger a cell (which mutates state) to re-run after it's done -- under some conditions? I was under the impression the following combination of switch/stop would do the trick, but it doesn't seem to be able to escape the infinite loop?
import marimo as mo
import random
import time
import marimo as mo
import random
import time
get_seconds, set_seconds = mo.state(1)
get_seconds, set_seconds = mo.state(1)
def expensive_computation():
seconds = get_seconds()
time.sleep(seconds)
set_seconds(random.randint(1,3))
def expensive_computation():
seconds = get_seconds()
time.sleep(seconds)
set_seconds(random.randint(1,3))
infinite_loop = mo.ui.switch(
False,
label="infinite loop",
)
infinite_loop
infinite_loop = mo.ui.switch(
False,
label="infinite loop",
)
infinite_loop
mo.stop(not infinite_loop.value)
expensive_computation()
mo.stop(not infinite_loop.value)
expensive_computation()
My actual use-case (https://marimo.io/p/@gvarnavides/iterative-ptychography) is less contrived. Essentially I'd like to plot the output of an iterated map at every iteration. Right now, I'm doing it with mo.ui.refresh - which works well, but I'd like a "as quick as possible" update instead of fixed time-increments (since the computation time varies with batch_size).
marimo | Iterative Ptychography
Explore data and build apps seamlessly with marimo, a next-generation Python notebook.
11 Replies
Myles Scolnick
Myles Scolnick5mo ago
On set_state, I think there is an include_self flag you can pass The refresh button has network latency and other latencies included so not great as an accurate timer
Georgios Varnavides
I played around with include_self and it doesn't seem to help (in-fact presumably the default False is designed to capture this infinite-loop behaviour?)
Myles Scolnick
Myles Scolnick5mo ago
I think you can put get and set in the same cell and use include_self
Georgios Varnavides
Not sure that's the issue? like I can get set/get to loop infinitely just fine. I just want a way to kill that once it's running. The following doesn't seem to work
mo.stop(not infinite_loop.value)
expensive_computation()
mo.stop(not infinite_loop.value)
expensive_computation()
playground here: https://marimo.app/?slug=19s2hr
marimo | a next-generation Python notebook
Explore data and build apps seamlessly with marimo, a next-generation Python notebook.
Myles Scolnick
Myles Scolnick5mo ago
oh sorry I misunderstood. Hmm yea I’m not sure how to interrupt that
Georgios Varnavides
I wondered if the on_change parameter of the mo.ui.switch input could be used to emit some sort of event to break the set/get cycle, but not sure what that would be
Myles Scolnick
Myles Scolnick5mo ago
Could you raise an error?
Georgios Varnavides
wow, that doesn't seem to bother it either haha
def raise_(val):
if not val:
raise ValueError()

infinite_loop = mo.ui.switch(
False,
label="infinite loop",
on_change=lambda v: raise_(v)
)
def raise_(val):
if not val:
raise ValueError()

infinite_loop = mo.ui.switch(
False,
label="infinite loop",
on_change=lambda v: raise_(v)
)
Myles Scolnick
Myles Scolnick5mo ago
hmm I have no other ideas. We can look into supporting this if there is a good api. Seems like start/stop has been somewhat tried a few times
Akshay
Akshay5mo ago
@Georgios Varnavides can you use mo.output.replace?
Georgios Varnavides
hmm, trying to think how exactly 🤔 I'd still need to mutate state for the iteration of the sort x_{n+1} = f[x_n] right?