Tutorials › Pharmacometric Workflows and Reproducible Modeling › Reproducible Random-Number Streams

Simulation Pipelines · Tutorial 260 of 399

Reproducible Random-Number Streams

Build reproducible simulation runs by assigning random-number streams to stochastic components and recording enough information to recreate their draws.

Advanced 10 min read

What You'll Learn

  • Distinguish a random-number seed, generator state, and random-number stream.
  • Assign and document streams for parameter draws, covariates, random effects, and residual error.
  • Explain why one global seed may not reproduce component-level draws after a workflow changes.
  • Identify the generator and execution details needed for a meaningful rerun.
  • Diagnose common causes of mismatched simulation outputs.
  • Compare stream-based replay with storing and reusing realized draws.

Why reproducible random-number streams matter

A simulation can change even when its model equations and input data appear unchanged. Random draws may differ because the seed was not set, the generator changed, or the order and number of random-number calls changed. In a pharmacometric workflow, draws may be used for several distinct purposes: sampling parameter uncertainty, constructing a target covariate population, generating individual random effects, and adding residual error. Reproducing the final output requires control over all of those sources of randomness.

A seed is useful, but recording a single seed is not always enough. If one section of a script makes an extra random draw, later draws from a shared sequence can shift. Assigning and recording streams by stochastic component makes it easier to reproduce a run and to determine which component changed. It does not, by itself, establish that the draws are statistically independent or that results will be identical across different software implementations.

Definition: A pseudorandom-number generator (PRNG) is a deterministic algorithm that updates an internal state and produces a sequence of values designed to have specified random-like properties. A seed initializes the generator state. A random-number stream is a specified sequence of generator outputs, or a generator-supported portion of such a sequence, assigned to a task. Reusing the same seed reproduces the same sequence only when the generator, its implementation, and the sequence of calls are held fixed.

A workflow typically transforms generator outputs into the values it needs. For example, uniform draws may be transformed into normal random effects, or uniforms may select records from an empirical covariate dataset. The seed controls the underlying generator sequence; the transformation and its implementation also affect the resulting values.

From a seed to a reproducible run

Let \(S_0\) be the initialized generator state, let \(T\) be its state-update rule, and let \(G\) convert a state into a returned random value. In a simple generator, the process can be written as

$$ S_{k+1}=T(S_k), \qquad U_k=G(S_{k+1}). $$

If two runs start from the same \(S_0\) and apply the same \(T\), \(G\), and call sequence, their states and returned values match at every step. The argument is by induction: the initial states agree; if the states agree at step \(k\), applying the same update rule gives the same next state, and applying the same output rule gives the same next value. Consequently, the same sequence of transformations produces the same simulated quantities, subject to the same numerical implementation.

The call sequence is important. If a routine consumes additional generator outputs, all later values from that same sequence may change. A seed therefore specifies a starting point, not a promise that every downstream result remains unchanged after edits to the workflow.

Worked Example: A seed is not a component-level guarantee

Suppose a hypothetical generator produces the uniform sequence \(0.12, 0.81, 0.37, 0.64, 0.25, \ldots\) from a fixed seed. A script uses the first three values to select three covariate records and the next value to generate a random effect. The random effect therefore uses \(0.64\).

Now the covariate procedure is changed to select four records. It consumes the first four values, so the random-effect procedure receives \(0.25\) instead. The seed did not change, but the random effect did, because the shared stream was consumed differently.

If covariate sampling and random effects instead use separate, fixed streams, adding a covariate draw consumes only the covariate stream. The random-effect stream still begins at its assigned state. This protects the random-effect draws from that particular change in covariate draw count. The values shown are hypothetical; the example illustrates sequence consumption, not the output of a specified software generator.

Assigning streams to simulation components

For a simulation that samples uncertain parameters, covariates, random effects, and residual error, define those as distinct stochastic components. Assign each a reproducible stream or generator-supported substream, and document the mapping. A stream identifier can be more informative than a bare integer seed when the generator provides a formal stream-management facility.

