rng
import "github.com/umbralcalc/stochadex/pkg/rng"Package rng provides a small, allocation-free random sampler for stochadex iterations and likelihood distributions. It is a drop-in, bit-identical replacement for the gonum distuv distribution draws the engine previously made, tuned to avoid their per-call overhead in hot loops.
Key Features:
- A single owned math/rand/v2.Rand per Sampler, seeded once from a partition seed
- Simple draws: standard normal, uniform, exponential (Float64/NormFloat64/…)
- Compound draws: gamma, beta, and Poisson variates for jump and likelihood models
- Bit-identical streams to distuv for the same seed, guaranteed by stream-identity tests
- No per-call allocation and no per-call distribution-value copy or wrapper construction
Why this exists: The stochastic iterations previously drew from gonum’s distuv distributions, e.g. distuv.Normal{Mu: 0, Sigma: 1, Src: rand.NewPCG(seed, seed)}.Rand(). Each such call copies the distribution (distuv methods have value receivers) and, internally, does rand.New(src) to build a fresh math/rand/v2.Rand wrapper — and for the compound distributions it also binds method values (rnd.Float64, rnd.NormFloat64, …) that are then called indirectly. gonum’s math/rand/v2 backing means the wrapper does not escape and so is not heap allocated, but the copy, the construction, and the indirection are all measurable in a per-element, per-step hot loop. A Sampler owns one *math/rand/v2.Rand and calls its methods directly, so all of that overhead disappears.
Mathematical Background: Each method reproduces the exact algorithm the corresponding distuv distribution uses, consuming the underlying source in the same order, so for a given seed it yields the identical stream (not merely the same distribution). The compound draws mirror gonum’s choices directly:
- Gamma: exponential for shape α=1; the Liu–Martin–Syring log-space method for α<0.2; Marsaglia–Tsang otherwise (with the α<1 boost m = U^(1/α))
- Beta(α, β): ratio X/(X+Y) of two Gamma(·, 1) draws from the same stream
- Poisson: the direct exponential-interarrival method for λ<10; Hörmann’s PTRS transformed-rejection method for λ≥10
Scope: Only the distributions where owning the generator is a clean, strict win live here. Binomial (a three-branch algorithm used in a single observation process) and Categorical (a stateful precomputed sampling heap) stay on distuv: the copied-algorithm maintenance cost outweighs the small per-draw saving, and neither sits in a tight per-element loop.
Usage Patterns:
- Create a Sampler in an iteration’s Configure, seeded from the partition Seed, and keep it on the iteration struct; call its methods in Iterate
- Use it for likelihood-distribution sampling (GenerateNewSamples) while keeping distuv for the log-likelihood evaluation, which needs distuv’s LogProb, not randomness
- Use NewFromSource to reproduce iterations that derive a distribution’s source from a master generator rather than directly from the partition seed
Concurrency: A Sampler is NOT safe for concurrent use by multiple goroutines. This matches the engine’s per-partition model: each partition owns its own iteration and therefore its own Sampler, and partitions never share one.
Index
- type Sampler
- func New(seed uint64) *Sampler
- func NewFromSource(src rand.Source) *Sampler
- func (s *Sampler) Beta(alpha, beta float64) float64
- func (s *Sampler) Exponential(rate float64) float64
- func (s *Sampler) Float64() float64
- func (s *Sampler) Gamma(alpha, beta float64) float64
- func (s *Sampler) NormFloat64() float64
- func (s *Sampler) Normal(mu, sigma float64) float64
- func (s *Sampler) Poisson(lambda float64) float64
- func (s *Sampler) Rand() *rand.Rand
- func (s *Sampler) Uniform(min, max float64) float64
type Sampler
Sampler is an allocation-free source of random samples backed by a single owned math/rand/v2.Rand.
type Sampler struct {
// contains filtered or unexported fields
}func New
func New(seed uint64) *SamplerNew returns a Sampler seeded deterministically from seed. It reproduces the source the iterations handed to distuv (rand.NewPCG(seed, seed)), so New(seed) yields a stream identical to a distuv distribution built with Src: rand.NewPCG(seed, seed).
func NewFromSource
func NewFromSource(src rand.Source) *SamplerNewFromSource returns a Sampler drawing from src. Use it to reproduce iterations that derive a distribution’s source from a master generator (e.g. rand.NewPCG(r.IntN(1e8), r.IntN(1e8))) rather than directly from the partition seed: wrap the same source and the stream is unchanged.
func (*Sampler) Beta
func (s *Sampler) Beta(alpha, beta float64) float64Beta returns a Beta(alpha, beta) sample — identical to distuv.Beta{Alpha: alpha, Beta: beta, Src: …}.Rand(): the ratio ga/(ga+gb) of two Gamma(·, 1) draws taken in order from this Sampler’s stream.
func (*Sampler) Exponential
func (s *Sampler) Exponential(rate float64) float64Exponential returns an Exponential(rate) sample — identical to distuv.Exponential{Rate: rate, Src: …}.Rand() (ExpFloat64()/rate form).
func (*Sampler) Float64
func (s *Sampler) Float64() float64Float64 returns a uniform sample in [0,1) — identical to distuv.Uniform{Min: 0, Max: 1, Src: …}.Rand().
func (*Sampler) Gamma
func (s *Sampler) Gamma(alpha, beta float64) float64Gamma returns a Gamma(alpha, beta) sample (shape alpha, rate beta) — identical to distuv.Gamma{Alpha: alpha, Beta: beta, Src: …}.Rand(), reproducing its exact branch structure and draw order: exponential for alpha==1, the Liu–Martin–Syring log-space method for alpha<0.2, and Marsaglia–Tsang otherwise (with the alpha<1 boost). Panics on beta<=0 or alpha<=0, matching distuv.
func (*Sampler) NormFloat64
func (s *Sampler) NormFloat64() float64NormFloat64 returns a standard-normal sample — identical to distuv.Normal{Mu: 0, Sigma: 1, Src: …}.Rand().
func (*Sampler) Normal
func (s *Sampler) Normal(mu, sigma float64) float64Normal returns a Normal(mu, sigma) sample — identical to distuv.Normal{Mu: mu, Sigma: sigma, Src: …}.Rand() (rnd*sigma+mu form).
func (*Sampler) Poisson
func (s *Sampler) Poisson(lambda float64) float64Poisson returns a Poisson(lambda) sample — identical to distuv.Poisson{Lambda: lambda, Src: …}.Rand(): the direct exponential-interarrival method for lambda<10, and Hörmann’s PTRS transformed-rejection method for lambda>=10.
func (*Sampler) Rand
func (s *Sampler) Rand() *rand.RandRand returns the owned generator, for the rare caller that needs a *rand.Rand directly (e.g. to derive further sources). Draws taken from it advance the same stream.
func (*Sampler) Uniform
func (s *Sampler) Uniform(min, max float64) float64Uniform returns a uniform sample in [min,max) — identical to distuv.Uniform{Min: min, Max: max, Src: …}.Rand() (same rnd*(max-min)+min form, so bit-identical, not merely equal in distribution).
Generated by gomarkdoc