Skip to content

v0.5 seed & RNG-stream contract (#38)#48

Open
DougManuel wants to merge 8 commits into
devfrom
v05-seed-contract
Open

v0.5 seed & RNG-stream contract (#38)#48
DougManuel wants to merge 8 commits into
devfrom
v05-seed-contract

Conversation

@DougManuel

Copy link
Copy Markdown
Collaborator

Phase 2, first workstream of the post-v0.4.0 plan. Implements the accepted seed-contract ADR. Co-releases with #39 (formula evaluator) — do not tag v0.5 on this alone. This is the single budgeted seeded-output break for v0.5.

What changes

.with_mock_seed() becomes the one seeding primitive across every generation path:

The break

Output for a given seed changes once (Mersenne-Twister → L'Ecuyer-CMRG). It is reproducible for a given seed and package version, and now independent of the session's ambient RNGkind(). Not comparable across the v0.4→v0.5 boundary — see the NEWS migration note. cchsflow/chmsflow pin against a version, so the note is mandatory.

Guarantees (ADR D1–D4)

  • D1 independent per-stage sub-streams; D2 unified isolation across all paths; D3 same seed + spec + version ⇒ identical output, independent of ambient RNGkind(); D4 always-on fallback message.

Review history

  • Three tasks, each TDD with a per-task spec+quality review.
  • A whole-branch review (strongest model) caught an Important defect the four per-task reviews missed: set.seed(kind = "L'Ecuyer-CMRG") pinned only the primary generator, so rnorm() and equal-probability sample() still leaked ambient normal.kind/sample.kind, partially breaking the D3 guarantee. Fixed by pinning all three kind components (a no-op under R ≥ 3.6 defaults, so pinned values were not re-baselined) plus a regression test that varies normal.kind/sample.kind.

Tests / docs

  • New tests/testthat/test-seed-contract.R: isolation (state + kind), determinism, ambient-RNGkind independence (primary, normal, and sample kinds), stage independence, pinned reference values. Fulfils Add seed reproducibility tests across mock-data generators #22.
  • Suite at branch head: FAIL 0 | WARN 35 | SKIP 10 | PASS 695. R CMD check: 0 errors, 0 warnings, 3 environmental NOTEs.
  • NEWS breaking-change note; harmonized @param seed docs across the three generators.

Implements #38. Fulfils #22. Resolves #33 item 11. Follow-up: #47 (route standalone create_*/survival generators through the primitive for package-wide isolation).

Includes the accepted ADR (development/adr/v05-seed-contract.md) and the implementation plan (development/plans/2026-07-06-seed-contract.md).

Records the seed/RNG-stream contract for v0.5: L'Ecuyer-CMRG stage
sub-streams via parallel::nextRNGStream(), unified save/restore
isolation across native/simstudy/legacy paths, RNG-kind pinning within
the generation scope, and an always-on fallback stream-switch message.
Co-released with the formula evaluator (#39) so v0.5 carries exactly one
seeded-output break. Fulfils #22; resolves #33 item 11. Implementation
is a separate ADR-gated plan.
Three-task plan implementing the accepted v0.5 seed-contract ADR:
L'Ecuyer-CMRG per-stage sub-streams, unified path isolation, always-on
fallback message, and the budgeted seeded-output re-baseline.
Replace the seed/seed+1L split with named, provably-independent
sub-streams (baseline, postprocess) derived from one public seed via
parallel::nextRNGStream(). .with_mock_seed() now pins RNGkind within
scope and restores it, so output no longer depends on the caller's
ambient RNGkind(). Native, postprocess, and simstudy paths route
through it. Re-baselines value-pinning tests for the RNG change.
The RNGkind-independence test switched the ambient generator to
Marsaglia-Multicarry, whose built-in "poor statistical properties"
caution fired twice (the switch and the on.exit restore). Wrap the
switch and its generation in suppressWarnings; the test's semantics
(output identical across ambient kinds) are unchanged.
Route the legacy generation loop through .with_mock_seed() so it no
longer clobbers the caller's RNG stream, matching the native path.
Emit an always-on message when the v0.4 path silently falls back to
the legacy generator for an unsupported feature (resolves #33 item 11).
Re-baselines affected legacy value-pin tests.
Add cross-path determinism and pinned-reference tests, harmonize the
seed @param docs across the three generators, and document the v0.5
reproducibility break in NEWS. Fulfils #22.
Whole-branch review found that set.seed(kind = "L'Ecuyer-CMRG") pinned
only the primary generator, so rnorm() and equal-probability sample()
still leaked the caller's ambient normal.kind/sample.kind - partially
breaking the ADR D3 'independent of ambient RNGkind()' guarantee. Pin
all three components (a no-op under R >= 3.6 defaults, so pinned values
are unchanged) and add a regression test that varies normal.kind and
sample.kind. Scope the NEWS isolation claim to seeded generation calls.
The on.exit restore of the ambient sample.kind = Rounding re-triggered
R's caution during the second generation call; extend suppressWarnings
to enclose it. Test semantics (output identical across ambient kinds)
unchanged.
Copilot AI review requested due to automatic review settings July 6, 2026 22:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the v0.5 seed & RNG-stream contract across generation paths by centralizing all seeded randomness in a staged .with_mock_seed() wrapper that uses L'Ecuyer-CMRG sub-streams and restores the caller’s RNG state/kind, with accompanying tests and release documentation.

Changes:

  • Introduces fixed stage → sub-stream mapping and updates native/simstudy/postprocess + legacy generation paths to use .with_mock_seed() with stage-specific sub-streams.
  • Removes the previous seed / seed + 1L split and adds an always-on legacy-fallback message when v0.4 features are unsupported.
  • Adds a comprehensive seed-contract test suite plus NEWS and roxygen updates; adds parallel to Imports.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/testthat/test-seed-contract.R Adds contract tests for determinism, stage independence, and RNG isolation/kind-independence.
R/mock_spec_native.R Adds .MOCK_STAGES, extends .with_mock_seed() to stage-based L'Ecuyer sub-streams, wires native baseline stage.
R/mock_spec_postprocess.R Routes post-processing through the "postprocess" stage sub-stream; updates seed docs.
R/mock_spec_simstudy.R Routes simstudy baseline generation through the "baseline" stage sub-stream; updates seed docs.
R/create_mock_data.R Drops seed+1L, wraps legacy loop with .with_mock_seed(), adds always-on fallback message.
NEWS.md Documents the v0.4 → v0.5 seeded-output breaking change and RNG isolation guarantee.
DESCRIPTION Adds base parallel to Imports for nextRNGStream().
development/adr/v05-seed-contract.md Adds the accepted ADR defining the seed/RNG-stream contract.
development/plans/2026-07-06-seed-contract.md Adds the implementation plan and task breakdown for the ADR.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread R/create_mock_data.R
Comment on lines +299 to +302
message(
"Falling back to the legacy generator for an unsupported v0.4 feature; ",
"for a given seed this produces different values than the v0.4 pipeline."
)
Comment thread R/mock_spec_postprocess.R
Comment on lines +309 to +312
#' @param seed Optional whole-number seed. Generation uses an isolated
#' L'Ecuyer-CMRG sub-stream and restores the caller's RNG state and kind on
#' exit, so output is reproducible for a given seed and package version
#' without perturbing the caller's RNG.
Comment thread R/mock_spec_native.R
Comment on lines +348 to +351
#' @param seed Optional whole-number seed. Generation uses an isolated
#' L'Ecuyer-CMRG sub-stream and restores the caller's RNG state and kind on
#' exit, so output is reproducible for a given seed and package version
#' without perturbing the caller's RNG.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants