1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Deterministic seed derivation for benchmark trials.
//!
//! A single `base_seed` fans out to per-trial, per-env, and per-agent seeds
//! via splitmix64 mixing. Outputs are independent of thread scheduling,
//! ensuring that two runs with the same base seed produce identical seed
//! sequences regardless of how work is distributed across threads.
//!
//! # Reproducibility contract
//!
//! All `rlevo` algorithms must draw randomness by calling into a
//! [`SeedStream`] rather than reaching for the backend's process-wide RNG
//! (e.g. `B::seed` + `Tensor::random`). The process-wide RNG is a global
//! mutex; parallel test workers racing on it produce non-deterministic
//! initialisation order even when the top-level seed is fixed.
//!
//! The recommended pattern:
//!
//! ```rust
//! use rlevo_core::util::seed::SeedStream;
//!
//! let stream = SeedStream::new(42);
//! let t = stream.trial_seed(/*env_idx=*/0, /*trial_idx=*/0);
//! let env_rng_seed = stream.env_seed(t);
//! let agent_rng_seed = stream.agent_seed(t);
//! // Pass env_rng_seed / agent_rng_seed to the respective constructors.
//! ```
/// A deterministic, fan-out seed generator for benchmark and training trials.
///
/// `SeedStream` derives independent 64-bit seeds for each (environment index,
/// trial index) pair, and further splits each trial seed into separate env and
/// agent seeds. Every derivation is a pure function of `base` and the index
/// arguments, so the same `SeedStream` always produces the same sequence.
///
/// # Design
///
/// Derivation uses splitmix64 — a bijective mixing function — XOR'd with
/// index-dependent multipliers. The env and agent branches are separated by
/// distinct domain constants so `env_seed(t) != agent_seed(t)` for all `t`.
///
/// # Thread safety
///
/// `SeedStream` holds no mutable state and is `Copy`. All methods are
/// `const fn`. It is safe to share across threads without synchronisation.
/// The splitmix64 finalizing mixer — a **frozen reference algorithm**.
///
/// Given any `u64`, returns a well-distributed `u64` via the canonical
/// splitmix64 constants (`0x9E37_79B9_7F4A_7C15`, `0xBF58_476D_1CE4_E5B9`,
/// `0x94D0_49BB_1331_11EB`). It is a pure, `const`, bijective mixing step.
///
/// # Stability contract
///
/// **This function's output must never change.** It is the single source of
/// truth for the mixer used by both [`SeedStream`] (trial/env/agent fan-out)
/// and `rlevo_evolution::rng::seed_stream` (operator sub-streams). Any edit to
/// the constants or shifts would silently break the reproducibility of every
/// stored seed, golden test, and recorded run in the workspace. It is pinned by
/// `splitmix64_golden_values_are_frozen`; treat a failure of that test as "do
/// not merge / revert," not "update the expected value."
///
/// The two derivation schemes that consume it are otherwise independent: they
/// share this mixer, not a seed-derivation contract. See ADR 0033.
pub const