code wiki / (root) / sketch_mann_kendall_vs_endpoint_bench.nx

sketch_mann_kendall_vs_endpoint_bench.nx source

↩ module page · 90 lines · 3334 B

1// sketch_mann_kendall_vs_endpoint_bench.nx -- trend detection bench. 2// 3// CLAIM: Mann-Kendall (1945/1948) tests trend via signed pairwise 4// comparisons; robust to outliers and noise. Endpoint baseline 5// (compare first vs last value) is naive and fragile to noise. 6// 7// WORKLOAD: 8// Trended series y_t = 100 + 5*t + noise, t=1..50 -> MK should detect. 9// Stationary noisy series with same noise -> MK should NOT detect. 10// Endpoint baseline: classify if (last - first) > 50 -> trend. 11// Endpoint can be fooled by noisy endpoints. 12 13import "syscalls.nx" 14import "sketch_mann_kendall.nx" 15import "sketch_comparator.nx" 16import "sketch_types.nx" 17 18const NX_MKE_LCG_A: i64 = 1103515245 19const NX_MKE_LCG_C: i64 = 12345 20const NX_MKE_LCG_MOD: i64 = 0x7FFFFFFF 21 22func main() -> i64 { 23 // ---- Trended series ---- 24 let mk_trend: *MannKendall = nx_mk_alloc(50) 25 if mk_trend == (0 as *MannKendall) { return __syscall(93, 1, 0, 0, 0, 0, 0) } 26 var sim: i64 = 42 27 var first_trended: i64 = 0 28 var last_trended: i64 = 0 29 var t: i64 = 1 30 while t <= 50 { 31 sim = ((sim * NX_MKE_LCG_A) + NX_MKE_LCG_C) & NX_MKE_LCG_MOD 32 let noise: i64 = (sim & 0x1F) - 16 // -16..15 33 let y: i64 = 100 + 5 * t + noise 34 nx_mk_push(mk_trend, y) 35 if t == 1 { first_trended = y } 36 if t == 50 { last_trended = y } 37 t = t + 1 38 } 39 40 // ---- Stationary noisy series ---- 41 let mk_flat: *MannKendall = nx_mk_alloc(50) 42 if mk_flat == (0 as *MannKendall) { return __syscall(93, 2, 0, 0, 0, 0, 0) } 43 sim = 42 // identical seed for fairness 44 var first_flat: i64 = 0 45 var last_flat: i64 = 0 46 t = 1 47 while t <= 50 { 48 sim = ((sim * NX_MKE_LCG_A) + NX_MKE_LCG_C) & NX_MKE_LCG_MOD 49 let noise: i64 = (sim & 0x1F) - 16 50 let y: i64 = 200 + noise // stationary mean=200 51 nx_mk_push(mk_flat, y) 52 if t == 1 { first_flat = y } 53 if t == 50 { last_flat = y } 54 t = t + 1 55 } 56 57 // ---- MK verdicts ---- 58 let v_trend: i64 = nx_mk_verdict(mk_trend) 59 let v_flat: i64 = nx_mk_verdict(mk_flat) 60 61 // ---- MK should detect upward trend in trended ---- 62 // (verdict semantics: > 0 = trend; check via s value) 63 let s_trend: i64 = nx_mk_s(mk_trend) 64 let s_flat: i64 = nx_mk_s(mk_flat) 65 66 if s_trend <= 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 67 // MK on stationary should be near zero (small s) 68 if s_flat >= s_trend / 2 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 69 70 // ---- Endpoint baseline ---- 71 let endpoint_trend_signal: i64 = last_trended - first_trended 72 let endpoint_flat_signal: i64 = last_flat - first_flat 73 // Endpoint correctly identifies trend (large positive) 74 if endpoint_trend_signal <= 50 { 75 // Endpoint missed it OR is misleading. MK shouldn't lose on this. 76 } 77 78 // ---- Sanity: MK trend confidence > MK flat confidence ---- 79 // |s_trend| should be MUCH greater than |s_flat| 80 var abs_s_trend: i64 = s_trend 81 if abs_s_trend < 0 { abs_s_trend = -abs_s_trend } 82 var abs_s_flat: i64 = s_flat 83 if abs_s_flat < 0 { abs_s_flat = -abs_s_flat } 84 if abs_s_trend < abs_s_flat * 3 { 85 // Should be at least 3x larger 86 return __syscall(93, 30, 0, 0, 0, 0, 0) 87 } 88 89 return 0 90}