Skip to main content

cram_codec/
irdec.rs

1//! The TCG3 rANS lane decoder as `tang-expr` integer graphs.
2//!
3//! One residual per evaluation: the lane's 32-bit rANS state and word
4//! position go in and come out (loop-carried state), with renormalization,
5//! 16-bit word fetch from the stream, and the binarization (static priors or
6//! the learned mixture CDF). The same graph is lowered to the WGSL kernel
7//! (replacing the hand-written `res_static_gen` / `res_learned_gen`) and run
8//! by the CPU interpreter; a stream can also ship its own graphs
9//! ([`crate::shipdec`]).
10//!
11//! The graphs are pure, so `if` becomes a predicate: a decision under a
12//! false predicate still computes its step but keeps the old state (and its
13//! word fetch reads in bounds or 0). Loops carry the predicate in their
14//! condition, so they run exactly the hand-written trip counts.
15//!
16//! ABI (both roles): buffer 0 `mt` (`u32`: priors / σ and 2^x tables),
17//! buffer 1 `stream` (`u32`, two little-endian 16-bit words each);
18//! params `p0 = x`, `p1 = pos`, `p2 = end` (lane state and word bound), then
19//! - static: `p3 = sig`, `p4 = e`, `p5 = cls`, `p6 = sctx`, `p7 = off_init`
20//!   (all `u32`);
21//! - learned: `p3..p7` = the net outputs `o0..o4` (`i32`), `p8 = off_sig`,
22//!   `p9 = off_pow` (`u32`);
23//! - adaptive learned (per-lane APM, [`crate::lmodel3::Adapt`]): the learned
24//!   params, then `p10 = off_stretch` (the stretch table in `mt`), `p11 = wa`,
25//!   `p12 = lim`, `p13 = mask` (`u32`), and scratch buffer 2 `adt` (136 `u32`
26//!   words, `(p << 8) | count` per bin, reset per block by the host);
27//!
28//! outputs `x'`, `pos'` (`u32`), `r` (`i32`), plus the final effect token
29//! (`u32`, always 0) for the adaptive role.
30
31use crate::coefcode::{KB, NE};
32use crate::gpuwav::{ESC0, LOW0, MAG0, SGN0, SIG0};
33use crate::lmodel3::{AB, AC};
34use tang_expr::int::{Elem, IId, IType, IntGraph};
35
36/// Buffer ABI of the residual graphs.
37pub const BUFS: [(&str, Elem); 2] = [("mt", Elem::U32), ("stream", Elem::U32)];
38/// Param types of the static graph.
39pub const STATIC_PARAMS: [IType; 8] = [IType::U32; 8];
40/// Param types of the learned graph.
41pub const LEARNED_PARAMS: [IType; 10] = [
42    IType::U32,
43    IType::U32,
44    IType::U32,
45    IType::I32,
46    IType::I32,
47    IType::I32,
48    IType::I32,
49    IType::I32,
50    IType::U32,
51    IType::U32,
52];
53/// Output types of both residual graphs.
54pub const OUTS: [IType; 3] = [IType::U32, IType::U32, IType::I32];
55/// Param types of the adaptive learned graph.
56pub const LEARNED_AD_PARAMS: [IType; 14] = [
57    IType::U32,
58    IType::U32,
59    IType::U32,
60    IType::I32,
61    IType::I32,
62    IType::I32,
63    IType::I32,
64    IType::I32,
65    IType::U32,
66    IType::U32,
67    IType::U32,
68    IType::U32,
69    IType::U32,
70    IType::U32,
71];
72/// Output types of the adaptive learned graph (the last is the effect token).
73pub const OUTS_AD: [IType; 4] = [IType::U32, IType::U32, IType::I32, IType::U32];
74/// Scratch-buffer ABI of the adaptive learned graph: the lane's APM bins
75/// (`adt[ad_b + i]` in the kernel's workgroup memory).
76pub const RW_AD: [(&str, Elem, u32, &str); 1] = [("adt", Elem::U32, (AC * AB) as u32, "ad_b")];
77
78/// rANS lane state in a graph.
79#[derive(Clone, Copy)]
80struct St {
81    x: IId,
82    pos: IId,
83}
84
85/// The adaptive layer's inputs in a graph.
86#[derive(Clone, Copy)]
87struct Ad {
88    buf: u16,
89    so: IId,
90    wa: IId,
91    lim: IId,
92    /// Per decision group: the layer is on.
93    en: [IId; 4],
94}
95
96struct B {
97    g: IntGraph,
98    mt: u16,
99    stream: u16,
100    end: IId,
101    ad: Option<Ad>,
102}
103
104impl B {
105    fn new() -> Self {
106        let mut g = IntGraph::new();
107        let mt = g.buffer(BUFS[0].0, BUFS[0].1);
108        let stream = g.buffer(BUFS[1].0, BUFS[1].1);
109        let end = g.param(2, IType::U32);
110        Self {
111            g,
112            mt,
113            stream,
114            end,
115            ad: None,
116        }
117    }
118    fn u(&mut self, v: u32) -> IId {
119        self.g.u32(v)
120    }
121    fn i(&mut self, v: i32) -> IId {
122        self.g.i32(v)
123    }
124    fn word(&mut self, i: IId) -> IId {
125        let g = &mut self.g;
126        let inb = g.lt(i, self.end);
127        let one = g.u32(1);
128        let w = g.shr(i, one);
129        let w = g.load(self.stream, w);
130        let par = g.and(i, one);
131        let sixteen = g.u32(16);
132        let sh = g.mul(par, sixteen);
133        let v = g.shr(w, sh);
134        let m = g.u32(0xFFFF);
135        let v = g.and(v, m);
136        let z = g.u32(0);
137        g.select(inb, v, z)
138    }
139    /// One rANS step (`cum`, `f`), applied only when `on`.
140    fn step(&mut self, s: St, cum: IId, f: IId, on: Option<IId>) -> St {
141        let sixteen = self.u(16);
142        let m = self.u(0xFFFF);
143        let lim = self.u(65536);
144        let one = self.u(1);
145        let g = &mut self.g;
146        let hi = g.shr(s.x, sixteen);
147        let lo = g.and(s.x, m);
148        let a = g.mul(f, hi);
149        let b = g.sub(lo, cum);
150        let x1 = g.add(a, b);
151        let ren = g.lt(x1, lim);
152        let w = self.word(s.pos);
153        let g = &mut self.g;
154        let xs = g.shl(x1, sixteen);
155        let xr = g.or(xs, w);
156        let x2 = g.select(ren, xr, x1);
157        let p1 = g.add(s.pos, one);
158        let p2 = g.select(ren, p1, s.pos);
159        match on {
160            None => St { x: x2, pos: p2 },
161            Some(c) => St {
162                x: g.select(c, x2, s.x),
163                pos: g.select(c, p2, s.pos),
164            },
165        }
166    }
167    /// A binary decision at `P(1) = p1/2^16`; the bit is false when off.
168    fn bit(&mut self, s: St, p1: IId, on: Option<IId>) -> (St, IId) {
169        let k = self.u(65536);
170        let m = self.u(0xFFFF);
171        let z = self.u(0);
172        let g = &mut self.g;
173        let f0 = g.sub(k, p1);
174        let lo = g.and(s.x, m);
175        let b = g.le(f0, lo);
176        let cum = g.select(b, f0, z);
177        let f = g.select(b, p1, f0);
178        let s = self.step(s, cum, f, on);
179        let b = match on {
180            None => b,
181            Some(c) => self.g.and(b, c),
182        };
183        (s, b)
184    }
185    /// A decision through the lane's adaptive probability map on context
186    /// `ctx` (decision group `grp`), as [`crate::lmodel3::Adapt`]: the two
187    /// stretch-domain bins around `p1` are interpolated and mixed with `p1`,
188    /// then the nearer bin moves toward the bit. Returns the state, the bit
189    /// and the next effect token; nothing is written when `on` is false.
190    fn ad_bit(
191        &mut self,
192        s: St,
193        tok: IId,
194        ctx: IId,
195        grp: usize,
196        p1: IId,
197        on: Option<IId>,
198    ) -> (St, IId, IId) {
199        let a = self.ad.expect("adaptive graph");
200        let g = &mut self.g;
201        let (one, four, eight) = (g.u32(1), g.u32(4), g.u32(8));
202        let si = g.shr(p1, four);
203        let si = g.add(a.so, si);
204        let z = g.load(self.mt, si);
205        let z = g.bitcast(IType::I32, z);
206        let (lo, hi, off) = (g.i32(-2048), g.i32(2047), g.i32(2048));
207        let z = g.clamp(z, lo, hi);
208        let z = g.add(z, off);
209        let st = g.bitcast(IType::U32, z);
210        let j = g.shr(st, eight);
211        let m255 = g.u32(255);
212        let w = g.and(st, m255);
213        let kab = g.u32(AB as u32);
214        let b = g.mul(ctx, kab);
215        let b = g.add(b, j);
216        let b1 = g.add(b, one);
217        let e0 = g.load_rw(a.buf, b, tok);
218        let e1 = g.load_rw(a.buf, b1, tok);
219        let t0 = g.shr(e0, eight);
220        let t1 = g.shr(e1, eight);
221        let k256 = g.u32(256);
222        let wi = g.sub(k256, w);
223        let x = g.mul(t0, wi);
224        let y = g.mul(t1, w);
225        let pr = g.add(x, y);
226        let pr = g.shr(pr, eight);
227        let x = g.mul(pr, a.wa);
228        let k16 = g.u32(16);
229        let wb = g.sub(k16, a.wa);
230        let y = g.mul(p1, wb);
231        let pf = g.add(x, y);
232        let pf = g.shr(pf, four);
233        let (c32, c65504) = (g.u32(32), g.u32(65504));
234        let pf = g.clamp(pf, c32, c65504);
235        let en = a.en[grp];
236        let pu = g.select(en, pf, p1);
237        let (s, got) = self.bit(s, pu, on);
238        let g = &mut self.g;
239        let k128 = g.u32(128);
240        let near = g.lt(w, k128);
241        let k = g.select(near, b, b1);
242        let e = g.select(near, e0, e1);
243        let t = g.shr(e, eight);
244        let t = g.bitcast(IType::I32, t);
245        let n = g.and(e, m255);
246        let (zi, top) = (g.i32(0), g.i32(65535));
247        let tg = g.select(got, top, zi);
248        let d = g.sub(tg, t);
249        // rate 2^-⌊log₂(n+2)⌋
250        let two = g.u32(2);
251        let n2 = g.add(n, two);
252        let lz = g.clz(n2);
253        let c31 = g.u32(31);
254        let sh = g.sub(c31, lz);
255        let dd = g.shr(d, sh);
256        let t2 = g.add(t, dd);
257        let t2 = g.bitcast(IType::U32, t2);
258        let t2 = g.shl(t2, eight);
259        let more = g.lt(n, a.lim);
260        let n1 = g.add(n, one);
261        let nn = g.select(more, n1, n);
262        let ne = g.or(t2, nn);
263        let wr = match on {
264            None => en,
265            Some(c) => g.and(en, c),
266        };
267        let v = g.select(wr, ne, e);
268        let tok = g.store(a.buf, k, v, tok);
269        (s, got, tok)
270    }
271    /// `n ≤ 16` raw bits.
272    fn raw(&mut self, s: St, n: IId, on: Option<IId>) -> (St, IId) {
273        let sixteen = self.u(16);
274        let m = self.u(0xFFFF);
275        let one = self.u(1);
276        let g = &mut self.g;
277        let sh = g.sub(sixteen, n);
278        let lo = g.and(s.x, m);
279        let v = g.shr(lo, sh);
280        let cum = g.shl(v, sh);
281        let f = g.shl(one, sh);
282        (self.step(s, cum, f, on), v)
283    }
284    /// `n` raw bits in chunks of ≤ 16 (`n = 0`: none).
285    fn raw_n(&mut self, s: St, n: IId) -> (St, IId) {
286        let z = self.u(0);
287        let mut b = std::mem::take(&mut self.g);
288        let (stream, end, mt) = (self.stream, self.end, self.mt);
289        let out = b.while_loop(&[n, z, s.x, s.pos], |g, st| {
290            let mut me = B {
291                g: std::mem::take(g),
292                mt,
293                stream,
294                end,
295                ad: None,
296            };
297            let zero = me.u(0);
298            let sixteen = me.u(16);
299            let go = me.g.lt(zero, st[0]);
300            let c = me.g.min(st[0], sixteen);
301            let (s2, v) = me.raw(
302                St {
303                    x: st[2],
304                    pos: st[3],
305                },
306                c,
307                None,
308            );
309            let o = me.g.shl(st[1], c);
310            let o = me.g.or(o, v);
311            let n2 = me.g.sub(st[0], c);
312            *g = me.g;
313            (go, vec![n2, o, s2.x, s2.pos])
314        });
315        b.cap_loop(out[0], 3);
316        self.g = b;
317        (
318            St {
319                x: out[2],
320                pos: out[3],
321            },
322            out[1],
323        )
324    }
325    fn prior(&mut self, idx: IId, off: IId) -> IId {
326        let g = &mut self.g;
327        let a = g.add(off, idx);
328        let m = g.load(self.mt, a);
329        let mask = g.u32(0xFFFF);
330        let sixteen = g.u32(16);
331        let one = g.u32(1);
332        let lo = g.and(m, mask);
333        let hi = g.shr(m, sixteen);
334        let s = g.add(lo, hi);
335        let s = g.shr(s, one);
336        let (a, b) = (g.u32(32), g.u32(65504));
337        g.clamp(s, a, b)
338    }
339    fn sbit(&mut self, s: St, idx: IId, off: IId, on: Option<IId>) -> (St, IId) {
340        let p = self.prior(idx, off);
341        self.bit(s, p, on)
342    }
343    /// A loop of decisions on consecutive models `base + j` while they come
344    /// out 1, at most `max` (gated by `on`): returns the count.
345    fn unary(&mut self, s: St, base: IId, off: IId, max: u32, on: IId) -> (St, IId) {
346        let z = self.u(0);
347        let mut b = std::mem::take(&mut self.g);
348        let (stream, end, mt) = (self.stream, self.end, self.mt);
349        let out = b.while_loop(&[z, s.x, s.pos, on], |g, st| {
350            let mut me = B {
351                g: std::mem::take(g),
352                mt,
353                stream,
354                end,
355                ad: None,
356            };
357            let mx = me.u(max);
358            let more = me.g.lt(st[0], mx);
359            let go = me.g.and(st[3], more);
360            let idx = me.g.add(base, st[0]);
361            let (s2, bit) = me.sbit(
362                St {
363                    x: st[1],
364                    pos: st[2],
365                },
366                idx,
367                off,
368                None,
369            );
370            let one = me.u(1);
371            let h1 = me.g.add(st[0], one);
372            let h = me.g.select(bit, h1, st[0]);
373            *g = me.g;
374            (go, vec![h, s2.x, s2.pos, bit])
375        });
376        b.cap_loop(out[0], max + 1);
377        self.g = b;
378        (
379            St {
380                x: out[1],
381                pos: out[2],
382            },
383            out[0],
384        )
385    }
386}
387
388/// The static-prior residual decoder (`res_static_gen`).
389pub fn static_graph() -> (IntGraph, Vec<IId>) {
390    let mut b = B::new();
391    let g = &mut b.g;
392    let x = g.param(0, IType::U32);
393    let pos = g.param(1, IType::U32);
394    let sig = g.param(3, IType::U32);
395    let e = g.param(4, IType::U32);
396    let cls = g.param(5, IType::U32);
397    let sctx = g.param(6, IType::U32);
398    let off = g.param(7, IType::U32);
399    let s = St { x, pos };
400    let sig0 = b.u(SIG0 as u32);
401    let si = b.g.add(sig0, sig);
402    let (s, en) = b.sbit(s, si, off, None);
403    // k = max(e, 2) - 2
404    let two = b.u(2);
405    let k = b.g.max(e, two);
406    let k = b.g.sub(k, two);
407    // magnitude unary over MAG0 + (cls·NE + e)·KB
408    let ne = b.u(NE as u32);
409    let kb = b.u(KB as u32);
410    let mag0 = b.u(MAG0 as u32);
411    let t = b.g.mul(cls, ne);
412    let t = b.g.add(t, e);
413    let t = b.g.mul(t, kb);
414    let mbase = b.g.add(mag0, t);
415    let (s, h) = b.unary(s, mbase, off, KB as u32, en);
416    // escape: Elias gamma on ESC0 + cls·32
417    let full = b.g.eq(h, kb);
418    let esc = b.g.and(en, full);
419    let esc0 = b.u(ESC0 as u32);
420    let c32 = b.u(32);
421    let t = b.g.mul(cls, c32);
422    let ebase = b.g.add(esc0, t);
423    let (s, nb) = b.unary(s, ebase, off, 31, esc);
424    let (s, glo) = b.raw_n(s, nb);
425    let one = b.u(1);
426    let p = b.g.shl(one, nb);
427    let p = b.g.or(p, glo);
428    let p = b.g.sub(p, one);
429    let hp = b.g.add(h, p);
430    let hv = b.g.select(esc, hp, h);
431    // low bits: two modelled, then raw
432    let low0 = b.u(LOW0 as u32);
433    let four = b.u(4);
434    let e4 = b.g.mul(e, four);
435    let lb = b.g.add(low0, e4);
436    let ge1 = b.g.le(one, k);
437    let on1 = b.g.and(en, ge1);
438    let (s, b1) = b.sbit(s, lb, off, Some(on1));
439    let z = b.u(0);
440    let lo1 = b.g.select(b1, one, z);
441    let ge2 = b.g.le(two, k);
442    let on2 = b.g.and(en, ge2);
443    let lb2 = b.g.add(lb, two);
444    let lb2 = b.g.add(lb2, lo1);
445    let (s, b2) = b.sbit(s, lb2, off, Some(on2));
446    let l2 = b.g.shl(lo1, one);
447    let bv = b.g.select(b2, one, z);
448    let l2 = b.g.or(l2, bv);
449    let lo = b.g.select(on2, l2, lo1);
450    let three = b.u(3);
451    let ge3 = b.g.le(three, k);
452    let on3 = b.g.and(en, ge3);
453    let km2 = b.g.sub(k, two);
454    let nraw = b.g.select(on3, km2, z);
455    let (s, rv) = b.raw_n(s, nraw);
456    let l3 = b.g.shl(lo, nraw);
457    let l3 = b.g.or(l3, rv);
458    let lo = b.g.select(on3, l3, lo);
459    let mv = b.g.shl(hv, k);
460    let mv = b.g.or(mv, lo);
461    let mv = b.g.add(mv, one);
462    // sign
463    let sgn0 = b.u(SGN0 as u32);
464    let si = b.g.add(sgn0, sctx);
465    let (s, neg) = b.sbit(s, si, off, Some(en));
466    let mi = b.g.bitcast(IType::I32, mv);
467    let nm = b.g.neg(mi);
468    let r = b.g.select(neg, nm, mi);
469    let zi = b.i(0);
470    let r = b.g.select(en, r, zi);
471    (b.g, vec![s.x, s.pos, r])
472}
473
474impl B {
475    fn sig16(&mut self, z: IId, off_sig: IId) -> IId {
476        let zero = self.i(0);
477        let g = &mut self.g;
478        let neg = g.lt(z, zero);
479        let nz = g.neg(z);
480        let a = g.select(neg, nz, z);
481        let a = g.bitcast(IType::U32, a);
482        let cap = g.u32(4096);
483        let a = g.min(a, cap);
484        let i = g.add(off_sig, a);
485        let s = g.load(self.mt, i);
486        let k = g.u32(65536);
487        let c = g.sub(k, s);
488        g.select(neg, c, s)
489    }
490    fn zval(&mut self, d0: IId, ls0: IId, off_pow: IId) -> IId {
491        let c = |b: &mut B, v: i32| b.i(v);
492        let (lo, hi) = (c(self, -4096), c(self, 6144));
493        let ls = self.g.clamp(ls0, lo, hi);
494        let eight = self.u(8);
495        let i = self.g.shr(ls, eight);
496        let m255 = c(self, 255);
497        let f = self.g.and(ls, m255);
498        let k256 = c(self, 256);
499        let fi = self.g.sub(k256, f);
500        let fu = self.g.bitcast(IType::U32, fi);
501        let pi = self.g.add(off_pow, fu);
502        let m = self.g.load(self.mt, pi);
503        let m = self.g.bitcast(IType::I32, m);
504        let (dl_, dh_) = (c(self, -33554432), c(self, 33554432));
505        let d = self.g.clamp(d0, dl_, dh_);
506        let sixteen = self.u(16);
507        let dh = self.g.shr(d, sixteen);
508        let du = self.g.bitcast(IType::U32, d);
509        let mask = self.u(0xFFFF);
510        let dl = self.g.and(du, mask);
511        let a = self.g.mul(dh, m);
512        let dli = self.g.bitcast(IType::I32, dl);
513        let a = self.g.add(a, dli);
514        let k65536 = c(self, 65536);
515        let mm = self.g.sub(m, k65536);
516        let mmu = self.g.bitcast(IType::U32, mm);
517        let t2 = self.g.mul(dl, mmu);
518        let h = self.u(32768);
519        let t2 = self.g.add(t2, h);
520        let t2 = self.g.shr(t2, sixteen);
521        let t2 = self.g.bitcast(IType::I32, t2);
522        let t = self.g.add(a, t2);
523        let one = c(self, 1);
524        let sh = self.g.add(i, one);
525        let zero = c(self, 0);
526        let pos = self.g.le(zero, sh);
527        let c31 = c(self, 31);
528        let shm = self.g.min(sh, c31);
529        let shu = self.g.bitcast(IType::U32, shm);
530        let rp = self.g.shr(t, shu);
531        let nsh = self.g.neg(sh);
532        let k = self.g.bitcast(IType::U32, nsh);
533        let c4097 = c(self, 4097);
534        let lim = self.g.shr(c4097, k);
535        let tl = self.g.shl(t, k);
536        let n4097 = c(self, -4097);
537        let nlim = self.g.neg(lim);
538        let below = self.g.lt(t, nlim);
539        let sat = self.g.select(below, n4097, tl);
540        let above = self.g.lt(lim, t);
541        let sat = self.g.select(above, c4097, sat);
542        self.g.select(pos, rp, sat)
543    }
544}
545
546struct Mix {
547    pi: IId,
548    mu: [IId; 2],
549    ls: [IId; 2],
550    off_sig: IId,
551    off_pow: IId,
552}
553
554impl B {
555    fn lcdf(&mut self, mx: &Mix, m: IId) -> IId {
556        let (a, b) = (self.i(-65535), self.i(65535));
557        let t = self.g.clamp(m, a, b);
558        let k256 = self.i(256);
559        let t = self.g.mul(t, k256);
560        let k128 = self.i(128);
561        let t = self.g.add(t, k128);
562        let mut c = Vec::with_capacity(2);
563        for j in 0..2 {
564            let d = self.g.sub(t, mx.mu[j]);
565            let z = self.zval(d, mx.ls[j], mx.off_pow);
566            c.push(self.sig16(z, mx.off_sig));
567        }
568        let k = self.u(32768);
569        let a = self.g.mul(mx.pi, c[0]);
570        let w = self.g.sub(k, mx.pi);
571        let b = self.g.mul(w, c[1]);
572        let s = self.g.add(a, b);
573        let f = self.u(15);
574        self.g.shr(s, f)
575    }
576    fn ratio16(&mut self, num: IId, den: IId) -> IId {
577        let g = &mut self.g;
578        let z = g.u32(0);
579        let nz = g.cmp(tang_expr::int::CmpOp::Ne, den, z);
580        let mn = g.min(num, den);
581        let k = g.u32(65535);
582        let p = g.mul(mn, k);
583        let p = g.div(p, den);
584        let h = g.u32(32768);
585        let p = g.select(nz, p, h);
586        let (a, b) = (g.u32(32), g.u32(65504));
587        g.clamp(p, a, b)
588    }
589    fn ltail(&mut self, mx: &Mix, m: IId, neg: IId) -> IId {
590        let mi = self.g.bitcast(IType::I32, m);
591        let nmi = self.g.neg(mi);
592        let one = self.i(1);
593        let mm1 = self.g.sub(mi, one);
594        let arg = self.g.select(neg, nmi, mm1);
595        let c = self.lcdf(mx, arg);
596        let k = self.u(65536);
597        let up = self.g.sub(k, c);
598        let v = self.g.select(neg, c, up);
599        let big = self.u(1 << 30);
600        let over = self.g.lt(big, m);
601        let z = self.u(0);
602        self.g.select(over, z, v)
603    }
604}
605
606/// The learned-mixture residual decoder (`res_learned_gen`).
607pub fn learned_graph() -> (IntGraph, Vec<IId>) {
608    learned_graph_with(false)
609}
610
611/// The learned-mixture residual decoder through the per-lane adaptive
612/// probability maps (the adaptive role, see the module docs).
613pub fn learned_ad_graph() -> (IntGraph, Vec<IId>) {
614    learned_graph_with(true)
615}
616
617fn learned_graph_with(adaptive: bool) -> (IntGraph, Vec<IId>) {
618    let mut b = B::new();
619    let x = b.g.param(0, IType::U32);
620    let pos = b.g.param(1, IType::U32);
621    let o: Vec<IId> = (0..5).map(|k| b.g.param(3 + k, IType::I32)).collect();
622    let off_sig = b.g.param(8, IType::U32);
623    let off_pow = b.g.param(9, IType::U32);
624    let one = b.u(1);
625    let z = b.u(0);
626    let mut tok = z;
627    if adaptive {
628        let g = &mut b.g;
629        let buf = g.buffer_rw(RW_AD[0].0, RW_AD[0].1, RW_AD[0].2, RW_AD[0].3);
630        let so = g.param(10, IType::U32);
631        let wa = g.param(11, IType::U32);
632        let lim = g.param(12, IType::U32);
633        let mask = g.param(13, IType::U32);
634        let on = g.cmp(tang_expr::int::CmpOp::Ne, wa, z);
635        let en = std::array::from_fn(|k| {
636            let kk = g.u32(k as u32);
637            let m = g.shr(mask, kk);
638            let m = g.and(m, one);
639            let m = g.cmp(tang_expr::int::CmpOp::Ne, m, z);
640            g.and(on, m)
641        });
642        b.ad = Some(Ad {
643            buf,
644            so,
645            wa,
646            lim,
647            en,
648        });
649        tok = b.g.token();
650    }
651    let ad = b.ad;
652    // one decision: adaptive (context `ctx`, group `grp`) or plain
653    let dec = |b: &mut B,
654               s: St,
655               tok: IId,
656               ctx: IId,
657               grp: usize,
658               p: IId,
659               on: Option<IId>|
660     -> (St, IId, IId) {
661        if adaptive {
662            b.ad_bit(s, tok, ctx, grp, p, on)
663        } else {
664            let (s, bit) = b.bit(s, p, on);
665            (s, bit, tok)
666        }
667    };
668    let pi = b.sig16(o[0], off_sig);
669    let pi = b.g.shr(pi, one);
670    let mx = Mix {
671        pi,
672        mu: [o[1], o[2]],
673        ls: [o[3], o[4]],
674        off_sig,
675        off_pow,
676    };
677    let s = St { x, pos };
678    let zi = b.i(0);
679    let m1 = b.i(-1);
680    let c0 = b.lcdf(&mx, zi);
681    let cm1 = b.lcdf(&mx, m1);
682    let gt = b.g.lt(cm1, c0);
683    let d = b.g.sub(c0, cm1);
684    let pz = b.g.select(gt, d, z);
685    let k = b.u(65536);
686    let nzm = b.g.sub(k, pz);
687    let p = b.ratio16(nzm, k);
688    let (s, en, tok) = dec(&mut b, s, tok, z, 0, p, None);
689    let posm = b.g.sub(k, c0);
690    let den = b.g.add(cm1, posm);
691    let p = b.ratio16(cm1, den);
692    let (s, neg, tok) = dec(&mut b, s, tok, one, 1, p, Some(en));
693    // tail(1) from the CDFs already at hand
694    let ta0 = b.g.select(neg, cm1, posm);
695    // exp-Golomb bucket: tails carried between steps
696    let mut gb = std::mem::take(&mut b.g);
697    let (stream, end, mt) = (b.stream, b.end, b.mt);
698    let mxr = &mx;
699    let out = gb.while_loop(&[z, ta0, z, s.x, s.pos, en, tok], |g, st| {
700        let mut me = B {
701            g: std::mem::take(g),
702            mt,
703            stream,
704            end,
705            ad,
706        };
707        let c30 = me.u(30);
708        let more = me.g.lt(st[0], c30);
709        let go = me.g.and(st[5], more);
710        let one = me.u(1);
711        let k1 = me.g.add(st[0], one);
712        let m = me.g.shl(one, k1);
713        let tb = me.ltail(mxr, m, neg);
714        let p = me.ratio16(tb, st[1]);
715        let (two, three) = (me.u(2), me.u(3));
716        let ctx = me.g.min(st[0], three);
717        let ctx = me.g.add(ctx, two);
718        let (s2, bit, tk) = dec(
719            &mut me,
720            St {
721                x: st[3],
722                pos: st[4],
723            },
724            st[6],
725            ctx,
726            2,
727            p,
728            None,
729        );
730        let ta = me.g.select(bit, tb, st[1]);
731        let kk = me.g.select(bit, k1, st[0]);
732        *g = me.g;
733        (go, vec![kk, ta, tb, s2.x, s2.pos, bit, tk])
734    });
735    gb.cap_loop(out[0], 31);
736    b.g = gb;
737    let (kq, ta, tb, tok) = (out[0], out[1], out[2], out[6]);
738    let s = St {
739        x: out[3],
740        pos: out[4],
741    };
742    let lo = b.g.shl(one, kq);
743    let lm1 = b.g.sub(lo, one);
744    let hi = b.g.add(lo, lm1);
745    let c30 = b.u(30);
746    let lt30 = b.g.lt(kq, c30);
747    let endv = b.g.select(lt30, tb, z);
748    // mantissa by bisection (context 6 for the first bit, then 7)
749    let first = b.g.bool(true);
750    let mut gb = std::mem::take(&mut b.g);
751    let out = gb.while_loop(&[lo, hi, ta, endv, s.x, s.pos, tok, first], |g, st| {
752        let mut me = B {
753            g: std::mem::take(g),
754            mt,
755            stream,
756            end,
757            ad,
758        };
759        let lt = me.g.lt(st[0], st[1]);
760        let go = me.g.and(en, lt);
761        let one = me.u(1);
762        let w = me.g.sub(st[1], st[0]);
763        let w = me.g.add(w, one);
764        let w = me.g.shr(w, one);
765        let mid = me.g.add(st[0], w);
766        let tm = me.ltail(mxr, mid, neg);
767        let z = me.u(0);
768        let gt = me.g.lt(st[3], tm);
769        let d = me.g.sub(tm, st[3]);
770        let up = me.g.select(gt, d, z);
771        let gt = me.g.lt(st[3], st[2]);
772        let d = me.g.sub(st[2], st[3]);
773        let all = me.g.select(gt, d, z);
774        let p = me.ratio16(up, all);
775        let (c6, c7) = (me.u(6), me.u(7));
776        let ctx = me.g.select(st[7], c6, c7);
777        let (s2, bit, tk) = dec(
778            &mut me,
779            St {
780                x: st[4],
781                pos: st[5],
782            },
783            st[6],
784            ctx,
785            3,
786            p,
787            None,
788        );
789        let mm1 = me.g.sub(mid, one);
790        let nlo = me.g.select(bit, mid, st[0]);
791        let ntl = me.g.select(bit, tm, st[2]);
792        let nhi = me.g.select(bit, st[1], mm1);
793        let nend = me.g.select(bit, st[3], tm);
794        let nf = me.g.bool(false);
795        *g = me.g;
796        (go, vec![nlo, nhi, ntl, nend, s2.x, s2.pos, tk, nf])
797    });
798    gb.cap_loop(out[0], 31);
799    b.g = gb;
800    let s = St {
801        x: out[4],
802        pos: out[5],
803    };
804    let li = b.g.bitcast(IType::I32, out[0]);
805    let nl = b.g.neg(li);
806    let r = b.g.select(neg, nl, li);
807    let r = b.g.select(en, r, zi);
808    let mut outs = vec![s.x, s.pos, r];
809    if adaptive {
810        outs.push(out[6]);
811    }
812    (b.g, outs)
813}
814
815/// WGSL for `res_static_gen` / `res_learned_gen` from the given graphs
816/// (`guarded`: bounds-checked loads, for untrusted graphs). A learned graph
817/// with four outputs is the adaptive role: it reads the kernel's `ad_*`
818/// state and `adt` bins (set up per block by `ad_reset`).
819pub fn wgsl(stat: (&IntGraph, &[IId]), learned: (&IntGraph, &[IId]), guarded: bool) -> String {
820    let body = |g: &IntGraph, o: &[IId], p: &[&str], d: &[&str]| {
821        if guarded {
822            g.to_wgsl_body_guarded(o, p, d, 1)
823        } else {
824            g.to_wgsl_body(o, p, d, 1)
825        }
826    };
827    let res = ["rx", "rpos", "r", "adk"];
828    let mut s = String::from(
829        "fn res_static_gen(sig: u32, e: u32, cls: u32, sctx: u32) -> i32 {\n    var r: i32;\n",
830    );
831    s += &body(
832        stat.0,
833        stat.1,
834        &[
835            "rx",
836            "rpos",
837            "rend",
838            "sig",
839            "e",
840            "cls",
841            "sctx",
842            "P.off_init",
843        ],
844        &res[..3],
845    );
846    s += "    return r;\n}\n\nfn res_learned_gen(o0: i32, o1: i32, o2: i32, o3: i32, o4: i32) -> i32 {\n    var r: i32;\n";
847    let mut p = vec![
848        "rx",
849        "rpos",
850        "rend",
851        "o0",
852        "o1",
853        "o2",
854        "o3",
855        "o4",
856        "mt[P.pad4]",
857        "mt[P.pad4 + 1u]",
858    ];
859    if learned.1.len() == OUTS_AD.len() {
860        s += "    var adk: u32;\n";
861        p.extend(["ad_so", "ad_wa", "ad_lim", "ad_mask"]);
862    }
863    s += &body(learned.0, learned.1, &p, &res[..learned.1.len()]);
864    s += "    return r;\n}\n";
865    s
866}
867
868/// The built-in residual decoders as WGSL.
869pub fn builtin_wgsl() -> &'static str {
870    static S: std::sync::OnceLock<String> = std::sync::OnceLock::new();
871    S.get_or_init(|| {
872        let (a, ao) = static_graph();
873        let (b, bo) = learned_graph();
874        wgsl((&a, &ao), (&b, &bo), false)
875    })
876}
877
878/// [`builtin_wgsl`] with the adaptive learned decoder.
879pub fn builtin_wgsl_ad() -> &'static str {
880    static S: std::sync::OnceLock<String> = std::sync::OnceLock::new();
881    S.get_or_init(|| {
882        let (a, ao) = static_graph();
883        let (b, bo) = learned_ad_graph();
884        wgsl((&a, &ao), (&b, &bo), false)
885    })
886}
887
888/// Replace the `// RES_BEGIN` … `// RES_END` region of a kernel source.
889pub fn splice_res(src: &str, res: &str) -> String {
890    let a = src.find("// RES_BEGIN").expect("RES_BEGIN");
891    let b = src.find("// RES_END").expect("RES_END");
892    let mut out = format!("{}// RES_BEGIN\n{}{}", &src[..a], res, &src[b..]);
893    if out.contains("tx_mulhi_") && !out.contains("fn tx_mulhi_u32") {
894        out.push_str(tang_expr::int::WGSL_PRELUDE);
895    }
896    out
897}
898
899/// Stream bytes as the kernels see them: `u32` words, little endian.
900pub fn stream_words(bytes: &[u8]) -> Vec<u32> {
901    bytes
902        .chunks(4)
903        .map(|c| {
904            let mut b = [0u8; 4];
905            b[..c.len()].copy_from_slice(c);
906            u32::from_le_bytes(b)
907        })
908        .collect()
909}
910
911/// One lane's rANS state (as `lane_start` in the kernel).
912#[derive(Clone, Copy, Debug)]
913pub struct Lane {
914    /// State.
915    pub x: u32,
916    /// Next 16-bit word index.
917    pub pos: u32,
918    /// Word bound.
919    pub end: u32,
920    lo: u32,
921}
922
923impl Lane {
924    /// Lane over 16-bit words `lo..hi` of `stream`.
925    pub fn new(stream: &[u32], lo: u32, hi: u32) -> Self {
926        let w = |i: u32| {
927            if i < hi {
928                (stream.get((i >> 1) as usize).copied().unwrap_or(0) >> ((i & 1) * 16)) & 0xFFFF
929            } else {
930                0
931            }
932        };
933        let x = if hi.wrapping_sub(lo) >= 2 && hi >= lo {
934            w(lo) | w(lo + 1) << 16
935        } else {
936            1 << 16
937        };
938        Self {
939            x,
940            pos: lo + 2,
941            end: hi,
942            lo,
943        }
944    }
945    /// Whether more words were consumed than the lane holds.
946    pub fn overrun(&self) -> bool {
947        self.end >= self.lo + 2 && self.pos > self.end
948    }
949}
950
951/// CPU evaluation of residual graphs (the interpreter; one per thread).
952#[derive(Clone)]
953pub struct CpuRes {
954    prog: tang_expr::int::Program,
955    learned: bool,
956    off: [u32; 2],
957    /// Adaptive role: `[off_stretch, wa, lim, mask]`.
958    ad: Option<[u32; 4]>,
959}
960
961impl CpuRes {
962    /// A static graph (`off_init` into `mt`) or a learned one
963    /// (`off_sig`, `off_pow`).
964    pub fn new(g: &IntGraph, outs: &[IId], learned: bool, off: [u32; 2]) -> Self {
965        Self {
966            prog: tang_expr::int::Program::new(g, outs),
967            learned,
968            off,
969            ad: None,
970        }
971    }
972    /// The adaptive learned role with settings `ad` (`off` as for a learned
973    /// graph; the stretch table follows the 2^x table, see [`cpu_mt`]).
974    pub fn new_ad(g: &IntGraph, outs: &[IId], off: [u32; 2], ad: crate::lmodel3::AdCfg) -> Self {
975        let so = off[1] + crate::lmodel3::tables().1.len() as u32;
976        Self {
977            prog: tang_expr::int::Program::new(g, outs),
978            learned: true,
979            off,
980            ad: Some([so, ad.wa, ad.lim, ad.mask]),
981        }
982    }
983    /// Whether this is the learned role.
984    pub fn learned(&self) -> bool {
985        self.learned
986    }
987    /// Decode one static-prior residual.
988    #[allow(clippy::too_many_arguments)]
989    pub fn res_static(
990        &mut self,
991        l: &mut Lane,
992        mt: &[u32],
993        stream: &[u32],
994        sig: u32,
995        e: u32,
996        cls: u32,
997        sctx: u32,
998    ) -> i32 {
999        let p = [l.x, l.pos, l.end, sig, e, cls, sctx, self.off[0]];
1000        self.run(l, &p, mt, stream)
1001    }
1002    /// Decode one learned residual from the net outputs; `adt` is the lane's
1003    /// adaptive state ([`crate::lmodel3::ad_init`], unused by the plain role).
1004    pub fn res_learned(
1005        &mut self,
1006        l: &mut Lane,
1007        mt: &[u32],
1008        stream: &[u32],
1009        o: &[i32; 5],
1010        adt: &mut [u32],
1011    ) -> i32 {
1012        let a = self.ad.unwrap_or_default();
1013        let p = [
1014            l.x,
1015            l.pos,
1016            l.end,
1017            o[0] as u32,
1018            o[1] as u32,
1019            o[2] as u32,
1020            o[3] as u32,
1021            o[4] as u32,
1022            self.off[0],
1023            self.off[1],
1024            a[0],
1025            a[1],
1026            a[2],
1027            a[3],
1028        ];
1029        let mut out = [0u32; 4];
1030        self.prog.run_rw(&p, &[mt, stream], &mut [adt], &mut out);
1031        l.x = out[0];
1032        l.pos = out[1];
1033        out[2] as i32
1034    }
1035    fn run(&mut self, l: &mut Lane, p: &[u32], mt: &[u32], stream: &[u32]) -> i32 {
1036        let mut o = [0u32; 3];
1037        self.prog.run(p, &[mt, stream], &mut o);
1038        l.x = o[0];
1039        l.pos = o[1];
1040        o[2] as i32
1041    }
1042}
1043
1044/// `mt` for CPU decoding: priors at 0, then the σ, 2^x and stretch tables
1045/// (the kernel's layout); returns the buffer and `(off_sig, off_pow)`.
1046pub fn cpu_mt(init: &[u32]) -> (Vec<u32>, [u32; 2]) {
1047    let (sig, pw) = crate::lmodel3::tables();
1048    let mut v = init.to_vec();
1049    let os = v.len() as u32;
1050    v.extend_from_slice(sig);
1051    let op = v.len() as u32;
1052    v.extend_from_slice(pw);
1053    v.extend(crate::lmodel3::stretch_tab().iter().map(|&z| z as u32));
1054    (v, [os, op])
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059    use super::*;
1060    use crate::gpuwav::{code_res, rans_encode, Feats, RecSink, NMODEL};
1061    use crate::lmodel3::{code_learned, Mix3};
1062
1063    fn rng(s: &mut u64) -> u32 {
1064        *s = s
1065            .wrapping_mul(6364136223846793005)
1066            .wrapping_add(1442695040888963407);
1067        (*s >> 33) as u32
1068    }
1069
1070    fn to_bytes(w: &[u16]) -> Vec<u8> {
1071        w.iter().flat_map(|v| v.to_le_bytes()).collect()
1072    }
1073
1074    #[test]
1075    fn static_graph_decodes_native_encoding() {
1076        let mut s = 1u64;
1077        let init: Vec<u32> = (0..NMODEL)
1078            .map(|_| (rng(&mut s) & 0xFFFF) | (rng(&mut s) & 0xFFFF) << 16)
1079            .collect();
1080        let mut syms = RecSink {
1081            syms: vec![Vec::new()],
1082        };
1083        let mut items = Vec::new();
1084        for _ in 0..4000 {
1085            let e = (rng(&mut s) % NE as u32) as usize;
1086            let cls = (rng(&mut s) % 10) as usize;
1087            let f = Feats {
1088                taps: [0; crate::gpuwav::NTAP],
1089                sig: (rng(&mut s) % 240) as usize,
1090                e,
1091                sctx: cls * 9 + (rng(&mut s) % 9) as usize,
1092                raw: [0; 18],
1093            };
1094            let r = match rng(&mut s) % 4 {
1095                0 => 0,
1096                1 => (rng(&mut s) % 7) as i32 - 3,
1097                2 => (rng(&mut s) % 70000) as i32 - 35000,
1098                _ => (rng(&mut s) as i32) >> (rng(&mut s) % 31),
1099            };
1100            let mut m = init.clone();
1101            let got = code_res::<RecSink, false>(&mut syms, 0, &mut m, cls, &f, r);
1102            assert_eq!(got, r);
1103            items.push((f, cls, r));
1104        }
1105        let words = rans_encode(&syms.syms[0]);
1106        let mut bytes = vec![0u8, 0, 0, 0, 0, 0]; // offset by 3 words
1107        bytes.extend(to_bytes(&words));
1108        let sw = stream_words(&bytes);
1109        let (g, o) = static_graph();
1110        let mut cr = CpuRes::new(&g, &o, false, [0, 0]);
1111        let mut l = Lane::new(&sw, 3, 3 + words.len() as u32);
1112        for (f, cls, r) in items {
1113            assert_eq!(
1114                cr.res_static(
1115                    &mut l,
1116                    &init,
1117                    &sw,
1118                    f.sig as u32,
1119                    f.e as u32,
1120                    cls as u32,
1121                    f.sctx as u32
1122                ),
1123                r
1124            );
1125        }
1126        assert!(!l.overrun());
1127        assert_eq!(l.pos, l.end);
1128    }
1129
1130    #[test]
1131    fn learned_graph_decodes_native_encoding() {
1132        let mut s = 2u64;
1133        let mut syms = RecSink {
1134            syms: vec![Vec::new()],
1135        };
1136        let mut items = Vec::new();
1137        for _ in 0..3000 {
1138            let o = [
1139                (rng(&mut s) % 4000) as i32 - 2000,
1140                (rng(&mut s) % 6000) as i32 - 3000,
1141                (rng(&mut s) % 6000) as i32 - 3000,
1142                (rng(&mut s) % 9000) as i32 - 4096,
1143                (rng(&mut s) % 9000) as i32 - 4096,
1144            ];
1145            let r = match rng(&mut s) % 3 {
1146                0 => 0,
1147                1 => (rng(&mut s) % 41) as i32 - 20,
1148                _ => (rng(&mut s) as i32) >> (rng(&mut s) % 31 + 1),
1149            };
1150            let got = code_learned(&mut syms, 0, &Mix3::new(&o), r);
1151            assert_eq!(got, r);
1152            items.push((o, r));
1153        }
1154        let words = rans_encode(&syms.syms[0]);
1155        let sw = stream_words(&to_bytes(&words));
1156        let (mt, off) = cpu_mt(&[]);
1157        let (g, go) = learned_graph();
1158        let mut cr = CpuRes::new(&g, &go, true, off);
1159        let mut l = Lane::new(&sw, 0, words.len() as u32);
1160        for (o, r) in items {
1161            assert_eq!(cr.res_learned(&mut l, &mt, &sw, &o, &mut []), r);
1162        }
1163        assert_eq!(l.pos, l.end);
1164    }
1165
1166    #[test]
1167    fn learned_ad_graph_decodes_native_encoding() {
1168        use crate::lmodel3::{ad_init, code_learned_ad, AdCfg, Adapt};
1169        let (g, go) = learned_ad_graph();
1170        // and through the wire (the shipped form)
1171        let w = g.to_bytes(&go);
1172        let (g2, go2) = IntGraph::from_bytes_rw(
1173            &w,
1174            &LEARNED_AD_PARAMS,
1175            &BUFS,
1176            &RW_AD,
1177            &OUTS_AD,
1178            &tang_expr::int::Limits::default(),
1179        )
1180        .unwrap();
1181        for c in [
1182            AdCfg {
1183                wa: 10,
1184                lim: 14,
1185                mask: 15,
1186            },
1187            AdCfg {
1188                wa: 16,
1189                lim: 3,
1190                mask: 5,
1191            },
1192            AdCfg {
1193                wa: 0,
1194                lim: 0,
1195                mask: 0,
1196            },
1197        ] {
1198            let mut s = 3u64;
1199            let mut syms = RecSink {
1200                syms: vec![Vec::new()],
1201            };
1202            let mut ad = Adapt::new(c);
1203            let mut items = Vec::new();
1204            for i in 0..4000 {
1205                if i % 1000 == 0 {
1206                    ad = Adapt::new(c);
1207                }
1208                let o = [
1209                    (rng(&mut s) % 4000) as i32 - 2000,
1210                    (rng(&mut s) % 600) as i32 - 300,
1211                    (rng(&mut s) % 6000) as i32 - 3000,
1212                    (rng(&mut s) % 3000) as i32 - 1000,
1213                    (rng(&mut s) % 9000) as i32 - 4096,
1214                ];
1215                let r = match rng(&mut s) % 3 {
1216                    0 => 0,
1217                    1 => (rng(&mut s) % 41) as i32 - 20,
1218                    _ => (rng(&mut s) as i32) >> (rng(&mut s) % 31 + 1),
1219                };
1220                assert_eq!(code_learned_ad(&mut syms, 0, &Mix3::new(&o), r, &mut ad), r);
1221                items.push((o, r));
1222            }
1223            let words = rans_encode(&syms.syms[0]);
1224            let sw = stream_words(&to_bytes(&words));
1225            let (mt, off) = cpu_mt(&[]);
1226            for (g, go) in [(&g, &go), (&g2, &go2)] {
1227                let mut cr = CpuRes::new_ad(g, go, off, c);
1228                let mut l = Lane::new(&sw, 0, words.len() as u32);
1229                let mut adt = ad_init();
1230                for (i, (o, r)) in items.iter().enumerate() {
1231                    if i % 1000 == 0 {
1232                        adt = ad_init();
1233                    }
1234                    assert_eq!(
1235                        cr.res_learned(&mut l, &mt, &sw, o, &mut adt),
1236                        *r,
1237                        "{c:?} item {i}"
1238                    );
1239                }
1240                assert_eq!(l.pos, l.end);
1241            }
1242        }
1243    }
1244}