import JaxLean.Stdlib import examples.randint_monte_carlo.generated.DieEstimate import examples.randint_monte_carlo.generated.GridEstimate import examples.randint_monte_carlo.generated.MCDie16 import examples.randint_monte_carlo.generated.MCGridSquare8

Two ordinary-JAX Monte Carlo examples, proved at estimator boundaries. The uniform/iid laws below are the explicit ideal randint/split specification. Deterministic estimator kernels have separate checked Jaxpr certificates. No claim about bitwise PRNG uniformity, seed independence, or floating rounding.

namespace JaxLean.MCJaxopen scoped BigOperatorsnoncomputable sectionabbrev dieLaw : Rand ℝ := Rand.uniformInt 1 6 (⊢ 0 < 6 All goals completed! 🐙)abbrev gridIndexLaw : Rand ℝ := Rand.uniformInt 0 4 (⊢ 0 < 4 All goals completed! 🐙)def gridValueLaw : Rand ℝ := Rand.map (fun v => (v / 4) ^ (2 : Nat)) gridIndexLaw

Contracts for the deterministic Python functions.

theorem die_estimate_spec (draws : Tensor ℝ [16]) : die_estimate draws () = (∑ i, draws (i, ())) / 16 := rfltheorem grid_square_spec (indices : Tensor ℝ [8]) : grid_square indices = Tensor.map (fun v => (v / 4) ^ (2 : Nat)) indices := indices:Tensor ℝ [8]⊢ grid_square indices = Tensor.map (fun v => (v / 4) ^ 2) indices indices:Tensor ℝ [8]i:Index [8]⊢ grid_square indices i = Tensor.map (fun v => (v / 4) ^ 2) indices i indices:Tensor ℝ [8]i:Index [8]⊢ (indices i / 4) ^ 2 = (indices i / 4) ^ 2 All goals completed! 🐙theorem grid_estimate_spec (indices : Tensor ℝ [8]) : grid_estimate indices () = (∑ i, (indices (i, ()) / 4) ^ (2 : Nat)) / 8 := indices:Tensor ℝ [8]⊢ grid_estimate indices () = (∑ i, (indices (i, ()) / 4) ^ 2) / 8 All goals completed! 🐙

Bridges from the emitted random programs to their certified estimator functions.

theorem mc_die_boundary : mc_die_16 (R := ℝ) = Rand.map (fun draws => die_estimate (fun i => draws i.1) ()) (Rand.iid 16 dieLaw) := rfltheorem mc_grid_boundary : mc_grid_square_8 (R := ℝ) = Rand.map (fun draws => grid_estimate (fun i => draws i.1) ()) (Rand.iid 8 gridIndexLaw) := rfl

Only the small, single-draw laws are enumerated; never the product sample space.

theorem die_mean : dieLaw.mean = 7 / 2 := ⊢ dieLaw.mean = 7 / 2 ⊢ ∑ ω, 1 / 6 * (↑1 + ↑↑ω) = 7 / 2 All goals completed! 🐙theorem die_variance : dieLaw.variance = 35 / 12 := ⊢ dieLaw.variance = 35 / 12 ⊢ ∑ ω, 1 / 6 * (↑1 + ↑↑ω - ∑ ω, 1 / 6 * (↑1 + ↑↑ω)) ^ 2 = 35 / 12 All goals completed! 🐙theorem grid_value_mean : gridValueLaw.mean = 7 / 32 := ⊢ gridValueLaw.mean = 7 / 32 ⊢ ∑ ω, 1 / 4 * ((↑0 + ↑↑ω) / 4) ^ 2 = 7 / 32 All goals completed! 🐙theorem grid_value_variance : gridValueLaw.variance = 49 / 1024 := ⊢ gridValueLaw.variance = 49 / 1024 ⊢ ∑ ω, 1 / 4 * (((↑0 + ↑↑ω) / 4) ^ 2 - ∑ ω, 1 / 4 * ((↑0 + ↑↑ω) / 4) ^ 2) ^ 2 = 49 / 1024 All goals completed! 🐙

Sampling and averaging preserve the one-draw mean.

⊢ (∑ i, (Rand.map (fun v => v) dieLaw).mean) / 16 = 7 / 2 ⊢ (∑ x, dieLaw.mean) / 16 = 7 / 2 All goals completed! 🐙

The 1/n law is obtained by propagating division and independent-sum rules.

⊢ (∑ i, (Rand.map (fun v => v) dieLaw).variance) / 16 ^ 2 = dieLaw.variance / 16 ⊢ (∑ x, dieLaw.variance) / 16 ^ 2 = dieLaw.variance / 16 ⊢ ↑16 * dieLaw.variance / 16 ^ 2 = dieLaw.variance / 16 All goals completed! 🐙⊢ 35 / 12 / 16 = 35 / 192 All goals completed! 🐙

The same argument works after a nonlinear per-sample transformation.

⊢ (∑ i, (Rand.map (fun v => (v / 4) ^ 2) gridIndexLaw).mean) / 8 = 7 / 32 ⊢ (∑ x, gridValueLaw.mean) / 8 = 7 / 32 All goals completed! 🐙⊢ (∑ i, (Rand.map (fun v => (v / 4) ^ 2) gridIndexLaw).variance) / 8 ^ 2 = gridValueLaw.variance / 8 ⊢ (∑ x, gridValueLaw.variance) / 8 ^ 2 = gridValueLaw.variance / 8 ⊢ ↑8 * gridValueLaw.variance / 8 ^ 2 = gridValueLaw.variance / 8 All goals completed! 🐙⊢ 49 / 1024 / 8 = 49 / 8192 All goals completed! 🐙

The same numeric result for the independent evaluator of the estimator's Jaxpr. Only the sampling law is supplied as a specification.

⊢ mc_die_16.variance = 35 / 192 All goals completed! 🐙⊢ mc_grid_square_8.variance = 49 / 8192 All goals completed! 🐙endend JaxLean.MCJax