use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr};
use super::gated_delta::RecurrentGatedDeltaError;
pub(super) struct GatedDeltaSpec<'a> {
pub(super) query: &'a str,
pub(super) key: &'a str,
pub(super) value: &'a str,
pub(super) decay_log: &'a str,
pub(super) beta_logits: &'a str,
pub(super) state_input: &'a str,
pub(super) output: &'a str,
pub(super) state_output: &'a str,
pub(super) batch: u32,
pub(super) sequence: u32,
pub(super) key_heads: u32,
pub(super) value_heads: u32,
pub(super) key_dim: u32,
pub(super) value_dim: u32,
pub(super) eps: f32,
pub(super) dtype: DataType,
}
pub(super) struct GatedDeltaCounts {
pub(super) qk: u32,
pub(super) value: u32,
pub(super) scalar: u32,
pub(super) state: u32,
pub(super) head: u32,
pub(super) group: u32,
}
impl GatedDeltaSpec<'_> {
pub(super) fn counts(&self) -> Result<GatedDeltaCounts, RecurrentGatedDeltaError> {
if self.batch == 0
|| self.sequence == 0
|| self.key_heads == 0
|| self.value_heads == 0
|| self.key_dim == 0
|| self.value_dim == 0
{
return Err(RecurrentGatedDeltaError::EmptyShape);
}
if self.value_heads % self.key_heads != 0 {
return Err(RecurrentGatedDeltaError::InvalidHeadGrouping {
key_heads: self.key_heads,
value_heads: self.value_heads,
});
}
if !matches!(
self.dtype,
DataType::F16 | DataType::BF16 | DataType::F32
) {
return Err(RecurrentGatedDeltaError::UnsupportedDtype {
dtype: self.dtype.clone(),
});
}
Ok(GatedDeltaCounts {
qk: checked(&[self.batch, self.sequence, self.key_heads, self.key_dim])?,
value: checked(&[self.batch, self.sequence, self.value_heads, self.value_dim])?,
scalar: checked(&[self.batch, self.sequence, self.value_heads])?,
state: checked(&[self.batch, self.value_heads, self.key_dim, self.value_dim])?,
head: checked(&[self.batch, self.value_heads])?,
group: self.value_heads / self.key_heads,
})
}
}
pub(super) fn checked(values: &[u32]) -> Result<u32, RecurrentGatedDeltaError> {
values.iter().try_fold(1_u32, |product, value| {
product
.checked_mul(*value)
.ok_or(RecurrentGatedDeltaError::ElementCountOverflow)
})
}
pub(super) fn qk_index(sequence: u32, heads: u32, dim: u32, token: Expr, feature: Expr) -> Expr {
Expr::add(
Expr::mul(
Expr::add(
Expr::mul(Expr::var("batch_index"), Expr::u32(sequence)),
token,
),
Expr::u32(heads * dim),
),
Expr::add(Expr::mul(Expr::var("key_head"), Expr::u32(dim)), feature),
)
}
pub(super) fn value_index(sequence: u32, heads: u32, dim: u32, token: Expr, feature: Expr) -> Expr {
Expr::add(
Expr::mul(
Expr::add(
Expr::mul(Expr::var("batch_index"), Expr::u32(sequence)),
token,
),
Expr::u32(heads * dim),
),
Expr::add(Expr::mul(Expr::var("value_head"), Expr::u32(dim)), feature),
)
}
pub(super) fn scalar_index(sequence: u32, heads: u32, token: Expr) -> Expr {
Expr::add(
Expr::mul(
Expr::add(
Expr::mul(Expr::var("batch_index"), Expr::u32(sequence)),
token,
),
Expr::u32(heads),
),
Expr::var("value_head"),
)
}
pub(super) fn state_index(key_dim: u32, value_dim: u32, key: Expr, value: Expr) -> Expr {
Expr::add(
Expr::mul(Expr::var("head_index"), Expr::u32(key_dim * value_dim)),
Expr::add(Expr::mul(key, Expr::u32(value_dim)), value),
)
}
pub(super) fn gated_delta_buffers(
spec: &GatedDeltaSpec<'_>,
counts: &GatedDeltaCounts,
) -> Vec<BufferDecl> {
let dtype = &spec.dtype;
vec![
BufferDecl::storage(spec.query, 0, BufferAccess::ReadOnly, dtype.clone())
.with_count(counts.qk),
BufferDecl::storage(spec.key, 1, BufferAccess::ReadOnly, dtype.clone())
.with_count(counts.qk),
BufferDecl::storage(spec.value, 2, BufferAccess::ReadOnly, dtype.clone())
.with_count(counts.value),
BufferDecl::storage(spec.decay_log, 3, BufferAccess::ReadOnly, dtype.clone())
.with_count(counts.scalar),
BufferDecl::storage(spec.beta_logits, 4, BufferAccess::ReadOnly, dtype.clone())
.with_count(counts.scalar),
BufferDecl::storage(spec.state_input, 5, BufferAccess::ReadWrite, DataType::F32)
.with_count(counts.state),
BufferDecl::output(spec.output, 6, dtype.clone()).with_count(counts.value),
BufferDecl::storage(
spec.state_output,
7,
BufferAccess::ReadWrite,
DataType::F32,
)
.with_count(counts.state),
]
}