nstat.extras.matlab_rng — MATLAB-aligned Mersenne Twister RNG

Provides a thin wrapper around NumPy’s legacy numpy.random.RandomState (MT19937) seeded the same way MATLAB’s rng(N) seeds its default twister generator. Used by recipes that compare Python output against MATLAB Monte Carlo outputs where reproducible cross-language streams matter (PPLFP_MStep, PPLFP_EM, v9_PPSS_EM Case C drift entries).

Install

No optional dependency — matlab_rng ships with the core nstat-toolbox package. The module has zero runtime dependencies beyond NumPy, which nstat-toolbox already requires.

pip install nstat-toolbox

Usage

from nstat.extras.matlab_rng import MatlabRNG, seeded_global_rng

# Bit-equivalent to MATLAB rand under matching seed:
r = MatlabRNG(42)
u = r.rand(3)                     # matches MATLAB rand(1,3) under rng(42)
n = r.randn(3)                    # Box-Muller from same MT stream (deterministic)

# Context manager: seeds NumPy global state + monkey-patches default_rng
# so any nstat function inside the block draws deterministically.
import numpy as np
with seeded_global_rng(42) as rng:
    a = np.random.randn(3)        # deterministic
    g = np.random.default_rng()   # also deterministic
    b = g.standard_normal(3)

Bit-equivalence guarantees

Operation

MATLAB equivalent

Bit-equivalent?

MatlabRNG(seed).rand(n)

rng(seed); rand(1, n)

yes

MatlabRNG(seed).randn(n)

rng(seed); randn(1, n)

no (statistically equivalent)

MatlabRNG(seed).normrnd(mu, sigma, n)

rng(seed); normrnd(mu, sigma, 1, n)

no (statistically equivalent)

Why randn isn’t bit-equivalent

MATLAB’s randn uses the Marsaglia-Tsang Ziggurat algorithm (R14sp1 and later) to convert uniform draws into standard normals. NumPy’s legacy RandomState.randn and MatlabRNG.randn both use Box-Muller instead. Both draw from the same MT19937 stream and produce the same N(0,1) distribution, but they consume different numbers of uint32 words per output sample.

A proper Ziggurat port would close the gap; deferred to a future release if any caller needs strict cross-language MC parity.

When to use

Goal

Use

Reproducible Python-side MC across runs

MatlabRNG or seeded_global_rng

Bit-equivalent uniform stream to MATLAB

MatlabRNG(seed).rand(...)

Bit-equivalent normal stream to MATLAB

Not available — use MatlabRNG for statistical equivalence

Default Python RNG (PCG64, faster, not MATLAB-compatible)

numpy.random.default_rng(seed) directly — don’t wrap