Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -0,0 +1,393 @@
//! Linear Attention Implementation
//!
//! Reduces O(n²) complexity to O(n) by using feature map approximations φ(Q)φ(K)ᵀ
//!
//! Key features:
//! - Feature map approximation for attention computation
//! - Causal masking support for autoregressive models
//! - Memory efficient computation avoiding n×n attention matrices
//! - Compatible with existing transformer infrastructure
use crate::{Result, TransformerError};
use rtx_tensor::{Tensor, Device, DType};
use crate::layers::{Layer, LayerNorm};
/// Feature map types for linear attention
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureMapType {
/// ELU activation: max(0, x) + 1
Elu,
/// ReLU activation: max(0, x)
Relu,
/// Softmax normalization
Softmax,
/// Random Fourier features
Fourier,
}
/// Configuration for Linear Attention
#[derive(Debug, Clone)]
pub struct LinearAttentionConfig {
pub d_model: usize,
pub num_heads: usize,
pub head_dim: usize,
pub feature_map_dim: usize,
pub causal: bool,
pub feature_map_type: FeatureMapType,
pub epsilon: f32,
}
impl LinearAttentionConfig {
/// Create a new linear attention configuration
pub fn new(d_model: usize, num_heads: usize) -> Self {
let head_dim = d_model / num_heads;
Self {
d_model,
num_heads,
head_dim,
feature_map_dim: head_dim, // default same as head_dim
causal: false,
feature_map_type: FeatureMapType::Elu,
epsilon: 1e-5,
}
}
}
/// Feature map for linear attention approximation
#[derive(Debug)]
pub struct FeatureMap {
feature_type: FeatureMapType,
input_dim: usize,
feature_dim: usize,
// For Fourier features
frequencies: Option<Tensor>,
device: Device,
}
impl FeatureMap {
/// Create a new feature map
pub fn new(input_dim: usize, feature_dim: usize, feature_type: FeatureMapType, device: &Device) -> Result<Self> {
let frequencies = if feature_type == FeatureMapType::Fourier {
// Random Fourier features
let freq = Tensor::randn(&[input_dim, feature_dim], DType::F32, device)?;
Some(freq)
} else {
None
};
Ok(Self {
feature_type,
input_dim,
feature_dim,
frequencies,
device: device.clone(),
})
}
/// Apply feature map transformation
pub fn forward(&self, input: &Tensor) -> Result<Tensor> {
match self.feature_type {
FeatureMapType::Elu => {
// φ(x) = ELU(x) + 1 for non-negative features
let elu_output = input.elu(1.0)?;
let output = (&elu_output + 1.0)?;
Ok(output)
},
FeatureMapType::Relu => {
// φ(x) = ReLU(x)
input.relu()
},
FeatureMapType::Softmax => {
// φ(x) = softmax(x) (along last dimension)
input.softmax(-1)
},
FeatureMapType::Fourier => {
// φ(x) = [cos(Wx), sin(Wx)] where W is random frequencies
if let Some(ref frequencies) = self.frequencies {
let projected = input.matmul(frequencies)?;
let cos_part = projected.cos()?;
let sin_part = projected.sin()?;
// Concatenate cos and sin parts
let output = Tensor::cat(&[cos_part, sin_part], -1)?;
// Scale by sqrt(feature_dim) for normalization
let scale = (self.feature_dim as f32).sqrt().recip();
let output = (&output * scale)?;
Ok(output)
} else {
return Err(TransformerError::InvalidInput("Fourier frequencies not initialized".to_string()));
}
}
}
}
}
/// Fourier feature map implementation
#[derive(Debug)]
pub struct FourierFeatureMap {
frequencies: Tensor,
scale: f32,
device: Device,
}
impl FourierFeatureMap {
/// Create a new Fourier feature map
pub fn new(input_dim: usize, feature_dim: usize, scale: f32, device: &Device) -> Result<Self> {
// Random frequencies sampled from Gaussian distribution
let frequencies = Tensor::randn(&[input_dim, feature_dim / 2], DType::F32, device)? * scale;
Ok(Self {
frequencies,
scale,
device: device.clone(),
})
}
/// Forward pass through Fourier feature map
pub fn forward(&self, input: &Tensor) -> Result<Tensor> {
let projected = input.matmul(&self.frequencies)?;
let cos_part = projected.cos()?;
let sin_part = projected.sin()?;
// Concatenate and normalize
let output = Tensor::cat(&[cos_part, sin_part], -1)?;
let scale_factor = (self.frequencies.dims()[1] as f32 * 2.0).sqrt().recip();
let output = (&output * scale_factor)?;
Ok(output)
}
}
/// Core linear attention mechanism
#[derive(Debug)]
pub struct LinearAttention {
q_proj: Tensor,
k_proj: Tensor,
v_proj: Tensor,
o_proj: Tensor,
feature_map: FeatureMap,
config: LinearAttentionConfig,
device: Device,
}
impl LinearAttention {
/// Create a new linear attention layer
pub fn new(config: &LinearAttentionConfig, device: &Device) -> Result<Self> {
// Initialize projection matrices
let scale = (config.d_model as f32).sqrt().recip();
let q_proj = Tensor::randn(&[config.d_model, config.d_model], DType::F32, device)? * scale;
let k_proj = Tensor::randn(&[config.d_model, config.d_model], DType::F32, device)? * scale;
let v_proj = Tensor::randn(&[config.d_model, config.d_model], DType::F32, device)? * scale;
let o_proj = Tensor::randn(&[config.d_model, config.d_model], DType::F32, device)? * scale;
let feature_map = FeatureMap::new(
config.head_dim,
config.feature_map_dim,
config.feature_map_type,
device,
)?;
Ok(Self {
q_proj,
k_proj,
v_proj,
o_proj,
feature_map,
config: config.clone(),
device: device.clone(),
})
}
/// Forward pass with self-attention
pub fn forward(&self, input: &Tensor) -> Result<Tensor> {
self.forward_with_kv(input, input, input)
}
/// Forward pass with separate K, V inputs (cross-attention)
pub fn forward_with_kv(&self, q: &Tensor, k: &Tensor, v: &Tensor) -> Result<Tensor> {
let input_shape = q.dims();
let batch_size = input_shape[0];
let seq_len = input_shape[1];
// Project inputs to Q, K, V
let q_proj = q.matmul(&self.q_proj)?;
let k_proj = k.matmul(&self.k_proj)?;
let v_proj = v.matmul(&self.v_proj)?;
// Reshape for multi-head attention
let q_proj = q_proj.reshape(&[batch_size, seq_len, self.config.num_heads, self.config.head_dim])?
.transpose(1, 2)?; // [B, H, T, D]
let k_proj = k_proj.reshape(&[batch_size, seq_len, self.config.num_heads, self.config.head_dim])?
.transpose(1, 2)?; // [B, H, T, D]
let v_proj = v_proj.reshape(&[batch_size, seq_len, self.config.num_heads, self.config.head_dim])?
.transpose(1, 2)?; // [B, H, T, D]
// Apply feature maps: φ(Q) and φ(K)
let phi_q = self.apply_feature_map_to_heads(&q_proj)?; // [B, H, T, F]
let phi_k = self.apply_feature_map_to_heads(&k_proj)?; // [B, H, T, F]
// Compute linear attention
let output = if self.config.causal {
self.causal_linear_attention(&phi_q, &phi_k, &v_proj)?
} else {
self.non_causal_linear_attention(&phi_q, &phi_k, &v_proj)?
};
// Reshape back and apply output projection
let output = output.transpose(1, 2)?.reshape(&[batch_size, seq_len, self.config.d_model])?;
let output = output.matmul(&self.o_proj)?;
Ok(output)
}
/// Apply feature map to multi-head tensor
fn apply_feature_map_to_heads(&self, input: &Tensor) -> Result<Tensor> {
let input_shape = input.dims();
let batch_size = input_shape[0];
let num_heads = input_shape[1];
let seq_len = input_shape[2];
let head_dim = input_shape[3];
// Reshape to apply feature map: [B, H, T, D] -> [B*H*T, D]
let reshaped = input.reshape(&[batch_size * num_heads * seq_len, head_dim])?;
let features = self.feature_map.forward(&reshaped)?;
// Reshape back: [B*H*T, F] -> [B, H, T, F]
let feature_dim = features.dims()[1];
let output = features.reshape(&[batch_size, num_heads, seq_len, feature_dim])?;
Ok(output)
}
/// Non-causal linear attention: O = φ(Q)(φ(K)ᵀV)
fn non_causal_linear_attention(&self, phi_q: &Tensor, phi_k: &Tensor, v: &Tensor) -> Result<Tensor> {
// φ(K)ᵀV: [B, H, F, T] × [B, H, T, D] -> [B, H, F, D]
let kv = phi_k.transpose(-1, -2)?.matmul(v)?;
// φ(Q)(φ(K)ᵀV): [B, H, T, F] × [B, H, F, D] -> [B, H, T, D]
let output = phi_q.matmul(&kv)?;
Ok(output)
}
/// Causal linear attention with cumulative computation
fn causal_linear_attention(&self, phi_q: &Tensor, phi_k: &Tensor, v: &Tensor) -> Result<Tensor> {
let input_shape = phi_q.dims();
let batch_size = input_shape[0];
let num_heads = input_shape[1];
let seq_len = input_shape[2];
let head_dim = v.dims()[3];
let mut outputs = Vec::new();
let mut cumulative_kv = Tensor::zeros(&[batch_size, num_heads, self.config.feature_map_dim, head_dim], &self.device)?;
// Process each position causally
for t in 0..seq_len {
let phi_k_t = phi_k.narrow(2, t, 1)?; // [B, H, 1, F]
let v_t = v.narrow(2, t, 1)?; // [B, H, 1, D]
// Update cumulative K*V
let kv_t = phi_k_t.transpose(-1, -2)?.matmul(&v_t)?; // [B, H, F, D]
cumulative_kv = (&cumulative_kv + &kv_t)?;
// Compute output for position t
let phi_q_t = phi_q.narrow(2, t, 1)?; // [B, H, 1, F]
let output_t = phi_q_t.matmul(&cumulative_kv)?; // [B, H, 1, D]
outputs.push(output_t);
}
// Concatenate outputs
let output = Tensor::cat(&outputs, 2)?;
Ok(output)
}
}
/// Linear attention layer with normalization and residual connections
#[derive(Debug)]
pub struct LinearAttentionLayer {
attention: LinearAttention,
norm: LayerNorm,
config: LinearAttentionConfig,
device: Device,
}
impl LinearAttentionLayer {
/// Create a new linear attention layer
pub fn new(config: &LinearAttentionConfig, device: &Device) -> Result<Self> {
let attention = LinearAttention::new(config, device)?;
let norm = LayerNorm::new(config.d_model, config.epsilon, device)?;
Ok(Self {
attention,
norm,
config: config.clone(),
device: device.clone(),
})
}
/// Forward pass with pre-normalization and residual connection
pub fn forward(&self, input: &Tensor) -> Result<Tensor> {
// Pre-normalization
let normalized = self.norm.forward(input)?;
// Linear attention
let attn_output = self.attention.forward(&normalized)?;
// Residual connection
let output = (input + &attn_output)?;
Ok(output)
}
}
impl Layer for LinearAttention {
fn forward(&self, input: &Tensor) -> Result<Tensor> {
self.forward(input)
}
fn layer_type(&self) -> &'static str {
"LinearAttention"
}
fn device(&self) -> &Device {
&self.device
}
fn parameters(&self) -> Vec<&Tensor> {
vec![&self.q_proj, &self.k_proj, &self.v_proj, &self.o_proj]
}
fn parameters_mut(&mut self) -> Vec<&mut Tensor> {
vec![&mut self.q_proj, &mut self.k_proj, &mut self.v_proj, &mut self.o_proj]
}
}
impl Layer for LinearAttentionLayer {
fn forward(&self, input: &Tensor) -> Result<Tensor> {
self.forward(input)
}
fn layer_type(&self) -> &'static str {
"LinearAttentionLayer"
}
fn device(&self) -> &Device {
&self.device
}
fn parameters(&self) -> Vec<&Tensor> {
let mut params = self.attention.parameters();
params.extend(self.norm.parameters());
params
}
fn parameters_mut(&mut self) -> Vec<&mut Tensor> {
let mut params = self.attention.parameters_mut();
params.extend(self.norm.parameters_mut());
params
}
}