# How to generate reproducible random numbers across versions via package manifest?

**URL:** <https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690>\
**Category:** General Usage\
**Tags:** random, reproducibility\
**Created:** [August 2, 2021, 5:20pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690 "2021-08-02T17:20:50Z")\
**Posts on this page:** 8\
**Page:** 2

<div class="post-metadata">

**Author:** ![viraltux](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/viraltux/32/15236_2.png) [@viraltux](https://discourse.julialang.org/u/viraltux)\
**Post date:** [August 3, 2021, 7:47am UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/21 "2021-08-03T07:47:20Z")

</div>

> [@Oscar\_Smith](#):
>
> It’s also worth noting that floating point functions aren’t guaranteed to produce the same results between Julia versions. Specifically, all of the `exp` family, `log2,log10` , most of the hyperbolic trig (so also any complex trig), and some of the regular trig will produce slightly different answers in 1.7 than 1.1.

When we want to reproduce results down to slightest difference -which sometimes is critical, the only guaranteed solution I am aware of is to containerize the whole environment; versioning Julia and its RNG’s might not be enough.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [August 3, 2021, 9:25am UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/22 "2021-08-03T09:25:02Z")

</div>

> [@torgo](#):
>
> The whole point of reproducibility is that the results reported in the paper are _exactly_ reproducible from the code, which requires getting the same draws.

Exact binary reproducibility is usually impossible for any nontrivial calculation (results can change on a different machine, even if you are using the same version of Julia and all packages).

Fortunately, it is relatively rarely required since (as @Sukera suggested) if your results are very sensitive to a particular set of random draws, then reproducibility becomes the least of your worries.

Generally it is already great if

1. your code remains _runnable_ (and that’s a big challenge, a lot of environments become hard to reproduce in 5–10 years)
2. running it gets you results reasonably close to what appeared in the article.

Unfortunately, in some fields vast majority of papers do not come even nearly close to this.

---

<div class="post-metadata">

**Author:** ![torgo](https://avatars.discourse-cdn.com/v4/letter/t/94ad74/32.png) [@torgo](https://discourse.julialang.org/u/torgo)\
**Post date:** [August 3, 2021, 2:37pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/23 "2021-08-03T14:37:29Z")

</div>

> [@giordano](#):
>
> > [@torgo](#):
> >
> > But two things remain unresolved for me:
> > 
> > 1. @Sukera said the opposite above:
> > 
> > > No, that should definitely work (no minor julia release should be breaking) - what kind of error do you get? Please post it in full.
> 
> How’s that the opposite of what I said? I said that you shouldn’t expect to be able to instantiate with an older version of Julia a manifest created with a newer version (it may happen that you can, but it won’t happen in general), but the other way around (new version of Julia instantiating a manifest created with an older one) should be safer.

Sorry, my mistake. I misread the direction of the inequality in your post. Yes, it makes sense to me that one should not be able to instantiate a manifest created by a newer version in an older version. I thought you were saying the opposite.

* * *

* * *

> [@Sukera](#):
>
> The `compat` section is for specifying which kinds of julia or package versions are compatible with the code you wrote. E.g. if you put `julia = "1"` in there, you’re claiming compatibility with all versions of the form 1.x.y. There’s a section in the Pkg docs that goes into more detail about these bounds. It doesn’t have any bearing on what julia version you’re using to actually run the code (other than requiring at least the minimum version specified). There’s no automatic selection of the code from a specific version.

Thanks, I think that makes sense now. So the purpose of `[compat]` is to not allow me to `using` a package unless I have the right versions? (Which is distinct from my intention here of ensuring the same packages are being loaded via `]instantiate`.)

* * *

* * *

> [@Sukera](#):
>
> > [@torgo](#):
> >
> > Any idea what I am missing?
> 
> After instantiating the project in 1.6, can you post the output of `]status`?

Here it is:

```julia
(TestingVersions) pkg> status
     Project TestingVersions v1.0.0
      Status `<redacted>/TestingVersions2/Project.toml`
  [a93c6f00] DataFrames v0.20.2

```

* * *

* * *

> [@Tamas\_Papp](#):
>
> Exact binary reproducibility is usually impossible for any nontrivial calculation (results can change on a different machine, even if you are using the same version of Julia and all packages).
> 
> Fortunately, it is relatively rarely required since (as @Sukera suggested) if your results are very sensitive to a particular set of random draws, then reproducibility becomes the least of your worries.
> 
> Generally it is already great if
> 
> ```
> your code remains runnable (and that’s a big challenge, a lot of environments become hard to reproduce in 5–10 years)
> running it gets you results reasonably close to what appeared in the article.
> 
> ```
> 
> Unfortunately, in some fields vast majority of papers do not come even nearly close to this.

Ok, maybe _exact_ is too extreme of a word to use here.  
Let me give you a statistical example that demonstrates the type of reproducibility that I desire.  
Here’s a small Monte Carlo simulation to compute the standard deviation of the sample mean of 100 draws from a standard normal random variable.  
The standard asymptotic approximation says that it should be _approximately_ .10 = 1/\sqrt{100}.

```julia
using Random
using Statistics

M = 2000
N = 100
results = fill(NaN, M)
for s in [1,2]
    Random.seed!(s)
    for m = 1:M
        results[m] = mean(randn(N))
    end
    println(std(results))
end

```

yields on Julia 1.6:

```julia
0.09878139709623658
0.09934571969978735

```

If I am reporting a table with precision up to the third or fourth digit, then the results “differ” depending on what the seed is. Yet that’s no reason to say that the theory is “very sensitive.” It’s an approximation. It’s used throughout statistics and science more generally. Nor does this have to do with exact binary reproducibility.

Is this important? It’s important to me because some of the journals I am submitting to _require_ this type of reproducibility now. That is, they want the exact same tables/figures to be produced when the code is run. They don’t want to have to use their judgment on whether `.0987` is “close” to `.0993` or not. My understanding was that Julia’s environment system was designed to help guarantee this type of reproducibility.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [August 5, 2021, 9:13am UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/25 "2021-08-05T09:13:00Z")

</div>

> [@torgo](#):
>
> that’s no reason to say that the theory is “very sensitive.”

Certainly, but most real-life code contains branches and other constructs which effectively make your outcome sensitive to last _bits_ of floats in key places, which are bound to matter with enough samples and operations and lead to “large”, discontinuous changes in the result.

Even seemingly continuous special functions have branches in their implementation. That said, if you are lucky and can guarantee that this does not happen, then the kind of reproducibility you are aiming for is possible. But this is very rare in practice.

---

<div class="post-metadata">

**Author:** ![torgo](https://avatars.discourse-cdn.com/v4/letter/t/94ad74/32.png) [@torgo](https://discourse.julialang.org/u/torgo)\
**Post date:** [August 5, 2021, 3:16pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/26 "2021-08-05T15:16:36Z")

</div>

I see. So it sounds like the only way to ensure the type of reproducibility I am aiming for (again, not something I came up with, but requested by journals) is to indicate the specific version of Julia that the results were generated with.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [August 5, 2021, 3:47pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/27 "2021-08-05T15:47:26Z")

</div>

Technically for full reproducibile, you might want to specify the operating system. This generally isn’t needed, but there are some edge cases where it affects things (mainly with C libraries that think Int is 32 bit on windows)

---

<div class="post-metadata">

**Author:** ![torgo](https://avatars.discourse-cdn.com/v4/letter/t/94ad74/32.png) [@torgo](https://discourse.julialang.org/u/torgo)\
**Post date:** [August 5, 2021, 4:01pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/28 "2021-08-05T16:01:33Z")

</div>

Right, good point. So I guess I should really specify the binary?  
For example:  
[https://julialang-s3.julialang.org/bin/linux/x64/1.1/julia-1.1.1-linux-x86\_64.tar.gz](https://julialang-s3.julialang.org/bin/linux/x64/1.1/julia-1.1.1-linux-x86_64.tar.gz)

Actually, that raises another question.  
On the older versions download page ([Julia Downloads (Old releases)](https://julialang.org/downloads/oldreleases/)) I only see 1.1.1, whereas I wrote my code in 1.1.0.  
Is it safe to assume that any changes between 1.1.0 and 1.1.1 are sufficiently minor not to jeopardize the type of reproducibility I want?  
(I guess I can always try it to be sure.)

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [August 5, 2021, 5:36pm UTC](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690/29 "2021-08-05T17:36:16Z")

</div>

I don’t think anything should change, but you definitely should double check. Also, I just remembered that cpu instruction set matters since functions like `sum` (or anything in BLAS) will do the operations in a different order to take advantage of avx instructions. Also, threading and using GPUs are probably impossible if you want exact reproducibility.

[Previous page](https://discourse.julialang.org/t/how-to-generate-reproducible-random-numbers-across-versions-via-package-manifest/65690.md?page=1)
