code wiki / (root) / sketch_kmeans1d_test.nx

sketch_kmeans1d_test.nx source

↩ module page · 104 lines · 3809 B

1// sketch_kmeans1d_test.nx -- 1D streaming k-means verification. 2 3import "syscalls.nx" 4import "sketch_kmeans1d.nx" 5import "sketch_types.nx" 6 7func iabs(x: i64) -> i64 { 8 if x < 0 { return -x } 9 return x 10} 11 12func main() -> i64 { 13 // ---- alloc ---- 14 let m: *KMeans1D = nx_kmeans_alloc(3) 15 if m == (0 as *KMeans1D) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 16 // Reject k<2. 17 if nx_kmeans_alloc(1) != (0 as *KMeans1D) { 18 return __syscall(93, 6, 0, 0, 0, 0, 0) 19 } 20 21 // ---- seeding: first k distinct values become centroids ---- 22 nx_kmeans_observe(m, 10) 23 nx_kmeans_observe(m, 100) 24 nx_kmeans_observe(m, 1000) 25 if nx_kmeans_n_clusters(m) != 3 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 26 if nx_kmeans_centroid(m, 0) != 10 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 27 if nx_kmeans_centroid(m, 1) != 100 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 28 if nx_kmeans_centroid(m, 2) != 1000 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 29 30 // ---- assignment: nearest centroid ---- 31 if nx_kmeans_predict(m, 12) != 0 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 32 if nx_kmeans_predict(m, 95) != 1 { return __syscall(93, 21, 0, 0, 0, 0, 0) } 33 if nx_kmeans_predict(m, 900) != 2 { return __syscall(93, 22, 0, 0, 0, 0, 0) } 34 35 // ---- observe values near each centroid: centroids update ---- 36 var i: i64 = 0 37 while i < 10 { 38 nx_kmeans_observe(m, 12) // near cluster 0 39 nx_kmeans_observe(m, 100) // near cluster 1 40 nx_kmeans_observe(m, 1100) // near cluster 2 41 i = i + 1 42 } 43 if nx_kmeans_total(m) != 33 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 44 // Cluster 0 centroid drifts from 10 toward 12; should be near 11-12. 45 let c0: i64 = nx_kmeans_centroid(m, 0) 46 if iabs(c0 - 12) > 2 { 47 return __syscall(93, 31, 0, 0, 0, 0, 0) 48 } 49 // Cluster 2 centroid drifts from 1000 toward 1100. 50 let c2: i64 = nx_kmeans_centroid(m, 2) 51 if iabs(c2 - 1080) > 50 { 52 return __syscall(93, 32, 0, 0, 0, 0, 0) 53 } 54 55 // ---- counts ---- 56 if nx_kmeans_count(m, 0) < 5 { return __syscall(93, 40, 0, 0, 0, 0, 0) } 57 if nx_kmeans_count(m, 1) < 5 { return __syscall(93, 41, 0, 0, 0, 0, 0) } 58 if nx_kmeans_count(m, 2) < 5 { return __syscall(93, 42, 0, 0, 0, 0, 0) } 59 60 // ---- typed envelope ---- 61 let q: *ApproxI64 = nx_kmeans_query_centroid(m, 0) 62 if q.envelope_kind != NX_ENV_REL_STDDEV { 63 return __syscall(93, 50, 0, 0, 0, 0, 0) 64 } 65 if q.maturity != NX_MATURITY_PRODUCTION { 66 return __syscall(93, 51, 0, 0, 0, 0, 0) 67 } 68 69 // ---- bimodal stream: two clear clusters ---- 70 let m2: *KMeans1D = nx_kmeans_alloc(2) 71 // 50 samples near 0, 50 samples near 100. 72 i = 0 73 while i < 50 { 74 nx_kmeans_observe(m2, i % 5) // values 0..4 75 i = i + 1 76 } 77 i = 0 78 while i < 50 { 79 nx_kmeans_observe(m2, 100 + (i % 5)) // values 100..104 80 i = i + 1 81 } 82 if nx_kmeans_n_clusters(m2) != 2 { 83 return __syscall(93, 60, 0, 0, 0, 0, 0) 84 } 85 // Cluster 0 should be near 2 (mean of 0..4). 86 let bm0: i64 = nx_kmeans_centroid(m2, 0) 87 if iabs(bm0 - 2) > 2 { 88 return __syscall(93, 61, 0, 0, 0, 0, 0) 89 } 90 // Cluster 1 should be near 102. 91 let bm1: i64 = nx_kmeans_centroid(m2, 1) 92 if iabs(bm1 - 102) > 2 { 93 return __syscall(93, 62, 0, 0, 0, 0, 0) 94 } 95 // Predict new values to correct cluster. 96 if nx_kmeans_predict(m2, 50) != 0 { // closer to 2 than 102 97 return __syscall(93, 70, 0, 0, 0, 0, 0) 98 } 99 if nx_kmeans_predict(m2, 51) != 1 { // closer to 102 than 2 100 return __syscall(93, 71, 0, 0, 0, 0, 0) 101 } 102 103 return 0 104}