pub mod decode;
pub mod differential;
pub mod flash;
pub mod flash_causal;
pub mod gated;
pub mod gdn;
#[cfg(feature = "train-backward")]
pub mod gdn_backward;
pub mod gdn_fused;
pub mod gqa;
pub mod native_sparse;
pub mod standard;
pub use self::standard::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttentionTag {
Mha,
Gqa,
FlashCpu,
FlashCausal,
Gdn,
GdnFused,
GatedGqa,
Differential,
Nsa,
Decode,
}
#[derive(Debug, Clone, Copy)]
pub enum AttentionKind {
Mha,
Gqa(gqa::GqaConfig),
Flash,
FlashCausal,
Gdn,
GdnFused,
GatedGqa,
Differential,
NativeSparse,
Decode,
}
impl AttentionKind {
#[inline]
pub fn name(&self) -> &'static str {
match self {
AttentionKind::Mha => "mha",
AttentionKind::Gqa(_) => "gqa",
AttentionKind::Flash => "flash",
AttentionKind::FlashCausal => "flash_causal",
AttentionKind::Gdn => "gdn",
AttentionKind::GdnFused => "gdn_fused",
AttentionKind::GatedGqa => "gated_gqa",
AttentionKind::Differential => "differential",
AttentionKind::NativeSparse => "native_sparse",
AttentionKind::Decode => "decode",
}
}
#[inline]
pub fn tag(&self) -> AttentionTag {
match self {
AttentionKind::Mha => AttentionTag::Mha,
AttentionKind::Gqa(_) => AttentionTag::Gqa,
AttentionKind::Flash => AttentionTag::FlashCpu,
AttentionKind::FlashCausal => AttentionTag::FlashCausal,
AttentionKind::Gdn => AttentionTag::Gdn,
AttentionKind::GdnFused => AttentionTag::GdnFused,
AttentionKind::GatedGqa => AttentionTag::GatedGqa,
AttentionKind::Differential => AttentionTag::Differential,
AttentionKind::NativeSparse => AttentionTag::Nsa,
AttentionKind::Decode => AttentionTag::Decode,
}
}
#[inline]
pub fn is_causal(&self) -> bool {
match self {
AttentionKind::Mha => false,
AttentionKind::Gqa(_) => true,
AttentionKind::Flash => false,
AttentionKind::FlashCausal => true,
AttentionKind::Gdn => true,
AttentionKind::GdnFused => true,
AttentionKind::GatedGqa => true,
AttentionKind::Differential => true,
AttentionKind::NativeSparse => true,
AttentionKind::Decode => true,
}
}
#[inline]
pub fn supports_kv_cache(&self) -> bool {
match self {
AttentionKind::Mha => false,
AttentionKind::Gqa(_) => true,
AttentionKind::Flash => false,
AttentionKind::FlashCausal => false,
AttentionKind::Gdn => false,
AttentionKind::GdnFused => false,
AttentionKind::GatedGqa => true,
AttentionKind::Differential => false,
AttentionKind::NativeSparse => false,
AttentionKind::Decode => true,
}
}
}
#[cfg(test)]
mod attention_kind_tests {
use super::*;
#[test]
fn name_mha() {
assert_eq!(AttentionKind::Mha.name(), "mha");
}
#[test]
fn name_gqa() {
let cfg = gqa::GqaConfig {
num_heads: 16,
num_kv_heads: 8,
head_dim: 64,
};
assert_eq!(AttentionKind::Gqa(cfg).name(), "gqa");
}
#[test]
fn name_flash() {
assert_eq!(AttentionKind::Flash.name(), "flash");
}
#[test]
fn name_flash_causal() {
assert_eq!(AttentionKind::FlashCausal.name(), "flash_causal");
}
#[test]
fn name_gdn() {
assert_eq!(AttentionKind::Gdn.name(), "gdn");
}
#[test]
fn name_gdn_fused() {
assert_eq!(AttentionKind::GdnFused.name(), "gdn_fused");
}
#[test]
fn name_gated_gqa() {
assert_eq!(AttentionKind::GatedGqa.name(), "gated_gqa");
}
#[test]
fn name_differential() {
assert_eq!(AttentionKind::Differential.name(), "differential");
}
#[test]
fn name_native_sparse() {
assert_eq!(AttentionKind::NativeSparse.name(), "native_sparse");
}
#[test]
fn name_decode() {
assert_eq!(AttentionKind::Decode.name(), "decode");
}
#[test]
fn tag_mha() {
assert_eq!(AttentionKind::Mha.tag(), AttentionTag::Mha);
}
#[test]
fn tag_gqa() {
let cfg = gqa::GqaConfig {
num_heads: 16,
num_kv_heads: 8,
head_dim: 64,
};
assert_eq!(AttentionKind::Gqa(cfg).tag(), AttentionTag::Gqa);
}
#[test]
fn tag_flash_maps_to_flash_cpu() {
assert_eq!(AttentionKind::Flash.tag(), AttentionTag::FlashCpu);
}
#[test]
fn tag_flash_causal() {
assert_eq!(AttentionKind::FlashCausal.tag(), AttentionTag::FlashCausal);
}
#[test]
fn tag_gdn() {
assert_eq!(AttentionKind::Gdn.tag(), AttentionTag::Gdn);
}
#[test]
fn tag_gdn_fused() {
assert_eq!(AttentionKind::GdnFused.tag(), AttentionTag::GdnFused);
}
#[test]
fn tag_gated_gqa() {
assert_eq!(AttentionKind::GatedGqa.tag(), AttentionTag::GatedGqa);
}
#[test]
fn tag_differential() {
assert_eq!(
AttentionKind::Differential.tag(),
AttentionTag::Differential
);
}
#[test]
fn tag_native_sparse_maps_to_nsa() {
assert_eq!(AttentionKind::NativeSparse.tag(), AttentionTag::Nsa);
}
#[test]
fn tag_decode() {
assert_eq!(AttentionKind::Decode.tag(), AttentionTag::Decode);
}
#[test]
fn is_causal_mha_false() {
assert!(!AttentionKind::Mha.is_causal());
}
#[test]
fn is_causal_gqa_true() {
let cfg = gqa::GqaConfig {
num_heads: 16,
num_kv_heads: 8,
head_dim: 64,
};
assert!(AttentionKind::Gqa(cfg).is_causal());
}
#[test]
fn is_causal_flash_false() {
assert!(!AttentionKind::Flash.is_causal());
}
#[test]
fn is_causal_flash_causal_true() {
assert!(AttentionKind::FlashCausal.is_causal());
}
#[test]
fn is_causal_gdn_true() {
assert!(AttentionKind::Gdn.is_causal());
}
#[test]
fn is_causal_gdn_fused_true() {
assert!(AttentionKind::GdnFused.is_causal());
}
#[test]
fn is_causal_gated_gqa_true() {
assert!(AttentionKind::GatedGqa.is_causal());
}
#[test]
fn is_causal_differential_true() {
assert!(AttentionKind::Differential.is_causal());
}
#[test]
fn is_causal_native_sparse_true() {
assert!(AttentionKind::NativeSparse.is_causal());
}
#[test]
fn is_causal_decode_true() {
assert!(AttentionKind::Decode.is_causal());
}
#[test]
fn kv_cache_mha_false() {
assert!(!AttentionKind::Mha.supports_kv_cache());
}
#[test]
fn kv_cache_gqa_true() {
let cfg = gqa::GqaConfig {
num_heads: 16,
num_kv_heads: 8,
head_dim: 64,
};
assert!(AttentionKind::Gqa(cfg).supports_kv_cache());
}
#[test]
fn kv_cache_flash_false() {
assert!(!AttentionKind::Flash.supports_kv_cache());
}
#[test]
fn kv_cache_flash_causal_false() {
assert!(!AttentionKind::FlashCausal.supports_kv_cache());
}
#[test]
fn kv_cache_gdn_false() {
assert!(!AttentionKind::Gdn.supports_kv_cache());
}
#[test]
fn kv_cache_gdn_fused_false() {
assert!(!AttentionKind::GdnFused.supports_kv_cache());
}
#[test]
fn kv_cache_gated_gqa_true() {
assert!(AttentionKind::GatedGqa.supports_kv_cache());
}
#[test]
fn kv_cache_differential_false() {
assert!(!AttentionKind::Differential.supports_kv_cache());
}
#[test]
fn kv_cache_native_sparse_false() {
assert!(!AttentionKind::NativeSparse.supports_kv_cache());
}
#[test]
fn kv_cache_decode_true() {
assert!(AttentionKind::Decode.supports_kv_cache());
}
#[test]
fn clone_gqa_preserves_config() {
let cfg = gqa::GqaConfig {
num_heads: 32,
num_kv_heads: 4,
head_dim: 128,
};
let kind = AttentionKind::Gqa(cfg);
let cloned = kind;
assert_eq!(cloned.name(), "gqa");
if let AttentionKind::Gqa(c) = cloned {
assert_eq!(c.num_heads, 32);
assert_eq!(c.num_kv_heads, 4);
assert_eq!(c.head_dim, 128);
} else {
panic!("clone changed variant");
}
}
#[test]
fn all_variants_have_names() {
let cfg = gqa::GqaConfig {
num_heads: 1,
num_kv_heads: 1,
head_dim: 1,
};
let variants: &[AttentionKind] = &[
AttentionKind::Mha,
AttentionKind::Gqa(cfg),
AttentionKind::Flash,
AttentionKind::FlashCausal,
AttentionKind::Gdn,
AttentionKind::GdnFused,
AttentionKind::GatedGqa,
AttentionKind::Differential,
AttentionKind::NativeSparse,
AttentionKind::Decode,
];
assert_eq!(variants.len(), 10, "update test when adding a new variant");
for v in variants {
assert!(!v.name().is_empty());
let _ = v.tag();
}
}
}