06-random-numbers

Implicit RNG with reproducible scopes — generate distributions, sample, and shuffle. Wrap code in Rng.run ~seed for deterministic results.

dune exec nx/examples/06-random-numbers/main.exe

What You'll Learn

  • Generating uniform, normal, and integer distributions
  • Running a Monte Carlo simulation to estimate pi
  • Creating synthetic training data with controlled noise
  • Verifying reproducibility with Rng.run ~seed
  • Shuffling arrays with Rng.shuffle

Key Functions

Function Purpose
Rng.run ~seed f Execute f in a deterministic RNG scope
Rng.uniform dtype shape Uniform random values in [0, 1)
Rng.normal dtype shape Standard normal distribution (mean=0, std=1)
Rng.randint ~high dtype shape low Random integers in [low, high)
Rng.shuffle t Randomly permute array elements
rand dtype shape Shorthand for uniform random values

Output Walkthrough

Monte Carlo pi estimation

Drop random points in a unit square. The fraction inside the unit circle approximates pi/4:

let xs = rand float64 [| n |] in
let ys = rand float64 [| n |] in
let inside = less_s ((xs * xs) + (ys * ys)) 1.0 in
let pi_est = item [] (sum (cast Float64 inside)) *. 4.0 /. Float.of_int n
Monte Carlo pi (100000 points): 3.1420  (actual: 3.1416)

Reproducibility

Same seed always produces the same numbers:

let a = Rng.run ~seed:99 (fun () -> Rng.normal Float64 [| 3 |]) in
let b = Rng.run ~seed:99 (fun () -> Rng.normal Float64 [| 3 |]) in
(* Identical? true *)

Try It

  1. Roll 1000 dice with Rng.randint and compute the mean — it should approach the theoretical expected value of 3.5.
  2. Increase the Monte Carlo sample count to 1,000,000 and observe how the pi estimate improves.
  3. Generate two clusters of 2D points (one centered at origin, one at (3, 3)) using Rng.normal with offsets.

Next Steps

Continue to 07-linear-algebra to learn matrix operations, decompositions, and solving linear systems.

(** Implicit RNG with reproducible scopes — generate distributions, sample, and
    shuffle.

    Roll dice, estimate pi with Monte Carlo, and generate noisy training data.
    Every result is reproducible inside an [Rng.run] scope: same seed, same
    numbers. Outside any scope the global fallback provides convenient but
    non-reproducible randomness. *)

open Nx
open Nx.Infix

let () =
  (* --- Dice simulation: roll 10 six-sided dice --- *)
  let dice = randint Int32 ~high:7 [| 10 |] 1 in
  Printf.printf "10 dice rolls: %s\n\n" (data_to_string dice);

  (* --- Uniform random floats in [0, 1) --- *)
  let uniform = rand Float64 [| 5 |] in
  Printf.printf "Uniform [0,1): %s\n\n" (data_to_string uniform);

  (* --- Normal distribution (mean=0, std=1) --- *)
  let normal = randn Float64 [| 5 |] in
  Printf.printf "Normal(0,1):   %s\n\n" (data_to_string normal);

  (* --- Monte Carlo pi estimation ---

     Drop N random points in a unit square. The fraction landing inside the unit
     circle (distance from origin < 1) approximates pi/4. *)
  let n = 100_000 in
  let xs = rand float64 [| n |] in
  let ys = rand float64 [| n |] in
  let inside = less_s ((xs * xs) + (ys * ys)) 1.0 in
  let count = sum (cast Float64 inside) in
  let pi_est = item [] count *. 4.0 /. Float.of_int n in
  Printf.printf "Monte Carlo pi (%d points): %.4f  (actual: %.4f)\n\n" n pi_est
    Float.pi;

  (* --- Synthetic training data: y = 3x + 2 + noise --- *)
  let x = rand Float64 [| 8 |] in
  let noise = randn Float64 [| 8 |] *$ 0.1 in
  let y = (x *$ 3.0) +$ 2.0 + noise in
  Printf.printf "x:     %s\n" (data_to_string x);
  Printf.printf "y ~ 3x+2: %s\n\n" (data_to_string y);

  (* --- Reproducibility: Rng.run ~seed gives the same result --- *)
  let a = Rng.run ~seed:99 (fun () -> randn Float64 [| 3 |]) in
  let b = Rng.run ~seed:99 (fun () -> randn Float64 [| 3 |]) in
  Printf.printf "Same seed, run 1: %s\n" (data_to_string a);
  Printf.printf "Same seed, run 2: %s\n" (data_to_string b);
  Printf.printf "Identical? %b\n\n" (item [] (all (equal a b)));

  (* --- Shuffle: random permutation of a dataset --- *)
  let data = arange int32 0 8 1 in
  let shuffled = shuffle data in
  Printf.printf "Original:  %s\n" (data_to_string data);
  Printf.printf "Shuffled:  %s\n" (data_to_string shuffled)