Conversation
Add a `ChoiceSource` trait with two methods (`next`, `next_biguint`) to `rand.rs`. Patching only these two methods wires all five evaluator call sites (builtins.rs:88-93, 174, 197-206; nondet.rs:86, 118-121) by construction, with zero call-site edits. Design decisions: - `Rand` gains `choice_source: Option<Box<dyn ChoiceSource>>`, defaulting to None (no behavioral change when absent). - Counter always advances on every draw even when a source is installed, keeping `get_state()` monotonic for simulator seed reporting. - Fallback path (no source) is byte-identical to unpatched crate; the early- return delegation leaves original lines visually unchanged in the diff. - `remaining()` is deliberately excluded from the trait (fuzzer-side introspection concern; would be rejected upstream). - Object-safe trait enables `Box<dyn ChoiceSource>` storage in Rand. - Public API: `set_choice_source`, `take_choice_source`. Motivations: external replay, deterministic debugging, model-based testing. New test file `evaluator/tests/choice_source_tests.rs` adds five tests: - tape_delivers_values_in_order (no CLI) - no_source_counter_advances_by_one_per_draw (no CLI) - with_source_counter_still_advances (no CLI) - action_any_consumes_n_draws (requires quint CLI) - nondet_retry_consumes_zero_extra_draws (requires quint CLI) All 169 evaluator tests pass (164 existing unmodified + 5 new). No-regression proof: byte-identical output at fixed seed vs unpatched rev.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Randis the single point through which the evaluator draws nondeterministic choices: all five call sites (actionAny,oneOf, andnondetselection) route throughRand::next/Rand::next_biguint. Today those draws come from an internally seeded PRNG, which limits what can be done with a run after the fact:This PR adds a small, additive seam for those use cases. Downstream consumers (fuzzing, exhaustive enumeration of choice sequences, symbolic guidance) can build on it without any change to the evaluator itself.
Non-breaking fallback
When no
ChoiceSourceis installed, the evaluator behaves exactly as before:Randdiffers only by aNone-valued field, and both draw methods take their original code paths verbatim. This was verified empirically rather than asserted: two binaries, one linking pristine6fb2924eand one linking this branch, generated the same fixed-seed sequence ofnext()andnext_biguint()draws — including bounds exceedingu64::MAX, which exercise the internal large-BigUint path — and produced byte-identical output.Tests
Five new tests in
evaluator/tests/choice_source_tests.rs:tape_delivers_values_in_order— a tape of[2, 0, 1, 7]is returned verbatim bynext(values are forwarded, not reduced or transformed).no_source_counter_advances_by_one_per_draw— without a source, the counter advances by exactly 1 per draw (no-regression gate).with_source_counter_still_advances— with a source, the counter still advances per draw, keepingget_state()monotonic for simulator statistics.action_any_consumes_n_draws—actionAnyover N actions consumes exactly N draws, one per Fisher–Yates iteration including the degenerate final draw with bound 1.nondet_retry_consumes_zero_extra_draws— anondetretry is pure mixed-radix arithmetic; the retry path charges zero additional draws, verified order-independently over all four initial positions.The two integration tests use the same helper-based
quintCLI path as existing evaluator tests. The full pre-existing suite (164 tests) passes unmodified;cargo fmt --checkandcargo clippy -- -D warningsare clean on the evaluator.API surface
ChoiceSource, with two methods —fn next(&mut self, bound: u64) -> u64andfn next_biguint(&mut self, bound: &BigUint) -> BigUint. It is object-safe, so it can be stored asBox<dyn ChoiceSource>, and both methods mirrorRand's own signatures. Implementations must return a value in[0, bound)and answer every call infallibly; there is no error path in the trait, keeping the seam free ofResultplumbing.Rand:choice_source: Option<Box<dyn ChoiceSource>>(defaultNone).Rand:set_choice_source(&mut self, source: Box<dyn ChoiceSource>)andtake_choice_source(&mut self) -> Option<Box<dyn ChoiceSource>>.Zero call-site changes: all five nondeterminism draw sites already route through
Rand, so delegation insidenext/next_biguintcovers them all. Both methods advance the internal counter on every draw even when a source is present, preservingget_state()semantics.Note on the second method: the large-powerset path in
next_biguintseeds an internalStdRngdirectly rather than going throughnext. Delegating onlynextwould therefore silently fall back to real randomness on large powersets and break replay;next_biguinton the trait closes that gap.