Random Generation#

masspcf provides deterministic random generation that is reproducible regardless of thread count or execution order. All random generation functions accept an optional Generator for explicit seed control.

Generators#

A Generator holds a seed used to derive independent, deterministic random streams for each tensor element:

import masspcf as mpcf

gen = mpcf.random.Generator(seed=42)

Pass a generator to any function that produces random output:

X = mpcf.random.noisy_sin((10, 20), generator=gen)

Two generators with the same seed always produce the same result:

gen_a = mpcf.random.Generator(seed=123)
gen_b = mpcf.random.Generator(seed=123)

X = mpcf.random.noisy_sin((5, 10), generator=gen_a)
Y = mpcf.random.noisy_sin((5, 10), generator=gen_b)
# X and Y are identical

A generator can be re-seeded with seed():

gen.seed(99)

Creating a generator without a seed uses a non-deterministic seed from the operating system:

gen = mpcf.random.Generator()  # non-deterministic

Global seed#

For convenience, seed() seeds a global generator used when no explicit generator is passed:

mpcf.random.seed(42)
A = mpcf.random.noisy_sin((10, 20))

mpcf.random.seed(42)
B = mpcf.random.noisy_sin((10, 20))
# A and B are identical

Noisy trigonometric PCFs#

noisy_sin() and noisy_cos() create tensors of piecewise constant functions that approximate \(\sin(2\pi t)\) and \(\cos(2\pi t)\) with additive Gaussian noise:

sines = mpcf.random.noisy_sin((200,), n_points=100)
cosines = mpcf.random.noisy_cos((10, 50), n_points=30)

Each breakpoint \(t_i\) is drawn uniformly from \([0, 1]\) and sorted, with the first breakpoint fixed at \(t = 0\). The value at each breakpoint is \(f(t_i) + \varepsilon_i\) where \(\varepsilon_i \sim \mathcal{N}(0, 0.1)\). The last value is always set to zero.

Pass dtype=mpcf.pcf64 for 64-bit precision (the default is pcf32).

How determinism works#

Each element in the output tensor is assigned a deterministic sub-seed derived from the master seed and the element’s flat (row-major) index. This means:

  • The same seed always produces the same tensor, even across different runs.

  • The result is independent of how many threads are used or the order in which elements are computed.

  • Different elements receive independent random streams.

For a deeper look at the seeding and hashing mechanism, see Deterministic random generation.