/bitgenerators

Psuedo-random number bitgenerators for OCaml users.

Primary LanguageOCamlBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

bitgenerators

codecov CI license

This library provides a port of numpy's random module bitgenerator interface for working with pseudo-random number generation. Several PRNG algorithms are implemented and exposed using numpy's bitgenerator interface in a purely functional manner for Ocaml users.

Documentation

Comprehensive documentation of available features is available at the project's site.

Installation

The library can be installed using opam with the following commands:

opam update
opam install bitgenerators

Alternatively, one can install the development version using the latest git commit:

opam pin add bitgenerators git+https://github.com/zoj613/bitgenerators

Usage

A SeedSequence module based on these ideas is available to providing a high quality seed sequence that can be used to initialize any of the supported PRNG's:

open Bitgen
open Stdint

let seedseq = SeedSequence.initialize [Uint128.of_int 123456789]
let rng = PCG64.initialize seedseq 

It can also be used to initialize any custom PRNG using the module's generate_64bit_state and generate_32bit_state functions:

SeedSequence.generate_64bit_state 4 seedseq |> Array.map Uint64.to_string 
(* - : string array =
   [|"5976902797103608158"; "11230215241436205182"; "1766494744865860250";
     "7661475472903581292"|] *)

Below is an example of using an initialized PCG64 bitgenerator to generate 10 random floats:

let rec compute t acc = function
    | 0 -> List.rev acc, t
    | i -> let u, t' = PCG64.next_uint64 t in compute t' (Uint64.to_string u :: acc) (i - 1)
in compute rng [] 10 |> fst
(* - : string list =
   ["511209809126027580"; "16725663875132018381"; "16258841331763118777";
    "11527320112047894150"; "14586113755615299794"; "15235313769381631730";
    "15526732141789886995"; "8701844723981253752"; "17657754321871037678";
    "17461531751233692673"] *)

One can use SeedSequence.spawn to generate many independent bitgenerators that can be used in parallel computations in a reproducible manner:

let next_float t = Some (PCG64.next_double t) in
let compute t = Seq.unfold next_float t |> Seq.take 6 |> List.of_seq in
SeedSequence.spawn 4 seedseq
|> fst
|> List.map PCG64.initialize
|> List.map (fun x -> Domain.spawn (fun () -> compute x))
|> List.map Domain.join
(* - : float list list =
[[0.407206833679825242; 0.189731803785485376; 0.564081542309661343;
  0.88682304196963746; 0.45229844727129942; 0.701372920128140565];
 [0.580212874721654503; 0.0892784737148068; 0.511665172650789257;
  0.931271866226736411; 0.928633846357239; 0.173606152636579414];
 [0.171815817392286574; 0.585509477690361213; 0.837844400599859318;
  0.569340928519763145; 0.680737776645169879; 0.620841051213270267];
 [0.736203907003532887; 0.479879743687943505; 0.506036578793879;
  0.596207202439843376; 0.792829648424435; 0.540970530700028429]] *)

Another pattern to generate independent non-overlapping instances of a bitgenerator for parallel applications is to use jump or advance functions for PRNG's that support such functions:

Philox4x64.initialize seedseq |> Seq.iterate Philox4x64.jump |> Seq.take 5 |> List.of_seq 
(* - : Philox4x64.t list = [<abstr>; <abstr>; <abstr>; <abstr>; <abstr>] *)

The resulting list of bitgenerators produce non-overlapping streams of random numbers.

Supported bitgenerators include: PCG64, Philox4x64, Xoshiro256, ChaCha, SFC64, LXM and EFIIX64x48. More coming soon!

Empirical Randomness Testing

Running the test suite provided by TestU01 on the supported generators is supported. To see the available commandline options run dune exec -- ./bin/crush.exe -help. Below is a sample output from running dune exec -- ./bin/crush.exe -name smallcrush pcg64 to test PCG64 on the Small Crush test suite:

========= Summary results of SmallCrush =========

 Version:          TestU01 1.2.3
 Generator:        pcg64
 Number of statistics:  15
 Total CPU time:   00:01:40.64

 All tests were passed

Benchmarks

A utility to compare the performance of each bitgenerator's next_uint64 function is provided. Run the microbenchmark using dune exec -- ./bin/bench.exe and once it has completed, a summary table will be displayed in stdout.