Coverage Report

Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/realizar/src/gpu/simd_ops.rs
Line
Count
Source
1
//! GPU SIMD Operations Module (PMAT-802)
2
//!
3
//! Extracted from gpu/mod.rs - SIMD-accelerated compute primitives.
4
//!
5
//! ## Contents
6
//! - `scalar_softmax`, `simd_softmax` - Softmax implementations (IMP-038)
7
//! - `scalar_rope`, `simd_rope` - RoPE implementations (IMP-041)
8
9
// ============================================================================
10
// SIMD-accelerated operations (M18 - IMP-038)
11
// ============================================================================
12
13
/// Scalar softmax implementation (baseline for comparison)
14
///
15
/// Computes softmax using standard scalar operations.
16
#[must_use]
17
127
pub fn scalar_softmax(input: &[f32]) -> Vec<f32> {
18
127
    if input.is_empty() {
19
4
        return Vec::new();
20
123
    }
21
22
    // Find max for numerical stability
23
123
    let max_val = input.iter().copied().fold(f32::NEG_INFINITY, f32::max);
24
25
    // Compute exp(x - max) and sum
26
112k
    let 
exp_vals123
:
Vec<f32>123
=
input123
.
iter123
().
map123
(|&x| (x - max_val).exp()).
collect123
();
27
123
    let sum: f32 = exp_vals.iter().sum();
28
29
    // Normalize
30
112k
    
exp_vals.iter()123
.
map123
(|&e| e / sum).
collect123
()
31
127
}
32
33
/// SIMD-accelerated softmax implementation (M18 - IMP-038)
34
///
35
/// Uses Trueno's SIMD operations for vectorized computation.
36
/// Falls back to scalar for unsupported sizes.
37
#[must_use]
38
122
pub fn simd_softmax(input: &[f32]) -> Vec<f32> {
39
122
    if input.is_empty() {
40
4
        return Vec::new();
41
118
    }
42
43
    // Find max using SIMD via trueno
44
118
    let max_val = input.iter().copied().fold(f32::NEG_INFINITY, f32::max);
45
46
    // Compute exp(x - max) - exp is not SIMD accelerated
47
112k
    let 
exp_vals118
:
Vec<f32>118
=
input118
.
iter118
().
map118
(|&x| (x - max_val).exp()).
collect118
();
48
49
    // Sum using trueno's SIMD sum
50
118
    let exp_vec = trueno::Vector::from_slice(&exp_vals);
51
118
    let sum = exp_vec.sum().unwrap_or_else(|_| 
exp_vals.iter()0
.
sum0
());
52
53
    // Normalize
54
112k
    
exp_vals.iter()118
.
map118
(|&e| e / sum).
collect118
()
55
122
}
56
57
// ============================================================================
58
// Scalar and SIMD RoPE implementations (M19 - IMP-041)
59
// ============================================================================
60
61
/// Scalar RoPE (Rotary Position Embedding) implementation
62
///
63
/// Standard scalar implementation of rotary position embeddings.
64
/// Input shape: [seq_len * hidden_dim] flattened
65
#[must_use]
66
21
pub fn scalar_rope(input: &[f32], seq_len: usize, head_dim: usize, theta: f32) -> Vec<f32> {
67
21
    if input.is_empty() || 
seq_len == 017
||
head_dim == 014
{
68
11
        return Vec::new();
69
10
    }
70
71
10
    let hidden_dim = input.len() / seq_len;
72
10
    let num_heads = hidden_dim / head_dim;
73
10
    let mut output = vec![0.0f32; input.len()];
74
75
    // Compute RoPE for each position
76
20
    for pos in 0..
seq_len10
{
77
22
        for head in 0..
num_heads20
{
78
22
            let head_start = pos * hidden_dim + head * head_dim;
79
80
            // Apply rotary embedding to pairs of elements
81
134
            for i in 0..
head_dim / 222
{
82
134
                let freq = 1.0 / theta.powf((2.0 * i as f32) / head_dim as f32);
83
134
                let angle = pos as f32 * freq;
84
134
                let cos_val = angle.cos();
85
134
                let sin_val = angle.sin();
86
87
134
                let idx0 = head_start + i;
88
134
                let idx1 = head_start + i + head_dim / 2;
89
90
134
                if idx1 < input.len() {
91
134
                    let x0 = input[idx0];
92
134
                    let x1 = input[idx1];
93
134
                    output[idx0] = x0 * cos_val - x1 * sin_val;
94
134
                    output[idx1] = x0 * sin_val + x1 * cos_val;
95
134
                
}0
96
            }
97
        }
98
    }
99
100
10
    output
101
21
}
102
103
/// SIMD-accelerated RoPE implementation (M19 - IMP-041)
104
///
105
/// Uses Trueno's SIMD operations for vectorized position encoding.
106
#[must_use]
107
14
pub fn simd_rope(input: &[f32], seq_len: usize, head_dim: usize, theta: f32) -> Vec<f32> {
108
14
    if input.is_empty() || 
seq_len == 010
||
head_dim == 08
{
109
8
        return Vec::new();
110
6
    }
111
112
6
    let hidden_dim = input.len() / seq_len;
113
6
    let num_heads = hidden_dim / head_dim;
114
6
    let half_head = head_dim / 2;
115
116
    // Pre-compute frequency table (cache-friendly)
117
6
    let mut freqs: Vec<f32> = Vec::with_capacity(half_head);
118
36
    for i in 0..
half_head6
{
119
36
        freqs.push(1.0 / theta.powf((2.0 * i as f32) / head_dim as f32));
120
36
    }
121
122
6
    let mut output = vec![0.0f32; input.len()];
123
124
    // Process each position using SIMD operations
125
13
    for pos in 0..
seq_len6
{
126
        // Pre-compute angles for this position
127
80
        let 
angles13
:
Vec<f32>13
=
freqs.iter()13
.
map13
(|&f| pos as f32 * f).
collect13
();
128
80
        let 
cos_vals13
:
Vec<f32>13
=
angles.iter()13
.
map13
(|&a| a.cos()).
collect13
();
129
80
        let 
sin_vals13
:
Vec<f32>13
=
angles.iter()13
.
map13
(|&a| a.sin()).
collect13
();
130
131
        // Use trueno vectors for batch operations
132
13
        let cos_vec = trueno::Vector::from_slice(&cos_vals);
133
13
        let sin_vec = trueno::Vector::from_slice(&sin_vals);
134
135
15
        for head in 0..
num_heads13
{
136
15
            let head_start = pos * hidden_dim + head * head_dim;
137
138
            // Extract x0 and x1 halves
139
15
            let x0_slice = &input[head_start..head_start + half_head];
140
15
            let x1_slice = &input[head_start + half_head..head_start + head_dim];
141
142
15
            let x0_vec = trueno::Vector::from_slice(x0_slice);
143
15
            let x1_vec = trueno::Vector::from_slice(x1_slice);
144
145
            // Compute: out0 = x0 * cos - x1 * sin
146
            //          out1 = x0 * sin + x1 * cos
147
15
            let x0_cos = x0_vec.mul(&cos_vec).unwrap_or_else(|_| 
x0_vec0
.
clone0
());
148
15
            let x1_sin = x1_vec.mul(&sin_vec).unwrap_or_else(|_| 
x1_vec0
.
clone0
());
149
15
            let x0_sin = x0_vec.mul(&sin_vec).unwrap_or_else(|_| 
x0_vec0
.
clone0
());
150
15
            let x1_cos = x1_vec.mul(&cos_vec).unwrap_or_else(|_| 
x1_vec0
.
clone0
());
151
152
15
            let out0 = x0_cos
153
15
                .sub(&x1_sin)
154
15
                .unwrap_or_else(|_| 
trueno::Vector::from_slice0
(
x0_slice0
));
155
15
            let out1 = x0_sin
156
15
                .add(&x1_cos)
157
15
                .unwrap_or_else(|_| 
trueno::Vector::from_slice0
(
x1_slice0
));
158
159
            // Copy results to output
160
15
            output[head_start..head_start + half_head].copy_from_slice(out0.as_slice());
161
15
            output[head_start + half_head..head_start + head_dim].copy_from_slice(out1.as_slice());
162
        }
163
    }
164
165
6
    output
166
14
}