| flake.nix | ||
| pcg.nix | ||
| README.md | ||
| test.nix | ||
Permuted congruential generator (in nix!)
Ever wanted your reproducible and declarative configurations to generate random numbers? No? Too bad!
This flake allows you to memory inefficiently and very slowly (100s of miliseconds for 10 numbers) generate random numbers using the nix language!
Specifically, it is a pure-nix implementation of a Permuted Congruential Generator (or at least the wikipedia example version of one), which was recommended after I looked up "how to implement mersenne twister" and found a stackoverflow post mentioning this as an easier option. My implementation probably has a flaw or two, but from some testing, generating 10,000 random numbers gave me all unique numbers so I can't complain.
How to use
DON'T
but if you insist, you can model your usage after the following:
let
# Variables
seed = 8372189789123;
randomCount = 5;
# Create the PSRNG
init = pcg-nix.lib.mkPsrng seed;
# Use the PSRNG to generate multiple random numbers
randoms = pcg-nix.lib.genRandoms init randomCount;
# Or, generate them individually
# When using this, note that `randN` is the STATE, not the number!
# To get the number, get `value` from the state attrset.
rand1 = pcg-nix.lib.getNext init;
rand2 = pcg-nix.lib.getNext rand1;
rand3 = pcg-nix.lib.getNext rand2;
rand4 = pcg-nix.lib.getNext rand3;
rand5 = pcg-nix.lib.getNext rand4;
in randoms
Implementation details
According to wikipedia, this should be a "PCG-XSH-RR with 64-bit state and 32-bit output". Looking at the current implementation, I get negative numbers so I don't know if it is actually 32-bit output, so take that with a grain of salt. I simply looked at the C example on wikipedia, and translated it as best I could to Nix. While doing this, I discovered the folowing:
Nix does not have the following language features that would be quite useful for PSRNG(s):
- Unsigned integers
- Bitshifts
- Bitwise and, or, xor
So, I worked around this by using the fun method of simply using lists of 0s and 1s rather than real integers. The result is a probably functional, but memory hogging and slow random number generator.
Then, I simply implemented all of the main bitwise operations plus addition and multiplication using lists instead! The only outlier of the "using binary lists" strategy is multiplication, which tried to allocate 14 exabytes of RAM when implemented with lists. Instead, I just convert from and to normal integers for that.
Improvements that can be made
- Use true/false instead of integers to store bits, for memory efficiency
- Delete this
- Wait for nix to implement proper bitwise operations
- Probably lots of stuff I missed