nx_gguf_load_block_test.nx source
↩ module page · 172 lines · 7019 B
1// nx_gguf_load_block_test.nx -- smoke for nx_gguf_load_block.nx.
2//
3// Two test classes:
4// A) Name-formatting unit checks: format "blk.{N}.{suffix}" with
5// different layer indices + suffix strings; verify byte-equal
6// to expected literal.
7// B) End-to-end integration: build a synthetic GGUF with a single
8// layer-0 attn_norm tensor (F32 shape [2]); call the loader's
9// _gbl_load_blk_tensor; verify it finds + loads correctly.
10//
11// Full 9-tensor end-to-end smoke is deferred to the `nx_llm_run` v2
12// brick where it composes with the transformer block forward pass.
13// THIS smoke validates the new code paths (name fmt + per-tensor
14// helper) under the bits-up canonical-brick discipline.
15
16import "nx_syscalls.nx"
17import "nx_tier.nx"
18import "nx_le.nx"
19import "nx_tensor.nx"
20import "nx_rope.nx"
21import "nx_gguf.nx"
22import "nx_gguf_load.nx"
23import "nx_gguf_load_block.nx"
24
25// Byte-equality helper.
26func _byte_equal(a: *u8, b: *u8, n: nx_int) -> nx_int {
27 var i: nx_int = 0
28 while i < n {
29 if a[i] != b[i] { return 0 }
30 i = i + 1
31 }
32 return 1
33}
34
35func main() -> i64 {
36 // ----- A) Name-formatting unit checks -----
37
38 // A.1: layer 0 + "attn_q.weight"
39 let suf_q: *u8 = sys_mmap(13)
40 suf_q[0]=0x61; suf_q[1]=0x74; suf_q[2]=0x74; suf_q[3]=0x6e
41 suf_q[4]=0x5f; suf_q[5]=0x71; suf_q[6]=0x2e; suf_q[7]=0x77
42 suf_q[8]=0x65; suf_q[9]=0x69; suf_q[10]=0x67; suf_q[11]=0x68
43 suf_q[12]=0x74
44 let out_a: *u8 = sys_mmap(64)
45 let n_a: nx_int = _gbl_fmt_blk_name(0, suf_q, 13, out_a)
46 if n_a != 19 { return 10 } // "blk.0.attn_q.weight" = 19 bytes
47 let exp_a: *u8 = sys_mmap(19)
48 exp_a[0]=0x62; exp_a[1]=0x6c; exp_a[2]=0x6b; exp_a[3]=0x2e
49 exp_a[4]=0x30 // '0'
50 exp_a[5]=0x2e // '.'
51 exp_a[6]=0x61; exp_a[7]=0x74; exp_a[8]=0x74; exp_a[9]=0x6e
52 exp_a[10]=0x5f; exp_a[11]=0x71; exp_a[12]=0x2e; exp_a[13]=0x77
53 exp_a[14]=0x65; exp_a[15]=0x69; exp_a[16]=0x67; exp_a[17]=0x68
54 exp_a[18]=0x74
55 if _byte_equal(out_a, exp_a, 19) != 1 { return 11 }
56
57 // A.2: layer 5 + "attn_q.weight" -> "blk.5.attn_q.weight" (19 bytes)
58 let out_b: *u8 = sys_mmap(64)
59 let n_b: nx_int = _gbl_fmt_blk_name(5, suf_q, 13, out_b)
60 if n_b != 19 { return 20 }
61 let exp_b: *u8 = sys_mmap(19)
62 exp_b[0]=0x62; exp_b[1]=0x6c; exp_b[2]=0x6b; exp_b[3]=0x2e
63 exp_b[4]=0x35 // '5'
64 exp_b[5]=0x2e
65 exp_b[6]=0x61; exp_b[7]=0x74; exp_b[8]=0x74; exp_b[9]=0x6e
66 exp_b[10]=0x5f; exp_b[11]=0x71; exp_b[12]=0x2e; exp_b[13]=0x77
67 exp_b[14]=0x65; exp_b[15]=0x69; exp_b[16]=0x67; exp_b[17]=0x68
68 exp_b[18]=0x74
69 if _byte_equal(out_b, exp_b, 19) != 1 { return 21 }
70
71 // A.3: layer 27 + "ffn_down.weight" -> "blk.27.ffn_down.weight" (22)
72 let suf_fd: *u8 = sys_mmap(15)
73 suf_fd[0]=0x66; suf_fd[1]=0x66; suf_fd[2]=0x6e; suf_fd[3]=0x5f
74 suf_fd[4]=0x64; suf_fd[5]=0x6f; suf_fd[6]=0x77; suf_fd[7]=0x6e
75 suf_fd[8]=0x2e; suf_fd[9]=0x77; suf_fd[10]=0x65; suf_fd[11]=0x69
76 suf_fd[12]=0x67; suf_fd[13]=0x68; suf_fd[14]=0x74
77 let out_c: *u8 = sys_mmap(64)
78 let n_c: nx_int = _gbl_fmt_blk_name(27, suf_fd, 15, out_c)
79 if n_c != 22 { return 30 }
80 let exp_c: *u8 = sys_mmap(22)
81 exp_c[0]=0x62; exp_c[1]=0x6c; exp_c[2]=0x6b; exp_c[3]=0x2e
82 exp_c[4]=0x32; exp_c[5]=0x37 // "27"
83 exp_c[6]=0x2e
84 exp_c[7]=0x66; exp_c[8]=0x66; exp_c[9]=0x6e; exp_c[10]=0x5f
85 exp_c[11]=0x64; exp_c[12]=0x6f; exp_c[13]=0x77; exp_c[14]=0x6e
86 exp_c[15]=0x2e; exp_c[16]=0x77; exp_c[17]=0x65; exp_c[18]=0x69
87 exp_c[19]=0x67; exp_c[20]=0x68; exp_c[21]=0x74
88 if _byte_equal(out_c, exp_c, 22) != 1 { return 31 }
89
90 // ----- B) Verdict range gate -----
91 var vi: nx_int = 0
92 while vi < NX_GBL_N_VERDICTS {
93 if nx_gbl_verdict_is_valid(vi) != 1 { return 40 + vi }
94 vi = vi + 1
95 }
96
97 // ----- C) Single per-layer tensor load integration -----
98 //
99 // Build a GGUF with one tensor "blk.0.attn_norm.weight" F32 [2].
100 let buf: *u8 = sys_mmap(512)
101 buf[0] = 0x47; buf[1] = 0x47; buf[2] = 0x55; buf[3] = 0x46
102 buf[4] = 3; buf[5] = 0; buf[6] = 0; buf[7] = 0
103 nx_le_write_u64(buf, 8, 1) // tensor_count
104 nx_le_write_u64(buf, 16, 0) // metadata_count
105
106 // Tensor: name = "blk.0.attn_norm.weight" (22 bytes)
107 nx_le_write_u64(buf, 24, 22)
108 // name bytes at 32..54
109 buf[32]=0x62; buf[33]=0x6c; buf[34]=0x6b; buf[35]=0x2e
110 buf[36]=0x30; buf[37]=0x2e
111 buf[38]=0x61; buf[39]=0x74; buf[40]=0x74; buf[41]=0x6e
112 buf[42]=0x5f; buf[43]=0x6e; buf[44]=0x6f; buf[45]=0x72
113 buf[46]=0x6d; buf[47]=0x2e; buf[48]=0x77; buf[49]=0x65
114 buf[50]=0x69; buf[51]=0x67; buf[52]=0x68; buf[53]=0x74
115 // n_dims = 1, dim_0 = 2, ggml_type = F32, offset = 0
116 nx_le_write_u32(buf, 54, 1)
117 nx_le_write_u64(buf, 58, 2)
118 nx_le_write_u32(buf, 66, NX_GGML_TYPE_F32)
119 nx_le_write_u64(buf, 70, 0)
120 // End of tensor_info at 78. data_off = align_up(78, 32) = 96.
121
122 // Data: two f32 values 1.0, 2.0
123 nx_le_write_u32(buf, 96 + 0, 0x3F800000)
124 nx_le_write_u32(buf, 96 + 4, 0x40000000)
125
126 let hdr: *NxGgufHeader = sys_mmap(NX_GGUF_HDR_BYTES) as *NxGgufHeader
127 let v_p: nx_int = nx_gguf_parse(buf, 512, hdr)
128 if v_p != NX_GGUF_OK { return 50 + v_p }
129 if hdr.n_tensors != 1 { return 60 }
130 if hdr.data_off != 96 { return 61 }
131
132 let suf_an: *u8 = sys_mmap(16)
133 suf_an[0]=0x61; suf_an[1]=0x74; suf_an[2]=0x74; suf_an[3]=0x6e
134 suf_an[4]=0x5f; suf_an[5]=0x6e; suf_an[6]=0x6f; suf_an[7]=0x72
135 suf_an[8]=0x6d; suf_an[9]=0x2e; suf_an[10]=0x77; suf_an[11]=0x65
136 suf_an[12]=0x69; suf_an[13]=0x67; suf_an[14]=0x68; suf_an[15]=0x74
137
138 let err: *i64 = sys_mmap(8) as *i64
139 err[0] = 0
140 let t_an: *NxTensor = _gbl_load_blk_tensor(buf, hdr, 0,
141 suf_an, 16, err)
142 if err[0] != NX_GL_OK { return 70 }
143 if t_an.numel != 2 { return 71 }
144 let p: *i64 = t_an.storage as *i64
145 if p[0] != 1024 { return 72 } // 1.0 -> Q10 1024
146 if p[1] != 2048 { return 73 } // 2.0 -> Q10 2048
147
148 // Negative path: ask for "blk.7.attn_norm.weight" -- not in GGUF.
149 err[0] = 0
150 let t_miss: *NxTensor = _gbl_load_blk_tensor(buf, hdr, 7,
151 suf_an, 16, err)
152 if err[0] != NX_GL_ERR_NOT_FOUND { return 80 }
153
154 // ----- D) RoPE inv_freq sanity ------------------------------
155 //
156 // For d=4, base=10000, expect:
157 // theta_0 = 1.0 in Q10 = 1024
158 // theta_1 = 10000^(-1/2) ~ 0.01 in Q10 = 10
159 //
160 // We don't bit-check (nx_rope already has its own KAT) -- just
161 // verify the inv_freq pointer is populated and theta_0 == 1024.
162
163 // Build a fake bundle to pass to the loader -- we only check the
164 // inv_freq behavior, so we skip the 9 tensor loads and call
165 // nx_rope_compute_inv_freq directly here.
166 let inv: *i64 = sys_mmap(2 * 8) as *i64
167 let v_r: nx_int = nx_rope_compute_inv_freq(4, 10000, inv)
168 if v_r != NX_ROPE_OK { return 90 }
169 if inv[0] != 1024 { return 91 }
170
171 return 0
172}