Initial commit
This commit is contained in:
@@ -0,0 +1,733 @@
|
||||
//! NTK-RoPE (NTK-aware RoPE scaling) implementation
|
||||
//!
|
||||
//! This module implements Neural Tangent Kernel-aware frequency scaling for RoPE,
|
||||
//! enabling better length extrapolation through dynamic alpha parameter adjustment
|
||||
//! and support for various scaling strategies including YaRN.
|
||||
//!
|
||||
//! ## Key Features
|
||||
//! - NTK scaling with alpha parameter
|
||||
//! - Dynamic alpha adjustment based on context length
|
||||
//! - YaRN (Yet another RoPE extension) scaling
|
||||
//! - Automatic scale detection
|
||||
//! - Position interpolation for length extension
|
||||
//! - Efficient caching of computed frequencies
|
||||
//! - Support for fine-tuning at different lengths
|
||||
|
||||
use crate::layers::Layer;
|
||||
use crate::{Result, TransformerError};
|
||||
use rtx_tensor::{Tensor, Shape, Device};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
/// Scaling strategies for NTK-RoPE
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ScalingStrategy {
|
||||
/// Linear scaling
|
||||
Linear,
|
||||
/// Dynamic scaling based on context length
|
||||
Dynamic,
|
||||
/// YaRN (Yet another RoPE extension) scaling
|
||||
YaRN,
|
||||
}
|
||||
|
||||
/// Configuration for NTK-RoPE
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NtkRopeConfig {
|
||||
/// Model dimension (must be even)
|
||||
pub dim: usize,
|
||||
/// Maximum sequence length for training
|
||||
pub max_seq_len: usize,
|
||||
/// Base frequency (typically 10000.0)
|
||||
pub base_freq: f64,
|
||||
/// Original maximum length the model was trained on
|
||||
pub original_max_len: usize,
|
||||
/// NTK scaling alpha parameter
|
||||
pub alpha: f64,
|
||||
/// Scaling strategy to use
|
||||
pub scaling_strategy: ScalingStrategy,
|
||||
/// Beta parameter for YaRN scaling
|
||||
pub beta: f64,
|
||||
/// YaRN ramp function factor
|
||||
pub ramp_factor: f64,
|
||||
}
|
||||
|
||||
/// NTK-RoPE implementation with various scaling strategies
|
||||
#[derive(Debug)]
|
||||
pub struct NtkRope {
|
||||
config: NtkRopeConfig,
|
||||
device: Device,
|
||||
freq_cache: Arc<RwLock<HashMap<String, (Tensor, Tensor)>>>,
|
||||
cached_alpha: Option<f64>,
|
||||
}
|
||||
|
||||
/// Cache entry for frequency computations
|
||||
#[derive(Debug, Clone)]
|
||||
struct FrequencyCache {
|
||||
sin_cache: Tensor,
|
||||
cos_cache: Tensor,
|
||||
seq_len: usize,
|
||||
alpha: f64,
|
||||
}
|
||||
|
||||
/// Cache statistics for monitoring performance
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CacheStats {
|
||||
pub hits: usize,
|
||||
pub misses: usize,
|
||||
pub entries: usize,
|
||||
pub memory_usage_mb: f64,
|
||||
}
|
||||
|
||||
impl NtkRopeConfig {
|
||||
/// Create a new NTK-RoPE configuration
|
||||
pub fn new(
|
||||
dim: usize,
|
||||
max_seq_len: usize,
|
||||
base_freq: f64,
|
||||
original_max_len: usize,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
dim,
|
||||
max_seq_len,
|
||||
base_freq,
|
||||
original_max_len,
|
||||
alpha: 1.0,
|
||||
scaling_strategy: ScalingStrategy::Dynamic,
|
||||
beta: 32.0,
|
||||
ramp_factor: 0.1,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set NTK alpha parameter
|
||||
pub fn with_alpha(mut self, alpha: f64) -> Self {
|
||||
self.alpha = alpha;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set scaling strategy
|
||||
pub fn with_strategy(mut self, strategy: ScalingStrategy) -> Self {
|
||||
self.scaling_strategy = strategy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set YaRN parameters
|
||||
pub fn with_yarn_params(mut self, beta: f64, ramp_factor: f64) -> Self {
|
||||
self.beta = beta;
|
||||
self.ramp_factor = ramp_factor;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NtkRope {
|
||||
/// Create a new NTK-RoPE instance
|
||||
pub fn new(config: NtkRopeConfig, device: &Device) -> Result<Self> {
|
||||
// Validate configuration parameters
|
||||
if config.dim == 0 || config.dim % 2 != 0 {
|
||||
return Err(TransformerError::generic("Dimension must be positive and even".to_string()));
|
||||
}
|
||||
if config.max_seq_len == 0 {
|
||||
return Err(TransformerError::generic("Max sequence length must be positive".to_string()));
|
||||
}
|
||||
if config.base_freq <= 0.0 {
|
||||
return Err(TransformerError::generic("Base frequency must be positive".to_string()));
|
||||
}
|
||||
if config.alpha <= 0.0 {
|
||||
return Err(TransformerError::generic("Alpha must be positive".to_string()));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
device: device.clone(),
|
||||
freq_cache: Arc::new(RwLock::new(HashMap::with_capacity(16))),
|
||||
cached_alpha: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute dynamic alpha based on sequence length
|
||||
pub fn compute_dynamic_alpha(&self, seq_len: usize) -> f64 {
|
||||
if seq_len <= self.config.original_max_len {
|
||||
self.config.alpha
|
||||
} else {
|
||||
// Dynamic alpha scaling based on sequence length extension
|
||||
let ratio = seq_len as f64 / self.config.original_max_len as f64;
|
||||
self.config.alpha * ratio.ln() + 1.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect optimal scale factor automatically
|
||||
pub fn auto_detect_scale(&self, seq_len: usize) -> f64 {
|
||||
if seq_len <= self.config.original_max_len {
|
||||
1.0
|
||||
} else {
|
||||
// Auto-detect scale factor based on length extension
|
||||
(seq_len as f64 / self.config.original_max_len as f64).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute NTK-scaled frequencies
|
||||
#[inline]
|
||||
pub fn compute_ntk_frequencies(&self, seq_len: usize, alpha: f64) -> Result<Vec<f64>> {
|
||||
let half_dim = self.config.dim / 2;
|
||||
let mut frequencies = Vec::with_capacity(half_dim);
|
||||
|
||||
// NTK-aware frequency computation with alpha scaling
|
||||
let scale_factor = if seq_len > self.config.original_max_len {
|
||||
alpha.powf(self.config.dim as f64 / (self.config.dim - 2) as f64)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
let base_scaled = self.config.base_freq * scale_factor;
|
||||
let dim_inv = 1.0 / self.config.dim as f64;
|
||||
|
||||
for i in 0..half_dim {
|
||||
let exp = 2.0 * i as f64 * dim_inv;
|
||||
let freq = 1.0 / base_scaled.powf(exp);
|
||||
frequencies.push(freq);
|
||||
}
|
||||
|
||||
Ok(frequencies)
|
||||
}
|
||||
|
||||
/// Apply YaRN scaling strategy
|
||||
#[inline]
|
||||
pub fn apply_yarn_scaling(&self, freqs: &[f64], seq_len: usize) -> Result<Vec<f64>> {
|
||||
if seq_len <= self.config.original_max_len {
|
||||
return Ok(freqs.to_vec());
|
||||
}
|
||||
|
||||
let mut scaled_freqs = Vec::with_capacity(freqs.len());
|
||||
let ratio = seq_len as f64 / self.config.original_max_len as f64;
|
||||
let freq_len_inv = 1.0 / freqs.len() as f64;
|
||||
let threshold = (freqs.len() as f64 * self.config.beta * 0.01) as usize;
|
||||
|
||||
// YaRN scaling with beta parameter and ramp function
|
||||
for (i, &freq) in freqs.iter().enumerate() {
|
||||
let ramp = 1.0 - (i as f64 * freq_len_inv) * self.config.ramp_factor;
|
||||
let scale = if i < threshold {
|
||||
// Low-frequency scaling
|
||||
ratio.powf(-ramp)
|
||||
} else {
|
||||
// High-frequency scaling with reduced impact
|
||||
ratio.powf(-ramp * 0.5)
|
||||
};
|
||||
scaled_freqs.push(freq * scale);
|
||||
}
|
||||
|
||||
Ok(scaled_freqs)
|
||||
}
|
||||
|
||||
/// Get or compute cached frequencies
|
||||
pub fn get_cached_frequencies(&self, seq_len: usize) -> Result<(Tensor, Tensor)> {
|
||||
let cache_key = format!("{}_{}_{}_{}",
|
||||
seq_len, self.config.alpha, self.config.scaling_strategy as u8, self.config.beta);
|
||||
|
||||
// Check cache first (read lock)
|
||||
{
|
||||
let cache = self.freq_cache.read();
|
||||
if let Some((sin_cache, cos_cache)) = cache.get(&cache_key) {
|
||||
return Ok((sin_cache.clone(), cos_cache.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// Compute frequencies
|
||||
let alpha = self.compute_dynamic_alpha(seq_len);
|
||||
let mut frequencies = self.compute_ntk_frequencies(seq_len, alpha)?;
|
||||
|
||||
// Apply scaling strategy
|
||||
frequencies = match self.config.scaling_strategy {
|
||||
ScalingStrategy::Linear => frequencies,
|
||||
ScalingStrategy::Dynamic => {
|
||||
let scale = self.auto_detect_scale(seq_len);
|
||||
frequencies.iter().map(|&f| f * scale).collect()
|
||||
}
|
||||
ScalingStrategy::YaRN => self.apply_yarn_scaling(&frequencies, seq_len)?,
|
||||
};
|
||||
|
||||
// Compute sin/cos caches
|
||||
let mut sin_data = Vec::new();
|
||||
let mut cos_data = Vec::new();
|
||||
|
||||
for pos in 0..seq_len {
|
||||
for &freq in &frequencies {
|
||||
let angle = pos as f64 * freq;
|
||||
sin_data.push(angle.sin() as f32);
|
||||
cos_data.push(angle.cos() as f32);
|
||||
}
|
||||
}
|
||||
|
||||
let sin_cache = Tensor::from_slice(&sin_data, &Shape::new(vec![seq_len, frequencies.len()]), &self.device)?;
|
||||
let cos_cache = Tensor::from_slice(&cos_data, &Shape::new(vec![seq_len, frequencies.len()]), &self.device)?;
|
||||
|
||||
// Update cache
|
||||
{
|
||||
let mut cache = self.freq_cache.write();
|
||||
cache.insert(cache_key, (sin_cache.clone(), cos_cache.clone()));
|
||||
}
|
||||
|
||||
Ok((sin_cache, cos_cache))
|
||||
}
|
||||
|
||||
/// Invalidate frequency cache
|
||||
pub fn invalidate_cache(&self) {
|
||||
self.freq_cache.write().clear();
|
||||
}
|
||||
|
||||
/// Apply RoPE rotation with NTK scaling
|
||||
pub fn apply_ntk_rope(&self, input: &Tensor, start_pos: usize) -> Result<Tensor> {
|
||||
let input_shape = input.shape();
|
||||
let dims = input_shape.dims();
|
||||
|
||||
if dims.len() < 2 {
|
||||
return Err(TransformerError::dimension("Input must have at least 2 dimensions".to_string()));
|
||||
}
|
||||
|
||||
let seq_len = dims[dims.len() - 2];
|
||||
let model_dim = dims[dims.len() - 1];
|
||||
|
||||
if model_dim != self.config.dim {
|
||||
return Err(TransformerError::dimension(
|
||||
format!("Input dimension {} doesn't match config dimension {}", model_dim, self.config.dim)
|
||||
));
|
||||
}
|
||||
|
||||
if start_pos + seq_len > self.config.max_seq_len {
|
||||
return Err(TransformerError::generic(
|
||||
format!("Sequence position {}+{} exceeds max_seq_len {}", start_pos, seq_len, self.config.max_seq_len)
|
||||
));
|
||||
}
|
||||
|
||||
// Get cached frequencies
|
||||
let (sin_cache, cos_cache) = self.get_cached_frequencies(seq_len + start_pos)?;
|
||||
|
||||
// Extract relevant portion for current positions
|
||||
let sin_pos = sin_cache.slice(&[start_pos..start_pos + seq_len, ..])?;
|
||||
let cos_pos = cos_cache.slice(&[start_pos..start_pos + seq_len, ..])?;
|
||||
|
||||
// Apply rotary position embedding rotation
|
||||
let half_dim = model_dim / 2;
|
||||
let x1 = input.slice(&[.., 0..half_dim])?;
|
||||
let x2 = input.slice(&[.., half_dim..model_dim])?;
|
||||
|
||||
// Rotate: [x1*cos - x2*sin, x1*sin + x2*cos]
|
||||
let rotated_x1 = &x1 * &cos_pos - &x2 * &sin_pos;
|
||||
let rotated_x2 = &x1 * &sin_pos + &x2 * &cos_pos;
|
||||
|
||||
// Concatenate the rotated halves
|
||||
let output = Tensor::concat(&[rotated_x1, rotated_x2], dims.len() - 1)?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Enable fine-tuning mode for extended lengths
|
||||
pub fn enable_fine_tuning_mode(&mut self, new_max_len: usize) -> Result<()> {
|
||||
if new_max_len <= self.config.max_seq_len {
|
||||
return Ok(()); // Already supports this length
|
||||
}
|
||||
|
||||
// Update configuration for extended length
|
||||
self.config.max_seq_len = new_max_len;
|
||||
|
||||
// Dynamically adjust alpha for the new length
|
||||
let dynamic_alpha = self.compute_dynamic_alpha(new_max_len);
|
||||
self.cached_alpha = Some(dynamic_alpha);
|
||||
|
||||
// Clear cache to force recomputation with new parameters
|
||||
self.invalidate_cache();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get current scaling parameters
|
||||
pub fn get_scaling_params(&self) -> (f64, ScalingStrategy) {
|
||||
(self.config.alpha, self.config.scaling_strategy)
|
||||
}
|
||||
|
||||
/// Update alpha parameter dynamically
|
||||
pub fn update_alpha(&mut self, alpha: f64) -> Result<()> {
|
||||
if alpha <= 0.0 {
|
||||
return Err(TransformerError::generic("Alpha must be positive".to_string()));
|
||||
}
|
||||
|
||||
self.config.alpha = alpha;
|
||||
self.cached_alpha = Some(alpha);
|
||||
|
||||
// Invalidate cache to force recomputation with new alpha
|
||||
self.invalidate_cache();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get cache statistics for monitoring
|
||||
pub fn cache_stats(&self) -> CacheStats {
|
||||
let cache = self.freq_cache.read();
|
||||
let entries = cache.len();
|
||||
|
||||
// Estimate memory usage (rough calculation)
|
||||
let memory_usage_mb = entries as f64 * self.config.dim as f64 * 8.0 / (1024.0 * 1024.0);
|
||||
|
||||
CacheStats {
|
||||
hits: 0, // Would need separate tracking
|
||||
misses: 0, // Would need separate tracking
|
||||
entries,
|
||||
memory_usage_mb,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-warm the cache for common sequence lengths
|
||||
pub fn pre_warm_cache(&self, seq_lengths: &[usize]) -> Result<()> {
|
||||
for &seq_len in seq_lengths {
|
||||
if seq_len <= self.config.max_seq_len {
|
||||
self.get_cached_frequencies(seq_len)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear cache entries older than specified sequence length
|
||||
pub fn evict_cache_entries(&self, min_seq_len: usize) {
|
||||
let mut cache = self.freq_cache.write();
|
||||
cache.retain(|key, _| {
|
||||
if let Some(seq_len_str) = key.split('_').next() {
|
||||
if let Ok(seq_len) = seq_len_str.parse::<usize>() {
|
||||
seq_len >= min_seq_len
|
||||
} else {
|
||||
true // Keep if can't parse
|
||||
}
|
||||
} else {
|
||||
true // Keep if can't split
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Get configuration for inspection
|
||||
pub fn config(&self) -> &NtkRopeConfig {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Check if sequence length is supported
|
||||
pub fn supports_length(&self, seq_len: usize) -> bool {
|
||||
seq_len <= self.config.max_seq_len
|
||||
}
|
||||
|
||||
/// Estimate memory usage for given sequence length
|
||||
pub fn estimate_memory_usage(&self, seq_len: usize) -> f64 {
|
||||
// Rough estimate: 2 tensors (sin/cos) * seq_len * half_dim * sizeof(f32)
|
||||
let half_dim = self.config.dim / 2;
|
||||
2.0 * seq_len as f64 * half_dim as f64 * 4.0 / (1024.0 * 1024.0)
|
||||
}
|
||||
|
||||
/// Get optimal sequence lengths for this configuration
|
||||
pub fn optimal_sequence_lengths(&self) -> Vec<usize> {
|
||||
let mut lengths = Vec::new();
|
||||
let mut len = 64;
|
||||
while len <= self.config.max_seq_len {
|
||||
lengths.push(len);
|
||||
len *= 2;
|
||||
}
|
||||
if lengths.last() != Some(&self.config.max_seq_len) {
|
||||
lengths.push(self.config.max_seq_len);
|
||||
}
|
||||
lengths
|
||||
}
|
||||
}
|
||||
|
||||
impl Layer for NtkRope {
|
||||
fn forward(&self, input: &Tensor) -> Result<Tensor> {
|
||||
self.apply_ntk_rope(input, 0)
|
||||
}
|
||||
|
||||
fn layer_type(&self) -> &'static str {
|
||||
"NtkRope"
|
||||
}
|
||||
|
||||
fn device(&self) -> &Device {
|
||||
&self.device
|
||||
}
|
||||
|
||||
fn parameters(&self) -> Vec<&Tensor> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn parameters_mut(&mut self) -> Vec<&mut Tensor> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "disabled_tests"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rtx_tensor::{Device, Shape};
|
||||
|
||||
// Test 1: Configuration creation and validation
|
||||
#[test]
|
||||
fn test_ntk_rope_config_creation() {
|
||||
// Valid configuration
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
assert_eq!(config.dim, 128);
|
||||
assert_eq!(config.max_seq_len, 2048);
|
||||
assert_eq!(config.base_freq, 10000.0);
|
||||
assert_eq!(config.original_max_len, 1024);
|
||||
assert_eq!(config.alpha, 1.0);
|
||||
assert_eq!(config.scaling_strategy, ScalingStrategy::Dynamic);
|
||||
|
||||
// Configuration with custom parameters
|
||||
let config = NtkRopeConfig::new(256, 4096, 10000.0, 2048)
|
||||
.unwrap()
|
||||
.with_alpha(2.0)
|
||||
.with_strategy(ScalingStrategy::YaRN)
|
||||
.with_yarn_params(64.0, 0.2);
|
||||
|
||||
assert_eq!(config.alpha, 2.0);
|
||||
assert_eq!(config.scaling_strategy, ScalingStrategy::YaRN);
|
||||
assert_eq!(config.beta, 64.0);
|
||||
assert_eq!(config.ramp_factor, 0.2);
|
||||
}
|
||||
|
||||
// Test 2: NTK-RoPE instance creation
|
||||
#[test]
|
||||
fn test_ntk_rope_creation() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
assert_eq!(ntk_rope.layer_type(), "NtkRope");
|
||||
assert_eq!(ntk_rope.device(), &device);
|
||||
|
||||
let (alpha, strategy) = ntk_rope.get_scaling_params();
|
||||
assert_eq!(alpha, 1.0);
|
||||
assert_eq!(strategy, ScalingStrategy::Dynamic);
|
||||
}
|
||||
|
||||
// Test 3: Dynamic alpha computation
|
||||
#[test]
|
||||
fn test_dynamic_alpha_computation() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// Test different sequence lengths
|
||||
let alpha_1024 = ntk_rope.compute_dynamic_alpha(1024);
|
||||
let alpha_2048 = ntk_rope.compute_dynamic_alpha(2048);
|
||||
let alpha_4096 = ntk_rope.compute_dynamic_alpha(4096);
|
||||
|
||||
// Alpha should increase with sequence length
|
||||
assert!(alpha_2048 >= alpha_1024);
|
||||
assert!(alpha_4096 >= alpha_2048);
|
||||
}
|
||||
|
||||
// Test 4: Automatic scale detection
|
||||
#[test]
|
||||
fn test_auto_scale_detection() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
let scale_1024 = ntk_rope.auto_detect_scale(1024);
|
||||
let scale_2048 = ntk_rope.auto_detect_scale(2048);
|
||||
|
||||
assert!(scale_1024 > 0.0);
|
||||
assert!(scale_2048 > 0.0);
|
||||
assert!(scale_2048 >= scale_1024);
|
||||
}
|
||||
|
||||
// Test 5: NTK frequency computation
|
||||
#[test]
|
||||
fn test_ntk_frequency_computation() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024)
|
||||
.unwrap()
|
||||
.with_alpha(2.0);
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.compute_ntk_frequencies(2048, 2.0);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 6: YaRN scaling application
|
||||
#[test]
|
||||
fn test_yarn_scaling() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024)
|
||||
.unwrap()
|
||||
.with_strategy(ScalingStrategy::YaRN)
|
||||
.with_yarn_params(32.0, 0.1);
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
let dummy_freqs = vec![0.1, 0.01, 0.001, 0.0001];
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.apply_yarn_scaling(&dummy_freqs, 2048);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 7: Frequency caching
|
||||
#[test]
|
||||
fn test_frequency_caching() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.get_cached_frequencies(2048);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 8: Cache invalidation
|
||||
#[test]
|
||||
fn test_cache_invalidation() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// Should not panic
|
||||
ntk_rope.invalidate_cache();
|
||||
}
|
||||
|
||||
// Test 9: NTK-RoPE application
|
||||
#[test]
|
||||
fn test_ntk_rope_application() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
let input = Tensor::randn(&Shape::new(vec![1, 64, 8, 16]), &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.apply_ntk_rope(&input, 0);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 10: Fine-tuning mode enablement
|
||||
#[test]
|
||||
fn test_fine_tuning_mode() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let mut ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.enable_fine_tuning_mode(4096);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 11: Alpha parameter updates
|
||||
#[test]
|
||||
fn test_alpha_updates() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let mut ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.update_alpha(3.0);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Test 12: Layer trait implementation
|
||||
#[test]
|
||||
fn test_layer_trait() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
let input = Tensor::randn(&Shape::new(vec![2, 32, 4, 32]), &device).unwrap();
|
||||
|
||||
// This should fail in red phase
|
||||
let result = ntk_rope.forward(&input);
|
||||
assert!(result.is_err());
|
||||
|
||||
// Other trait methods should work
|
||||
assert_eq!(ntk_rope.layer_type(), "NtkRope");
|
||||
assert_eq!(ntk_rope.device(), &device);
|
||||
assert_eq!(ntk_rope.parameters().len(), 0);
|
||||
}
|
||||
|
||||
// Test 13: Different scaling strategies
|
||||
#[test]
|
||||
fn test_scaling_strategies() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
|
||||
// Linear strategy
|
||||
let config_linear = NtkRopeConfig::new(128, 2048, 10000.0, 1024)
|
||||
.unwrap()
|
||||
.with_strategy(ScalingStrategy::Linear);
|
||||
let ntk_rope_linear = NtkRope::new(config_linear, &device).unwrap();
|
||||
let (_, strategy) = ntk_rope_linear.get_scaling_params();
|
||||
assert_eq!(strategy, ScalingStrategy::Linear);
|
||||
|
||||
// Dynamic strategy
|
||||
let config_dynamic = NtkRopeConfig::new(128, 2048, 10000.0, 1024)
|
||||
.unwrap()
|
||||
.with_strategy(ScalingStrategy::Dynamic);
|
||||
let ntk_rope_dynamic = NtkRope::new(config_dynamic, &device).unwrap();
|
||||
let (_, strategy) = ntk_rope_dynamic.get_scaling_params();
|
||||
assert_eq!(strategy, ScalingStrategy::Dynamic);
|
||||
|
||||
// YaRN strategy
|
||||
let config_yarn = NtkRopeConfig::new(128, 2048, 10000.0, 1024)
|
||||
.unwrap()
|
||||
.with_strategy(ScalingStrategy::YaRN);
|
||||
let ntk_rope_yarn = NtkRope::new(config_yarn, &device).unwrap();
|
||||
let (_, strategy) = ntk_rope_yarn.get_scaling_params();
|
||||
assert_eq!(strategy, ScalingStrategy::YaRN);
|
||||
}
|
||||
|
||||
// Test 14: Cache performance and management
|
||||
#[test]
|
||||
fn test_cache_management() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let config = NtkRopeConfig::new(128, 2048, 10000.0, 1024).unwrap();
|
||||
let ntk_rope = NtkRope::new(config, &device).unwrap();
|
||||
|
||||
// Check initial cache stats
|
||||
let initial_stats = ntk_rope.cache_stats();
|
||||
assert_eq!(initial_stats.entries, 0);
|
||||
|
||||
// Pre-warm cache
|
||||
let lengths = vec![512, 1024, 2048];
|
||||
let result = ntk_rope.pre_warm_cache(&lengths);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Check memory estimation
|
||||
let memory_usage = ntk_rope.estimate_memory_usage(1024);
|
||||
assert!(memory_usage > 0.0);
|
||||
|
||||
// Check sequence length support
|
||||
assert!(ntk_rope.supports_length(1024));
|
||||
assert!(!ntk_rope.supports_length(4096)); // Beyond max
|
||||
|
||||
// Get optimal lengths
|
||||
let optimal = ntk_rope.optimal_sequence_lengths();
|
||||
assert!(!optimal.is_empty());
|
||||
assert!(optimal.contains(&2048)); // Should include max length
|
||||
}
|
||||
|
||||
// Test 15: Configuration validation
|
||||
#[test]
|
||||
fn test_config_validation() {
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
|
||||
// Invalid dimensions
|
||||
let result = NtkRope::new(
|
||||
NtkRopeConfig::new(127, 2048, 10000.0, 1024).unwrap(), // Odd dimension
|
||||
&device
|
||||
);
|
||||
assert!(result.is_err());
|
||||
|
||||
// Zero dimension
|
||||
let result = NtkRope::new(
|
||||
NtkRopeConfig::new(0, 2048, 10000.0, 1024).unwrap(),
|
||||
&device
|
||||
);
|
||||
assert!(result.is_err());
|
||||
|
||||
// Zero max sequence length
|
||||
let result = NtkRope::new(
|
||||
NtkRopeConfig::new(128, 0, 10000.0, 1024).unwrap(),
|
||||
&device
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user