use crate::config::QwenConfig;
use crate::kernels::*;
use crate::model::{LayerCache, Lin, Quant, Table, linear};
use crate::qwen::{Attn, Ffn, Loader, QwenModel, QwenSession};
use crate::safetensors::ModelFiles;
use crate::sampler::Sampler;
use anyhow::{Result, bail};
pub struct DraftHead<'a> {
head: Lin<'a>, d2t: Vec<u32>,
}
impl<'a> DraftHead<'a> {
pub fn new(head: Lin<'a>, d2t: Vec<u32>, vocab_size: usize) -> Result<Self> {
if d2t.is_empty() {
bail!("empty draft vocabulary");
}
if let Some(&bad) = d2t.iter().find(|&&t| t as usize >= vocab_size) {
bail!("draft token map entry {bad} is outside the target vocabulary ({vocab_size})");
}
Ok(Self { head, d2t })
}
pub fn load(files: &'a ModelFiles, name: &str, d2t: Vec<u32>, cfg: &QwenConfig, quant: Quant) -> Result<Self> {
let quant = Quant::stored(files).unwrap_or(quant);
let head = crate::model::load_lin(files, name, d2t.len(), cfg.hidden_size, quant)?;
Self::new(head, d2t, cfg.vocab_size)
}
pub fn vocab(&self) -> usize {
self.d2t.len()
}
}
pub struct QwenMtp<'a> {
fc: Lin<'a>, norm_emb: Vec<f32>,
norm_hidden: Vec<f32>,
attn: Attn<'a>,
ffn: Ffn<'a>,
ln_in: Vec<f32>,
ln_post: Vec<f32>,
norm: Vec<f32>,
head: Option<DraftHead<'a>>,
embed: Option<Table<'a>>,
hidden: usize,
stream: Option<crate::stream::StreamHook>,
}
pub struct MtpSession {
kv: LayerCache,
pub(crate) spans: Vec<crate::qwen::VisionSpan>,
}
impl MtpSession {
pub fn len(&self) -> usize {
self.kv.len
}
pub fn is_empty(&self) -> bool {
self.kv.len == 0
}
pub fn truncate(&mut self, len: usize) {
self.kv.truncate(len);
self.spans.retain(|s| s.start < len);
}
}
pub fn mtp_prefix(files: &ModelFiles) -> Option<&'static str> {
let has = |n: &str| files.has(n) || files.has(&format!("{n}.q8")) || files.has(&format!("{n}.q4"));
["mtp.", "model.mtp."].into_iter().find(|p| has(&format!("{p}fc.weight")))
}
impl<'a> QwenMtp<'a> {
pub fn load(cfg: &QwenConfig, files: &'a ModelFiles, quant: Quant) -> Result<Self> {
if cfg.mtp_num_hidden_layers > 1 {
bail!("{} MTP layers: only one is supported", cfg.mtp_num_hidden_layers);
}
let Some(p) = mtp_prefix(files) else {
bail!("no MTP weights (mtp.fc.weight) in this checkpoint");
};
let quant = Quant::stored(files).unwrap_or(quant);
let ld = Loader { files, cfg, quant };
let h = cfg.hidden_size;
let lp = format!("{p}layers.0.");
Ok(Self {
fc: ld.lin(&format!("{p}fc.weight"), h, 2 * h)?,
norm_emb: ld.norm(&format!("{p}pre_fc_norm_embedding.weight"), h)?,
norm_hidden: ld.norm(&format!("{p}pre_fc_norm_hidden.weight"), h)?,
attn: ld.attn(&format!("{lp}self_attn."))?,
ffn: ld.ffn(&format!("{lp}mlp."))?,
ln_in: ld.norm(&format!("{lp}input_layernorm.weight"), h)?,
ln_post: ld.norm(&format!("{lp}post_attention_layernorm.weight"), h)?,
norm: ld.norm(&format!("{p}norm.weight"), h)?,
head: None,
embed: None,
hidden: h,
stream: None,
})
}
fn unit_lins(&self) -> Vec<(&'static str, Vec<&Lin<'a>>)> {
let mut lins = vec![&self.fc];
lins.extend(self.attn.lins());
lins.extend(self.ffn.lins());
let mut v = vec![("mtp", lins)];
if let Some(d) = &self.head {
v.push(("draft_head", vec![&d.head]));
}
v
}
pub fn stream_units(&self, draft_tokens: usize) -> Vec<crate::stream::UnitSpec> {
let uses = draft_tokens.max(1) as f64 + 0.5;
let mut lins = vec![&self.fc];
lins.extend(self.attn.lins());
lins.extend(self.ffn.dense_lins());
let mut v = vec![crate::stream::UnitSpec {
name: "mtp".into(),
ranges: lins.iter().flat_map(|l| l.mem_ranges()).collect(),
uses,
parts: self.ffn.expert_ranges(),
}];
if let Some(d) = &self.head {
v.push(crate::stream::UnitSpec {
name: "draft_head".into(),
ranges: d.head.mem_ranges(),
uses,
parts: Vec::new(),
});
}
v
}
pub fn mark_streamed(&self, streamed: &[bool]) {
for ((_, lins), &s) in self.unit_lins().iter().zip(streamed) {
for l in lins {
l.set_transient_device(s);
}
}
for l in self.ffn.expert_lins().into_iter().flatten() {
l.set_transient_device(true);
}
}
pub fn set_stream(&mut self, hook: Option<crate::stream::StreamHook>) {
self.stream = hook;
}
pub fn attach_sidecar(&mut self, cfg: &QwenConfig, files: &'a ModelFiles, quant: Quant) -> Result<bool> {
if files.has("model.embed_tokens.weight") {
self.embed = Some(Table::load(
files,
"model.embed_tokens.weight",
cfg.vocab_size,
cfg.hidden_size,
quant,
)?);
}
if !files.has("mtp.d2t") {
return Ok(false);
}
let d2t: Vec<u32> = files
.bytes("mtp.d2t")?
.as_chunks::<4>()
.0
.iter()
.map(|c| i32::from_le_bytes(*c) as u32)
.collect();
self.head = Some(DraftHead::load(files, "lm_head.weight", d2t, cfg, quant)?);
Ok(true)
}
pub fn set_draft_head(&mut self, head: Option<DraftHead<'a>>) {
self.head = head;
}
pub fn draft_head(&self) -> Option<&DraftHead<'a>> {
self.head.as_ref()
}
pub fn new_session(&self, cfg: &QwenConfig) -> MtpSession {
MtpSession {
kv: LayerCache::new(cfg.num_key_value_heads, cfg.head_dim),
spans: Vec::new(),
}
}
pub fn step_bytes(&self, model: &QwenModel) -> usize {
self.fc.bytes()
+ self.attn.bytes()
+ self.ffn.streamed_bytes()
+ self.head.as_ref().map_or(model.lm_head.bytes(), |d| d.head.bytes())
}
pub fn forward(&self, model: &QwenModel, sess: &mut MtpSession, hidden: &[f32], tokens: &[u32]) -> Vec<f32> {
let t = tokens.len();
let h = self.hidden;
assert!(t > 0 && hidden.len() == t * h);
let cfg = &model.cfg;
let eps = cfg.rms_norm_eps as f32;
let mut cat = vec![0f32; t * 2 * h];
let table = self.embed.as_ref().unwrap_or(&model.embed);
if let Some(s) = self.stream.as_ref().or(model.stream.as_ref()) {
let rows: Vec<(usize, usize)> = tokens.iter().flat_map(|&tk| table.row_ranges(tk as usize, h)).collect();
s.need_ranges(&rows);
}
let mut e = vec![0f32; h];
for (i, row) in cat.chunks_mut(2 * h).enumerate() {
self.embed.as_ref().unwrap_or(&model.embed).row(tokens[i] as usize, &mut e);
let (re, rh) = row.split_at_mut(h);
rmsnorm_f32w(&e, &self.norm_emb, eps, re);
rmsnorm_f32w(&hidden[i * h..(i + 1) * h], &self.norm_hidden, eps, rh);
}
let mut x = vec![0f32; t * h];
let _unit = crate::stream::enter(&self.stream, 0);
linear(&self.fc, h, 2 * h, &cat, t, &mut x);
let start = sess.kv.len;
let (cos, sin) = model.rope_cs(&sess.spans, start, t);
let mut hn = vec![0f32; t * h];
let mut y = vec![0f32; t * h];
rmsnorm_f32w_rows(&x, h, &self.ln_in, eps, &mut hn);
model.attn_forward(&self.attn, &mut sess.kv, &hn, t, start, &cos, &sin, &mut y);
add_inplace(&mut x, &y);
rmsnorm_f32w_rows(&x, h, &self.ln_post, eps, &mut hn);
let need = |ids: &[usize]| {
if let Some(s) = &self.stream {
s.need_parts(0, ids)
}
};
self.ffn
.forward(&hn, t, h, &mut y, self.stream.as_ref().map(|_| &need as &dyn Fn(&[usize])));
add_inplace(&mut x, &y);
rmsnorm_f32w_rows(&x, h, &self.norm, eps, &mut hn);
hn
}
pub fn draft_distribution(&self, model: &QwenModel, out: &[f32], sampler: &Sampler) -> Vec<(u32, f32)> {
let h = self.hidden;
match &self.head {
None => sampler.distribution(&model.logits(out, 1)),
Some(d) => {
let mut logits = vec![0f32; d.d2t.len()];
let unit = crate::stream::enter(&self.stream, 1);
linear(&d.head, d.d2t.len(), h, out, 1, &mut logits);
drop(unit);
sampler
.distribution(&logits)
.into_iter()
.map(|(i, p)| (d.d2t[i as usize], p))
.collect()
}
}
}
}
impl<'a> QwenModel<'a> {
pub fn load_mtp<'b>(&self, files: &'b ModelFiles) -> Result<QwenMtp<'b>> {
QwenMtp::load(&self.cfg, files, self.quant)
}
}
pub const MAX_EXACT_DRAFT: usize = 6;
pub fn max_exact_draft() -> usize {
crate::kernels::decode_max_tokens() - 1
}
pub const MAX_DRAFT: usize = 14;
#[derive(Clone, Debug)]
pub struct MtpOptions {
pub draft_tokens: usize,
pub adaptive: bool,
pub stop: Vec<u32>,
pub cost_aware: bool,
pub exact: bool,
}
impl Default for MtpOptions {
fn default() -> Self {
Self {
draft_tokens: 3,
adaptive: false,
stop: Vec::new(),
cost_aware: false,
exact: false,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DraftCosts {
pub round_secs: Vec<(f64, usize)>,
pub step_secs: f64,
pub tried: Vec<usize>,
pub ok: Vec<usize>,
}
impl DraftCosts {
fn record(&mut self, k: usize, secs: f64, accepted: usize, step_secs: Option<f64>) {
let ema = |old: f64, n: usize, v: f64| if n == 0 { v } else { 0.7 * old + 0.3 * v };
if self.round_secs.len() <= k {
self.round_secs.resize(k + 1, (0.0, 0));
}
let (t, n) = self.round_secs[k];
self.round_secs[k] = (ema(t, n, secs), n + 1);
if let Some(s) = step_secs {
self.step_secs = ema(self.step_secs, (self.step_secs > 0.0) as usize, s);
}
if self.tried.len() < k {
self.tried.resize(k, 0);
self.ok.resize(k, 0);
}
for j in 0..k.min(accepted + 1) {
self.tried[j] += 1;
self.ok[j] += (j < accepted) as usize;
}
}
fn round_time(&self, k: usize) -> Option<f64> {
if let Some(&(t, n)) = self.round_secs.get(k)
&& n > 0
{
return Some(t);
}
let (k0, &(t0, _)) = self
.round_secs
.iter()
.enumerate()
.filter(|(_, e)| e.1 > 0)
.min_by_key(|(j, _)| j.abs_diff(k))?;
Some((t0 + (k as f64 - k0 as f64) * self.step_secs).max(1e-6))
}
fn expected_tokens(&self, k: usize) -> f64 {
let mut e = 1.0;
let mut p = 1.0;
for j in 0..k {
let (t, o) = (self.tried.get(j).copied().unwrap_or(0), self.ok.get(j).copied().unwrap_or(0));
p *= (o as f64 + 1.0) / (t as f64 + 2.0);
e += p;
}
e
}
pub fn best(&self, k_max: usize) -> Option<usize> {
let mut best: Option<(usize, f64)> = None;
for k in 1..=k_max.max(1) {
let rate = self.expected_tokens(k) / self.round_time(k)?;
if best.is_none_or(|b| rate > b.1) {
best = Some((k, rate));
}
}
best.map(|b| b.0)
}
}
#[derive(Clone, Debug, Default)]
pub struct MtpStats {
pub rounds: usize,
pub drafted: usize,
pub accepted: usize,
pub emitted: usize,
pub per_round: Vec<usize>,
pub draft_secs: f64,
pub verify_secs: f64,
pub costs: DraftCosts,
}
impl MtpStats {
pub fn acceptance(&self) -> f64 {
self.accepted as f64 / self.drafted.max(1) as f64
}
pub fn tokens_per_round(&self) -> f64 {
self.emitted as f64 / self.rounds.max(1) as f64
}
}
pub struct MtpDecoder<'m> {
model: &'m QwenModel<'m>,
mtp: &'m QwenMtp<'m>,
pub sess: QwenSession,
msess: MtpSession,
pub opts: MtpOptions,
pend_h: Vec<f32>,
pend_t: Vec<u32>,
next: Option<u32>,
k_cur: usize,
pub stats: MtpStats,
}
impl<'m> MtpDecoder<'m> {
pub fn new(model: &'m QwenModel<'m>, mtp: &'m QwenMtp<'m>, opts: MtpOptions) -> Self {
let opts = MtpOptions {
draft_tokens: if opts.cost_aware {
opts.draft_tokens.min(MAX_DRAFT)
} else {
opts.draft_tokens
},
..opts
};
if opts.exact && opts.draft_tokens + 1 > crate::kernels::decode_max_tokens() {
crate::kernels::set_decode_max_tokens(opts.draft_tokens + 1);
}
let k_cur = if opts.adaptive || opts.cost_aware {
opts.draft_tokens.clamp(1, 3)
} else {
opts.draft_tokens
};
Self {
model,
mtp,
sess: model.new_session(),
msess: mtp.new_session(&model.cfg),
opts,
pend_h: Vec::new(),
pend_t: Vec::new(),
next: None,
k_cur,
stats: MtpStats::default(),
}
}
pub fn model(&self) -> &'m QwenModel<'m> {
self.model
}
pub fn mtp(&self) -> &'m QwenMtp<'m> {
self.mtp
}
pub fn draft_len(&self) -> usize {
self.k_cur
}
fn flush(&mut self) -> Option<Vec<f32>> {
let n = self.pend_t.len();
if n == 0 {
return None;
}
let h = self.model.cfg.hidden_size;
let out = self.mtp.forward(self.model, &mut self.msess, &self.pend_h[..n * h], &self.pend_t);
self.pend_h.drain(..n * h);
self.pend_t.clear();
Some(out[(n - 1) * h..].to_vec())
}
pub fn prefill(&mut self, tokens: &[u32], chunk: usize) -> Vec<f32> {
self.prefill_mm(tokens, chunk, &[])
}
pub fn prefill_mm(&mut self, tokens: &[u32], chunk: usize, overrides: &[(usize, Vec<f32>)]) -> Vec<f32> {
assert!(!tokens.is_empty());
let h = self.model.cfg.hidden_size;
if self.next.take().is_some() {
self.pend_t.pop();
}
self.msess.spans = self.sess.spans.clone();
let mut last = Vec::new();
let mut start = 0;
for c in tokens.chunks(chunk.max(1)) {
if self.pend_h.len() > self.pend_t.len() * h {
self.pend_t.push(c[0]);
}
let end = start + c.len();
let local: Vec<(usize, &[f32])> = overrides
.iter()
.filter(|(p, _)| *p >= start && *p < end)
.map(|(p, e)| (p - start, e.as_slice()))
.collect();
start = end;
let hs = self.model.forward_hidden_mm(&mut self.sess, c, &local);
self.pend_h.extend_from_slice(&hs);
self.pend_t.extend_from_slice(&c[1..]);
self.flush();
last = hs[hs.len() - h..].to_vec();
}
self.model.logits(&last, 1)
}
pub fn push(&mut self, token: u32) {
assert!(self.next.is_none(), "push follows prefill; step commits its own tokens");
assert_eq!(self.pend_h.len(), (self.pend_t.len() + 1) * self.model.cfg.hidden_size);
self.pend_t.push(token);
self.next = Some(token);
}
pub fn step(&mut self, sampler: &mut Sampler, max_new: usize) -> Vec<u32> {
let next = self.next.expect("push a first token before step");
let (model, mtp) = (self.model, self.mtp);
let h = model.cfg.hidden_size;
let v = model.cfg.vocab_size;
let n = self.sess.pos; let k = self.k_cur.min(max_new.max(1) - 1);
let t_round = crate::clock::Instant::now();
let mut step_secs = None;
let mut drafts = Vec::with_capacity(k);
let mut qs: Vec<Vec<(u32, f32)>> = Vec::with_capacity(k);
if k > 0 {
let mut g = self.flush().expect("pending MTP row");
debug_assert_eq!(self.msess.len(), n);
for j in 0..k {
let q = mtp.draft_distribution(model, &g, sampler);
let d = if sampler.greedy() { q[0].0 } else { sampler.sample_from(&q) };
drafts.push(d);
qs.push(q);
if j + 1 < k {
let t = crate::clock::Instant::now();
g = mtp.forward(model, &mut self.msess, &g, &[d]);
if j == 0 {
step_secs = Some(t.elapsed().as_secs_f64());
}
}
}
}
let draft_secs = t_round.elapsed().as_secs_f64();
let mut seq = Vec::with_capacity(k + 1);
seq.push(next);
seq.extend_from_slice(&drafts);
let check = if std::env::var_os("OXINFER_CHECK_SPEC").is_some() {
Some(self.sess.clone())
} else {
None
};
let hs = if k > 0 {
model.forward_hidden_rec(&mut self.sess, &seq)
} else {
model.forward_hidden_mm(&mut self.sess, &seq, &[])
};
let logits = model.logits(&hs, seq.len());
let verify_secs = t_round.elapsed().as_secs_f64() - draft_secs;
if let Some(mut s1) = check {
for (i, &tk) in seq.iter().enumerate() {
let l1 = model.forward(&mut s1, &[tk]);
let lk = &logits[i * v..(i + 1) * v];
let diff = l1.iter().zip(lk).map(|(a, b)| (a - b).abs()).fold(0f32, f32::max);
if diff > 0.0 {
let (a1, ak) = (crate::sampler::argmax(&l1), crate::sampler::argmax(lk));
eprintln!(
"[check: pos {} of {}: max logit diff {diff:e}, argmax t=1 {a1} vs t={} {ak}]",
n + i,
seq.len(),
seq.len()
);
}
}
}
let mut out = Vec::with_capacity(k + 1);
let mut accepted = 0;
for (j, (&d, q)) in drafts.iter().zip(&qs).enumerate() {
let p = sampler.distribution(&logits[j * v..(j + 1) * v]);
match speculative_accept(sampler, &p, q, d) {
None => {
out.push(d);
accepted += 1;
if self.opts.stop.contains(&d) {
break;
}
}
Some(t) => {
out.push(t);
break;
}
}
}
if accepted == drafts.len() && !out.last().is_some_and(|t| self.opts.stop.contains(t)) {
out.push(sampler.sample(&logits[k * v..(k + 1) * v]));
}
if let Some(i) = out.iter().position(|t| self.opts.stop.contains(t)) {
out.truncate(i + 1);
}
let c = out.len();
self.sess.rewind(n + c).expect("rewind inside the recorded span");
self.msess.truncate(n);
self.pend_h.extend_from_slice(&hs[..c * h]);
self.pend_t.extend_from_slice(&out);
self.next = out.last().copied();
self.stats.rounds += 1;
self.stats.drafted += k;
self.stats.accepted += accepted;
self.stats.emitted += c;
if self.stats.per_round.len() <= accepted {
self.stats.per_round.resize(accepted + 1, 0);
}
self.stats.per_round[accepted] += 1;
self.stats.draft_secs += draft_secs;
self.stats.verify_secs += verify_secs;
if k == self.k_cur && k > 0 {
let step = step_secs.or(Some(draft_secs / k as f64));
self.stats.costs.record(k, draft_secs + verify_secs, accepted, step);
}
if self.opts.cost_aware {
if let Some(b) = self.stats.costs.best(self.opts.draft_tokens.max(1)) {
self.k_cur = b;
}
} else if self.opts.adaptive && k == self.k_cur {
if accepted == k {
self.k_cur = (self.k_cur + 1).min(self.opts.draft_tokens.max(1));
} else if accepted == 0 {
self.k_cur = self.k_cur.saturating_sub(1).max(1);
}
}
out
}
}
pub fn speculative_accept(sampler: &mut Sampler, p: &[(u32, f32)], q: &[(u32, f32)], d: u32) -> Option<u32> {
let pd = p.iter().find(|e| e.0 == d).map_or(0.0, |e| e.1);
let qd = q.iter().find(|e| e.0 == d).map_or(0.0, |e| e.1);
if pd >= qd || sampler.uniform() < pd / qd {
return None;
}
let qm: std::collections::HashMap<u32, f32> = q.iter().copied().collect();
let r: Vec<(u32, f32)> = p
.iter()
.map(|&(t, pt)| (t, (pt - qm.get(&t).copied().unwrap_or(0.0)).max(0.0)))
.filter(|e| e.1 > 0.0)
.collect();
Some(sampler.sample_from(if r.is_empty() { p } else { &r }))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn speculative_sampling_preserves_target() {
let p = [(0u32, 0.5f32), (1, 0.3), (2, 0.2)];
let q = [(1u32, 0.5f32), (3, 0.3), (0, 0.2)];
let mut s = Sampler::new(1.0, 0, 1.0, 7);
let n = 400_000;
let mut hist = [0usize; 4];
for _ in 0..n {
let d = s.sample_from(&q);
let t = speculative_accept(&mut s, &p, &q, d).unwrap_or(d);
hist[t as usize] += 1;
}
for &(t, pt) in &p {
let f = hist[t as usize] as f32 / n as f32;
assert!((f - pt).abs() < 0.005, "token {t}: {f} vs {pt}");
}
assert_eq!(hist[3], 0);
}
#[test]
fn small_batch_matmuls_are_batch_invariant() {
use crate::quant::{quantize_bf16, quantize_q4};
let (n_out, n_in) = (70, 512);
let mut seed = 1u64;
let mut rnd = || {
seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
((seed >> 33) as f32 / (1u64 << 31) as f32) * 2.0 - 1.0
};
let w: Vec<u16> = (0..n_out * n_in).map(|_| (rnd().to_bits() >> 16) as u16).collect();
let x: Vec<f32> = (0..7 * n_in).map(|_| rnd()).collect();
let lins = [
("bf16", Lin::bf16(&w)),
("q8", Lin::q8(quantize_bf16(&w, n_out, n_in))),
("q4", Lin::q4(quantize_q4(&w, n_out, n_in))),
];
for (name, l) in &lins {
let mut y1 = vec![0f32; 7 * n_out];
for (xr, yr) in x.chunks(n_in).zip(y1.chunks_mut(n_out)) {
linear(l, n_out, n_in, xr, 1, yr);
}
for t in 2..=7 {
let mut yt = vec![0f32; t * n_out];
linear(l, n_out, n_in, &x[..t * n_in], t, &mut yt);
let same = yt.iter().zip(&y1).all(|(a, b)| a.to_bits() == b.to_bits());
assert!(same, "{name}: t={t} differs from one-token matmuls");
}
}
}
#[test]
fn long_verification_is_batch_invariant() {
use crate::ggml::{GgmlType, GgufMatrix, random_blocks};
use crate::kernels::matmul_quant_multi_at;
let (n_out, n_in) = (21, 512);
let mut seed = 5u64;
let mut rnd = || {
seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
((seed >> 33) as f32 / (1u64 << 31) as f32) * 2.0 - 1.0
};
let x: Vec<f32> = (0..15 * n_in).map(|_| rnd()).collect();
let mut s2 = 9u64;
for kind in [GgmlType::Q4_K, GgmlType::Q6_K, GgmlType::Q2_K, GgmlType::IQ4_XS] {
let raw = random_blocks(kind, n_out * n_in / kind.block_size(), &mut s2);
let m = GgufMatrix::from_bytes(kind, &raw, n_out, n_in).unwrap();
let mut y1 = vec![0f32; 15 * n_out];
for (xr, yr) in x.chunks(n_in).zip(y1.chunks_mut(n_out)) {
matmul_quant_multi_at(&mut [(&m, yr)], xr, 1, 15);
}
for t in 8..=15 {
let mut yt = vec![0f32; t * n_out];
matmul_quant_multi_at(&mut [(&m, &mut yt[..])], &x[..t * n_in], t, 15);
let same = yt.iter().zip(&y1).all(|(a, b)| a.to_bits() == b.to_bits());
assert!(same, "{kind}: t={t} differs from one-token matmuls");
}
}
}
#[test]
fn cost_aware_policy_prefers_long_drafts_when_verification_is_flat() {
let mut seed = 3u64;
let mut accept = |k: usize| {
(0..k)
.take_while(|_| {
seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
(seed >> 33) % 10 < 7
})
.count()
};
let mut c = DraftCosts::default();
for round in 0..400 {
let k = 1 + round % 8;
c.record(k, 1.0 + 0.01 * k as f64, accept(k), Some(0.01));
}
assert!(c.best(8).unwrap() >= 6, "{:?}", c.best(8));
let mut c = DraftCosts::default();
for round in 0..400 {
let k = 1 + round % 8;
c.record(k, (k + 1) as f64, accept(k), Some(0.01));
}
assert_eq!(c.best(8), Some(1));
}
#[test]
fn greedy_accepts_only_argmax() {
let mut s = Sampler::new(0.0, 0, 1.0, 1);
let p = [(5u32, 1.0f32)];
assert_eq!(speculative_accept(&mut s, &p, &[(5, 1.0)], 5), None);
assert_eq!(speculative_accept(&mut s, &p, &[(4, 1.0)], 4), Some(5));
}
}