Random Draws
Cimba gives every trial its own pseudo-random stream. Pass seed=... to
model.experiment(...) when a run should be reproducible; leave it unset
when Cimba should choose independent seeds for you. In model code, import
cimba.random alongside cimba.sim and draw from that per-trial stream:
import cimba.random as random
import cimba.sim as sim
class Clinic(sim.Model):
mean_interarrival: sim.Param
mean_service: sim.Param
served: sim.Output
queue: sim.Queue
model = Clinic()
@model.process
def arrivals(env: Clinic):
while True:
sim.hold(random.exponential(env.mean_interarrival))
env.queue.put(1)
@model.process
def server(env: Clinic):
while True:
env.queue.get(1)
sim.hold(random.gamma(shape=2.0, scale=env.mean_service / 2.0))
Use cimba.random in both model code and ordinary Python code. Importing it
as random keeps process bodies compact, while the package boundary stays
visible at the top of the file:
import cimba
cimba.random.seed(1234)
samples = [cimba.random.normal(mu=10.0, sigma=2.0) for _ in range(5)]
The random API is intentionally namespaced. Flat spellings such as
sim.exponential(...), sim.random.exponential(...), or
cimba.exponential(...) are not part of the public API.
Parameter Conventions
The distribution functions use native parameter names rather than abbreviated legacy aliases. In particular:
exponential(mean=1.0)uses a mean, not a rate.normal(mu=0.0, sigma=1.0)uses mean and standard deviation.gamma(shape, scale=1.0)uses shape and scale.beta(a, b, min=0.0, max=1.0)returns a beta variate scaled to[min, max].student_t(v, m=0.0, s=1.0)uses degrees of freedomv, locationm, and scales.
Keyword arguments are supported in compiled model callbacks and standalone
@numba.njit helpers:
@model.process
def customer(env):
patience = random.triangular(min=0.5, mode=1.0, max=2.0)
priority = 5 if random.bernoulli(p=0.25) else 0
sim.hold(random.normal(mu=patience, sigma=0.1))
Continuous Draws
Function |
Meaning |
|---|---|
|
Continuous uniform draw between |
|
Exponential draw with the given mean interarrival or service time. |
|
Gamma draw with positive |
|
Normal draw with mean |
|
Lognormal draw where the underlying normal has location |
|
Logistic draw with location |
|
Cauchy draw with the given mode and positive scale. |
|
Rayleigh draw with positive scale |
|
Weibull draw with positive shape and scale. |
|
Pareto draw with positive shape and positive mode/minimum. |
|
Beta draw scaled from |
|
Erlang draw with integer shape |
|
Sum of independent exponential stages, one for each positive mean in
|
|
Choose one positive mean according to |
|
PERT draw for bounded expert estimates with minimum, most-likely value, and maximum. |
|
Modified PERT draw; larger |
|
Chi-squared draw with positive degrees of freedom |
|
F distribution draw with positive numerator and denominator degrees of freedom. |
|
Student’s t draw with positive degrees of freedom |
Discrete Draws
Discrete functions return Python integers except bernoulli(), which returns
True or False.
Function |
Meaning |
|---|---|
|
|
|
Integer draw from the inclusive range |
|
Poisson draw with positive rate/mean |
|
Number of trials until the first success, using success probability
|
|
Number of successes in |
|
Number of failures before |
|
Zero-based index sampled from non-negative probabilities that sum to
|
Probability Vectors
categorical() and hyperexponential() accept Python sequences, tuples, or
NumPy arrays. Probabilities may contain zero entries, must not contain negative
entries, and must sum to 1.0. The selected index is zero-based, which makes
it convenient for arrays:
DESTINATION = (0.55, 0.30, 0.15)
WALK_TIME = (2.0, 5.0, 12.0)
@model.process
def visitor(env):
i = random.categorical(DESTINATION)
sim.hold(WALK_TIME[i])
For repeated categorical sampling outside model code, cimba.random also
provides AliasSampler:
sampler = cimba.random.AliasSampler([0.55, 0.30, 0.15])
try:
choice = sampler.sample()
finally:
sampler.close()
AliasSampler can also be used as a context manager. It is mainly useful for
ordinary Python code that samples the same probability vector many times; inside
model callbacks, prefer random.categorical(...) from the imported
cimba.random module.
Seeds And Reproducibility
For simulation experiments, prefer the experiment-level seed:
exp = model.experiment(replications=20, duration=1_000.0, seed=20260705)
exp.run()
That seed is expanded into independent per-trial streams, so parallel execution
stays reproducible. The seed helpers on cimba.random are lower-level tools
for ordinary Python code:
Helper |
Meaning |
|---|---|
|
Initialize the current thread’s random generator and return the seed
used. Passing |
|
Return the current thread’s Cimba random seed. |
|
Return a hardware-derived seed without installing it. |
|
Draw a raw unsigned 64-bit random integer. |
|
Deterministically mix a seed and nonce into another 64-bit seed. |
Do not reseed from inside process bodies. Use experiment seeds to reproduce
models, and use sim.Param fields when distribution parameters should vary
across design points.