Repository navigation
[Discussion]: Handling operations that use/modify RNG #291
Copy link
Copy link
Open
Labels
Description
What is your question?
Per discussion with @llrs-roche, here are some topics for the contributors to discuss and research.
Problem
Random/stoichastic operations are not being well handled in within/teal.code::eval_code, in particular when code parse is being used.
In the section below you can observe that q |> get_code(names = "IRIS") is not fully reproducible as the random operations before were not included, nor was the RNG state updated before code execution
Reproducible code
This code highlights the 2 main problems:
set.seed()and other random operations are not kept in partial code (q |> get_code(names = "XXX"))MTCARS/IRISrandom operations are influenced by preceding code
library(teal.code)
q <- qenv() |>
within({
IRIS <- datasets::iris
MTCARS <- datasets::mtcars
set.seed(1)
.random <- runif(1)
IRIS$new <- runif(nrow(IRIS))
MTCARS$new <- runif(nrow(MTCARS))
})
q |> get_code() |> cat()
#> iris <- datasets::iris
#> mtcars <- datasets::mtcars
#> set.seed(1)
#> .random <- runif(1)
#> iris$new <- runif(nrow(iris))
#> mtcars$new <- runif(nrow(mtcars))
q |> get_code(names = "IRIS") |> cat()
#> IRIS <- datasets::iris
#> IRIS$new <- runif(nrow(IRIS))
local({
eval(str2expression(q |> get_code(names = "IRIS")))
q$IRIS |> rlang::hash() |> cat("# hash qenv IRIS\n")
IRIS |> rlang::hash() |> cat("# hash reproduced IRIS\n")
})
#> 9bba33c8ed5cd90cd13316f35f812f55 # hash qenv IRIS
#> c90a89c1814206bc19fdc32d04d1eefa # hash reproduced IRIS
q |> get_code(names = "MTCARS") |> cat()
#> MTCARS <- datasets::mtcars
#> MTCARS$new <- runif(nrow(MTCARS))
local({
eval(str2expression(q |> get_code(names = "MTCARS")))
q$mtcars |> rlang::hash() |> cat("# hash qenv MTCARS\n")
mtcars |> rlang::hash() |> cat("# hash reproduced MTCARS\n")
})
#> 2c0a8a99dc147d5445c3b49d035665b2 # hash qenv MTCARS
#> 6755d143ff87b73a1196c186cec7e86a # hash reproduced MTCARSReference material
- ref1: [Bug]: Functions with side effects are not detected as dependencies #278
- ref2: https://www.jottr.org/2020/09/21/detect-when-the-random-number-generator-was-used/
- Detect when random seed is modified
- ref3: https://blog.djnavarro.net/posts/2023-12-27_seedcatcher/
Possible set of solutions
- Use
teal.code::eval_code()and# @linksto XXXXXto include random operations- Status quo
- Cons: Requires character base code execution and requires manual maintenance
- Detect random seed at start and inform user
- Track random seed and incorporate it in code execution
- When using code parser, re-run partial code to sync modules with reproducible code (while keeping
set.seed()expressions)- cons: slow down teal app even more and data is not consistent with initial
dataargument in teal.
- cons: slow down teal app even more and data is not consistent with initial
- Always include
set.seed()expression and hope it doesn't have a big impact
Code of Conduct
- I agree to follow this project's Code of Conduct.
Contribution Guidelines
- I agree to follow this project's Contribution Guidelines.
Security Policy
- I agree to follow this project's Security Policy.
Reactions are currently unavailable
Activity
averissimo commentedon Aug 26, 2026
My latest idea to tackle this problem is to inject a random seed state before each code LINE execution, and remove it if random seed does not change (see
ref2)Key ideas
Workflow
Let's say if we would execute code:
Then teal.code would inject an indirect state of the current seed and execute in the background:
Discard from code
set.seed()that are not needed, as internal random seed did not changeWe would need to link every injected
set.seed()with the following expression so that the injected line would be included.Limitations
set.seed(<generated number by teal.code>)guarantees a different statellrs-roche commentedon Aug 27, 2026
I don't don't think we need to change the random seed number generation each time. The seed is fine for parallel process and doesn't change the actual numbers only the index:
Created on 2026-08-27 with reprex v2.1.1
The .Random.seed is filled from the main R process and it is not "available" on the qenv, based on these tests:
But uses the external one:
But they can use the internal one:
Created on 2026-08-27 with reprex v2.1.1
The good news is that adding seeds won't slow down the report as when we save it, it runs all the code chunks again IIRC.