Do not assume that four different integer seeds automatically produce independent streams. The quality and relationship of the sequences depend on the generator and the way streams are constructed. Use a generator's documented stream or substream mechanism when available, and follow its guidance for assigning streams. If the implementation offers only seed-based initialization, document that limitation rather than claiming independence from distinct seed values alone.

A practical seed record should connect each stochastic component to its stream and to the settings that determine how that stream is interpreted. At minimum, record:

  • the component name and its assigned seed or stream identifier;
  • the PRNG algorithm and implementation, including relevant software or package version;
  • the number of draws requested and the transformations applied to them;
  • the simulation code version and the inputs that affect draw generation.

For a rerun to match, also avoid hidden randomness: every stochastic operation must use the intended generator rather than an uncontrolled default. Record or fix relevant options for transformations and numerical routines when they can affect results. Exact reproduction across software versions or computing environments may require more than matching integer seeds.

Worked Example: Building a component seed ledger

Consider a hypothetical simulation with four stochastic components. The team uses one documented generator implementation and a supported stream-allocation procedure. The values in the ledger below are illustrative stream identifiers, not a recommendation that these integers are suitable for every generator.

ComponentAssigned stream identifierUse
Parameter draws41021Draw parameter sets for each simulation replicate
Covariate sampling41022Sample or generate each virtual patient's covariates
Random effects41023Generate individual-level random effects
Residual error41024Generate observation-level residual error

Suppose the run requests 500 parameter sets, 10,000 covariate records, 10,000 random-effect vectors, and 50,000 residual draws. The ledger should record those requested counts, along with any distributional transformations and their parameterization. These counts help detect a changed call pattern: if a rerun requests 10,001 random-effect vectors, the run is not the same computation even if the stream identifier is unchanged.

For instance, if the random effects are generated by transforming standard-normal draws through a specified covariance factor, record that transformation and the covariance factor used. A seed alone does not define which covariance model was applied. The stream ledger provides reproducible starting points; the model inputs and transformations define what is done with the draws.

Keeping draws aligned with their intended role

A stream assignment is most useful when it is stable: parameter draws always come from the parameter stream, covariate draws from the covariate stream, and so on. Avoid resetting one shared generator to the same seed at the start of multiple components. Doing so can make components reuse identical underlying sequences, which may induce unintended dependence. Separate component streams should be assigned using the generator's supported mechanism, not created by repeatedly restarting an identical sequence.

The component boundary should reflect the simulation design. If residual errors are generated separately for multiple endpoints, the workflow may need a documented allocation within the residual component so that a change to one endpoint does not unexpectedly shift another endpoint's draws. The choice of allocation depends on the intended design; it should be explicit and stable, not an accidental consequence of code execution order.

Worked Example: Reproducing one virtual patient's prediction

Suppose a hypothetical concentration model uses a parameter draw \(\theta=1.80\) mg/L, a patient's weight \(w=70\) kg, a random effect \(\eta=-0.100\), and residual error \(\epsilon=0.0800\) mg/L. For illustration, the simulated observation is defined as

$$ C=\theta\left(\frac{w}{70\ \mathrm{kg}}\right)\exp(\eta)+\epsilon. $$

The covariate stream supplies \(w=70\) kg, the parameter stream supplies \(\theta=1.80\) mg/L, the random-effects stream supplies \(\eta=-0.100\), and the residual stream supplies \(\epsilon=0.0800\) mg/L. Substitution gives

$$ C=(1.80\ \mathrm{mg/L})\left(\frac{70\ \mathrm{kg}}{70\ \mathrm{kg}}\right)\exp(-0.100)+0.0800\ \mathrm{mg/L} =1.80(0.9048)+0.0800 =1.709\ \mathrm{mg/L}. $$

