#[derive(Debug, Clone)]
pub struct DoraLayer {
pub in_features: usize,
pub out_features: usize,
pub rank: usize,
pub alpha: f32,
pub dropout: f32,
}
impl DoraLayer {
pub fn new(in_features: usize, out_features: usize, rank: usize, alpha: f32) -> Self {
Self {
in_features,
out_features,
rank,
alpha,
dropout: 0.0,
}
}
pub fn scaling(&self) -> f32 {
self.alpha / self.rank as f32
}
pub fn trainable_params(&self) -> usize {
let lora_params = self.rank * self.in_features + self.out_features * self.rank;
let magnitude_params = self.out_features; lora_params + magnitude_params
}
pub fn frozen_params(&self) -> usize {
self.in_features * self.out_features
}
pub fn adapter_bytes(&self) -> usize {
self.trainable_params() * 2 }
pub fn frozen_base_bytes(&self) -> usize {
self.frozen_params() * 2 }
pub fn total_vram_bytes(&self) -> usize {
self.frozen_base_bytes() + self.adapter_bytes()
}
pub fn vram_savings_ratio(&self) -> f64 {
let full_trainable = self.frozen_params() * 2; let adapter_only = self.adapter_bytes();
1.0 - (adapter_only as f64 / full_trainable as f64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dora_layer() {
let layer = DoraLayer::new(4096, 4096, 16, 32.0);
let expected = 16 * 4096 + 4096 * 16 + 4096;
assert_eq!(layer.trainable_params(), expected);
assert_eq!(layer.scaling(), 2.0);
}
#[test]
fn test_dora_vram_estimation() {
let layer = DoraLayer::new(4096, 4096, 16, 32.0);
assert!(layer.adapter_bytes() > 0);
assert!(layer.frozen_base_bytes() > 0);
assert!(layer.total_vram_bytes() > layer.adapter_bytes());
assert!(layer.vram_savings_ratio() > 0.0);
assert!(layer.vram_savings_ratio() < 1.0);
}
}