1use crate::{Context, Result, Tensor};
7
8impl Context {
11 pub fn tensor(&self, data: &[f32], shape: &[usize]) -> Tensor {
13 Tensor { buf: self.storage("t", data), shape: shape.to_vec() }
14 }
15 pub fn empty(&self, shape: &[usize]) -> Tensor {
17 let len: usize = shape.iter().product();
18 Tensor { buf: self.out_buffer(len), shape: shape.to_vec() }
19 }
20 pub async fn to_vec(&self, t: &Tensor) -> Result<Vec<f32>> { self.readback(&t.buf, t.len()).await }
22
23 pub fn mm(&self, a: &Tensor, b: &Tensor, m: u32, k: u32, n: u32) -> Tensor {
25 let out = self.empty(&[m as usize, n as usize]);
26 let dims = self.uniform_u32("d", &[m, k, n, 0]);
27 let pipe = self.pipeline("mm", crate::MATMUL_WGSL);
28 self.dispatch(&pipe, &[&a.buf, &b.buf, &out.buf, &dims], ((m + 15) / 16, (n + 15) / 16, 1));
29 out
30 }
31 pub fn mm_bt(&self, a: &Tensor, b: &Tensor, m: u32, n: u32, k: u32, scale: f32) -> Tensor {
33 let out = self.empty(&[m as usize, n as usize]);
34 let dims = self.uniform_u32("d", &[m, n, k, scale.to_bits()]);
35 let pipe = self.pipeline("mm_bt", MATMUL_BT_WGSL);
36 self.dispatch(&pipe, &[&a.buf, &b.buf, &out.buf, &dims], ((m + 15) / 16, (n + 15) / 16, 1));
37 out
38 }
39 pub fn silu_t(&self, x: &Tensor) -> Tensor {
40 let n = x.len();
41 let out = self.empty(&x.shape);
42 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
43 let pipe = self.pipeline("silu", SILU_WGSL);
44 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
45 out
46 }
47 pub fn dup(&self, x: &Tensor, shape: Vec<usize>) -> Tensor {
49 Tensor { buf: self.copy_buf(&x.buf, x.len()), shape }
50 }
51 pub fn gather0(&self, data: &Tensor, idx: &[u32], d: usize) -> Tensor {
53 let n = idx.len();
54 let out = self.empty(&[n, d]);
55 let idx_buf = self.storage_u32("idx", idx);
56 let dims = self.uniform_u32("d", &[n as u32, d as u32, 0, 0]);
57 let pipe = self.pipeline("gather", GATHER_WGSL);
58 self.dispatch(&pipe, &[&data.buf, &idx_buf, &out.buf, &dims], ((n * d) as u32 / 64 + 1, 1, 1));
59 out
60 }
61 pub fn sigmoid_t(&self, x: &Tensor) -> Tensor { self.unary(x, SIGMOID_WGSL, "sigmoid") }
62 pub fn sqrt_t(&self, x: &Tensor) -> Tensor { self.unary(x, SQRT_WGSL, "sqrt") }
63 pub fn gelu_t(&self, x: &Tensor) -> Tensor { self.unary(x, GELU_WGSL, "gelu") }
64 pub fn sub_t(&self, a: &Tensor, b: &Tensor) -> Tensor { self.binary(a, b, SUB_WGSL, "sub") }
65 pub fn div_t(&self, a: &Tensor, b: &Tensor) -> Tensor { self.binary(a, b, DIV_WGSL, "div") }
66 fn unary(&self, x: &Tensor, wgsl: &str, name: &str) -> Tensor {
67 let n = x.len();
68 let out = self.empty(&x.shape);
69 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
70 let pipe = self.pipeline(name, wgsl);
71 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
72 out
73 }
74 fn binary(&self, a: &Tensor, b: &Tensor, wgsl: &str, name: &str) -> Tensor {
75 let n = a.len();
76 let out = self.empty(&a.shape);
77 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
78 let pipe = self.pipeline(name, wgsl);
79 self.dispatch(&pipe, &[&a.buf, &b.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
80 out
81 }
82 pub fn mul_t(&self, a: &Tensor, b: &Tensor) -> Tensor {
84 let n = a.len();
85 let out = self.empty(&a.shape);
86 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
87 let pipe = self.pipeline("mul", MUL_WGSL);
88 self.dispatch(&pipe, &[&a.buf, &b.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
89 out
90 }
91 pub fn mul_scalar_t(&self, a: &Tensor, s: &Tensor) -> Tensor {
93 let n = a.len();
94 let out = self.empty(&a.shape);
95 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
96 let pipe = self.pipeline("mul_scalar", MUL_SCALAR_WGSL);
97 self.dispatch(&pipe, &[&a.buf, &s.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
98 out
99 }
100 pub fn transpose2d_t(&self, x: &Tensor, rows: u32, cols: u32) -> Tensor {
102 let out = self.empty(&[cols as usize, rows as usize]);
103 let dims = self.uniform_u32("d", &[rows, cols, 0, 0]);
104 let pipe = self.pipeline("transpose", TRANSPOSE_WGSL);
105 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims], ((rows + 15) / 16, (cols + 15) / 16, 1));
106 out
107 }
108 pub fn add_bias_t(&self, a: &Tensor, bias: &Tensor) -> Tensor {
110 let (n, d) = (a.len(), bias.len());
111 let out = self.empty(&a.shape);
112 let dims = self.uniform_u32("d", &[n as u32, d as u32, 0, 0]);
113 let pipe = self.pipeline("add_bias", ADD_BIAS_WGSL);
114 self.dispatch(&pipe, &[&a.buf, &bias.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
115 out
116 }
117 pub fn relu_t(&self, x: &Tensor) -> Tensor {
118 let n = x.len();
119 let out = self.empty(&x.shape);
120 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
121 let pipe = self.pipeline("relu", RELU_WGSL);
122 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
123 out
124 }
125 pub fn add_t(&self, a: &Tensor, b: &Tensor) -> Tensor {
126 let n = a.len();
127 let out = self.empty(&a.shape);
128 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
129 let pipe = self.pipeline("add", ADD_WGSL);
130 self.dispatch(&pipe, &[&a.buf, &b.buf, &out.buf, &dims], ((n as u32 + 63) / 64, 1, 1));
131 out
132 }
133 pub fn softmax_t(&self, x: &Tensor, rows: u32, d: u32) -> Tensor {
134 let out = self.empty(&x.shape);
135 let dims = self.uniform_u32("d", &[rows, d, 0, 0]);
136 let pipe = self.pipeline("softmax", SOFTMAX_WGSL);
137 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims], ((rows + 63) / 64, 1, 1));
138 out
139 }
140 pub fn rope_t(&self, x: &Tensor, t: u32, h: u32, dh: u32, base: f32) -> Tensor {
143 self.rope_off_t(x, t, h, dh, base, 0)
144 }
145 pub fn rope_off_t(&self, x: &Tensor, t: u32, h: u32, dh: u32, base: f32, offset: u32) -> Tensor {
148 let out = self.empty(&x.shape);
149 let dims = self.uniform_u32("d", &[t, h, dh, base.to_bits()]);
150 let meta = self.uniform_u32("m", &[offset, 0, 0, 0]);
151 let pipe = self.pipeline("rope", ROPE_WGSL);
152 self.dispatch(&pipe, &[&x.buf, &out.buf, &dims, &meta], (t * h / 64 + 1, 1, 1));
153 out
154 }
155 pub fn mha_decode_t(&self, q: &Tensor, k: &Tensor, v: &Tensor, hq: u32, hkv: u32, dh: u32, s: u32) -> Tensor {
158 let out = self.empty(&q.shape);
159 let scale = 1.0f32 / (dh as f32).sqrt();
160 let dims = self.uniform_u32("d", &[s, hq, dh, scale.to_bits()]);
161 let gqa = self.uniform_u32("g", &[hkv, 0, 0, 0]);
162 let pipe = self.pipeline("mha_decode", MHA_DECODE_WGSL);
163 self.dispatch(&pipe, &[&q.buf, &k.buf, &v.buf, &out.buf, &dims, &gqa], (hq / 64 + 1, 1, 1));
164 out
165 }
166 pub fn mha_causal_t(&self, q: &Tensor, k: &Tensor, v: &Tensor, t: u32, hq: u32, hkv: u32, dh: u32) -> Tensor {
170 let out = self.empty(&q.shape);
171 let scale = 1.0f32 / (dh as f32).sqrt();
172 let dims = self.uniform_u32("d", &[t, hq, dh, scale.to_bits()]);
173 let gqa = self.uniform_u32("g", &[hkv, 0, 0, 0]);
174 let pipe = self.pipeline("mha", MHA_CAUSAL_WGSL);
175 self.dispatch(&pipe, &[&q.buf, &k.buf, &v.buf, &out.buf, &dims, &gqa], (t * hq / 64 + 1, 1, 1));
176 out
177 }
178 pub fn rmsnorm_t(&self, x: &Tensor, w: &Tensor, rows: u32, d: u32, eps: f32) -> Tensor {
180 let out = self.empty(&x.shape);
181 let dims = self.uniform_u32("d", &[rows, d, eps.to_bits(), 0]);
182 let pipe = self.pipeline("rmsnorm", RMSNORM_WGSL);
183 self.dispatch(&pipe, &[&x.buf, &w.buf, &out.buf, &dims], (rows, 1, 1));
184 out
185 }
186 pub fn layernorm_t(&self, x: &Tensor, w: &Tensor, b: &Tensor, rows: u32, d: u32, eps: f32) -> Tensor {
187 let out = self.empty(&x.shape);
188 let dims = self.uniform_u32("d", &[rows, d, eps.to_bits(), 0]);
189 let pipe = self.pipeline("layernorm", LAYERNORM_WGSL);
190 self.dispatch(&pipe, &[&x.buf, &w.buf, &b.buf, &out.buf, &dims], ((rows + 63) / 64, 1, 1));
191 out
192 }
193 pub fn attention_t(&self, q: &Tensor, k: &Tensor, v: &Tensor, rq: u32, rk: u32, d: u32, dv: u32, scale: f32) -> Tensor {
195 let scores = self.mm_bt(q, k, rq, rk, d, scale);
196 let probs = self.softmax_t(&scores, rq, rk);
197 self.mm(&probs, v, rq, rk, dv)
198 }
199}
200
201impl Context {
202 pub async fn add(&self, a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
204 assert_eq!(a.len(), b.len());
205 let n = a.len();
206 let (ab, bb) = (self.storage("a", a), self.storage("b", b));
207 let out = self.out_buffer(n);
208 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
209 let pipe = self.pipeline("add", ADD_WGSL);
210 self.dispatch(&pipe, &[&ab, &bb, &out, &dims], ((n as u32 + 63) / 64, 1, 1));
211 self.readback(&out, n).await
212 }
213
214 pub async fn silu(&self, x: &[f32]) -> Result<Vec<f32>> {
216 let n = x.len();
217 let xb = self.storage("x", x);
218 let out = self.out_buffer(n);
219 let dims = self.uniform_u32("n", &[n as u32, 0, 0, 0]);
220 let pipe = self.pipeline("silu", SILU_WGSL);
221 self.dispatch(&pipe, &[&xb, &out, &dims], ((n as u32 + 63) / 64, 1, 1));
222 self.readback(&out, n).await
223 }
224
225 pub async fn layernorm(&self, x: &[f32], weight: &[f32], bias: &[f32], rows: u32, d: u32, eps: f32) -> Result<Vec<f32>> {
227 assert_eq!(x.len(), (rows * d) as usize);
228 assert_eq!(weight.len(), d as usize);
229 assert_eq!(bias.len(), d as usize);
230 let (xb, wb, bb) = (self.storage("x", x), self.storage("w", weight), self.storage("b", bias));
231 let out = self.out_buffer((rows * d) as usize);
232 let dims = self.uniform_u32("dims", &[rows, d, eps.to_bits(), 0]);
233 let pipe = self.pipeline("layernorm", LAYERNORM_WGSL);
234 self.dispatch(&pipe, &[&xb, &wb, &bb, &out, &dims], ((rows + 63) / 64, 1, 1));
235 self.readback(&out, (rows * d) as usize).await
236 }
237
238 pub async fn softmax(&self, x: &[f32], rows: u32, d: u32) -> Result<Vec<f32>> {
240 assert_eq!(x.len(), (rows * d) as usize);
241 let xb = self.storage("x", x);
242 let out = self.out_buffer((rows * d) as usize);
243 let dims = self.uniform_u32("dims", &[rows, d, 0, 0]);
244 let pipe = self.pipeline("softmax", SOFTMAX_WGSL);
245 self.dispatch(&pipe, &[&xb, &out, &dims], ((rows + 63) / 64, 1, 1));
246 self.readback(&out, (rows * d) as usize).await
247 }
248
249 pub async fn matmul_bt(&self, a: &[f32], b: &[f32], m: u32, n: u32, k: u32, scale: f32) -> Result<Vec<f32>> {
252 assert_eq!(a.len(), (m * k) as usize);
253 assert_eq!(b.len(), (n * k) as usize);
254 let (ab, bb) = (self.storage("a", a), self.storage("b", b));
255 let out = self.out_buffer((m * n) as usize);
256 let dims = self.uniform_u32("dims", &[m, n, k, scale.to_bits()]);
257 let pipe = self.pipeline("matmul_bt", MATMUL_BT_WGSL);
258 self.dispatch(&pipe, &[&ab, &bb, &out, &dims], ((m + 15) / 16, (n + 15) / 16, 1));
259 self.readback(&out, (m * n) as usize).await
260 }
261
262 pub async fn attention(&self, q: &[f32], k: &[f32], v: &[f32], rows_q: u32, rows_k: u32, d: u32, dv: u32, scale: f32) -> Result<Vec<f32>> {
265 let scores = self.matmul_bt(q, k, rows_q, rows_k, d, scale).await?; let probs = self.softmax(&scores, rows_q, rows_k).await?;
267 self.matmul(&probs, v, rows_q, rows_k, dv).await }
269}
270
271const MATMUL_BT_WGSL: &str = r#"
272@group(0) @binding(0) var<storage, read> a: array<f32>;
273@group(0) @binding(1) var<storage, read> b: array<f32>;
274@group(0) @binding(2) var<storage, read_write> out: array<f32>;
275@group(0) @binding(3) var<uniform> dims: vec4<u32>; // m, n, k, bitcast(scale)
276@compute @workgroup_size(16, 16, 1)
277fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
278 let m = dims.x; let n = dims.y; let k = dims.z; let scale = bitcast<f32>(dims.w);
279 let row = gid.x; let col = gid.y;
280 if (row >= m || col >= n) { return; }
281 var acc: f32 = 0.0;
282 for (var l: u32 = 0u; l < k; l = l + 1u) {
283 acc = acc + a[row * k + l] * b[col * k + l]; // A · Bᵀ
284 }
285 out[row * n + col] = acc * scale;
286}
287"#;
288
289const ADD_WGSL: &str = r#"
290@group(0) @binding(0) var<storage, read> a: array<f32>;
291@group(0) @binding(1) var<storage, read> b: array<f32>;
292@group(0) @binding(2) var<storage, read_write> out: array<f32>;
293@group(0) @binding(3) var<uniform> dims: vec4<u32>; // n
294@compute @workgroup_size(64)
295fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
296 let i = gid.x;
297 if (i >= dims.x) { return; }
298 out[i] = a[i] + b[i];
299}
300"#;
301
302const ADD_BIAS_WGSL: &str = r#"
303@group(0) @binding(0) var<storage, read> a: array<f32>;
304@group(0) @binding(1) var<storage, read> bias: array<f32>;
305@group(0) @binding(2) var<storage, read_write> out: array<f32>;
306@group(0) @binding(3) var<uniform> dims: vec4<u32>; // n, d
307@compute @workgroup_size(64)
308fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
309 let i = gid.x; if (i >= dims.x) { return; }
310 out[i] = a[i] + bias[i % dims.y];
311}
312"#;
313
314const MUL_WGSL: &str = r#"
315@group(0) @binding(0) var<storage, read> a: array<f32>;
316@group(0) @binding(1) var<storage, read> b: array<f32>;
317@group(0) @binding(2) var<storage, read_write> out: array<f32>;
318@group(0) @binding(3) var<uniform> dims: vec4<u32>;
319@compute @workgroup_size(64)
320fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
321 let i = gid.x; if (i >= dims.x) { return; }
322 out[i] = a[i] * b[i];
323}
324"#;
325
326const MUL_SCALAR_WGSL: &str = r#"
327@group(0) @binding(0) var<storage, read> a: array<f32>;
328@group(0) @binding(1) var<storage, read> s: array<f32>;
329@group(0) @binding(2) var<storage, read_write> out: array<f32>;
330@group(0) @binding(3) var<uniform> dims: vec4<u32>;
331@compute @workgroup_size(64)
332fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
333 let i = gid.x; if (i >= dims.x) { return; }
334 out[i] = a[i] * s[0];
335}
336"#;
337
338const TRANSPOSE_WGSL: &str = r#"
339@group(0) @binding(0) var<storage, read> x: array<f32>;
340@group(0) @binding(1) var<storage, read_write> out: array<f32>;
341@group(0) @binding(2) var<uniform> dims: vec4<u32>; // rows, cols
342@compute @workgroup_size(16, 16, 1)
343fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
344 let rows = dims.x; let cols = dims.y;
345 let r = gid.x; let c = gid.y;
346 if (r >= rows || c >= cols) { return; }
347 out[c * rows + r] = x[r * cols + c]; // [rows,cols] → [cols,rows]
348}
349"#;
350
351const RELU_WGSL: &str = r#"
352@group(0) @binding(0) var<storage, read> x: array<f32>;
353@group(0) @binding(1) var<storage, read_write> out: array<f32>;
354@group(0) @binding(2) var<uniform> dims: vec4<u32>; // n
355@compute @workgroup_size(64)
356fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
357 let i = gid.x;
358 if (i >= dims.x) { return; }
359 out[i] = max(x[i], 0.0);
360}
361"#;
362
363const SILU_WGSL: &str = r#"
364@group(0) @binding(0) var<storage, read> x: array<f32>;
365@group(0) @binding(1) var<storage, read_write> out: array<f32>;
366@group(0) @binding(2) var<uniform> dims: vec4<u32>; // n
367@compute @workgroup_size(64)
368fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
369 let i = gid.x;
370 if (i >= dims.x) { return; }
371 let v = x[i];
372 out[i] = v / (1.0 + exp(-v));
373}
374"#;
375
376const GATHER_WGSL: &str = r#"
377@group(0) @binding(0) var<storage, read> data: array<f32>;
378@group(0) @binding(1) var<storage, read> idx: array<u32>;
379@group(0) @binding(2) var<storage, read_write> out: array<f32>;
380@group(0) @binding(3) var<uniform> dims: vec4<u32>; // n, d
381@compute @workgroup_size(64)
382fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
383 let n = dims.x; let d = dims.y;
384 let t = gid.x; if (t >= n * d) { return; }
385 let i = t / d; let j = t % d;
386 out[i * d + j] = data[idx[i] * d + j];
387}
388"#;
389
390const SIGMOID_WGSL: &str = r#"
391@group(0) @binding(0) var<storage, read> x: array<f32>;
392@group(0) @binding(1) var<storage, read_write> out: array<f32>;
393@group(0) @binding(2) var<uniform> dims: vec4<u32>; // n
394@compute @workgroup_size(64)
395fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
396 let i = gid.x; if (i >= dims.x) { return; }
397 out[i] = 1.0 / (1.0 + exp(-x[i]));
398}
399"#;
400
401const SQRT_WGSL: &str = r#"
402@group(0) @binding(0) var<storage, read> x: array<f32>;
403@group(0) @binding(1) var<storage, read_write> out: array<f32>;
404@group(0) @binding(2) var<uniform> dims: vec4<u32>; // n
405@compute @workgroup_size(64)
406fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
407 let i = gid.x; if (i >= dims.x) { return; }
408 out[i] = sqrt(x[i]);
409}
410"#;
411
412const GELU_WGSL: &str = r#"
415@group(0) @binding(0) var<storage, read> x: array<f32>;
416@group(0) @binding(1) var<storage, read_write> out: array<f32>;
417@group(0) @binding(2) var<uniform> dims: vec4<u32>; // n
418fn erf(z: f32) -> f32 {
419 let s = sign(z); let a = abs(z);
420 let t = 1.0 / (1.0 + 0.3275911 * a);
421 let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-a * a);
422 return s * y;
423}
424@compute @workgroup_size(64)
425fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
426 let i = gid.x; if (i >= dims.x) { return; }
427 let v = x[i];
428 out[i] = 0.5 * v * (1.0 + erf(v * 0.7071067811865476));
429}
430"#;
431
432const SUB_WGSL: &str = r#"
433@group(0) @binding(0) var<storage, read> a: array<f32>;
434@group(0) @binding(1) var<storage, read> b: array<f32>;
435@group(0) @binding(2) var<storage, read_write> out: array<f32>;
436@group(0) @binding(3) var<uniform> dims: vec4<u32>;
437@compute @workgroup_size(64)
438fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
439 let i = gid.x; if (i >= dims.x) { return; }
440 out[i] = a[i] - b[i];
441}
442"#;
443
444const DIV_WGSL: &str = r#"
445@group(0) @binding(0) var<storage, read> a: array<f32>;
446@group(0) @binding(1) var<storage, read> b: array<f32>;
447@group(0) @binding(2) var<storage, read_write> out: array<f32>;
448@group(0) @binding(3) var<uniform> dims: vec4<u32>;
449@compute @workgroup_size(64)
450fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
451 let i = gid.x; if (i >= dims.x) { return; }
452 out[i] = a[i] / b[i];
453}
454"#;
455
456const ROPE_WGSL: &str = r#"
457@group(0) @binding(0) var<storage, read> x: array<f32>;
458@group(0) @binding(1) var<storage, read_write> out: array<f32>;
459@group(0) @binding(2) var<uniform> dims: vec4<u32>; // T, H, dh, bitcast(base)
460@group(0) @binding(3) var<uniform> rmeta: vec4<u32>; // pos_offset, _, _, _
461@compute @workgroup_size(64)
462fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
463 let t = dims.x; let h = dims.y; let dh = dims.z; let base = bitcast<f32>(dims.w);
464 let id = gid.x; if (id >= t * h) { return; }
465 let i = id / h; let head = id % h;
466 let half = dh / 2u;
467 let o = (i * h + head) * dh;
468 let lb = log(base);
469 for (var c: u32 = 0u; c < half; c = c + 1u) {
470 let inv = exp(-2.0 * f32(c) / f32(dh) * lb);
471 let ang = f32(i + rmeta.x) * inv;
472 let cs = cos(ang); let sn = sin(ang);
473 let x1 = x[o + c]; let x2 = x[o + c + half];
474 out[o + c] = x1 * cs - x2 * sn;
475 out[o + c + half] = x2 * cs + x1 * sn;
476 }
477}
478"#;
479
480const MHA_CAUSAL_WGSL: &str = r#"
481@group(0) @binding(0) var<storage, read> q: array<f32>;
482@group(0) @binding(1) var<storage, read> k: array<f32>;
483@group(0) @binding(2) var<storage, read> v: array<f32>;
484@group(0) @binding(3) var<storage, read_write> out: array<f32>;
485@group(0) @binding(4) var<uniform> dims: vec4<u32>; // T, Hq, dh, bitcast(scale)
486@group(0) @binding(5) var<uniform> gqa: vec4<u32>; // Hkv, _, _, _
487@compute @workgroup_size(64)
488fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
489 let t = dims.x; let hq = dims.y; let dh = dims.z; let scale = bitcast<f32>(dims.w);
490 let hkv = gqa.x;
491 let id = gid.x; if (id >= t * hq) { return; }
492 let i = id / hq; let head = id % hq;
493 let kvhead = head / (hq / hkv); // GQA: query head → shared kv head
494 let qo = (i * hq + head) * dh;
495 var acc: array<f32, 128>;
496 for (var c: u32 = 0u; c < dh; c = c + 1u) { acc[c] = 0.0; }
497 var m: f32 = -3.0e38; var l: f32 = 0.0;
498 for (var j: u32 = 0u; j <= i; j = j + 1u) { // causal: attend to keys 0..=i
499 let ko = (j * hkv + kvhead) * dh;
500 var s: f32 = 0.0;
501 for (var c: u32 = 0u; c < dh; c = c + 1u) { s = s + q[qo + c] * k[ko + c]; }
502 s = s * scale;
503 let mnew = max(m, s);
504 let corr = exp(m - mnew);
505 let p = exp(s - mnew);
506 l = l * corr + p;
507 for (var c: u32 = 0u; c < dh; c = c + 1u) { acc[c] = acc[c] * corr + p * v[ko + c]; }
508 m = mnew;
509 }
510 for (var c: u32 = 0u; c < dh; c = c + 1u) { out[qo + c] = acc[c] / l; }
511}
512"#;
513
514const MHA_DECODE_WGSL: &str = r#"
515@group(0) @binding(0) var<storage, read> q: array<f32>; // [1, H*dh]
516@group(0) @binding(1) var<storage, read> k: array<f32>; // [S, H*dh]
517@group(0) @binding(2) var<storage, read> v: array<f32>; // [S, H*dh]
518@group(0) @binding(3) var<storage, read_write> out: array<f32>; // [1, H*dh]
519@group(0) @binding(4) var<uniform> dims: vec4<u32>; // S, Hq, dh, bitcast(scale)
520@group(0) @binding(5) var<uniform> gqa: vec4<u32>; // Hkv, _, _, _
521@compute @workgroup_size(64)
522fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
523 let s = dims.x; let hq = dims.y; let dh = dims.z; let scale = bitcast<f32>(dims.w);
524 let hkv = gqa.x;
525 let head = gid.x; if (head >= hq) { return; }
526 let kvhead = head / (hq / hkv); // GQA: query head → shared kv head
527 let qo = head * dh;
528 var acc: array<f32, 128>;
529 for (var c: u32 = 0u; c < dh; c = c + 1u) { acc[c] = 0.0; }
530 var m: f32 = -3.0e38; var l: f32 = 0.0;
531 for (var j: u32 = 0u; j < s; j = j + 1u) {
532 let ko = (j * hkv + kvhead) * dh;
533 var sc: f32 = 0.0;
534 for (var c: u32 = 0u; c < dh; c = c + 1u) { sc = sc + q[qo + c] * k[ko + c]; }
535 sc = sc * scale;
536 let mnew = max(m, sc);
537 let corr = exp(m - mnew);
538 let p = exp(sc - mnew);
539 l = l * corr + p;
540 for (var c: u32 = 0u; c < dh; c = c + 1u) { acc[c] = acc[c] * corr + p * v[ko + c]; }
541 m = mnew;
542 }
543 for (var c: u32 = 0u; c < dh; c = c + 1u) { out[qo + c] = acc[c] / l; }
544}
545"#;
546
547const RMSNORM_WGSL: &str = r#"
548@group(0) @binding(0) var<storage, read> x: array<f32>;
549@group(0) @binding(1) var<storage, read> weight: array<f32>;
550@group(0) @binding(2) var<storage, read_write> out: array<f32>;
551@group(0) @binding(3) var<uniform> dims: vec4<u32>; // rows, d, bitcast(eps), _
552@compute @workgroup_size(64)
553fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
554 let row = gid.x;
555 let rows = dims.x; let d = dims.y; let eps = bitcast<f32>(dims.z);
556 if (row >= rows) { return; }
557 let base = row * d;
558 var ms: f32 = 0.0;
559 for (var j: u32 = 0u; j < d; j = j + 1u) { let v = x[base + j]; ms = ms + v * v; }
560 ms = ms / f32(d);
561 let inv = 1.0 / sqrt(ms + eps);
562 for (var j: u32 = 0u; j < d; j = j + 1u) { out[base + j] = x[base + j] * inv * weight[j]; }
563}
564"#;
565
566const LAYERNORM_WGSL: &str = r#"
567@group(0) @binding(0) var<storage, read> x: array<f32>;
568@group(0) @binding(1) var<storage, read> weight: array<f32>;
569@group(0) @binding(2) var<storage, read> bias: array<f32>;
570@group(0) @binding(3) var<storage, read_write> out: array<f32>;
571@group(0) @binding(4) var<uniform> dims: vec4<u32>; // rows, d, bitcast(eps), _
572@compute @workgroup_size(64)
573fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
574 let row = gid.x;
575 let rows = dims.x; let d = dims.y; let eps = bitcast<f32>(dims.z);
576 if (row >= rows) { return; }
577 let base = row * d;
578 var mean: f32 = 0.0;
579 for (var j: u32 = 0u; j < d; j = j + 1u) { mean = mean + x[base + j]; }
580 mean = mean / f32(d);
581 var vari: f32 = 0.0;
582 for (var j: u32 = 0u; j < d; j = j + 1u) { let c = x[base + j] - mean; vari = vari + c * c; }
583 vari = vari / f32(d);
584 let inv = 1.0 / sqrt(vari + eps);
585 for (var j: u32 = 0u; j < d; j = j + 1u) {
586 out[base + j] = (x[base + j] - mean) * inv * weight[j] + bias[j];
587 }
588}
589"#;
590
591const SOFTMAX_WGSL: &str = r#"
592@group(0) @binding(0) var<storage, read> x: array<f32>;
593@group(0) @binding(1) var<storage, read_write> out: array<f32>;
594@group(0) @binding(2) var<uniform> dims: vec4<u32>; // rows, d
595@compute @workgroup_size(64)
596fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
597 let row = gid.x;
598 let rows = dims.x; let d = dims.y;
599 if (row >= rows) { return; }
600 let base = row * d;
601 var mx: f32 = x[base];
602 for (var j: u32 = 1u; j < d; j = j + 1u) { mx = max(mx, x[base + j]); }
603 var sum: f32 = 0.0;
604 for (var j: u32 = 0u; j < d; j = j + 1u) { let e = exp(x[base + j] - mx); out[base + j] = e; sum = sum + e; }
605 let inv = 1.0 / sum;
606 for (var j: u32 = 0u; j < d; j = j + 1u) { out[base + j] = out[base + j] * inv; }
607}
608"#;
609
610pub mod cpu {
612 pub fn add(a: &[f32], b: &[f32]) -> Vec<f32> { a.iter().zip(b).map(|(x, y)| x + y).collect() }
613 pub fn silu(x: &[f32]) -> Vec<f32> { x.iter().map(|&v| v / (1.0 + (-v).exp())).collect() }
614 pub fn relu(x: &[f32]) -> Vec<f32> { x.iter().map(|&v| v.max(0.0)).collect() }
615 pub fn sigmoid(x: &[f32]) -> Vec<f32> { x.iter().map(|&v| 1.0 / (1.0 + (-v).exp())).collect() }
616 pub fn sqrt(x: &[f32]) -> Vec<f32> { x.iter().map(|&v| v.sqrt()).collect() }
617 pub fn sub(a: &[f32], b: &[f32]) -> Vec<f32> { a.iter().zip(b).map(|(x, y)| x - y).collect() }
618 pub fn div(a: &[f32], b: &[f32]) -> Vec<f32> { a.iter().zip(b).map(|(x, y)| x / y).collect() }
619 pub fn gelu(x: &[f32]) -> Vec<f32> {
620 x.iter().map(|&v| 0.5 * v * (1.0 + libm_erf(v * std::f32::consts::FRAC_1_SQRT_2))).collect()
621 }
622 fn libm_erf(z: f32) -> f32 {
623 let s = z.signum(); let a = z.abs();
624 let t = 1.0 / (1.0 + 0.3275911 * a);
625 let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * (-a * a).exp();
626 s * y
627 }
628 pub fn layernorm(x: &[f32], w: &[f32], b: &[f32], rows: usize, d: usize, eps: f32) -> Vec<f32> {
629 let mut o = vec![0.0f32; rows * d];
630 for r in 0..rows {
631 let base = r * d;
632 let mean = x[base..base + d].iter().sum::<f32>() / d as f32;
633 let var = x[base..base + d].iter().map(|v| (v - mean) * (v - mean)).sum::<f32>() / d as f32;
634 let inv = 1.0 / (var + eps).sqrt();
635 for j in 0..d { o[base + j] = (x[base + j] - mean) * inv * w[j] + b[j]; }
636 }
637 o
638 }
639 pub fn rmsnorm(x: &[f32], w: &[f32], rows: usize, d: usize, eps: f32) -> Vec<f32> {
640 let mut o = vec![0.0f32; rows * d];
641 for r in 0..rows {
642 let base = r * d;
643 let ms = x[base..base + d].iter().map(|v| v * v).sum::<f32>() / d as f32;
644 let inv = 1.0 / (ms + eps).sqrt();
645 for j in 0..d { o[base + j] = x[base + j] * inv * w[j]; }
646 }
647 o
648 }
649 pub fn softmax(x: &[f32], rows: usize, d: usize) -> Vec<f32> {
650 let mut o = vec![0.0f32; rows * d];
651 for r in 0..rows {
652 let base = r * d;
653 let mx = x[base..base + d].iter().cloned().fold(f32::NEG_INFINITY, f32::max);
654 let mut sum = 0.0f32;
655 for j in 0..d { let e = (x[base + j] - mx).exp(); o[base + j] = e; sum += e; }
656 for j in 0..d { o[base + j] /= sum; }
657 }
658 o
659 }
660 pub fn matmul_bt(a: &[f32], b: &[f32], m: usize, n: usize, k: usize, scale: f32) -> Vec<f32> {
661 let mut c = vec![0.0f32; m * n];
662 for i in 0..m {
663 for j in 0..n {
664 let mut acc = 0.0f32;
665 for l in 0..k { acc += a[i * k + l] * b[j * k + l]; }
666 c[i * n + j] = acc * scale;
667 }
668 }
669 c
670 }
671 pub fn rope(x: &[f32], t: usize, h: usize, dh: usize, base: f32) -> Vec<f32> {
672 let mut o = x.to_vec();
673 let half = dh / 2;
674 for i in 0..t {
675 for head in 0..h {
676 let off = (i * h + head) * dh;
677 for c in 0..half {
678 let inv = (-2.0 * c as f32 / dh as f32 * base.ln()).exp(); let ang = i as f32 * inv;
680 let (cs, sn) = (ang.cos(), ang.sin());
681 let (x1, x2) = (x[off + c], x[off + c + half]);
682 o[off + c] = x1 * cs - x2 * sn;
683 o[off + c + half] = x2 * cs + x1 * sn;
684 }
685 }
686 }
687 o
688 }
689 pub fn mha_causal(q: &[f32], k: &[f32], v: &[f32], t: usize, hq: usize, hkv: usize, dh: usize) -> Vec<f32> {
691 let scale = 1.0 / (dh as f32).sqrt();
692 let mut o = vec![0.0f32; t * hq * dh];
693 for i in 0..t {
694 for head in 0..hq {
695 let kvhead = head / (hq / hkv);
696 let qo = (i * hq + head) * dh;
697 let mut scores = vec![0.0f32; i + 1];
698 for j in 0..=i {
699 let ko = (j * hkv + kvhead) * dh;
700 scores[j] = (0..dh).map(|c| q[qo + c] * k[ko + c]).sum::<f32>() * scale;
701 }
702 let mx = scores.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
703 let mut sum = 0.0;
704 for s in scores.iter_mut() { *s = (*s - mx).exp(); sum += *s; }
705 for c in 0..dh {
706 let mut acc = 0.0;
707 for j in 0..=i { acc += scores[j] / sum * v[(j * hkv + kvhead) * dh + c]; }
708 o[qo + c] = acc;
709 }
710 }
711 }
712 o
713 }
714 pub fn attention(q: &[f32], k: &[f32], v: &[f32], rows_q: usize, rows_k: usize, d: usize, dv: usize, scale: f32) -> Vec<f32> {
715 let scores = matmul_bt(q, k, rows_q, rows_k, d, scale);
716 let probs = softmax(&scores, rows_q, rows_k);
717 crate::matmul_cpu(&probs, v, rows_q, rows_k, dv)
718 }
719}