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}