To reproduce this value, it is not sufficient to record only the final concentration or one global seed. The run record must identify the four stream assignments, the generator implementation, the transformations used to obtain each draw, and the model equation. This example describes one hypothetical realization; the numbers are not study results.

Diagnostics, alternatives, and limitations

When a rerun differs, first determine whether the underlying draws differ or whether the same draws were transformed differently. Compare saved component-level draws, if available, and inspect the generator settings, stream assignments, draw counts, and call order. If the component draws agree but predictions do not, investigate model inputs, transformations, and numerical settings. If only one component's draws differ, check whether its stream or generator changed; if several downstream components differ, a shared-stream call-count change is a plausible cause.

A useful reproducibility check is to run the same workflow twice from a clean initialization and compare selected draws and outputs. For exact comparisons, specify what must match: individual random draws, simulated records, or only a summary. A summary can match by coincidence even when underlying draws differ, so it is a weaker check than comparing component draws directly.

ApproachStrengthLimitation
One global seedSimple to set and can reproduce an unchanged script in a fixed environmentChanges in call order or draw counts can shift later components' draws
Separate component streamsHelps isolate changes and makes each source of randomness identifiableRequires careful stream assignment and documentation; distinct seeds alone do not prove independence
Save and reuse realized drawsCan replay the exact sampled inputs without regenerating themRequires storage and clear links between saved draws and the model inputs or run that used them

Stream-based reproduction has limits. A generator may produce different results after a software update, or a transformation routine may change its algorithm. Different hardware or numerical libraries can also affect derived calculations even when the raw draws match. Therefore, record the generator implementation and relevant versions, and distinguish reproducibility of random draws from bit-for-bit reproducibility of all model outputs.

Common mistake

Key takeaway: “I used the same seed, so the simulation must be reproducible” is incomplete reasoning. The corrected statement is: the same seed reproduces the same generator sequence when the generator implementation and initialization are the same; reproducing the simulation also requires the same draw-consumption pattern, transformations, inputs, and relevant computational settings.

A related mistake is to treat different integer seeds as proof that streams are independent. Different seeds can initialize different states, but statistical independence is a property of the stream-construction method and generator, not something established merely by comparing seed values. Use documented stream allocation and describe exactly what is guaranteed.

1
List stochastic components.
Identify every step that consumes randomness, including parameter, covariate, random-effect, and residual draws.
2
Assign streams deliberately.
Use the generator's documented method for separate streams or substreams; do not infer independence from distinct integers.
3
Record settings and draw use.
Store stream identifiers, generator implementation, versions, requested draw counts, and transformations alongside the relevant run inputs.
4
Check a clean rerun.
Reinitialize the streams and compare component-level draws and outputs to the reproducibility target you specified.

Check Your Understanding

Answer each question, then compare your reasoning with the solutions.

  1. Why can two runs with the same global seed produce different random-effect draws after the covariate sampler is changed to consume one additional draw?
  2. Does assigning different integer seeds to parameter and residual draws establish statistical independence? Explain.
  3. Name four items, beyond a single seed, that should be documented to support a reproducible rerun.
  4. In a rerun, component-level random draws match, but predicted concentrations differ. What should be checked next?
  5. What does a successful same-seed rerun establish, and what does it not establish about results from a different software version?

Solutions. (1) With a shared sequence, the extra covariate draw consumes a value that would otherwise have been used by the random-effect procedure, shifting its position in the sequence. (2) No. Independence depends on the generator and its documented stream-construction method; different seed values alone are not proof. (3) Suitable items include the PRNG algorithm and implementation version, stream identifiers, draw counts, transformations, simulation code version, and relevant inputs. (4) Check model inputs, equations, transformations, and numerical settings, because the discrepancy occurs after the component draws. (5) It supports reproducibility under the same generator, call sequence, transformations, and computational settings; it does not guarantee identical draws or outputs after a generator, software, or numerical implementation change.

Clinical Biostats