Skip to main content

trueno/simulation/
mod.rs

1//! Simulation Testing Framework (TRUENO-SPEC-012)
2//!
3//! Provides deterministic, reproducible, and falsifiable validation of compute
4//! operations across all backends: SIMD (CPU), PTX (CUDA), and WGPU.
5//!
6//! This module integrates with the sovereign stack (simular) and follows
7//! Toyota Production System principles:
8//!
9//! - **Jidoka**: Built-in quality - stop on defect
10//! - **Poka-Yoke**: Mistake-proofing via type safety
11//! - **Heijunka**: Leveled testing across backends
12//! - **Genchi Genbutsu**: Visual inspection of results
13//! - **Kaizen**: Continuous performance improvement
14//!
15//! # Example
16//!
17//! ```rust,ignore
18//! use trueno::simulation::{SimTestConfig, BackendTolerance};
19//!
20//! let config = SimTestConfig::builder()
21//!     .seed(42)
22//!     .tolerance(BackendTolerance::default())
23//!     .build();
24//! ```
25
26mod jidoka;
27mod scheduler;
28mod stress;
29mod visual;
30
31// Re-export visual regression types
32pub use visual::{
33    BufferRenderer, ColorPalette, GoldenBaseline, PixelDiffResult, Rgb, VisualRegressionConfig,
34};
35
36// Re-export stress testing types
37pub use stress::{
38    StressAnomaly, StressAnomalyKind, StressResult, StressTestConfig, StressThresholds,
39};
40
41// Re-export jidoka types
42pub use jidoka::{JidokaAction, JidokaCondition, JidokaError, JidokaGuard};
43
44// Re-export scheduler types
45pub use scheduler::{
46    BackendCategory, BackendSelector, BackendTolerance, HeijunkaScheduler, NeedsSeed, Ready,
47    SimTestConfig, SimTestConfigBuilder, SimulationTest,
48};
49
50/// Re-export SimRng from simular for deterministic testing
51#[cfg(test)]
52pub use simular::engine::rng::SimRng;
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use crate::Backend;
58
59    #[test]
60    fn test_simrng_reproducibility() {
61        // Falsifiable claim B-016, B-017
62        let mut rng1 = SimRng::new(42);
63        let mut rng2 = SimRng::new(42);
64
65        let seq1: Vec<f64> = (0..100).map(|_| rng1.gen_f64()).collect();
66        let seq2: Vec<f64> = (0..100).map(|_| rng2.gen_f64()).collect();
67
68        assert_eq!(seq1, seq2, "Same seed must produce identical sequences");
69    }
70
71    #[test]
72    fn test_simrng_different_seeds() {
73        // Falsifiable claim B-018
74        let mut rng1 = SimRng::new(42);
75        let mut rng2 = SimRng::new(43);
76
77        let seq1: Vec<f64> = (0..100).map(|_| rng1.gen_f64()).collect();
78        let seq2: Vec<f64> = (0..100).map(|_| rng2.gen_f64()).collect();
79
80        assert_ne!(seq1, seq2, "Different seeds must produce different sequences");
81    }
82
83    #[test]
84    fn test_simrng_partitioning() {
85        // Falsifiable claim B-019
86        let mut rng = SimRng::new(42);
87        let partitions = rng.partition(4);
88
89        assert_eq!(partitions.len(), 4);
90
91        // Each partition should be independent
92        let mut seqs: Vec<Vec<f64>> = Vec::new();
93        for mut p in partitions {
94            seqs.push((0..10).map(|_| p.gen_f64()).collect());
95        }
96
97        for i in 0..seqs.len() {
98            for j in (i + 1)..seqs.len() {
99                assert_ne!(seqs[i], seqs[j], "Partitions must be independent");
100            }
101        }
102    }
103
104    #[test]
105    fn test_simrng_gen_f32_for_trueno() {
106        // Generate f32 test data using SimRng
107        let mut rng = SimRng::new(42);
108
109        let test_data: Vec<f32> = (0..1000).map(|_| rng.gen_f64() as f32).collect();
110
111        // Verify all values are in valid range
112        for v in &test_data {
113            assert!(v.is_finite(), "Generated value should be finite");
114            assert!(*v >= 0.0 && *v < 1.0, "Value should be in [0, 1)");
115        }
116    }
117
118    #[test]
119    fn test_full_simulation_workflow() {
120        // Create test configuration
121        let config = SimTestConfig::builder()
122            .seed(42)
123            .tolerance(BackendTolerance::default())
124            .backends(vec![Backend::Scalar, Backend::AVX2])
125            .input_sizes(vec![100])
126            .cycles(2)
127            .build();
128
129        // Create scheduler
130        let mut scheduler = config.create_scheduler();
131
132        // Create Jidoka guards
133        let nan_guard = JidokaGuard::nan_guard("simulation_test");
134
135        // Run through all tests
136        let mut test_count = 0;
137        while let Some(test) = scheduler.next_test() {
138            // Generate deterministic test data
139            let mut rng = SimRng::new(test.seed);
140            let data: Vec<f32> = (0..test.input_size).map(|_| rng.gen_f64() as f32).collect();
141
142            // Check for NaN (should pass with valid data)
143            let result = nan_guard.check_output(&data);
144            assert!(result.is_ok(), "Generated data should not contain NaN");
145
146            test_count += 1;
147        }
148
149        // 2 backends * 1 size * 2 cycles = 4 tests
150        assert_eq!(test_count, 4);
151    }
152}
153
154#[cfg(test)]
155mod proptests {
156    use super::*;
157    use proptest::prelude::*;
158
159    proptest! {
160        /// Falsifiable claim: NaN detection never misses
161        #[test]
162        fn prop_nan_detection_complete(values in prop::collection::vec(-1000.0f32..1000.0, 0..100)) {
163            let guard = JidokaGuard::nan_guard("test");
164
165            // Clean input should pass
166            let result = guard.check_output(&values);
167            prop_assert!(result.is_ok());
168        }
169    }
170}