code wiki / (root) / sketch_zscore_test.nx

sketch_zscore_test.nx source

↩ module page · 104 lines · 3129 B

1// sketch_zscore_test.nx -- z-score anomaly detector verification. 2 3import "syscalls.nx" 4import "sketch_zscore.nx" 5import "sketch_stream_stats.nx" 6import "sketch_types.nx" 7 8func iabs(x: i64) -> i64 { 9 if x < 0 { return -x } 10 return x 11} 12 13func main() -> i64 { 14 // ---- alloc ---- 15 let z: *ZScore = nx_zs_alloc(2000000, 30) // threshold 2.0 16 if z == (0 as *ZScore) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 17 // Reject threshold <= 0. 18 if nx_zs_alloc(0, 30) != (0 as *ZScore) { 19 return __syscall(93, 6, 0, 0, 0, 0, 0) 20 } 21 if nx_zs_alloc(-1, 30) != (0 as *ZScore) { 22 return __syscall(93, 7, 0, 0, 0, 0, 0) 23 } 24 25 // ---- warm-up: no alarms before min_count ---- 26 var i: i64 = 0 27 while i < 10 { 28 let v: i64 = nx_zs_step(z, 10000) // way above expected baseline 29 if v != NX_ZS_VERDICT_NORMAL { 30 return __syscall(93, 10, 0, 0, 0, 0, 0) // false positive in warm-up 31 } 32 i = i + 1 33 } 34 if nx_zs_n_alarms(z) != 0 { 35 return __syscall(93, 11, 0, 0, 0, 0, 0) 36 } 37 38 // ---- past warm-up: stable baseline at ~100, then spike ---- 39 let z2: *ZScore = nx_zs_alloc(2000000, 20) 40 // Feed 30 samples near 100. 41 i = 0 42 while i < 30 { 43 // Jitter +/-3. 44 var v: i64 = 100 45 if (i & 1) == 0 { v = 103 } 46 if (i & 1) == 1 { v = 97 } 47 nx_zs_step(z2, v) 48 i = i + 1 49 } 50 // Now feed extreme outlier: x = 1000. 51 let verdict_spike: i64 = nx_zs_verdict(z2, 1000) 52 if verdict_spike != NX_ZS_VERDICT_HIGH { 53 return __syscall(93, 20, 0, 0, 0, 0, 0) 54 } 55 // Negative outlier. 56 let verdict_drop: i64 = nx_zs_verdict(z2, -500) 57 if verdict_drop != NX_ZS_VERDICT_LOW { 58 return __syscall(93, 21, 0, 0, 0, 0, 0) 59 } 60 61 // ---- normal sample within baseline: NORMAL ---- 62 let verdict_normal: i64 = nx_zs_verdict(z2, 102) 63 if verdict_normal != NX_ZS_VERDICT_NORMAL { 64 return __syscall(93, 30, 0, 0, 0, 0, 0) 65 } 66 67 // ---- step pipeline tracks alarms ---- 68 let z3: *ZScore = nx_zs_alloc(2000000, 20) 69 i = 0 70 while i < 30 { 71 // Stable baseline at 100. 72 if (i & 1) == 0 { nx_zs_step(z3, 102) } 73 if (i & 1) == 1 { nx_zs_step(z3, 98) } 74 i = i + 1 75 } 76 // 3 outliers. 77 nx_zs_step(z3, 5000) 78 nx_zs_step(z3, -5000) 79 nx_zs_step(z3, 3000) 80 if nx_zs_n_alarms(z3) != 3 { 81 return __syscall(93, 40, 0, 0, 0, 0, 0) 82 } 83 84 // ---- typed envelope ---- 85 let q: *ApproxI64 = nx_zs_query(z2, 1000) 86 if q.envelope_kind != NX_ENV_ABS { 87 return __syscall(93, 50, 0, 0, 0, 0, 0) 88 } 89 if q.maturity != NX_MATURITY_PRODUCTION { 90 return __syscall(93, 51, 0, 0, 0, 0, 0) 91 } 92 93 // ---- query basic stats accessors ---- 94 if nx_zs_count(z2) < 30 { 95 return __syscall(93, 60, 0, 0, 0, 0, 0) 96 } 97 // Mean should be near 100. 98 let mn: i64 = nx_zs_mean(z2) 99 if iabs(mn - 100) > 5 { 100 return __syscall(93, 61, 0, 0, 0, 0, 0) 101 } 102 103 return 0 104}