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/apr/helpers.rs
Line
Count
Source
1
//! APR inference helper functions (PMAT-802)
2
//!
3
//! Extracted from apr/mod.rs - Pure Rust inference primitives.
4
//!
5
//! ## Contents
6
//! - RMS normalization
7
//! - Matrix multiplication with SIMD
8
//! - Transpose operations
9
//! - Simple attention computation
10
//! - Format detection utilities
11
12
use std::path::Path;
13
use std::fs;
14
use super::MAGIC;
15
16
/// RMS normalization
17
10
pub(crate) fn rms_norm(x: &[f32], weight: &[f32], eps: f32) -> Vec<f32> {
18
10
    let hidden_dim = weight.len();
19
10
    let seq_len = x.len() / hidden_dim;
20
10
    let mut output = Vec::with_capacity(x.len());
21
22
13
    for s in 0..
seq_len10
{
23
13
        let start = s * hidden_dim;
24
13
        let slice = &x[start..start + hidden_dim];
25
26
        // Compute RMS
27
104
        let 
sum_sq13
:
f3213
=
slice13
.
iter13
().
map13
(|&v| v * v).
sum13
();
28
13
        let rms = (sum_sq / hidden_dim as f32 + eps).sqrt();
29
30
        // Normalize and scale
31
104
        for (i, &v) in 
slice13
.
iter13
().
enumerate13
() {
32
104
            output.push((v / rms) * weight.get(i).copied().unwrap_or(1.0));
33
104
        }
34
    }
35
10
    output
36
10
}
37
38
/// Matrix multiplication with SIMD dot products
39
/// [seq, in_dim] @ [out_dim, in_dim]^T -> [seq, out_dim]
40
10
pub(crate) fn matmul(x: &[f32], w: &[f32], seq_len: usize, in_dim: usize, out_dim: usize) -> Vec<f32> {
41
10
    let mut output = vec![0.0; seq_len * out_dim];
42
43
15
    for s in 0..
seq_len10
{
44
15
        let x_start = s * in_dim;
45
15
        let x_end = x_start + in_dim;
46
15
        if x_end > x.len() {
47
1
            continue; // Skip if out of bounds
48
14
        }
49
14
        let x_row = &x[x_start..x_end];
50
51
277
        for o in 0..
out_dim14
{
52
277
            let w_start = o * in_dim;
53
277
            let w_end = w_start + in_dim;
54
277
            if w_end > w.len() {
55
2
                continue; // Skip if out of bounds
56
275
            }
57
275
            let w_row = &w[w_start..w_end];
58
            // SIMD dot product
59
275
            output[s * out_dim + o] = simd_dot(x_row, w_row);
60
        }
61
    }
62
10
    output
63
10
}
64
65
/// Transpose a matrix from [rows, cols] to [cols, rows] for GEMM compatibility.
66
/// Weight matrices are stored as [out_dim, in_dim] but GEMM needs [in_dim, out_dim].
67
#[cfg(feature = "cuda")]
68
pub(crate) fn transpose_matrix(m: &[f32], rows: usize, cols: usize) -> Vec<f32> {
69
    let mut transposed = vec![0.0f32; rows * cols];
70
    for r in 0..rows {
71
        for c in 0..cols {
72
            // m[r, c] -> transposed[c, r]
73
            let src_idx = r * cols + c;
74
            let dst_idx = c * rows + r;
75
            if src_idx < m.len() && dst_idx < transposed.len() {
76
                transposed[dst_idx] = m[src_idx];
77
            }
78
        }
79
    }
80
    transposed
81
}
82
83
/// SIMD-accelerated dot product
84
#[inline]
85
286
pub fn simd_dot(a: &[f32], b: &[f32]) -> f32 {
86
    #[cfg(target_arch = "x86_64")]
87
    {
88
286
        if is_x86_feature_detected!("avx2") {
89
            // SAFETY: AVX2 feature is runtime-checked above, simd_dot_avx2 requires AVX2
90
286
            return unsafe { simd_dot_avx2(a, b) };
91
0
        }
92
    }
93
    // Scalar fallback
94
0
    a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
95
286
}
96
97
#[cfg(target_arch = "x86_64")]
98
#[target_feature(enable = "avx2", enable = "fma")]
99
286
unsafe fn simd_dot_avx2(a: &[f32], b: &[f32]) -> f32 {
100
    use std::arch::x86_64::{
101
        _mm256_castps256_ps128, _mm256_extractf128_ps, _mm256_fmadd_ps, _mm256_loadu_ps,
102
        _mm256_setzero_ps, _mm_add_ps, _mm_add_ss, _mm_cvtss_f32, _mm_movehl_ps, _mm_shuffle_ps,
103
    };
104
105
286
    let n = a.len().min(b.len());
106
286
    let chunks = n / 8;
107
108
    // SAFETY: This entire fn is unsafe with target_feature(avx2, fma)
109
    // All intrinsics are safe to call given the target_feature guarantee
110
    // The unsafe block is required for Rust 2024 edition compliance
111
    unsafe {
112
286
        let mut sum = _mm256_setzero_ps();
113
114
2.08k
        for i in 0..
chunks286
{
115
2.08k
            let av = _mm256_loadu_ps(a.as_ptr().add(i * 8));
116
2.08k
            let bv = _mm256_loadu_ps(b.as_ptr().add(i * 8));
117
2.08k
            sum = _mm256_fmadd_ps(av, bv, sum);
118
2.08k
        }
119
120
        // Horizontal sum
121
286
        let hi = _mm256_extractf128_ps(sum, 1);
122
286
        let lo = _mm256_castps256_ps128(sum);
123
286
        let sum128 = _mm_add_ps(lo, hi);
124
286
        let sum64 = _mm_add_ps(sum128, _mm_movehl_ps(sum128, sum128));
125
286
        let sum32 = _mm_add_ss(sum64, _mm_shuffle_ps(sum64, sum64, 1));
126
286
        let mut result = _mm_cvtss_f32(sum32);
127
128
        // Handle remainder (scalar)
129
286
        for 
i67
in (chunks * 8)..n {
130
67
            result += a.get(i).copied().unwrap_or(0.0) * b.get(i).copied().unwrap_or(0.0);
131
67
        }
132
133
286
        result
134
    }
135
286
}
136
137
/// Simplified multi-head attention (no RoPE, causal mask)
138
7
pub(crate) fn simple_attention(
139
7
    q: &[f32],
140
7
    k: &[f32],
141
7
    v: &[f32],
142
7
    seq_len: usize,
143
7
    num_heads: usize,
144
7
    num_kv_heads: usize,
145
7
    head_dim: usize,
146
7
) -> Vec<f32> {
147
7
    let hidden_dim = num_heads * head_dim;
148
7
    let kv_dim = num_kv_heads * head_dim;
149
7
    let heads_per_kv = num_heads / num_kv_heads;
150
7
    let scale = 1.0 / (head_dim as f32).sqrt();
151
152
7
    let mut output = vec![0.0; seq_len * hidden_dim];
153
154
10
    for s in 0..
seq_len7
{
155
21
        for h in 0..
num_heads10
{
156
21
            let kv_h = h / heads_per_kv;
157
158
            // Compute attention scores for this head
159
21
            let mut scores = vec![0.0; seq_len];
160
30
            for t in 0..=
s21
{
161
                // Causal: only attend to past
162
30
                let mut score = 0.0;
163
96
                for d in 0..
head_dim30
{
164
96
                    let q_val = q
165
96
                        .get(s * hidden_dim + h * head_dim + d)
166
96
                        .copied()
167
96
                        .unwrap_or(0.0);
168
96
                    let k_val = k
169
96
                        .get(t * kv_dim + kv_h * head_dim + d)
170
96
                        .copied()
171
96
                        .unwrap_or(0.0);
172
96
                    score += q_val * k_val;
173
96
                }
174
30
                scores[t] = score * scale;
175
            }
176
177
            // Softmax
178
21
            let max_score = scores[..=s]
179
21
                .iter()
180
21
                .cloned()
181
21
                .fold(f32::NEG_INFINITY, f32::max);
182
21
            let mut sum = 0.0;
183
30
            for score in &mut 
scores[..=s]21
{
184
30
                *score = (*score - max_score).exp();
185
30
                sum += *score;
186
30
            }
187
30
            for score in &mut 
scores[..=s]21
{
188
30
                *score /= sum;
189
30
            }
190
191
            // Weighted sum of values
192
62
            for d in 0..
head_dim21
{
193
62
                let mut val = 0.0;
194
96
                for t in 0..=
s62
{
195
96
                    let v_val = v
196
96
                        .get(t * kv_dim + kv_h * head_dim + d)
197
96
                        .copied()
198
96
                        .unwrap_or(0.0);
199
96
                    val += scores[t] * v_val;
200
96
                }
201
62
                output[s * hidden_dim + h * head_dim + d] = val;
202
            }
203
        }
204
    }
205
206
7
    output
207
7
}
208
209
/// Check if a file is a valid .apr v2 file
210
13
pub fn is_apr_file<P: AsRef<Path>>(path: P) -> bool {
211
13
    fs::read(path.as_ref()).is_ok_and(|data| 
data.len() >= 45
&&
data[0..4] == MAGIC5
)
212
13
}
213
214
/// Detect model format from magic bytes
215
15
pub fn detect_format<P: AsRef<Path>>(path: P) -> &'static str {
216
15
    let path = path.as_ref();
217
218
15
    if let Some(
ext10
) = path.extension() {
219
10
        let ext = ext.to_string_lossy().to_lowercase();
220
10
        match ext.as_str() {
221
10
            "apr" => return 
"apr"2
,
222
8
            "gguf" => return 
"gguf"2
,
223
6
            "safetensors" => return 
"safetensors"2
,
224
4
            _ => {},
225
        }
226
5
    }
227
228
9
    if let Ok(
data3
) = fs::read(path) {
229
3
        if data.len() >= 4 {
230
3
            if data[0..4] == MAGIC {
231
1
                return "apr";
232
2
            }
233
2
            if data[0..4] == [0x47, 0x47, 0x55, 0x46] {
234
1
                return "gguf";
235
1
            }
236
1
            if data[0] == b'{' {
237
1
                return "safetensors";
238
0
            }
239
0
        }
240
6
    }
241
242
6
    "unknown"
243
15
}