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
//! Determinism Guards for Discovery Algorithms
//!
//! **Issue:** Stochastic algorithms (GA, PSO, ACO, SA) use hardcoded seed `StdRng::seed_from_u64(42)`.
//! This ensures determinism but prevents user-controlled reproducibility.
//!
//! **Guards:**
//! 1. Document the hardcoded seed in each algorithm's docstring
//! 2. Provide determinism audit tests
//! 3. Reserve capacity for future seed parameter without breaking API
use rand::rngs::StdRng;
use rand::SeedableRng;
/// Marker trait for deterministic algorithms.
/// Guarantees: Same input + same seed → bit-identical output.
pub trait DeterministicAlgorithm {
/// Standard seed used for deterministic reproducibility
const DEFAULT_SEED: u64 = 42;
/// Get the RNG seed for this algorithm
fn get_seed(&self) -> u64 {
Self::DEFAULT_SEED
}
}
/// Guard: Verify determinism of a stochastic algorithm
///
/// Usage:
/// ```ignore
/// let result1 = discover_genetic_algorithm_from_log(...);
/// let result2 = discover_genetic_algorithm_from_log(...);
/// assert_determinism(&result1, &result2, "genetic_algorithm");
/// ```
pub fn assert_determinism(result1: &(f64,), result2: &(f64,), algo_name: &str) {
let (fitness1,) = result1;
let (fitness2,) = result2;
assert_eq!(
fitness1, fitness2,
"{}: Non-deterministic fitness {:.6} vs {:.6}",
algo_name, fitness1, fitness2
);
}
/// Constant: RNG seed used across all stochastic algorithms
///
/// **Determinism Contract:** If this constant is unchanged, all stochastic
/// algorithms are guaranteed to produce identical results on identical inputs.
///
/// **Future:** To support user-supplied seeds without breaking changes,
/// add optional seed parameter to each algorithm's `_from_log()` function:
///
/// ```ignore
/// pub fn discover_genetic_algorithm_from_log(
/// log: &EventLog,
/// activity_key: &str,
/// population_size: usize,
/// generations: usize,
/// seed: Option<u64>, // NEW
/// ) -> Option<(DFG, f64)> {
/// let mut rng = StdRng::seed_from_u64(seed.unwrap_or(STOCHASTIC_ALGORITHM_SEED));
/// // ...
/// }
/// ```
pub const STOCHASTIC_ALGORITHM_SEED: u64 = 42;
/// Create a deterministic RNG for stochastic algorithms
///
/// All algorithms must use this function to ensure consistency.
pub fn create_deterministic_rng() -> StdRng {
StdRng::seed_from_u64(STOCHASTIC_ALGORITHM_SEED)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_rng_same_state_same_seed() {
let rng1 = create_deterministic_rng();
let rng2 = create_deterministic_rng();
// Both RNGs should have same initial state (same seed)
// Verify by checking they're both seeded from constant
assert_eq!(
STOCHASTIC_ALGORITHM_SEED, 42,
"Default seed should remain constant"
);
}
#[test]
fn determinism_contract_documented() {
// Marker: This test documents that determinism is a feature,
// not an accident. Algorithms are deterministic by design via
// StdRng::seed_from_u64(42).
assert_eq!(
STOCHASTIC_ALGORITHM_SEED, 42,
"Seed constant is immutable — determinism is guaranteed"
);
}
}