1use crate::abac::{BitModel, Decoder, Encoder, UintModel};
43use crate::bitio::{ByteReader, ByteWriter, DecodeError};
44use crate::coefcode::{visit, Bits, CoefModel, Feat, Pred, NCLASS};
45use crate::wavelet::Pyramid;
46use std::sync::{Arc, OnceLock};
47
48pub const NF: usize = 37;
50pub const NO: usize = 5;
52const Q: i64 = 256; const CDF_BITS: u32 = 24;
54const TOT: u64 = 1 << CDF_BITS;
55const ZMAX: i64 = 16 * 256; #[inline]
59pub fn lg_q8(v: u32) -> i32 {
60 let w = v as u64 + 1;
61 let e = w.ilog2();
62 let m = if e >= 8 {
63 (w >> (e - 8)) & 0xFF
64 } else {
65 (w << (8 - e)) & 0xFF
66 };
67 (e as i32) * 256 + m as i32
68}
69
70pub fn feat_vec(f: &Feat) -> [i32; NF] {
72 let slg = |v: i32| {
73 if v < 0 {
74 -lg_q8(v.unsigned_abs())
75 } else {
76 lg_q8(v as u32)
77 }
78 };
79 let mut x = [0i32; NF];
80 for i in 0..9 {
81 x[i] = slg(f.sv[i]);
82 }
83 for i in 9..crate::coefcode::NSV {
84 x[19 + i] = slg(f.sv[i]);
85 }
86 x[9] = lg_q8(f.pnb as u32);
87 x[10] = slg(f.pv);
88 let a: u32 = f.nb[..3].iter().map(|&v| v as u32).sum();
91 let b: u32 = f.nb[3..].iter().map(|&v| v as u32).sum();
92 x[11] = lg_q8(a);
93 x[12] = lg_q8(b);
94 x[13] = lg_q8(f.par as u32);
95 x[14] = f.has_par as i32 * 256;
96 x[15 + f.class as usize] = 256;
97 for a in 0..3 {
98 x[15 + NCLASS + a] = ((f.mask >> a) & 1) as i32 * 256;
99 }
100 x
101}
102
103fn tables() -> &'static (Vec<u32>, Vec<u32>) {
106 static T: OnceLock<(Vec<u32>, Vec<u32>)> = OnceLock::new();
107 T.get_or_init(|| {
108 let x = 1.0f64 / 256.0;
110 let mut term = 1.0f64;
111 let mut em = 1.0f64;
112 for k in 1..20 {
113 term = term * (-x) / k as f64;
114 em += term;
115 }
116 let mut sig = Vec::with_capacity(ZMAX as usize + 1);
118 let mut p = 1.0f64;
119 for _ in 0..=ZMAX {
120 sig.push(((TOT as f64) / (1.0 + p)).round() as u32);
121 p *= em;
122 }
123 let mut r = 2.0f64;
125 for _ in 0..8 {
126 r = r.sqrt();
127 }
128 let mut pw = Vec::with_capacity(257);
129 let mut v = 1.0f64;
130 for _ in 0..=256 {
131 pw.push((v * 65536.0).round() as u32);
132 v *= r;
133 }
134 (sig, pw)
135 })
136}
137
138#[inline]
140fn sigmoid_q(z: i64) -> u64 {
141 let (sig, _) = tables();
142 let a = z.unsigned_abs().min(ZMAX as u64) as usize;
143 let s = sig[a] as u64;
144 if z >= 0 {
145 s
146 } else {
147 TOT - s
148 }
149}
150
151#[inline]
153fn inv_scale_q16(ls: i64) -> u64 {
154 let (_, pw) = tables();
155 let ls = ls.clamp(-20 * 256, 40 * 256);
157 let i = ls.div_euclid(256);
158 let f = ls.rem_euclid(256);
159 let m = pw[(256 - f) as usize] as u64;
160 let sh = i + 1;
161 if sh >= 0 {
162 m >> sh.min(63)
163 } else {
164 (m << (-sh).min(20)).min(1 << 40)
165 }
166}
167
168#[derive(Clone, Copy, Debug)]
170struct Mix {
171 pi: u64,
172 mu: [i64; 2],
173 inv: [u64; 2],
174}
175
176impl Mix {
177 fn from_out(o: &[i32; NO]) -> Self {
178 Self {
179 pi: sigmoid_q(o[0] as i64),
180 mu: [o[1] as i64, o[2] as i64],
181 inv: [inv_scale_q16(o[3] as i64), inv_scale_q16(o[4] as i64)],
182 }
183 }
184 #[inline]
186 fn cdf(&self, m: i64) -> u64 {
187 let t = m.saturating_mul(Q).saturating_add(Q / 2);
188 let c = |k: usize| {
189 let d = (t as i128 - self.mu[k] as i128) * self.inv[k] as i128;
190 let z = (d >> 16).clamp(-(ZMAX as i128) - 1, ZMAX as i128 + 1) as i64;
191 sigmoid_q(z)
192 };
193 (self.pi * c(0) + (TOT - self.pi) * c(1)) >> CDF_BITS
194 }
195}
196
197#[inline]
198fn ratio(num: u64, den: u64) -> u32 {
199 if den == 0 {
200 return 1 << 15;
201 }
202 ((num.min(den) as u128 * 65536 / den as u128) as u32).min(65535)
203}
204
205fn code_mix<B: Bits>(b: &mut B, mx: &Mix, v: i32, apm: &mut Apm, cls: usize) -> i32 {
208 let c0 = mx.cdf(0);
209 let cm1 = mx.cdf(-1);
210 let p_zero = c0.saturating_sub(cm1);
211 b.tag(0);
212 let nz = apm.code(b, v != 0, ratio(TOT - p_zero.min(TOT), TOT), cls * 4);
213 if !nz {
214 return 0;
215 }
216 let neg_mass = cm1;
217 let pos_mass = TOT - c0.min(TOT);
218 b.tag(3);
219 let neg = apm.code(b, v < 0, ratio(neg_mass, neg_mass + pos_mass), cls * 4 + 1);
220 let tail = |m: u64| -> u64 {
221 if m > (1u64 << 40) {
222 return 0;
223 }
224 if neg {
225 mx.cdf(-(m as i64))
226 } else {
227 TOT - mx.cdf(m as i64 - 1).min(TOT)
228 }
229 };
230 let mag = v.unsigned_abs() as u64;
231 let mut k = 0u32;
232 b.tag(1);
233 while k < 30 {
234 let a = tail(1 << k);
235 let bb = tail(1 << (k + 1));
236 if !apm.code(b, mag >= (1 << (k + 1)), ratio(bb, a), cls * 4 + 2) {
237 break;
238 }
239 k += 1;
240 }
241 let (mut lo, mut hi) = (1u64 << k, (1u64 << (k + 1)) - 1);
242 b.tag(2);
243 let mut first = true;
244 while lo < hi {
245 let mid = (lo + hi).div_ceil(2);
246 let end = tail(hi + 1);
247 let up = tail(mid).saturating_sub(end);
248 let all = tail(lo).saturating_sub(end);
249 let p = ratio(up, all);
250 let bit = if first {
251 apm.code(b, mag >= mid, p, cls * 4 + 3)
252 } else {
253 b.bitp(mag >= mid, p)
254 };
255 first = false;
256 if bit {
257 lo = mid;
258 } else {
259 hi = mid - 1;
260 }
261 }
262 if neg {
263 -(lo as i32)
264 } else {
265 lo as i32
266 }
267}
268
269#[derive(Clone, Debug)]
274struct Apm {
275 on: bool,
276 t: Vec<BitModel>,
277}
278
279const APM_N: usize = 33;
280
281fn stretch_tab() -> &'static Vec<i32> {
282 static S: OnceLock<Vec<i32>> = OnceLock::new();
284 S.get_or_init(|| {
285 (0..APM_N)
286 .map(|i| (sigmoid_q((i as i64 - 16) * 128) >> 8) as i32)
287 .collect()
288 })
289}
290
291impl Apm {
292 fn new(on: bool) -> Self {
293 let st = stretch_tab();
294 let t = (0..(NCLASS + 1) * 4)
295 .flat_map(|_| st.iter().map(|&p| BitModel::new(p as u32)))
296 .collect();
297 Self { on, t }
298 }
299 #[inline]
300 fn code<B: Bits>(&mut self, b: &mut B, bit: bool, p1: u32, ctx: usize) -> bool {
301 if !self.on {
302 return b.bitp(bit, p1);
303 }
304 let st = stretch_tab();
305 let p = p1 as i32;
306 let j = st.partition_point(|&s| s <= p).clamp(1, APM_N - 1) - 1;
307 let (a, c) = (st[j], st[j + 1]);
308 let w = if c > a {
309 ((p - a).clamp(0, c - a) as i64 * 4096 / (c - a) as i64) as u32
310 } else {
311 0
312 };
313 let base = ctx * APM_N;
314 let pa = self.t[base + j].p1();
315 let pc = self.t[base + j + 1].p1();
316 let pr = (pa * (4096 - w) + pc * w) >> 12;
317 let pf = (pr + p1) / 2;
318 let got = b.bitp(bit, pf);
319 let jj = if w < 2048 { j } else { j + 1 };
320 self.t[base + jj].update(got);
321 got
322 }
323}
324
325#[derive(Clone, Debug)]
329pub struct Net {
330 pub sizes: Vec<usize>,
332 pub wq: Vec<Vec<i32>>,
334 pub bq: Vec<Vec<i32>>,
336 pub apm: bool,
338}
339
340impl Net {
341 pub fn infer(&self, x: &[i32; NF]) -> [i32; NO] {
343 let mut a = [0i64; 256];
344 let mut b = [0i64; 256];
345 for (d, &v) in a.iter_mut().zip(x) {
346 *d = v as i64;
347 }
348 let nl = self.sizes.len() - 1;
349 for l in 0..nl {
350 let (ni, no) = (self.sizes[l], self.sizes[l + 1]);
351 let w = &self.wq[l];
352 let src = &a[..ni];
353 for o in 0..no {
354 let row = &w[o * ni..(o + 1) * ni];
355 let mut acc = self.bq[l][o] as i64;
356 for (&wi, &ai) in row.iter().zip(src) {
357 acc += wi as i64 * ai;
358 }
359 let v = (acc + 128) >> 8;
360 b[o] = if l + 1 < nl {
361 v.clamp(0, 1 << 22)
362 } else {
363 v.clamp(-(1 << 22), 1 << 22)
364 };
365 }
366 std::mem::swap(&mut a, &mut b);
367 }
368 let mut o = [0i32; NO];
369 for k in 0..NO {
370 o[k] = a[k] as i32;
371 }
372 o
373 }
374
375 pub fn fingerprint(&self) -> u64 {
377 self.to_bytes()
378 .iter()
379 .fold(0xcbf2_9ce4_8422_2325u64, |h, &b| {
380 (h ^ b as u64).wrapping_mul(0x100_0000_01b3)
381 })
382 }
383
384 pub fn num_params(&self) -> usize {
386 self.wq.iter().map(Vec::len).sum::<usize>() + self.bq.iter().map(Vec::len).sum::<usize>()
387 }
388
389 fn write_sizes(&self, w: &mut ByteWriter) {
390 w.varint(self.sizes.len() as u64);
391 for &s in &self.sizes {
392 w.varint(s as u64);
393 }
394 w.u8(self.apm as u8);
395 }
396
397 fn read_sizes(r: &mut ByteReader) -> Result<(Vec<usize>, bool), DecodeError> {
398 let nl = r.varint()? as usize;
399 if !(2..=6).contains(&nl) {
400 return Err(DecodeError::Invalid("net layers"));
401 }
402 let mut sizes = Vec::new();
403 for _ in 0..nl {
404 let s = r.varint()? as usize;
405 if s == 0 || s > 256 {
406 return Err(DecodeError::Invalid("net size"));
407 }
408 sizes.push(s);
409 }
410 if sizes[0] != NF || sizes[nl - 1] != NO {
411 return Err(DecodeError::Invalid("net io"));
412 }
413 Ok((sizes, r.u8()? != 0))
414 }
415
416 pub fn to_bytes(&self) -> Vec<u8> {
418 let mut w = ByteWriter::new();
419 self.write_sizes(&mut w);
420 for l in 0..self.wq.len() {
421 for &v in self.wq[l].iter().chain(&self.bq[l]) {
422 w.svarint(v as i64);
423 }
424 }
425 w.buf
426 }
427
428 pub fn from_bytes(b: &[u8]) -> Result<Self, DecodeError> {
430 let mut r = ByteReader::new(b);
431 let (sizes, apm) = Self::read_sizes(&mut r)?;
432 let mut wq = Vec::new();
433 let mut bq = Vec::new();
434 for l in 0..sizes.len() - 1 {
435 let mut w = Vec::new();
436 for _ in 0..sizes[l] * sizes[l + 1] {
437 w.push(r.svarint()?.clamp(-(1 << 30), 1 << 30) as i32);
438 }
439 let mut b = Vec::new();
440 for _ in 0..sizes[l + 1] {
441 b.push(r.svarint()?.clamp(-(1 << 30), 1 << 30) as i32);
442 }
443 wq.push(w);
444 bq.push(b);
445 }
446 Ok(Self { sizes, wq, bq, apm })
447 }
448
449 fn write_coded(&self, w: &mut ByteWriter) {
451 self.write_sizes(w);
452 let mut e = Encoder::new();
453 let mut mw = UintModel::default();
454 let mut mb = UintModel::default();
455 for l in 0..self.wq.len() {
456 for &v in &self.wq[l] {
457 mw.encode(&mut e, zz(v as i64));
458 }
459 for &v in &self.bq[l] {
460 mb.encode(&mut e, zz(v as i64));
461 }
462 }
463 w.block(&e.finish());
464 }
465
466 fn read_coded(r: &mut ByteReader) -> Result<Self, DecodeError> {
467 let (sizes, apm) = Self::read_sizes(r)?;
468 let buf = r.block()?;
469 let mut d = Decoder::new(buf);
470 let mut mw = UintModel::default();
471 let mut mb = UintModel::default();
472 let mut wq = Vec::new();
473 let mut bq = Vec::new();
474 for l in 0..sizes.len() - 1 {
475 wq.push(
476 (0..sizes[l] * sizes[l + 1])
477 .map(|_| unzz(mw.decode(&mut d)).clamp(-(1 << 30), 1 << 30) as i32)
478 .collect(),
479 );
480 bq.push(
481 (0..sizes[l + 1])
482 .map(|_| unzz(mb.decode(&mut d)).clamp(-(1 << 30), 1 << 30) as i32)
483 .collect(),
484 );
485 }
486 Ok(Self { sizes, wq, bq, apm })
487 }
488}
489
490fn zz(v: i64) -> u64 {
491 ((v << 1) ^ (v >> 63)) as u64
492}
493
494fn unzz(u: u64) -> i64 {
495 ((u >> 1) as i64) ^ -((u & 1) as i64)
496}
497
498pub struct NetModel {
500 net: Arc<Net>,
501 cache: Vec<Option<[i32; NO]>>,
502 apm: Apm,
503}
504
505impl NetModel {
506 pub fn new(net: Arc<Net>) -> Self {
508 let apm = Apm::new(net.apm);
509 Self {
510 net,
511 cache: vec![None; NCLASS * 8 * 2],
512 apm,
513 }
514 }
515}
516
517impl CoefModel for NetModel {
518 fn code<B: Bits>(&mut self, b: &mut B, f: &Feat, v: i32) -> i32 {
519 let quiet = f.sv.iter().all(|&a| a == 0) && f.pm == 0;
520 let o = if quiet {
521 let k = (f.class as usize * 8 + f.mask as usize) * 2 + f.has_par as usize;
522 *self.cache[k].get_or_insert_with(|| self.net.infer(&feat_vec(f)))
523 } else {
524 self.net.infer(&feat_vec(f))
525 };
526 code_mix(b, &Mix::from_out(&o), v, &mut self.apm, f.class as usize)
527 }
528}
529
530#[derive(Clone, Debug)]
534pub enum LearnedSpec {
535 Pretrained(Arc<Net>),
537 Overfit(TrainConfig),
539}
540
541const TAG_PRETRAINED: u8 = 1;
542const TAG_OVERFIT: u8 = 2;
543
544impl LearnedSpec {
545 pub fn prepare(&self, p: &Pyramid, q: &[i32], preds: &[Pred], w: &mut ByteWriter) -> NetModel {
547 match self {
548 LearnedSpec::Pretrained(net) => {
549 w.u8(TAG_PRETRAINED);
550 w.bytes(&net.fingerprint().to_le_bytes());
551 NetModel::new(net.clone())
552 }
553 LearnedSpec::Overfit(cfg) => {
554 let samples = collect(p, q, preds);
555 let net = train(&samples, cfg);
556 w.u8(TAG_OVERFIT);
557 net.write_coded(w);
558 NetModel::new(Arc::new(net))
559 }
560 }
561 }
562}
563
564fn registry() -> &'static std::sync::Mutex<Vec<Arc<Net>>> {
566 static R: OnceLock<std::sync::Mutex<Vec<Arc<Net>>>> = OnceLock::new();
567 R.get_or_init(|| std::sync::Mutex::new(Vec::new()))
568}
569
570pub fn register(net: Arc<Net>) {
572 let mut r = registry().lock().unwrap();
573 if !r.iter().any(|n| n.fingerprint() == net.fingerprint()) {
574 r.push(net);
575 }
576}
577
578pub fn lookup(fp: u64) -> Option<Arc<Net>> {
580 registry()
581 .lock()
582 .unwrap()
583 .iter()
584 .find(|n| n.fingerprint() == fp)
585 .cloned()
586}
587
588pub fn read_model(tag: u8, r: &mut ByteReader) -> Result<NetModel, DecodeError> {
590 match tag {
591 TAG_PRETRAINED => {
592 let fp = u64::from_le_bytes(r.take(8)?.try_into().unwrap());
593 let reg = registry().lock().unwrap();
594 let net = reg
595 .iter()
596 .find(|n| n.fingerprint() == fp)
597 .ok_or(DecodeError::Invalid("pretrained model not registered"))?;
598 Ok(NetModel::new(net.clone()))
599 }
600 TAG_OVERFIT => Ok(NetModel::new(Arc::new(Net::read_coded(r)?))),
601 _ => Err(DecodeError::Invalid("model tag")),
602 }
603}
604
605pub fn parse_model_arg(s: &str) -> crate::wavcodec::ModelChoice {
608 use crate::wavcodec::ModelChoice;
609 if s == "hand" {
610 return ModelChoice::Hand;
611 }
612 if let Some(rest) = s.strip_prefix("overfit") {
613 let mut cfg = TrainConfig::overfit();
614 if let Some(h) = rest.strip_prefix(':') {
615 cfg.hidden = h.split(',').map(|v| v.parse().unwrap()).collect();
616 }
617 return ModelChoice::Learned(LearnedSpec::Overfit(cfg));
618 }
619 if let Some(path) = s.strip_prefix("pretrained:") {
620 let net = Arc::new(
621 Net::from_bytes(&std::fs::read(path).expect("read model")).expect("parse model"),
622 );
623 register(net.clone());
624 return ModelChoice::Learned(LearnedSpec::Pretrained(net));
625 }
626 panic!("unknown model {s}")
627}
628
629#[derive(Clone, Copy, Debug)]
633pub struct Sample {
634 pub x: [i32; NF],
636 pub r: i32,
638}
639
640pub fn collect(p: &Pyramid, q: &[i32], preds: &[Pred]) -> Vec<Sample> {
643 let mut out = Vec::with_capacity(q.len());
644 visit(p, q, preds, |f, r| out.push(Sample { x: feat_vec(f), r }));
645 out
646}
647
648#[derive(Clone, Debug)]
650pub struct TrainConfig {
651 pub hidden: Vec<usize>,
653 pub epochs: usize,
655 pub per_epoch: usize,
657 pub batch: usize,
659 pub lr: f64,
661 pub apm: bool,
663 pub seed: u64,
665}
666
667impl TrainConfig {
668 pub fn overfit() -> Self {
670 Self {
671 hidden: vec![16],
672 epochs: 4,
673 per_epoch: 200_000,
674 batch: 256,
675 lr: 4e-3,
676 apm: true,
677 seed: 1,
678 }
679 }
680 pub fn pretrain() -> Self {
682 Self {
683 hidden: vec![32, 16],
684 epochs: 10,
685 per_epoch: 500_000,
686 batch: 256,
687 lr: 3e-3,
688 apm: true,
689 seed: 7,
690 }
691 }
692}
693
694struct FNet {
695 sizes: Vec<usize>,
696 off: Vec<(usize, usize)>, n: usize,
698}
699
700impl FNet {
701 fn new(sizes: Vec<usize>) -> Self {
702 let mut off = Vec::new();
703 let mut n = 0;
704 for l in 0..sizes.len() - 1 {
705 off.push((n, n + sizes[l] * sizes[l + 1]));
706 n += sizes[l] * sizes[l + 1] + sizes[l + 1];
707 }
708 Self { sizes, off, n }
709 }
710 fn forward(&self, th: &[f64], x: &[f64]) -> Vec<Vec<f64>> {
711 let mut acts = vec![x.to_vec()];
712 let nl = self.sizes.len() - 1;
713 for l in 0..nl {
714 let (ni, no) = (self.sizes[l], self.sizes[l + 1]);
715 let (wo, bo) = self.off[l];
716 let a = acts.last().unwrap();
717 let mut out = vec![0.0; no];
718 for o in 0..no {
719 let row = &th[wo + o * ni..wo + (o + 1) * ni];
720 let mut s = th[bo + o];
721 for i in 0..ni {
722 s += row[i] * a[i];
723 }
724 out[o] = if l + 1 < nl { s.max(0.0) } else { s };
725 }
726 acts.push(out);
727 }
728 acts
729 }
730 fn backward(&self, th: &[f64], acts: &[Vec<f64>], gout: &[f64], g: &mut [f64]) {
731 let nl = self.sizes.len() - 1;
732 let mut delta = gout.to_vec();
733 for l in (0..nl).rev() {
734 let (ni, no) = (self.sizes[l], self.sizes[l + 1]);
735 let (wo, bo) = self.off[l];
736 let a = &acts[l];
737 let mut dprev = vec![0.0; ni];
738 for o in 0..no {
739 let d = delta[o];
740 if d == 0.0 {
741 continue;
742 }
743 g[bo + o] += d;
744 let row = wo + o * ni;
745 for i in 0..ni {
746 g[row + i] += d * a[i];
747 dprev[i] += d * th[row + i];
748 }
749 }
750 if l > 0 {
751 for i in 0..ni {
752 if acts[l][i] <= 0.0 {
753 dprev[i] = 0.0;
754 }
755 }
756 }
757 delta = dprev;
758 }
759 }
760}
761
762#[inline]
763fn sigm(z: f64) -> f64 {
764 1.0 / (1.0 + (-z).exp())
765}
766
767fn nll_grad(o: &[f64], r: i32) -> (f64, [f64; NO]) {
769 let pi = sigm(o[0]);
770 let mut d = [0.0; 2];
771 let mut dmu = [0.0; 2];
772 let mut dls = [0.0; 2];
773 for c in 0..2 {
774 let mu = o[1 + c];
775 let ls = o[3 + c].clamp(-20.0, 40.0);
776 let s = ls.exp2();
777 let (za, zb) = ((r as f64 + 0.5 - mu) / s, (r as f64 - 0.5 - mu) / s);
778 let (a, b) = (sigm(za), sigm(zb));
779 d[c] = a - b;
780 let (ga, gb) = (a * (1.0 - a), b * (1.0 - b));
781 dmu[c] = -(ga - gb) / s;
782 dls[c] = -(ga * za - gb * zb) * std::f64::consts::LN_2;
783 }
784 let p = (pi * d[0] + (1.0 - pi) * d[1]).max(1e-12);
785 let g = [
786 -(d[0] - d[1]) * pi * (1.0 - pi) / p,
787 -pi * dmu[0] / p,
788 -(1.0 - pi) * dmu[1] / p,
789 -pi * dls[0] / p,
790 -(1.0 - pi) * dls[1] / p,
791 ];
792 (-p.ln(), g)
793}
794
795fn lcg(s: &mut u64) -> u64 {
796 *s = s
797 .wrapping_mul(6364136223846793005)
798 .wrapping_add(1442695040888963407);
799 *s >> 11
800}
801
802pub fn train(samples: &[Sample], cfg: &TrainConfig) -> Net {
805 let mut sizes = vec![NF];
806 sizes.extend(&cfg.hidden);
807 sizes.push(NO);
808 let fnet = FNet::new(sizes.clone());
809 let mut seed = cfg.seed;
810 let mut th = vec![0.0f64; fnet.n];
811 for l in 0..sizes.len() - 1 {
812 let (wo, bo) = fnet.off[l];
813 let scale = (6.0 / sizes[l] as f64).sqrt() * 0.5;
814 for v in &mut th[wo..bo] {
815 *v = ((lcg(&mut seed) % 2_000_001) as f64 / 1_000_000.0 - 1.0) * scale;
816 }
817 }
818 let n = samples.len();
819 let mean_abs = samples
820 .iter()
821 .map(|s| s.r.unsigned_abs() as f64)
822 .sum::<f64>()
823 / n.max(1) as f64;
824 let (_, bo) = fnet.off[sizes.len() - 2];
825 th[bo + 3] = -1.5;
826 th[bo + 4] = (mean_abs + 0.5).log2();
827 if n == 0 {
828 return quantize_net(&fnet, &th, cfg.apm);
829 }
830 let mut adam = tang_optim::Adam::new(cfg.lr);
831 let mut params = tang_la::DVec::from_vec(th);
832 let mut idx: Vec<usize> = (0..n).collect();
833 let m = cfg.per_epoch.min(n);
834 let steps_total = cfg.epochs * m.div_ceil(cfg.batch);
835 let mut step = 0usize;
836 for _ in 0..cfg.epochs {
837 for i in (1..n).rev() {
838 let j = (lcg(&mut seed) % (i as u64 + 1)) as usize;
839 idx.swap(i, j);
840 }
841 for chunk in idx[..m].chunks(cfg.batch) {
842 let th = params.as_slice().to_vec();
843 let mut g = vec![0.0; fnet.n];
844 for &i in chunk {
845 let x = samples[i].x.map(|v| v as f64 / 256.0);
846 let acts = fnet.forward(&th, &x);
847 let (_, go) = nll_grad(acts.last().unwrap(), samples[i].r);
848 fnet.backward(&th, &acts, &go, &mut g);
849 }
850 let inv = 1.0 / chunk.len() as f64;
851 for v in g.iter_mut() {
852 *v *= inv;
853 }
854 let t = step as f64 / steps_total.max(1) as f64;
855 adam.lr = cfg.lr * (0.1 + 0.45 * (1.0 + (std::f64::consts::PI * t).cos()));
856 adam.step(&mut params, &tang_la::DVec::from_vec(g));
857 step += 1;
858 }
859 }
860 quantize_net(&fnet, params.as_slice(), cfg.apm)
861}
862
863fn quantize_net(f: &FNet, th: &[f64], apm: bool) -> Net {
864 let mut wq = Vec::new();
865 let mut bq = Vec::new();
866 for l in 0..f.sizes.len() - 1 {
867 let (wo, bo) = f.off[l];
868 let no = f.sizes[l + 1];
869 wq.push(
870 th[wo..bo]
871 .iter()
872 .map(|v| (v * 256.0).round().clamp(-1e9, 1e9) as i32)
873 .collect(),
874 );
875 bq.push(
876 th[bo..bo + no]
877 .iter()
878 .map(|v| (v * 65536.0).round().clamp(-1e9, 1e9) as i32)
879 .collect(),
880 );
881 }
882 Net {
883 sizes: f.sizes.clone(),
884 wq,
885 bq,
886 apm,
887 }
888}
889
890pub fn mean_bits_float(net: &Net, samples: &[Sample]) -> f64 {
894 let f = FNet::new(net.sizes.clone());
895 let mut th = vec![0.0; f.n];
896 for l in 0..net.sizes.len() - 1 {
897 let (wo, bo) = f.off[l];
898 for (i, &v) in net.wq[l].iter().enumerate() {
899 th[wo + i] = v as f64 / 256.0;
900 }
901 for (i, &v) in net.bq[l].iter().enumerate() {
902 th[bo + i] = v as f64 / 65536.0;
903 }
904 }
905 let s: f64 = samples
906 .iter()
907 .map(|s| {
908 nll_grad(
909 f.forward(&th, &s.x.map(|v| v as f64 / 256.0))
910 .last()
911 .unwrap(),
912 s.r,
913 )
914 .0
915 })
916 .sum();
917 s / samples.len().max(1) as f64 / std::f64::consts::LN_2
918}
919
920#[cfg(test)]
921mod tests {
922 use super::*;
923 use crate::coefcode::{code_bands, fit_predictors};
924 use crate::wavelet::{forward_97, MAX_LEVELS};
925
926 #[test]
927 fn lg_is_monotone_and_exact_at_powers() {
928 assert_eq!(lg_q8(0), 0);
929 assert_eq!(lg_q8(1), 256);
930 assert_eq!(lg_q8(3), 512);
931 let mut last = -1;
932 for v in 0..5000 {
933 let l = lg_q8(v);
934 assert!(l >= last);
935 last = l;
936 }
937 }
938
939 #[test]
940 fn tables_are_sane() {
941 assert_eq!(sigmoid_q(0), TOT / 2);
942 assert!(sigmoid_q(ZMAX) > TOT - 4);
943 assert_eq!(inv_scale_q16(0), 65536);
944 assert_eq!(inv_scale_q16(256), 32768);
945 assert_eq!(inv_scale_q16(-256), 131072);
946 }
947
948 #[test]
949 fn cdf_monotone_and_saturates() {
950 let mx = Mix::from_out(&[100, -300, 900, -200, 700]);
951 let mut last = 0;
952 for m in -2000..2000 {
953 let c = mx.cdf(m);
954 assert!(c >= last && c <= TOT);
955 last = c;
956 }
957 assert!(mx.cdf(-1_000_000) < 1000 && mx.cdf(1_000_000) > TOT - 1000);
958 }
959
960 #[test]
961 fn gradient_matches_finite_differences() {
962 let o = [0.3, -0.7, 2.1, 0.4, 1.9];
963 for r in [-5, 0, 1, 7] {
964 let (_, g) = nll_grad(&o, r);
965 for k in 0..NO {
966 let mut a = o;
967 let mut b = o;
968 a[k] += 1e-6;
969 b[k] -= 1e-6;
970 let fd = (nll_grad(&a, r).0 - nll_grad(&b, r).0) / 2e-6;
971 assert!(
972 (fd - g[k]).abs() < 1e-4 * (1.0 + fd.abs()),
973 "r={r} k={k}: {fd} vs {}",
974 g[k]
975 );
976 }
977 }
978 }
979
980 #[test]
981 fn overfit_model_round_trips_bit_exact() {
982 let d = [10, 24, 30];
983 let p = Pyramid::new(d, MAX_LEVELS);
984 let mut x: Vec<f64> = (0..p.len())
985 .map(|i| ((i as f64) * 0.021).sin() * 50.0 + ((i * 7) % 13) as f64)
986 .collect();
987 forward_97(&p, &mut x);
988 let mut q: Vec<i32> = x.iter().map(|v| (v / 0.9).round() as i32).collect();
989 q[3] = 2_000_000; let preds = fit_predictors(&p, &q);
991 let cfg = TrainConfig {
992 hidden: vec![8],
993 epochs: 2,
994 per_epoch: 4000,
995 batch: 128,
996 lr: 5e-3,
997 apm: true,
998 seed: 3,
999 };
1000 let mut w = ByteWriter::new();
1001 let mut m = LearnedSpec::Overfit(cfg.clone()).prepare(&p, &q, &preds, &mut w);
1002 let mut e = Encoder::new();
1003 let mut qq = q.clone();
1004 code_bands(&p, &mut qq, &preds, &mut e, &mut m);
1005 let bytes = e.finish();
1006 let mut r = ByteReader::new(&w.buf);
1007 let tag = r.u8().unwrap();
1008 let mut m2 = read_model(tag, &mut r).unwrap();
1009 let mut d2 = Decoder::new(&bytes);
1010 let mut out = vec![0i32; q.len()];
1011 code_bands(&p, &mut out, &preds, &mut d2, &mut m2);
1012 assert_eq!(out, q);
1013 let mut w2 = ByteWriter::new();
1014 LearnedSpec::Overfit(cfg).prepare(&p, &q, &preds, &mut w2);
1015 assert_eq!(w.buf, w2.buf, "deterministic training");
1016 }
1017}