use crate::{Result, invalid};
pub const MAX_SPECULATIVE_DRAFT_TOKENS: usize = 8;
pub const MAX_SUFFIX_MATCH_TOKENS: usize = 64;
#[must_use]
pub fn mtp_first_decode_fits(batch: i32, catchup_rows: i32, anchors: i32) -> bool {
batch > 0
&& catchup_rows >= 0
&& anchors >= 0
&& i64::from(catchup_rows) + i64::from(anchors) <= i64::from(batch)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SuffixDraftConfig {
pub min_match_tokens: usize,
pub max_match_tokens: usize,
pub max_draft_tokens: usize,
}
impl Default for SuffixDraftConfig {
fn default() -> Self {
Self {
min_match_tokens: 2,
max_match_tokens: 16,
max_draft_tokens: 4,
}
}
}
impl SuffixDraftConfig {
pub fn new(
min_match_tokens: usize,
max_match_tokens: usize,
max_draft_tokens: usize,
) -> Result<Self> {
let config = Self {
min_match_tokens,
max_match_tokens,
max_draft_tokens,
};
config.validate()?;
Ok(config)
}
pub fn validate(&self) -> Result<()> {
if self.min_match_tokens == 0
|| self.min_match_tokens > self.max_match_tokens
|| self.max_match_tokens > MAX_SUFFIX_MATCH_TOKENS
|| self.max_draft_tokens == 0
|| self.max_draft_tokens > MAX_SPECULATIVE_DRAFT_TOKENS
{
return Err(invalid("invalid suffix speculative draft configuration"));
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct SpeculativeStats {
pub draft_rounds: usize,
pub draft_tokens_proposed: usize,
pub draft_tokens_accepted: usize,
pub target_evaluations: usize,
pub rollback_restores: usize,
}
impl SpeculativeStats {
#[must_use]
#[allow(
clippy::as_conversions,
clippy::cast_precision_loss,
reason = "Diagnostic acceptance ratio converts bounded token counts to f64."
)]
pub const fn acceptance_rate(&self) -> f64 {
if self.draft_tokens_proposed == 0 {
0.0
} else {
(self.draft_tokens_accepted as f64) / (self.draft_tokens_proposed as f64)
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct SuffixDrafter;
impl SuffixDrafter {
pub fn propose(
history: &[u32],
config: SuffixDraftConfig,
remaining_tokens: usize,
) -> Result<Vec<u32>> {
config.validate()?;
let limit = config.max_draft_tokens.min(remaining_tokens);
if limit == 0 || history.len() <= config.min_match_tokens {
return Ok(Vec::new());
}
let max_len = config.max_match_tokens.min(history.len() - 1);
for match_len in (config.min_match_tokens..=max_len).rev() {
let suffix_start = history.len() - match_len;
let suffix = &history[suffix_start..];
for candidate_start in (0..suffix_start).rev() {
if history[candidate_start..candidate_start + match_len] == *suffix {
let cont_start = candidate_start + match_len;
let available = (history.len() - cont_start).min(limit);
if available > 0 {
let mut draft = Vec::new();
draft.try_reserve_exact(available)?;
draft.extend_from_slice(&history[cont_start..cont_start + available]);
return Ok(draft);
}
}
}
}
Ok(Vec::new())
}
}
pub fn verify_stochastic_step(
target_probs: &[f64],
draft_probs: &[f64],
draft_token: u32,
accept_draw: f64,
residual_draw: f64,
) -> Result<(bool, u32)> {
if target_probs.is_empty()
|| target_probs.len() > 1_048_576
|| target_probs.len() != draft_probs.len()
|| !accept_draw.is_finite()
|| !(0.0..1.0).contains(&accept_draw)
|| !residual_draw.is_finite()
|| !(0.0..1.0).contains(&residual_draw)
{
return Err(invalid(
"stochastic speculative verification requires matching bounded distributions and draws in [0, 1)",
));
}
let draft_index = usize::try_from(draft_token)?;
if draft_index >= target_probs.len() {
return Err(invalid(
"draft token is outside the verification vocabulary",
));
}
let mut target_sum = 0.0_f64;
let mut draft_sum = 0.0_f64;
for (&p, &q) in target_probs.iter().zip(draft_probs) {
if !p.is_finite() || p < 0.0 || !q.is_finite() || q < 0.0 {
return Err(invalid(
"speculative verification probabilities must be finite and nonnegative",
));
}
target_sum += p;
draft_sum += q;
}
if !target_sum.is_finite() || target_sum <= 0.0 || !draft_sum.is_finite() || draft_sum <= 0.0 {
return Err(invalid(
"speculative verification distributions must have positive total mass",
));
}
let p_draft = target_probs[draft_index] / target_sum;
let q_draft = draft_probs[draft_index] / draft_sum;
let acceptance_ratio = if q_draft <= 0.0 {
if p_draft > 0.0 { 1.0 } else { 0.0 }
} else {
(p_draft / q_draft).min(1.0)
};
if accept_draw < acceptance_ratio {
return Ok((true, draft_token));
}
let mut residual_sum = 0.0_f64;
for (&p, &q) in target_probs.iter().zip(draft_probs) {
let p_norm = p / target_sum;
let q_norm = q / draft_sum;
let diff = (p_norm - q_norm).max(0.0);
residual_sum += diff;
}
if residual_sum <= 0.0 {
let target_threshold = residual_draw * target_sum;
let mut cumulative = 0.0_f64;
let mut selected = 0_u32;
for (index, &p) in target_probs.iter().enumerate() {
cumulative += p;
if p > 0.0 {
selected = u32::try_from(index)?;
}
if target_threshold < cumulative {
break;
}
}
return Ok((false, selected));
}
let threshold = residual_draw * residual_sum;
let mut cumulative = 0.0_f64;
let mut selected = 0_u32;
for (index, (&p, &q)) in target_probs.iter().zip(draft_probs).enumerate() {
let p_norm = p / target_sum;
let q_norm = q / draft_sum;
let diff = (p_norm - q_norm).max(0.0);
cumulative += diff;
if diff > 0.0 {
selected = u32::try_from(index)?;
}
if threshold < cumulative {
break;
}
}
Ok((false, selected))
}
pub const DFLASH2_DEFAULT_TAP_LAYERS: [usize; 5] = [2, 17, 32, 47, 63];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DFlash2DraftConfig {
pub tap_layers: [usize; 5],
pub block_tokens: usize,
pub drafter_layers: usize,
}
impl Default for DFlash2DraftConfig {
fn default() -> Self {
Self {
tap_layers: DFLASH2_DEFAULT_TAP_LAYERS,
block_tokens: 4,
drafter_layers: 5,
}
}
}
impl DFlash2DraftConfig {
pub fn new(
tap_layers: [usize; 5],
block_tokens: usize,
drafter_layers: usize,
target_layers: usize,
) -> Result<Self> {
let config = Self {
tap_layers,
block_tokens,
drafter_layers,
};
config.validate(target_layers)?;
Ok(config)
}
pub fn validate(&self, target_layers: usize) -> Result<()> {
if self.block_tokens == 0
|| self.block_tokens > MAX_SPECULATIVE_DRAFT_TOKENS
|| self.drafter_layers == 0
|| self.drafter_layers > 16
|| self.tap_layers.windows(2).any(|pair| pair[0] >= pair[1])
|| self.tap_layers[4] >= target_layers
{
return Err(invalid("invalid DFlash2 block-diffusion draft configuration"));
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct GdnVerifyStepTape {
pub keys: Vec<f32>,
pub queries: Vec<f32>,
pub values: Vec<f32>,
pub decays: Vec<f32>,
pub betas: Vec<f32>,
}
#[derive(Clone, Debug)]
pub struct GdnVerifyTape {
key_heads: usize,
value_heads: usize,
key_dim: usize,
value_dim: usize,
steps: Vec<GdnVerifyStepTape>,
}
impl GdnVerifyTape {
pub fn new(
key_heads: usize,
value_heads: usize,
key_dim: usize,
value_dim: usize,
max_steps: usize,
) -> Result<Self> {
if key_heads == 0
|| value_heads == 0
|| key_dim == 0
|| value_dim == 0
|| !value_heads.is_multiple_of(key_heads)
|| max_steps == 0
|| max_steps > MAX_SPECULATIVE_DRAFT_TOKENS
{
return Err(invalid("invalid GDN verification tape geometry"));
}
let mut steps = Vec::new();
steps.try_reserve_exact(max_steps)?;
Ok(Self {
key_heads,
value_heads,
key_dim,
value_dim,
steps,
})
}
#[must_use]
pub const fn len(&self) -> usize {
self.steps.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.steps.is_empty()
}
pub fn clear(&mut self) {
self.steps.clear();
}
pub fn record_step(
&mut self,
keys: &[f32],
queries: &[f32],
values: &[f32],
decays: &[f32],
betas: &[f32],
) -> Result<()> {
if self.steps.len() >= MAX_SPECULATIVE_DRAFT_TOKENS {
return Err(invalid("GDN verification tape exceeded max draft steps"));
}
let expected_kq = self
.key_heads
.checked_mul(self.key_dim)
.ok_or_else(|| invalid("GDN key dimension overflow"))?;
let expected_v = self
.value_heads
.checked_mul(self.value_dim)
.ok_or_else(|| invalid("GDN value dimension overflow"))?;
if keys.len() != expected_kq
|| queries.len() != expected_kq
|| values.len() != expected_v
|| decays.len() != self.value_heads
|| betas.len() != self.value_heads
|| !crate::kernels::is_finite_slice(keys)
|| !crate::kernels::is_finite_slice(queries)
|| !crate::kernels::is_finite_slice(values)
|| !crate::kernels::is_finite_slice(decays)
|| !crate::kernels::is_finite_slice(betas)
{
return Err(invalid("invalid or nonfinite GDN verification step slice"));
}
self.steps.push(GdnVerifyStepTape {
keys: keys.to_vec(),
queries: queries.to_vec(),
values: values.to_vec(),
decays: decays.to_vec(),
betas: betas.to_vec(),
});
Ok(())
}
pub fn replay_accepted_prefix(
&self,
base_state: &mut [f32],
accepted_steps: usize,
) -> Result<()> {
let expected_state = self
.value_heads
.checked_mul(self.value_dim)
.and_then(|rows| rows.checked_mul(self.key_dim))
.ok_or_else(|| invalid("GDN state dimension overflow"))?;
if accepted_steps > self.steps.len() || base_state.len() != expected_state {
return Err(invalid("invalid GDN verification tape replay bounds"));
}
let group = self.value_heads / self.key_heads;
let head_stride = self.value_dim * self.key_dim;
for step in &self.steps[..accepted_steps] {
for v_head in 0..self.value_heads {
let k_head = v_head / group;
let key = &step.keys[k_head * self.key_dim..(k_head + 1) * self.key_dim];
let query = &step.queries[k_head * self.key_dim..(k_head + 1) * self.key_dim];
let v_slice = &step.values[v_head * self.value_dim..(v_head + 1) * self.value_dim];
let decay = step.decays[v_head];
let beta = step.betas[v_head];
let state_head = &mut base_state[v_head * head_stride..(v_head + 1) * head_stride];
for (row_idx, &v_elem) in v_slice.iter().enumerate() {
let row_state =
&mut state_head[row_idx * self.key_dim..(row_idx + 1) * self.key_dim];
crate::kernels::pinned_gdn::step_row_in_place(
row_state, key, query, decay, v_elem, beta,
)?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{
DFLASH2_DEFAULT_TAP_LAYERS, DFlash2DraftConfig, GdnVerifyTape,
MAX_SPECULATIVE_DRAFT_TOKENS, SpeculativeStats, SuffixDraftConfig, SuffixDrafter,
mtp_first_decode_fits, verify_stochastic_step,
};
use crate::Result;
#[test]
fn mtp_bounds_check_handles_extremes_and_negatives() {
assert!(mtp_first_decode_fits(8, 4, 4));
assert!(!mtp_first_decode_fits(8, 5, 4));
assert!(!mtp_first_decode_fits(0, 0, 0));
assert!(!mtp_first_decode_fits(8, -1, 2));
assert!(!mtp_first_decode_fits(8, 2, -1));
assert!(!mtp_first_decode_fits(i32::MAX, i32::MAX, 1));
}
#[test]
fn suffix_drafter_prefers_longest_then_most_recent_match() -> Result<()> {
let config = SuffixDraftConfig::new(2, 8, 4)?;
let history = vec![10, 20, 30, 40, 50, 10, 20, 31, 41, 10, 20];
let draft = SuffixDrafter::propose(&history, config, 4)?;
assert_eq!(draft, vec![31, 41, 10, 20]);
let history_longer = vec![7, 10, 20, 99, 98, 8, 10, 20, 31, 41, 7, 10, 20];
let draft_longer = SuffixDrafter::propose(&history_longer, config, 3)?;
assert_eq!(draft_longer, vec![99, 98, 8]);
let capped = SuffixDrafter::propose(&history_longer, config, 1)?;
assert_eq!(capped, vec![99]);
assert_eq!(
SuffixDrafter::propose(&history_longer, config, 0)?,
Vec::<u32>::new()
);
assert!(SuffixDraftConfig::new(0, 4, 2).is_err());
assert!(SuffixDraftConfig::new(5, 4, 2).is_err());
assert!(SuffixDraftConfig::new(2, 4, MAX_SPECULATIVE_DRAFT_TOKENS + 1).is_err());
Ok(())
}
#[test]
fn leviathan_stochastic_verification_preserves_exact_residual_distribution() -> Result<()> {
let target = [0.2_f64, 0.5, 0.3];
let draft = [0.5_f64, 0.2, 0.3];
assert_eq!(
verify_stochastic_step(&target, &draft, 0, 0.39, 0.0)?,
(true, 0)
);
assert_eq!(
verify_stochastic_step(&target, &draft, 0, 0.41, 0.75)?,
(false, 1)
);
assert_eq!(
verify_stochastic_step(&target, &draft, 1, 0.999, 0.0)?,
(true, 1)
);
let stats = SpeculativeStats {
draft_rounds: 2,
draft_tokens_proposed: 4,
draft_tokens_accepted: 3,
target_evaluations: 5,
rollback_restores: 1,
};
assert!((stats.acceptance_rate() - 0.75).abs() <= 1e-12);
assert_eq!(SpeculativeStats::default().acceptance_rate(), 0.0);
Ok(())
}
#[test]
fn dflash2_draft_config_validates_tap_layers_and_block_bounds() -> Result<()> {
let cfg = DFlash2DraftConfig::default();
cfg.validate(64)?;
assert_eq!(cfg.tap_layers, DFLASH2_DEFAULT_TAP_LAYERS);
assert!(DFlash2DraftConfig::new([2, 17, 17, 47, 63], 4, 5, 64).is_err());
assert!(DFlash2DraftConfig::new([2, 17, 32, 47, 64], 4, 5, 64).is_err());
assert!(DFlash2DraftConfig::new(DFLASH2_DEFAULT_TAP_LAYERS, 0, 5, 64).is_err());
assert!(DFlash2DraftConfig::new(DFLASH2_DEFAULT_TAP_LAYERS, 9, 5, 64).is_err());
Ok(())
}
#[test]
fn gdn_verify_tape_replay_matches_sequential_step_zero_ulp() -> Result<()> {
let key_heads = 2;
let value_heads = 4;
let key_dim = 16;
let value_dim = 8;
let state_len = value_heads * value_dim * key_dim;
let mut sequential_state: Vec<f32> = (0..state_len)
.map(|i| (f32::from(u16::try_from(i).unwrap_or(0)) - 128.0) * 0.005)
.collect();
let mut replay_state = sequential_state.clone();
let mut tape = GdnVerifyTape::new(key_heads, value_heads, key_dim, value_dim, 4)?;
assert!(tape.is_empty());
for step_idx in 0..4_u8 {
let s = f32::from(step_idx) + 1.0;
let keys: Vec<f32> = (0..key_heads * key_dim)
.map(|i| f32::from(u8::try_from(i).unwrap_or(0)).mul_add(0.03, s * 0.1))
.collect();
let queries: Vec<f32> = (0..key_heads * key_dim)
.map(|i| f32::from(u8::try_from(i).unwrap_or(0)).mul_add(-0.02, s * 0.05))
.collect();
let values: Vec<f32> = (0..value_heads * value_dim)
.map(|i| f32::from(u8::try_from(i).unwrap_or(0)).mul_add(0.04, -(s * 0.07)))
.collect();
let decays = vec![0.92_f32, 0.88, 0.95, 0.90];
let betas = vec![0.25_f32, 0.30, 0.20, 0.35];
tape.record_step(&keys, &queries, &values, &decays, &betas)?;
if step_idx < 2 {
let group = value_heads / key_heads;
let head_stride = value_dim * key_dim;
for v_head in 0..value_heads {
let k_head = v_head / group;
let key = &keys[k_head * key_dim..(k_head + 1) * key_dim];
let query = &queries[k_head * key_dim..(k_head + 1) * key_dim];
let v_slice = &values[v_head * value_dim..(v_head + 1) * value_dim];
let state_head =
&mut sequential_state[v_head * head_stride..(v_head + 1) * head_stride];
for (row_idx, &v_elem) in v_slice.iter().enumerate() {
let row_state = &mut state_head[row_idx * key_dim..(row_idx + 1) * key_dim];
crate::kernels::pinned_gdn::step_row_in_place(
row_state,
key,
query,
decays[v_head],
v_elem,
betas[v_head],
)?;
}
}
}
}
assert_eq!(tape.len(), 4);
tape.replay_accepted_prefix(&mut replay_state, 2)?;
for (lhs, rhs) in replay_state.iter().zip(&sequential_state) {
assert_eq!(lhs.to_bits(), rhs.to_bits());
}
Ok(())
}
}