Skip to main content

voxtral_micro/models/
time_embedding.rs

1//! Time embedding for Voxtral Realtime.
2//!
3//! Sinusoidal embedding that encodes the transcription delay.
4
5use burn::prelude::*;
6
7/// Time embedding module that produces sinusoidal embeddings.
8///
9/// Used to encode the transcription delay as a conditioning signal
10/// for the ADA RMSNorm modulation in the decoder layers.
11#[derive(Debug)]
12pub struct TimeEmbedding {
13    /// Dimension of the embedding
14    dim: usize,
15    /// Base frequency (default: 10000.0)
16    theta: f32,
17}
18
19impl TimeEmbedding {
20    /// Create a new time embedding with given dimension.
21    pub fn new(dim: usize) -> Self {
22        Self {
23            dim,
24            theta: 10000.0,
25        }
26    }
27
28    /// Create a new time embedding with custom theta.
29    pub fn with_theta(dim: usize, theta: f32) -> Self {
30        Self { dim, theta }
31    }
32
33    /// Compute sinusoidal embedding for a time value.
34    ///
35    /// # Arguments
36    /// * `t` - Time value (typically the number of delay tokens)
37    /// * `device` - Device to create the tensor on
38    ///
39    /// # Returns
40    /// Tensor of shape [1, 1, dim] containing the sinusoidal embedding
41    pub fn embed<B: Backend>(&self, t: f32, device: &B::Device) -> Tensor<B, 3> {
42        let half_dim = self.dim / 2;
43
44        // Compute inverse frequencies: exp(-log(theta) * i / (dim/2)) for i in 0..dim/2
45        let mut inv_freq = Vec::with_capacity(half_dim);
46        let log_theta = self.theta.ln();
47        for i in 0..half_dim {
48            let freq = (-log_theta * (i as f32) / (half_dim as f32)).exp();
49            inv_freq.push(freq);
50        }
51
52        // Compute t * inv_freq
53        let mut cos_vals = Vec::with_capacity(half_dim);
54        let mut sin_vals = Vec::with_capacity(half_dim);
55        for &freq in &inv_freq {
56            let angle = t * freq;
57            cos_vals.push(angle.cos());
58            sin_vals.push(angle.sin());
59        }
60
61        // Concatenate [cos, sin] to get full embedding
62        let mut embedding = Vec::with_capacity(self.dim);
63        embedding.extend_from_slice(&cos_vals);
64        embedding.extend_from_slice(&sin_vals);
65
66        // Create tensor with shape [1, 1, dim]
67        Tensor::from_data(
68            burn::tensor::TensorData::new(embedding, [1, 1, self.dim]),
69            device,
70        )
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use burn::backend::Wgpu;
78
79    type TestBackend = Wgpu;
80
81    #[test]
82    fn test_time_embedding_shape() {
83        let device = Default::default();
84        let embed = TimeEmbedding::new(3072);
85
86        let t_cond = embed.embed::<TestBackend>(6.0, &device);
87        assert_eq!(t_cond.dims(), [1, 1, 3072]);
88    }
89
90    #[test]
91    fn test_time_embedding_values() {
92        let device = Default::default();
93        let embed = TimeEmbedding::new(4);
94
95        let t_cond = embed.embed::<TestBackend>(1.0, &device);
96        let data = t_cond.to_data();
97        let slice = data.as_slice::<f32>().unwrap();
98
99        // For t=1, dim=4:
100        // inv_freq[0] = exp(-log(10000) * 0 / 2) = exp(0) = 1.0
101        // inv_freq[1] = exp(-log(10000) * 1 / 2) = exp(-log(10000)/2) = 1/100 = 0.01
102        // cos(1 * 1.0) = cos(1) ≈ 0.5403
103        // cos(1 * 0.01) = cos(0.01) ≈ 0.99995
104        // sin(1 * 1.0) = sin(1) ≈ 0.8415
105        // sin(1 * 0.01) = sin(0.01) ≈ 0.01
106
107        // Check approximate values
108        assert!(
109            (slice[0] - 0.5403).abs() < 0.01,
110            "cos(1) wrong: {}",
111            slice[0]
112        );
113        assert!(
114            (slice[1] - 0.99995).abs() < 0.001,
115            "cos(0.01) wrong: {}",
116            slice[1]
117        );
118        assert!(
119            (slice[2] - 0.8415).abs() < 0.01,
120            "sin(1) wrong: {}",
121            slice[2]
122        );
123        assert!(
124            (slice[3] - 0.01).abs() < 0.001,
125            "sin(0.01) wrong: {}",
126            slice[3]
127        );
128    }
129
130    #[test]
131    fn test_time_embedding_vs_python() {
132        // Test against expected Python output
133        // Python: TimeEmbedding(dim=8, theta=10000)(torch.tensor([6.0]))
134        // Should produce cos and sin of 6 * inv_freq
135        let device = Default::default();
136        let embed = TimeEmbedding::new(8);
137
138        let t_cond = embed.embed::<TestBackend>(6.0, &device);
139        let data = t_cond.to_data();
140        let slice = data.as_slice::<f32>().unwrap();
141
142        // inv_freq = [1.0, 0.1, 0.01, 0.001] (approx for theta=10000, dim=8)
143        // 6 * inv_freq = [6.0, 0.6, 0.06, 0.006]
144        // cos(6) ≈ 0.9602
145        // cos(0.6) ≈ 0.8253
146        // cos(0.06) ≈ 0.9982
147        // cos(0.006) ≈ 0.99998
148
149        println!("t_cond for t=6: {:?}", slice);
150        assert!(slice.len() == 8);
151    }
152}