How to hand-craft an edge into a Pluto's a computational graph?

Consider the following contrived example of a Pluto notebook:

First of all, I have a hard time even to visualise the corresponding computational graph. Below is my attempt to indicate by the dashed arrow that the computation of sum(x) is triggered by changes of x defined upstream, but not by changes in n:

Put in words, the middle node (the 3d cell) doesn’t trigger downstream computations: sum(x) (the 4th cell) is not recomputed whenever n changes its value.

This ill-defined behaviour is just a symptom. It is easy, of course, to link n to sum(x) to obtain a well-defined state in this case. My objective, however, is to understand how could I introduce an explicit edge between nodes/cells 3 and 4, without referencing upstream n.

What triggering technique could be used to this end?

I know this is probably not what you are looking for, but my answer would be to combine expressions into cells using begin ... end (like 1 and 3) or to introduce a function that returns a modified copy of x instead of the in-place modification that you have now.

Yeah, of course. But, you’re right, it’s not what I’m after.
(Thanks for looking into it, anyway.)

I think Pluto’s graph follows assignments, not necessarily modifications.

### A Pluto.jl notebook ###
# v0.19.36

using Markdown
using InteractiveUtils

# ╔═╡ 371228e6-afce-11ee-0e08-ffcab94f43bb
x = collect(1:10)

# ╔═╡ a0c5e59e-102b-4d04-a568-b19456a6d79a
n = 5

# ╔═╡ f59fac3f-6c9c-4b4e-87f6-44a80f036af0
y = if isodd(n)
    x[1:2:n] .= 42
    x
end

# ╔═╡ e75712ff-f50d-4f00-be5b-77408a49ece3
sum(y)

# ╔═╡ Cell order:
# ╠═371228e6-afce-11ee-0e08-ffcab94f43bb
# ╠═a0c5e59e-102b-4d04-a568-b19456a6d79a
# ╠═f59fac3f-6c9c-4b4e-87f6-44a80f036af0
# ╠═e75712ff-f50d-4f00-be5b-77408a49ece3

I don’t see how to add an assignment that catches reducing n yet.

Edit. ugh:

y = if isodd(n)
	y = copy(x)
	y[1:2:n] .= 42
	y
end
1 Like

The core issue here is that modification have no deeper order. E.g.
Consider cells like:

# cell 1
x = Ref(2)

# cell 2
x[] = 5

# cell 3
@show x

Then it is clear that cell 1 must be executed first becuase it defines the variable x used in the other cells. But there is no unambigous order of cells 2 and 3.

You can introduce “auxiliary variables” to establish an order like:

# cell 1
x = Ref(2)

# cell 2
x[] = 5; aux=nothing;

# cell 3
aux; @show x
3 Likes

I think Pluto’s graph follows assignments, not necessarily modifications.

Yep, good point. Thanks!

The proposed solution would work for this case, but it still misses the point.

Correct! The issue is clear and there is no way to remove the unambiguity automatically. Hence the need to introduce an order explicitly.

Auxiliary variables solution is close. Yet, the goal here is to trigger the follow-up computations only if the input modification actually did take place. I.e., in the given example, I seek to recompute sum(x) only when x[1:2:n] .= 42 was actually executed.

I see now, that it was not clear from the initial description. Sorry.

The if block was meant to illustrate the problem: an aux variable put within the scope of if would result in the aux not defined error for even values of n.

That’s where I kinda stuck.

Is ‘undoing’ the modification of x when n becomes even or smaller a desired outcome?

Nope, previously modified entries of x remain intact.

Below, the closest I’ve got to to the required functionality so far.

The last bit is somewhat weird but it does the job: sum(x) is recomputed when either the whole vector x is re-assigned or a subset of its entries gets modified.

Thanks everyone for discussion! Please do let me know if you find of a better way to achieve the desired goal.

# ╔═╡ b6de62ec-afa8-11ee-3cab-c1aacaec6cb3
x = collect(1:10)

# ╔═╡ 10e03418-8a12-4cd9-9283-bf5bcad49618
n = 3

# ╔═╡ aa375da5-dba0-4a0e-8e96-3b0d80d32458
isodd(n) && (aux=nothing; x[1:2:n] .= 42)

# ╔═╡ 669e5597-2506-409d-b13c-39c81539e60a
if @isdefined aux
	sum(x)
else
	sum(x)
end

The part of Pluto analyzing expression and determining their order and graph has been recently moved to separate packages that are standalone registered in General:

You might have a look there for more details or directly use them for your use case if relevant!

Fun question! The “official” answer to the question in the title is: create variables! Pluto only tracks assignments and references, so that’s the mechanism you need to use to tell Pluto about extra edges.

The example from abraemer will work, also when n is even.

x = collect(1:10)

n = 3

begin
    modification_done = nothing
    if isodd(n)
        x[1:2:n] .= 37
    end
end

begin
    # reference this variable to create the reactive edge
    modification_done
    sum(x)
end

But I think that a solution that uses copy like contradict suggested is probably better, as this eliminates any surprises when working on one of the intermediate cells.

2 Likes