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,761 @@
//! `LLaMA` Attention Mechanisms and Components (Simplified Stub)
//!
//! This is a simplified stub version to get compilation working.
//! TODO: Implement full `LLaMA` attention with rotary embeddings and grouped-query attention.
use crate::architectures::TransformerConfig;
use crate::tensor_bridge::TensorBridge;
use crate::{Result, TransformerError};
use rtx_tensor::{Device, Tensor};
use serde::{Deserialize, Serialize};
/// LLaMA-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LLaMAConfig {
/// Base transformer configuration
pub base: TransformerConfig,
/// Vocabulary size
pub vocab_size: usize,
/// Hidden dimension
pub hidden_size: usize,
/// Feed-forward intermediate dimension
pub intermediate_size: usize,
/// Number of transformer layers
pub num_hidden_layers: usize,
/// Number of attention heads
pub num_attention_heads: usize,
/// Number of key-value heads (for grouped-query attention)
pub num_key_value_heads: usize,
/// Maximum sequence length
pub max_position_embeddings: usize,
/// `RMSNorm` epsilon
pub rms_norm_eps: f64,
/// Initializer range for weights
pub initializer_range: f64,
/// Use cache for generation
pub use_cache: bool,
/// Pad token ID
pub pad_token_id: i64,
/// BOS token ID
pub bos_token_id: i64,
/// EOS token ID
pub eos_token_id: i64,
/// Pretraining type
pub pretraining_tp: usize,
/// Tie word embeddings
pub tie_word_embeddings: bool,
/// Rope scaling configuration
pub rope_scaling: Option<RopeScaling>,
/// Rope theta parameter
pub rope_theta: f64,
/// Attention bias
pub attention_bias: bool,
/// MLP bias
pub mlp_bias: bool,
}
/// `RoPE` (Rotary Position Embedding) scaling configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RopeScaling {
/// Scaling type ("linear" or "dynamic")
pub scaling_type: String,
/// Scaling factor
pub factor: f64,
}
impl Default for LLaMAConfig {
fn default() -> Self {
Self {
base: TransformerConfig::default(),
vocab_size: 32000,
hidden_size: 4096,
intermediate_size: 11008,
num_hidden_layers: 32,
num_attention_heads: 32,
num_key_value_heads: 32, // Standard multi-head attention
max_position_embeddings: 2048,
rms_norm_eps: 1e-6,
initializer_range: 0.02,
use_cache: true,
pad_token_id: -1,
bos_token_id: 1,
eos_token_id: 2,
pretraining_tp: 1,
tie_word_embeddings: false,
rope_scaling: None,
rope_theta: 10000.0,
attention_bias: false,
mlp_bias: false,
}
}
}
impl LLaMAConfig {
/// Create `LLaMA` 7B configuration
#[must_use]
pub fn llama_7b() -> Self {
Self::default()
}
/// Create `LLaMA` 13B configuration
#[must_use]
pub fn llama_13b() -> Self {
let mut config = Self::default();
config.hidden_size = 5120;
config.intermediate_size = 13824;
config.num_hidden_layers = 40;
config.num_attention_heads = 40;
config.num_key_value_heads = 40;
config
}
/// Create `LLaMA` 30B configuration
#[must_use]
pub fn llama_30b() -> Self {
let mut config = Self::default();
config.hidden_size = 6656;
config.intermediate_size = 17920;
config.num_hidden_layers = 60;
config.num_attention_heads = 52;
config.num_key_value_heads = 52;
config
}
/// Create `LLaMA` 65B configuration
#[must_use]
pub fn llama_65b() -> Self {
let mut config = Self::default();
config.hidden_size = 8192;
config.intermediate_size = 22016;
config.num_hidden_layers = 80;
config.num_attention_heads = 64;
config.num_key_value_heads = 64;
config
}
/// Create Code Llama configuration
pub fn code_llama(base_size: &str) -> Result<Self> {
let mut config = match base_size {
"7b" => Self::llama_7b(),
"13b" => Self::llama_13b(),
"34b" => {
let mut config = Self::default();
config.hidden_size = 8192;
config.intermediate_size = 22016;
config.num_hidden_layers = 48;
config.num_attention_heads = 64;
config.num_key_value_heads = 8; // Grouped-query attention
config.max_position_embeddings = 16384; // Extended context
config
}
_ => {
return Err(TransformerError::config(format!(
"Unsupported Code Llama size: {base_size}"
)));
}
};
// Code Llama specific adjustments
config.vocab_size = 32016; // Extended vocabulary
config.rope_theta = 1000000.0; // Extended RoPE
Ok(config)
}
/// Validate configuration parameters
pub fn validate(&self) -> Result<()> {
if self.vocab_size == 0 {
return Err(TransformerError::Generic(
"vocab_size must be positive".to_string(),
));
}
if self.hidden_size == 0 {
return Err(TransformerError::Generic(
"hidden_size must be positive".to_string(),
));
}
if self.num_hidden_layers == 0 {
return Err(TransformerError::Generic(
"num_hidden_layers must be positive".to_string(),
));
}
if self.num_attention_heads == 0 {
return Err(TransformerError::Generic(
"num_attention_heads must be positive".to_string(),
));
}
if !self.hidden_size.is_multiple_of(self.num_attention_heads) {
return Err(TransformerError::Generic(
"hidden_size must be divisible by num_attention_heads".to_string(),
));
}
Ok(())
}
}
/// Rotary Position Embedding (`RoPE`) implementation
#[derive(Debug)]
pub struct RotaryPositionEmbedding {
dim: usize,
max_seq_len: usize,
theta: f64,
device: Device,
// Precomputed frequency tensors for efficiency
cos_cached: Option<Tensor>,
sin_cached: Option<Tensor>,
}
impl RotaryPositionEmbedding {
/// Create new `RoPE` layer
pub fn new(dim: usize, max_seq_len: usize, theta: f64, device: &Device) -> Result<Self> {
let mut rope = Self {
dim,
max_seq_len,
theta,
device: device.clone(),
cos_cached: None,
sin_cached: None,
};
// Precompute cos/sin values for efficiency
rope.precompute_freqs(max_seq_len)?;
Ok(rope)
}
/// Precompute frequency tensors
fn precompute_freqs(&mut self, seq_len: usize) -> Result<()> {
// Create frequency tensor: theta^(-2k/dim) for k = 0, 1, ..., dim//2-1
let half_dim = self.dim / 2;
let freqs: Vec<f32> = (0..half_dim)
.map(|i| 1.0 / self.theta.powf(2.0 * i as f64 / self.dim as f64) as f32)
.collect();
let freq_tensor = Tensor::from_vec(freqs, &[half_dim], &self.device)?;
// Create position tensor: [0, 1, 2, ..., seq_len-1]
let positions: Vec<f32> = (0..seq_len).map(|i| i as f32).collect();
let pos_tensor = Tensor::from_vec(positions, &[seq_len], &self.device)?;
// Compute outer product: pos[i] * freq[j] for all i, j
let pos_expanded = pos_tensor.unsqueeze(1)?; // [seq_len, 1]
let freq_expanded = freq_tensor.unsqueeze(0)?; // [1, half_dim]
let angles = pos_expanded.matmul(&freq_expanded)?; // [seq_len, half_dim]
// Compute cos and sin
self.cos_cached = Some(angles.cos()?);
self.sin_cached = Some(angles.sin()?);
Ok(())
}
/// Apply rotary position encoding to queries and keys
pub fn forward(&self, q: &Tensor, k: &Tensor, seq_len: usize) -> Result<(Tensor, Tensor)> {
if seq_len > self.max_seq_len {
return Err(TransformerError::architecture(format!(
"Sequence length {} exceeds maximum {}",
seq_len, self.max_seq_len
)));
}
// Get cached cos/sin values
let cos = self.cos_cached.as_ref().ok_or_else(|| {
TransformerError::architecture("RoPE frequencies not precomputed".to_string())
})?;
let sin = self.sin_cached.as_ref().ok_or_else(|| {
TransformerError::architecture("RoPE frequencies not precomputed".to_string())
})?;
// Apply rotary position encoding
let q_rot = self.apply_rotary_pos_emb(q, cos, sin, seq_len)?;
let k_rot = self.apply_rotary_pos_emb(k, cos, sin, seq_len)?;
Ok((q_rot, k_rot))
}
/// Apply rotary position embedding to a tensor
fn apply_rotary_pos_emb(
&self,
x: &Tensor,
cos: &Tensor,
sin: &Tensor,
seq_len: usize,
) -> Result<Tensor> {
// Extract shape: [batch_size, seq_len, num_heads, head_dim]
let shape = x.shape();
let batch_size = shape[0];
let input_seq_len = shape[1];
let num_heads = shape[2];
let head_dim = shape[3];
let actual_seq_len = input_seq_len.min(seq_len);
// Reshape x to [batch_size, seq_len, num_heads, head_dim/2, 2]
let half_dim = head_dim / 2;
let x_reshaped = x.view([batch_size, actual_seq_len, num_heads, half_dim, 2])?;
// Split into x1 and x2 (real and imaginary parts)
let x1 = x_reshaped.slice(4, 0, 1)?; // [..., 0]
let x2 = x_reshaped.slice(4, 1, 2)?; // [..., 1]
// Get cos/sin for the sequence length
let cos_seq = cos.slice(0, 0, actual_seq_len)?; // [actual_seq_len, half_dim]
let sin_seq = sin.slice(0, 0, actual_seq_len)?; // [actual_seq_len, half_dim]
// Expand cos/sin to match tensor dimensions
// [actual_seq_len, half_dim] -> [1, actual_seq_len, 1, half_dim, 1]
let cos_expanded = cos_seq.view([1, actual_seq_len, 1, half_dim, 1])?;
let sin_expanded = sin_seq.view([1, actual_seq_len, 1, half_dim, 1])?;
// Apply rotation:
// x_rot1 = x1 * cos - x2 * sin
// x_rot2 = x1 * sin + x2 * cos
let x_rot1 = x1.mul(&cos_expanded)?.sub(&x2.mul(&sin_expanded)?)?;
let x_rot2 = x1.mul(&sin_expanded)?.add(&x2.mul(&cos_expanded)?)?;
// Concatenate rotated parts
let x_rot = Tensor::cat(&[x_rot1, x_rot2], 4)?;
// Reshape back to original shape
x_rot
.view([batch_size, actual_seq_len, num_heads, head_dim])
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
}
/// `LLaMA` Multi-Head Attention with `RoPE` and GQA
#[derive(Debug)]
pub struct LLaMAAttention {
num_heads: usize,
num_key_value_heads: usize,
head_dim: usize,
hidden_size: usize,
rope: RotaryPositionEmbedding,
// Linear projection weights
q_proj: Tensor,
k_proj: Tensor,
v_proj: Tensor,
o_proj: Tensor,
device: Device,
}
impl LLaMAAttention {
/// Create new `LLaMA` attention layer
pub fn new(config: &LLaMAConfig, device: &Device) -> Result<Self> {
let head_dim = config.hidden_size / config.num_attention_heads;
let rope = RotaryPositionEmbedding::new(
head_dim,
config.max_position_embeddings,
config.rope_theta,
device,
)?;
// Initialize projection weights
let scale = (config.hidden_size as f32).recip().sqrt();
// Query projection: [hidden_size, num_heads * head_dim]
let q_proj = Tensor::randn(
&[config.hidden_size, config.num_attention_heads * head_dim],
device,
)?
.mul_scalar(scale)?;
// Key and Value projections for GQA: [hidden_size, num_kv_heads * head_dim]
let k_proj = Tensor::randn(
&[config.hidden_size, config.num_key_value_heads * head_dim],
device,
)?
.mul_scalar(scale)?;
let v_proj = Tensor::randn(
&[config.hidden_size, config.num_key_value_heads * head_dim],
device,
)?
.mul_scalar(scale)?;
// Output projection: [num_heads * head_dim, hidden_size]
let o_proj = Tensor::randn(
&[config.num_attention_heads * head_dim, config.hidden_size],
device,
)?
.mul_scalar(scale)?;
Ok(Self {
num_heads: config.num_attention_heads,
num_key_value_heads: config.num_key_value_heads,
head_dim,
hidden_size: config.hidden_size,
rope,
q_proj,
k_proj,
v_proj,
o_proj,
device: device.clone(),
})
}
/// Forward pass of attention
pub fn forward(&self, hidden_states: &Tensor, position_ids: Option<&Tensor>) -> Result<Tensor> {
let batch_size = hidden_states.shape()[0];
let seq_len = hidden_states.shape()[1];
// 1. Linear projections
let q = self.compute_queries(hidden_states)?;
let k = self.compute_keys(hidden_states)?;
let v = self.compute_values(hidden_states)?;
// Reshape for multi-head attention
// Q: [batch, seq, num_heads, head_dim]
let q = q.view([batch_size, seq_len, self.num_heads, self.head_dim])?;
// K, V: [batch, seq, num_kv_heads, head_dim]
let k = k.view([batch_size, seq_len, self.num_key_value_heads, self.head_dim])?;
let v = v.view([batch_size, seq_len, self.num_key_value_heads, self.head_dim])?;
// 2. Apply rotary position encoding
let (q, k) = self.rope.forward(&q, &k, seq_len)?;
// 3. Expand K, V for grouped-query attention if needed
let (k, v) = if self.num_key_value_heads < self.num_heads {
let repeat_factor = self.num_heads / self.num_key_value_heads;
let k = self.repeat_kv(&k, repeat_factor)?;
let v = self.repeat_kv(&v, repeat_factor)?;
(k, v)
} else {
(k, v)
};
// 4. Compute scaled dot-product attention
let attention_output = self.scaled_dot_product_attention(&q, &k, &v)?;
// 5. Reshape and apply output projection
let attention_output =
attention_output.view([batch_size, seq_len, self.num_heads * self.head_dim])?;
let output = attention_output.matmul(&self.o_proj)?;
Ok(output)
}
/// Compute query projections
fn compute_queries(&self, hidden_states: &Tensor) -> Result<Tensor> {
hidden_states
.matmul(&self.q_proj)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Compute key projections
fn compute_keys(&self, hidden_states: &Tensor) -> Result<Tensor> {
hidden_states
.matmul(&self.k_proj)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Compute value projections
fn compute_values(&self, hidden_states: &Tensor) -> Result<Tensor> {
hidden_states
.matmul(&self.v_proj)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Repeat key/value tensors for grouped-query attention
fn repeat_kv(&self, tensor: &Tensor, repeat_factor: usize) -> Result<Tensor> {
if repeat_factor == 1 {
return Ok(tensor.clone());
}
let shape = tensor.shape();
let batch_size = shape[0];
let seq_len = shape[1];
let num_kv_heads = shape[2];
let head_dim = shape[3];
// Expand tensor by repeating along the head dimension
// [batch, seq, num_kv_heads, head_dim] -> [batch, seq, num_kv_heads, repeat_factor, head_dim]
let expanded = tensor.unsqueeze(3)?; // [batch, seq, num_kv_heads, 1, head_dim]
let repeated =
expanded.expand(&[batch_size, seq_len, num_kv_heads, repeat_factor, head_dim])?;
// Reshape to [batch, seq, num_kv_heads * repeat_factor, head_dim]
repeated
.view([batch_size, seq_len, num_kv_heads * repeat_factor, head_dim])
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Compute scaled dot-product attention
fn scaled_dot_product_attention(&self, q: &Tensor, k: &Tensor, v: &Tensor) -> Result<Tensor> {
let scale = (self.head_dim as f32).recip().sqrt();
// Transpose for attention computation: [batch, seq, heads, dim] -> [batch, heads, seq, dim]
let q = q.transpose(1, 2)?;
let k = k.transpose(1, 2)?;
let v = v.transpose(1, 2)?;
// Compute attention scores: Q @ K^T / sqrt(d_k)
let k_transposed = k.transpose(-2, -1)?;
let attention_scores = q.matmul(&k_transposed)?.mul_scalar(scale)?;
// Apply causal mask (lower triangular)
let seq_len = attention_scores.shape()[2];
let causal_mask = self.create_causal_mask(seq_len)?;
let masked_scores = attention_scores.add(&causal_mask)?;
// Apply softmax
let attention_probs = masked_scores.softmax(-1)?;
// Apply attention to values: attention_probs @ V
let attention_output = attention_probs.matmul(&v)?;
// Transpose back: [batch, heads, seq, dim] -> [batch, seq, heads, dim]
attention_output
.transpose(1, 2)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Create causal attention mask
fn create_causal_mask(&self, seq_len: usize) -> Result<Tensor> {
// Create a lower triangular mask filled with 0s and -inf
let mut mask_data = Vec::with_capacity(seq_len * seq_len);
for i in 0..seq_len {
for j in 0..seq_len {
if j <= i {
mask_data.push(0.0f32); // Allow attention
} else {
mask_data.push(f32::NEG_INFINITY); // Mask future positions
}
}
}
Tensor::from_vec(mask_data, &[seq_len, seq_len], &self.device)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
}
/// `RMSNorm` layer for `LLaMA`
#[derive(Debug)]
pub struct RMSNorm {
hidden_size: usize,
eps: f64,
weight: Tensor,
}
impl RMSNorm {
/// Create new `RMSNorm` layer
pub fn new(hidden_size: usize, eps: f64, device: &Device) -> Result<Self> {
// Initialize weight to ones
let weight = Tensor::ones([hidden_size], device)?;
Ok(Self {
hidden_size,
eps,
weight,
})
}
/// Get the weight tensor (for parameter access)
#[must_use]
pub fn weight(&self) -> &Tensor {
&self.weight
}
/// Forward pass of `RMSNorm`
pub fn forward(&self, hidden_states: &Tensor) -> Result<Tensor> {
// RMSNorm: x / sqrt(mean(x^2) + eps) * weight
// Compute x^2
let x_squared = hidden_states.mul(hidden_states)?;
// Compute mean along last dimension (hidden_size)
let mean_squared = x_squared.mean_dim(-1, true)?; // Keep dim for broadcasting
// Add epsilon for numerical stability
let variance = mean_squared.add_scalar(self.eps as f32)?;
// Compute 1 / sqrt(variance)
let inv_std = variance.rsqrt()?;
// Normalize
let normalized = hidden_states.mul(&inv_std)?;
// Apply learnable weight
normalized
.mul(&self.weight)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
}
/// `SwiGLU` activation function for `LLaMA` MLP
#[derive(Debug)]
pub struct SwiGLU {
gate_proj: Tensor, // W_gate: [hidden_size, intermediate_size]
up_proj: Tensor, // W_up: [hidden_size, intermediate_size]
down_proj: Tensor, // W_down: [intermediate_size, hidden_size]
}
impl SwiGLU {
/// Create new `SwiGLU` layer
pub fn new(config: &LLaMAConfig, device: &Device) -> Result<Self> {
let scale = (config.hidden_size as f32).recip().sqrt();
// Initialize projection weights
let gate_proj = Tensor::randn(&[config.hidden_size, config.intermediate_size], device)?
.mul_scalar(scale)?;
let up_proj = Tensor::randn(&[config.hidden_size, config.intermediate_size], device)?
.mul_scalar(scale)?;
let down_proj = Tensor::randn(&[config.intermediate_size, config.hidden_size], device)?
.mul_scalar(scale)?;
Ok(Self {
gate_proj,
up_proj,
down_proj,
})
}
/// Forward pass of `SwiGLU`
pub fn forward(&self, hidden_states: &Tensor) -> Result<Tensor> {
// SwiGLU(x) = Swish(xW_gate) * (xW_up) @ W_down
// where Swish(x) = x * sigmoid(x)
// Gate projection and activation
let gate_output = hidden_states.matmul(&self.gate_proj)?;
let gate_activated = self.swish(&gate_output)?;
// Up projection (no activation)
let up_output = hidden_states.matmul(&self.up_proj)?;
// Element-wise multiplication (gating)
let gated = gate_activated.mul(&up_output)?;
// Down projection
gated
.matmul(&self.down_proj)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
/// Swish activation function: x * sigmoid(x)
fn swish(&self, x: &Tensor) -> Result<Tensor> {
// Swish(x) = x * sigmoid(x) = x * (1 / (1 + exp(-x)))
let sigmoid = x.sigmoid()?;
x.mul(&sigmoid)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
}
/// `LLaMA` MLP layer
#[derive(Debug)]
pub struct LLaMAMLP {
swiglu: SwiGLU,
// TODO: Add down projection
}
impl LLaMAMLP {
/// Create new `LLaMA` MLP layer
pub fn new(config: &LLaMAConfig, device: &Device) -> Result<Self> {
let swiglu = SwiGLU::new(config, device)?;
Ok(Self { swiglu })
}
/// Forward pass of MLP
pub fn forward(&self, hidden_states: &Tensor) -> Result<Tensor> {
// TODO: Implement proper MLP computation
// For now, just pass through SwiGLU
self.swiglu.forward(hidden_states)
}
}
/// Complete `LLaMA` transformer layer
#[derive(Debug)]
pub struct LLaMADecoderLayer {
self_attn: LLaMAAttention,
mlp: LLaMAMLP,
input_layernorm: RMSNorm,
post_attention_layernorm: RMSNorm,
}
impl LLaMADecoderLayer {
/// Create new `LLaMA` decoder layer
pub fn new(config: &LLaMAConfig, device: &Device) -> Result<Self> {
let self_attn = LLaMAAttention::new(config, device)?;
let mlp = LLaMAMLP::new(config, device)?;
let input_layernorm = RMSNorm::new(config.hidden_size, config.rms_norm_eps, device)?;
let post_attention_layernorm =
RMSNorm::new(config.hidden_size, config.rms_norm_eps, device)?;
Ok(Self {
self_attn,
mlp,
input_layernorm,
post_attention_layernorm,
})
}
/// Forward pass of decoder layer
pub fn forward(
&self,
hidden_states: &Tensor,
attention_mask: Option<&Tensor>,
) -> Result<Tensor> {
// Pre-attention norm
let normed_hidden_states = self.input_layernorm.forward(hidden_states)?;
// Self-attention with residual connection
let attn_output = self
.self_attn
.forward(&normed_hidden_states, attention_mask)?;
let hidden_states = hidden_states.add(&attn_output)?;
// Pre-MLP norm
let normed_hidden_states = self.post_attention_layernorm.forward(&hidden_states)?;
// MLP with residual connection
let mlp_output = self.mlp.forward(&normed_hidden_states)?;
let hidden_states = hidden_states.add(&mlp_output)?;
Ok(hidden_states)
}
}
/// Complete `LLaMA` model
#[derive(Debug)]
pub struct LLaMAModel {
config: LLaMAConfig,
layers: Vec<LLaMADecoderLayer>,
norm: RMSNorm,
// TODO: Add embed_tokens and lm_head
}
impl LLaMAModel {
/// Create new `LLaMA` model
pub fn new(config: LLaMAConfig, device: &Device) -> Result<Self> {
let mut layers = Vec::new();
for _ in 0..config.num_hidden_layers {
layers.push(LLaMADecoderLayer::new(&config, device)?);
}
let norm = RMSNorm::new(config.hidden_size, config.rms_norm_eps, device)?;
Ok(Self {
config,
layers,
norm,
})
}
/// Forward pass of `LLaMA` model
pub fn forward(&self, input_ids: &Tensor, _attention_mask: Option<&Tensor>) -> Result<Tensor> {
// TODO: Implement proper forward pass with:
// 1. Token embeddings
// 2. All transformer layers
// 3. Final normalization
// 4. Language modeling head
// For now, just return a dummy tensor
let device = Device::cuda(0).unwrap_or_default();
Tensor::zeros(
vec![1, input_ids.shape().dims()[1], self.config.hidden_size],
&device,
)
.map_err(|e| TransformerError::tensor_op(e.to_string()))
}
}