code wiki / _hdl_build / nx_vq_train_gate.nx
nx_vq_train_gate.nx source
↩ module page · 82 lines · 4501 B
1// nx_vq_train_gate.nx -- proves Lloyd's training (nx_vq_train) lowers VQ distortion at the SAME bitrate (better codebook
2// coverage), the bridge from raw VQ toward the neural codebook. Trains a K-entry codebook on T period-varied LPC
3// reflection vectors and measures total distortion before vs after training.
4import "nx_syscalls_x86_64.nx"
5import "nx_lpc_autocorr.nx"
6import "nx_lpc_levinson.nx"
7import "nx_vq.nx"
8import "nx_vq_train.nx"
9import "nx_gate_verdict.nx"
10
11func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func g_pn(v: i64) -> i64 {
13 let b: *u8 = sys_mmap(28); var x: i64 = v
14 if x < 0 { b[0]=45; sys_write(1,b,1); x = 0 - x }
15 if x == 0 { b[0]=48; sys_write(1,b,1); return 0 }
16 var d: i64=0; var y: i64=x
17 while y>0 { d=d+1; y=y/10 }
18 var i: i64=d-1; y=x
19 while i>=0 { b[i]=(48+(y%10)) as u8; y=y/10; i=i-1 }
20 sys_write(1,b,d); return 0
21}
22func g_check(name: *u8, cond: i64) -> i64 {
23 if cond==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) }
24 g_puts(name); g_puts("\n" as *u8); return cond
25}
26func st16(buf: *u8, idx: i64, v: i64) -> i64 { var x: i64 = v; if x < 0 { x = x + 65536 } buf[idx*2] = x & 0xff; buf[idx*2+1] = (x >> 8) & 0xff; return 0 }
27func ld64(buf: *u8, byteoff: i64) -> i64 { var v: i64 = 0; var b: i64 = 0; while b < 8 { v = v | (buf[byteoff + b] << (b*8)); b = b + 1 } return v }
28func fill_parabola(s: *u8, n: i64, period: i64) -> i64 { var i: i64=0; while i<n { let p: i64 = i % period; st16(s, i, 40 + p*(period-p)*2); i=i+1 } return 0 }
29
30func main() -> i64 {
31 g_puts("nx_vq_train gate (Lloyd k-means lowers VQ distortion at same bitrate, MEASURED)\n" as *u8)
32 var pass: i64 = 0; var total: i64 = 0
33 let n: i64 = 64; let order: i64 = 10; let D: i64 = order
34 let T: i64 = 16; let K: i64 = 8
35
36 let s_pcm: *u8 = sys_mmap(n*2)
37 let R: *u8 = sys_mmap((order+1)*8)
38 let kb: *u8 = sys_mmap((order+1)*8)
39 let ab: *u8 = sys_mmap((order+1)*8)
40 let eb: *u8 = sys_mmap((order+1)*8)
41 let train: *i64 = sys_mmap(T*D*8) as *i64
42 let cb: *i64 = sys_mmap(K*D*8) as *i64
43 let assign: *i64 = sys_mmap(T*8) as *i64
44 let sumb: *i64 = sys_mmap(K*D*8) as *i64
45 let cnt: *i64 = sys_mmap(K*8) as *i64
46
47 // training set: T period-varied reflection vectors, in Q15 (>>15 from levinson Q30) to keep the squared distances bounded
48 var t: i64 = 0
49 while t < T {
50 fill_parabola(s_pcm, n, 8 + t)
51 nx_lpc_autocorr(s_pcm, n, order, R)
52 nx_lpc_levinson(R, order, kb, ab, eb)
53 var i: i64 = 0
54 while i < D { train[t*D + i] = ld64(kb, i*8) >> 15; i = i + 1 }
55 t = t + 1
56 }
57 // initialise the codebook with K evenly-spaced training vectors (a coarse start)
58 var c: i64 = 0
59 while c < K { var i: i64 = 0; while i < D { cb[c*D + i] = train[(c*2)*D + i]; i = i + 1 } c = c + 1 }
60
61 let dist_before: i64 = vqt_distortion(train, T, cb, K, D)
62 var it: i64 = 0
63 while it < 6 { vqt_iterate(train, T, cb, K, D, assign, sumb, cnt); it = it + 1 }
64 let dist_after: i64 = vqt_distortion(train, T, cb, K, D)
65
66 g_puts(" [measure] codebook K=" as *u8); g_pn(K); g_puts(" over T=" as *u8); g_pn(T); g_puts(" vectors: distortion before=" as *u8); g_pn(dist_before); g_puts(" after 6 Lloyd iters=" as *u8); g_pn(dist_after); g_puts("\n" as *u8)
67 if dist_before > 0 { g_puts(" [measure] distortion cut by training = " as *u8); g_pn((dist_before - dist_after) * 100 / dist_before); g_puts("% (same 3-bit index, better coverage)\n" as *u8) }
68
69 pass = pass + g_check("training lowers VQ distortion (Lloyd is non-increasing)" as *u8, dist_after <= dist_before); total=total+1
70 pass = pass + g_check("training measurably improves coverage (>= 10% distortion cut)" as *u8, dist_after * 10 <= dist_before * 9); total=total+1
71
72 g_puts("---- vq train gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
73 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
74 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
75 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
76 let ctr__dry: *i64 = gv_ctr()
77 ctr__dry[0] = pass
78 ctr__dry[1] = total
79 let rc__dry: i64 = gv_verdict("VQ-TRAIN-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
80 sys_exit(rc__dry)
81 return rc__dry
82}