Data-driven Traces
Declare demand: sim.Trace and pass per-trial replay data to
model.experiment(demand=...): a 1-D array is shared by every trial, a 2-D
array maps row i to trial i (trial order is design-point-major with
replications innermost), and a sequence of 1-D arrays gives ragged per-trial
traces.
A trace field also accepts a callable f(rng) or f(rng, trial_index)
returning a 1-D array – the idiom for bootstrap resampling and fitted
generators. It runs once per trial with a numpy.random.Generator seeded
from that trial’s own cimba seed and the field name, so the single
experiment(seed=...) argument reproduces the simulation streams and the
generated traces together, and distinct trace fields draw independent
streams. sim.trace_rng(trial_seed, field_name) rebuilds any trial’s
generator from its recorded exp["seed"], e.g. to inspect the trace a
failed trial replayed.
Callable traces run serially inside experiment(), before the parallel
trial run – negligible for bootstrap resampling, but a bottleneck when a
single generation is expensive (fitted time-series or ML models).
model.trial_seeds(seed=..., replications=..., **params) returns the exact
per-trial seeds that experiment() will assign, so such traces can be
generated in parallel outside cimba and passed in as finished rows, with
bit-identical results:
seeds = model.trial_seeds(seed=42, scale=[1.0, 2.0], replications=100)
rows = Parallel(n_jobs=-1)(
delayed(slow_generator)(sim.trace_rng(s, "demand")) for s in seeds)
exp = model.experiment(scale=[1.0, 2.0], demand=rows,
replications=100, seed=42)
cimba.bootstrap provides ready-made trace generators that resample an
observed series: iid(data, length) for serially independent data,
moving_block(data, length, block) and circular_block(data, length,
block) for stationary dependent series, and stationary(data, length,
mean_block) (random geometric block lengths – a good default for
autocorrelated data such as demand histories). Each returns an f(rng)
closure to pass directly as a trace field value:
from cimba import bootstrap
demand = bootstrap.stationary(history, length=horizon, mean_block=7)
exp = model.experiment(demand=demand, replications=200, seed=42)
For trending, seasonal, or autoregressive data there are three model-based
factories that fit the structure internally from the raw series:
residual(data, length, trend=1, period=None, mean_block=None) (polynomial
trend or, with a period, STL decomposition; residuals resampled i.i.d. or
stationary-block), wild(data, length=None, trend=1, period=None,
weights="rademacher") (heteroskedastic residuals, weighted in place), and
sieve(data, length, order=None) (AR(p) with AIC order selection and
Yule–Walker coefficients, simulated forward with resampled innovations).
trend and period also accept "auto"; all three take
nonnegative=True (clip at zero, for demand data) and start (evaluate
the structure on start..start+length-1, e.g. start=len(data) for the
horizon after the history).
For supply-chain demand there are two more:
intermittent(data, length, jitter=False) (zero-inflated series:
Markov-chain occurrence plus resampled nonzero sizes) and
joint(panel, length, name=..., mean_block=...) (a mapping of field name to
series, resampled with shared block draws so cross-correlation survives;
the returned generators carry a trace_rng_name attribute, which
experiment() uses instead of the field name when deriving each trial’s
generator – callables sharing the tag receive identical rngs).
Size length to cover warmup + duration + cooldown.
Inside a process body, values = sim.Trace(env.demand) returns the
trial’s trace as a float64 NumPy view supporting len(), indexing,
slicing, and iteration; treat it as read-only. A generator that exhausts its
trace simply finishes – the trial still runs to its configured recording
window, so generate traces that cover warmup + duration + cooldown (or
derive the experiment duration from the trace span), and consider recording
sim.now() into an Output when the loop ends as an exhaustion check.
For a narrative walkthrough of trace replay and bootstrapping, see Data-Driven Traces and Bootstrapping Methods.