use crate::cache::{WhisperCrossCache, WhisperKvCache};
pub fn replicate_encoder_for_beams(enc: &[f32], n: usize, beam: usize, plane: usize) -> Vec<f32> {
let mut out = vec![0f32; n * beam * plane];
for r in 0..n {
let src = r * plane;
for k in 0..beam {
let dst = (r * beam + k) * plane;
out[dst..dst + plane].copy_from_slice(&enc[src..src + plane]);
}
}
out
}
pub fn batched_prompt_f32(prompt: &[u32], batch: usize) -> Vec<f32> {
let seq = prompt.len();
let mut out = Vec::with_capacity(batch * seq);
for _ in 0..batch {
for &t in prompt {
out.push(t as f32);
}
}
out
}
pub fn slice_cross_cache(
cross: &WhisperCrossCache,
index: usize,
batch: usize,
) -> Result<WhisperCrossCache, String> {
if batch == 1 {
return Ok(cross.clone());
}
let n_layers = cross.layers_k.len();
let per = cross.layers_k[0].len() / batch;
if per == 0 || cross.layers_k[0].len() != batch * per {
return Err("slice_cross_cache: bad batch layout".into());
}
let off = index * per;
let mut layers_k = Vec::with_capacity(n_layers);
let mut layers_v = Vec::with_capacity(n_layers);
for layer in 0..n_layers {
layers_k.push(cross.layers_k[layer][off..off + per].to_vec());
layers_v.push(cross.layers_v[layer][off..off + per].to_vec());
}
Ok(WhisperCrossCache {
past_len: cross.past_len,
layers_kv_base: vec![0; n_layers],
layers_k,
layers_v,
})
}
pub fn stack_kv_caches(caches: &[WhisperKvCache]) -> Result<WhisperKvCache, String> {
if caches.is_empty() {
return Err("stack_kv_caches: empty".into());
}
let past_len = caches[0].past_len;
let n_layers = caches[0].layers_k.len();
for (i, c) in caches.iter().enumerate().skip(1) {
if c.past_len != past_len {
return Err(format!("cache {i} past_len {} != {past_len}", c.past_len));
}
if c.layers_k.len() != n_layers {
return Err(format!("cache {i} layer count mismatch"));
}
}
let _n = caches.len();
let mut layers_k = Vec::with_capacity(n_layers);
let mut layers_v = Vec::with_capacity(n_layers);
for layer in 0..n_layers {
let mut k = Vec::new();
let mut v = Vec::new();
for c in caches {
k.extend_from_slice(&c.layers_k[layer]);
v.extend_from_slice(&c.layers_v[layer]);
}
layers_k.push(k);
layers_v.push(v);
}
Ok(WhisperKvCache {
past_len,
layers_kv_base: vec![0; n_layers],
layers_k,
layers_v,
})
}
pub fn unstack_kv_cache(cache: &WhisperKvCache, n: usize) -> Result<Vec<WhisperKvCache>, String> {
if n == 0 {
return Ok(Vec::new());
}
let n_layers = cache.layers_k.len();
let per_batch = cache.layers_k.first().map(|k| k.len() / n).unwrap_or(0);
if per_batch == 0 || cache.layers_k[0].len() != n * per_batch {
return Err("unstack_kv_cache: length not divisible by batch".into());
}
let mut out = Vec::with_capacity(n);
for b in 0..n {
let off = b * per_batch;
let mut layers_k = Vec::with_capacity(n_layers);
let mut layers_v = Vec::with_capacity(n_layers);
for layer in 0..n_layers {
layers_k.push(cache.layers_k[layer][off..off + per_batch].to_vec());
layers_v.push(cache.layers_v[layer][off..off + per_batch].to_vec());
}
out.push(WhisperKvCache {
past_len: cache.past_len,
layers_kv_base: vec![0; n_layers],
layers_k,
layers_v,
});
}
Ok(out)
}
pub fn reorder_kv_beams(
cache: &WhisperKvCache,
indices: &[usize],
batch: usize,
) -> Result<WhisperKvCache, String> {
if indices.len() != batch {
return Err("reorder_kv_beams: indices length mismatch".into());
}
let per_batch = cache.layers_k.first().map(|k| k.len() / batch).unwrap_or(0);
let n_layers = cache.layers_k.len();
let mut layers_k = Vec::with_capacity(n_layers);
let mut layers_v = Vec::with_capacity(n_layers);
for layer in 0..n_layers {
let mut k = vec![0f32; batch * per_batch];
let mut v = vec![0f32; batch * per_batch];
for (dst, &src) in indices.iter().enumerate() {
if src >= batch {
return Err(format!("reorder_kv_beams: bad index {src}"));
}
let s0 = src * per_batch;
let d0 = dst * per_batch;
k[d0..d0 + per_batch].copy_from_slice(&cache.layers_k[layer][s0..s0 + per_batch]);
v[d0..d0 + per_batch].copy_from_slice(&cache.layers_v[layer][s0..s0 + per_batch]);
}
layers_k.push(k);
layers_v.push(v);
}
Ok(WhisperKvCache {
past_len: cache.past_len,
layers_kv_base: vec![0; n_layers],
layers_k,
layers_v,
})
}
pub fn stack_cross_caches(
caches: &[crate::cache::WhisperCrossCache],
) -> Result<crate::cache::WhisperCrossCache, String> {
if caches.is_empty() {
return Err("stack_cross_caches: empty".into());
}
let enc_len = caches[0].layers_k[0].len();
let n_layers = caches[0].layers_k.len();
for c in caches.iter().skip(1) {
if c.layers_k.len() != n_layers {
return Err("cross layer count mismatch".into());
}
if c.layers_k[0].len() != enc_len {
return Err("cross enc length mismatch".into());
}
}
let mut layers_k = Vec::with_capacity(n_layers);
let mut layers_v = Vec::with_capacity(n_layers);
for layer in 0..n_layers {
let mut k = Vec::new();
let mut v = Vec::new();
for c in caches {
k.extend_from_slice(&c.layers_k[layer]);
v.extend_from_slice(&c.layers_v[layer]);
}
layers_k.push(k);
layers_v.push(v);
}
Ok(crate::cache::WhisperCrossCache {
past_len: caches[0].past_len,
layers_kv_base: vec![0; n_layers],
layers_k,
layers_v,
})
}