## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/trueno/src/blis/jidoka.rs

    Line
    Count
    Source
    1
    //! Jidoka (Autonomation) - Stop on Defect
    2
    //!
    3
    //! Runtime guards that detect and halt computation on numerical errors.
    4
    //! Part of Toyota Production System integration in BLIS.
    5
    //!
    6
    //! # Philosophy
    7
    //!
    8
    //! Jidoka (自働化) is a Toyota Production System principle meaning "automation
    9
    //! with a human touch." When a defect is detected, the process stops immediately
    10
    //! rather than propagating bad data downstream.
    11
    //!
    12
    //! # Usage
    13
    //!
    14
    //! ```
    15
    //! use trueno::blis::jidoka::{JidokaGuard, JidokaError};
    16
    //!
    17
    //! let guard = JidokaGuard::strict();
    18
    //! guard.check_input(1.0, "matrix_a")?;
    19
    //! let computed = 1.0f32;
    20
    //! let expected = 1.0f32;
    21
    //! guard.validate(computed, expected)?;
    22
    //! # Ok::<(), JidokaError>(())
    23
    //! ```
    24
    25
    /// Jidoka error types for runtime validation
    26
    #[derive(Debug, Clone, PartialEq)]
    27
    pub enum JidokaError {
    28
        /// Numerical deviation beyond acceptable threshold
    29
        NumericalDeviation {
    30
            computed: f32,
    31
            expected: f32,
    32
            relative_error: f32,
    33
        },
    34
        /// NaN detected in computation
    35
        NaNDetected { location: &'static str },
    36
        /// Infinity detected in computation
    37
        InfDetected { location: &'static str },
    38
        /// Dimension mismatch
    39
        DimensionMismatch {
    40
            expected: (usize, usize, usize),
    41
            actual: (usize, usize, usize),
    42
        },
    43
    }
    44
    45
    impl std::fmt::Display for JidokaError {
    46
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    47
    0
            match self {
    48
                Self::NumericalDeviation {
    49
    0
                    computed,
    50
    0
                    expected,
    51
    0
                    relative_error,
    52
                } => {
    53
    0
                    write!(
    54
    0
                        f,
    55
    0
                        "Jidoka: numerical deviation - computed={}, expected={}, error={}",
    56
                        computed, expected, relative_error
    57
                    )
    58
                }
    59
    0
                Self::NaNDetected { location } => {
    60
    0
                    write!(f, "Jidoka: NaN detected at {}", location)
    61
                }
    62
    0
                Self::InfDetected { location } => {
    63
    0
                    write!(f, "Jidoka: Inf detected at {}", location)
    64
                }
    65
    0
                Self::DimensionMismatch { expected, actual } => {
    66
    0
                    write!(
    67
    0
                        f,
    68
    0
                        "Jidoka: dimension mismatch - expected {:?}, got {:?}",
    69
                        expected, actual
    70
                    )
    71
                }
    72
            }
    73
    0
        }
    74
    }
    75
    76
    impl std::error::Error for JidokaError {}
    77
    78
    /// Jidoka guard for runtime validation
    79
    #[derive(Debug, Clone)]
    80
    pub struct JidokaGuard {
    81
        /// Maximum allowed relative error
    82
        pub epsilon: f32,
    83
        /// Whether to check for NaN/Inf
    84
        pub check_special: bool,
    85
        /// Sample rate (check every N outputs)
    86
        pub sample_rate: usize,
    87
    }
    88
    89
    impl Default for JidokaGuard {
    90
    0
        fn default() -> Self {
    91
    0
            Self {
    92
    0
                epsilon: 1e-5,
    93
    0
                check_special: true,
    94
    0
                sample_rate: 1000, // Check every 1000th output in release
    95
    0
            }
    96
    0
        }
    97
    }
    98
    99
    impl JidokaGuard {
    100
        /// Create a strict guard for testing (checks every output)
    101
    0
        pub fn strict() -> Self {
    102
    0
            Self {
    103
    0
                epsilon: 1e-6,
    104
    0
                check_special: true,
    105
    0
                sample_rate: 1,
    106
    0
            }
    107
    0
        }
    108
    109
        /// Validate a computed value against expected
    110
        #[inline]
    111
    0
        pub fn validate(&self, computed: f32, expected: f32) -> Result<(), JidokaError> {
    112
    0
            if self.check_special {
    113
    0
                if computed.is_nan() {
    114
    0
                    return Err(JidokaError::NaNDetected {
    115
    0
                        location: "output",
    116
    0
                    });
    117
    0
                }
    118
    0
                if computed.is_infinite() {
    119
    0
                    return Err(JidokaError::InfDetected {
    120
    0
                        location: "output",
    121
    0
                    });
    122
    0
                }
    123
    0
            }
    124
    125
    0
            let abs_diff = (computed - expected).abs();
    126
    0
            let max_abs = computed.abs().max(expected.abs()).max(1e-10);
    127
    0
            let relative_error = abs_diff / max_abs;
    128
    129
    0
            if relative_error > self.epsilon {
    130
    0
                return Err(JidokaError::NumericalDeviation {
    131
    0
                    computed,
    132
    0
                    expected,
    133
    0
                    relative_error,
    134
    0
                });
    135
    0
            }
    136
    137
    0
            Ok(())
    138
    0
        }
    139
    140
        /// Check input for NaN/Inf
    141
        #[inline]
    142
    0
        pub fn check_input(&self, value: f32, location: &'static str) -> Result<(), JidokaError> {
    143
    0
            if !self.check_special {
    144
    0
                return Ok(());
    145
    0
            }
    146
    0
            if value.is_nan() {
    147
    0
                return Err(JidokaError::NaNDetected { location });
    148
    0
            }
    149
    0
            if value.is_infinite() {
    150
    0
                return Err(JidokaError::InfDetected { location });
    151
    0
            }
    152
    0
            Ok(())
    153
    0
        }
    154
    }
    155
    156
    #[cfg(test)]
    157
    mod tests {
    158
        use super::*;
    159
    160
        #[test]
    161
        fn test_jidoka_default() {
    162
            let guard = JidokaGuard::default();
    163
            assert!((guard.epsilon - 1e-5).abs() < 1e-10);
    164
            assert!(guard.check_special);
    165
            assert_eq!(guard.sample_rate, 1000);
    166
        }
    167
    168
        #[test]
    169
        fn test_jidoka_strict() {
    170
            let guard = JidokaGuard::strict();
    171
            assert!((guard.epsilon - 1e-6).abs() < 1e-10);
    172
            assert!(guard.check_special);
    173
            assert_eq!(guard.sample_rate, 1);
    174
        }
    175
    176
        #[test]
    177
        fn test_validate_pass() {
    178
            let guard = JidokaGuard::default();
    179
            assert!(guard.validate(1.0, 1.0).is_ok());
    180
            assert!(guard.validate(1.0, 1.000001).is_ok());
    181
        }
    182
    183
        #[test]
    184
        fn test_validate_nan() {
    185
            let guard = JidokaGuard::default();
    186
            let result = guard.validate(f32::NAN, 1.0);
    187
            assert!(matches!(result, Err(JidokaError::NaNDetected { .. })));
    188
        }
    189
    190
        #[test]
    191
        fn test_validate_inf() {
    192
            let guard = JidokaGuard::default();
    193
            let result = guard.validate(f32::INFINITY, 1.0);
    194
            assert!(matches!(result, Err(JidokaError::InfDetected { .. })));
    195
        }
    196
    197
        #[test]
    198
        fn test_validate_deviation() {
    199
            let guard = JidokaGuard::strict();
    200
            let result = guard.validate(1.0, 2.0);
    201
            assert!(matches!(
    202
                result,
    203
                Err(JidokaError::NumericalDeviation { .. })
    204
            ));
    205
        }
    206
    207
        #[test]
    208
        fn test_check_input_nan() {
    209
            let guard = JidokaGuard::default();
    210
            let result = guard.check_input(f32::NAN, "test");
    211
            assert!(matches!(result, Err(JidokaError::NaNDetected { .. })));
    212
        }
    213
    214
        #[test]
    215
        fn test_check_input_inf() {
    216
            let guard = JidokaGuard::default();
    217
            let result = guard.check_input(f32::INFINITY, "test");
    218
            assert!(matches!(result, Err(JidokaError::InfDetected { .. })));
    219
        }
    220
    221
        #[test]
    222
        fn test_error_display() {
    223
            let err = JidokaError::NaNDetected { location: "test" };
    224
            assert!(format!("{}", err).contains("NaN"));
    225
    226
            let err = JidokaError::InfDetected { location: "test" };
    227
            assert!(format!("{}", err).contains("Inf"));
    228
    229
            let err = JidokaError::NumericalDeviation {
    230
                computed: 1.0,
    231
                expected: 2.0,
    232
                relative_error: 0.5,
    233
            };
    234
            assert!(format!("{}", err).contains("deviation"));
    235
    236
            let err = JidokaError::DimensionMismatch {
    237
                expected: (1, 2, 3),
    238
                actual: (4, 5, 6),
    239
            };
    240
            assert!(format!("{}", err).contains("mismatch"));
    241
        }
    242
    }