Skip to main content

singe_kernel/cpu/
matmul.rs

1//! GEMM, batched GEMM, ragged batched GEMM, and matvec variants.
2
3pub fn matmul_f32(
4    lhs: &[f32],
5    rhs: &[f32],
6    rows: usize,
7    columns: usize,
8    reduction: usize,
9    lhs_row_stride: usize,
10    rhs_row_stride: usize,
11    transpose_lhs: bool,
12    transpose_rhs: bool,
13) -> Vec<f32> {
14    let mut out = vec![0.0f32; rows * columns];
15    for row in 0..rows {
16        for column in 0..columns {
17            let mut sum = 0.0f32;
18            for k in 0..reduction {
19                let lhs_index = if transpose_lhs {
20                    k * lhs_row_stride + row
21                } else {
22                    row * lhs_row_stride + k
23                };
24                let rhs_index = if transpose_rhs {
25                    column * rhs_row_stride + k
26                } else {
27                    k * rhs_row_stride + column
28                };
29                sum += lhs[lhs_index] * rhs[rhs_index];
30            }
31            out[row * columns + column] = sum;
32        }
33    }
34    out
35}
36
37pub fn matmul_f64(
38    lhs: &[f64],
39    rhs: &[f64],
40    rows: usize,
41    columns: usize,
42    reduction: usize,
43    lhs_row_stride: usize,
44    rhs_row_stride: usize,
45    transpose_lhs: bool,
46    transpose_rhs: bool,
47) -> Vec<f64> {
48    let mut out = vec![0.0f64; rows * columns];
49    for row in 0..rows {
50        for column in 0..columns {
51            let mut sum = 0.0f64;
52            for k in 0..reduction {
53                let lhs_index = if transpose_lhs {
54                    k * lhs_row_stride + row
55                } else {
56                    row * lhs_row_stride + k
57                };
58                let rhs_index = if transpose_rhs {
59                    column * rhs_row_stride + k
60                } else {
61                    k * rhs_row_stride + column
62                };
63                sum += lhs[lhs_index] * rhs[rhs_index];
64            }
65            out[row * columns + column] = sum;
66        }
67    }
68    out
69}
70
71pub fn bmm_f32(
72    lhs: &[f32],
73    rhs: &[f32],
74    batch: usize,
75    rows: usize,
76    columns: usize,
77    reduction: usize,
78    lhs_batch_stride: usize,
79    lhs_row_stride: usize,
80    rhs_batch_stride: usize,
81    rhs_row_stride: usize,
82    transpose_lhs: bool,
83    transpose_rhs: bool,
84) -> Vec<f32> {
85    let mut out = vec![0.0f32; batch * rows * columns];
86    for batch_index in 0..batch {
87        for row in 0..rows {
88            for column in 0..columns {
89                let mut sum = 0.0f32;
90                for k in 0..reduction {
91                    let lhs_index = if transpose_lhs {
92                        batch_index * lhs_batch_stride + k * lhs_row_stride + row
93                    } else {
94                        batch_index * lhs_batch_stride + row * lhs_row_stride + k
95                    };
96                    let rhs_index = if transpose_rhs {
97                        batch_index * rhs_batch_stride + column * rhs_row_stride + k
98                    } else {
99                        batch_index * rhs_batch_stride + k * rhs_row_stride + column
100                    };
101                    sum += lhs[lhs_index] * rhs[rhs_index];
102                }
103                out[batch_index * rows * columns + row * columns + column] = sum;
104            }
105        }
106    }
107    out
108}
109
110pub fn masked_bmm_f32(
111    lhs: &[f32],
112    rhs: &[f32],
113    initial_out: &[f32],
114    masked_rows: &[i32],
115    batch: usize,
116    rows: usize,
117    columns: usize,
118    reduction: usize,
119    lhs_batch_stride: usize,
120    lhs_row_stride: usize,
121    rhs_batch_stride: usize,
122    rhs_row_stride: usize,
123    transpose_lhs: bool,
124    transpose_rhs: bool,
125) -> Vec<f32> {
126    let mut out = initial_out.to_vec();
127    for batch_index in 0..batch {
128        let valid_rows = masked_rows[batch_index].clamp(0, rows as i32) as usize;
129        for row in 0..valid_rows {
130            for column in 0..columns {
131                let mut sum = 0.0f32;
132                for k in 0..reduction {
133                    let lhs_index = if transpose_lhs {
134                        batch_index * lhs_batch_stride + k * lhs_row_stride + row
135                    } else {
136                        batch_index * lhs_batch_stride + row * lhs_row_stride + k
137                    };
138                    let rhs_index = if transpose_rhs {
139                        batch_index * rhs_batch_stride + column * rhs_row_stride + k
140                    } else {
141                        batch_index * rhs_batch_stride + k * rhs_row_stride + column
142                    };
143                    sum += lhs[lhs_index] * rhs[rhs_index];
144                }
145                out[batch_index * rows * columns + row * columns + column] = sum;
146            }
147        }
148    }
149    out
150}
151
152pub fn ragged_bmm_f32(
153    lhs: &[f32],
154    rhs: &[f32],
155    row_indptr: &[i32],
156    batch: usize,
157    total_rows: usize,
158    columns: usize,
159    reduction: usize,
160    lhs_row_stride: usize,
161    rhs_batch_stride: usize,
162    rhs_row_stride: usize,
163    transpose_lhs: bool,
164    transpose_rhs: bool,
165) -> Vec<f32> {
166    let mut out = vec![0.0f32; total_rows * columns];
167    for batch_index in 0..batch {
168        let row_start = row_indptr[batch_index] as usize;
169        let row_end = row_indptr[batch_index + 1] as usize;
170        for global_row in row_start..row_end {
171            for column in 0..columns {
172                let mut sum = 0.0f32;
173                for k in 0..reduction {
174                    let lhs_index = if transpose_lhs {
175                        k * lhs_row_stride + global_row
176                    } else {
177                        global_row * lhs_row_stride + k
178                    };
179                    let rhs_index = if transpose_rhs {
180                        batch_index * rhs_batch_stride + column * rhs_row_stride + k
181                    } else {
182                        batch_index * rhs_batch_stride + k * rhs_row_stride + column
183                    };
184                    sum += lhs[lhs_index] * rhs[rhs_index];
185                }
186                out[global_row * columns + column] = sum;
187            }
188        }
189    }
190    out
191}
192
193pub fn group_gemm_f32(
194    lhs: &[f32],
195    rhs: &[f32],
196    rows: &[i32],
197    columns: &[i32],
198    reductions: &[i32],
199    lhs_offsets: &[i32],
200    rhs_offsets: &[i32],
201    output_offsets: &[i32],
202    output_len: usize,
203    transpose_rhs: bool,
204) -> Vec<f32> {
205    let mut out = vec![0.0f32; output_len];
206    for group in 0..rows.len() {
207        let group_rows = rows[group] as usize;
208        let group_columns = columns[group] as usize;
209        let group_reduction = reductions[group] as usize;
210        let lhs_base = lhs_offsets[group] as usize;
211        let rhs_base = rhs_offsets[group] as usize;
212        let output_base = output_offsets[group] as usize;
213        for row in 0..group_rows {
214            for column in 0..group_columns {
215                let mut sum = 0.0f32;
216                for k in 0..group_reduction {
217                    let lhs_index = lhs_base + row * group_reduction + k;
218                    let rhs_index = if transpose_rhs {
219                        rhs_base + column * group_reduction + k
220                    } else {
221                        rhs_base + k * group_columns + column
222                    };
223                    sum += lhs[lhs_index] * rhs[rhs_index];
224                }
225                out[output_base + row * group_columns + column] = sum;
226            }
227        }
228    }
229    out
230}
231
232/// Grouped expert GEMM with optional input/output permutation metadata.
233///
234/// `m_sizes` gives the token count for each expert. For each expert group, rows
235/// are multiplied by that expert's `[columns, reduction]` weight tile.
236/// When `permute_input` or `permute_output` is set, `gather_indices` maps sorted expert rows back to the original token/top-k routing order.
237pub fn grouped_gemm_f32(
238    input: &[f32],
239    weights: &[f32],
240    m_sizes: &[i32],
241    gather_indices: &[i32],
242    total_tokens: usize,
243    columns: usize,
244    reduction: usize,
245    input_row_stride: usize,
246    weight_expert_stride: usize,
247    weight_row_stride: usize,
248    output_row_stride: usize,
249    permute_input: bool,
250    permute_output: bool,
251    top_k: usize,
252) -> Vec<f32> {
253    let mut out = vec![0.0f32; total_tokens * output_row_stride];
254    let mut token_start = 0usize;
255    for (expert, m_size) in m_sizes.iter().copied().enumerate() {
256        let expert_tokens = m_size as usize;
257        for local_row in 0..expert_tokens {
258            let sorted_row = token_start + local_row;
259            let gathered_row = gather_indices[sorted_row] as usize;
260            let input_row = if permute_input {
261                gathered_row / top_k
262            } else {
263                sorted_row
264            };
265            let output_row = if permute_output {
266                gathered_row
267            } else {
268                sorted_row
269            };
270            for column in 0..columns {
271                let mut sum = 0.0f32;
272                for k in 0..reduction {
273                    let input_index = input_row * input_row_stride + k;
274                    let weight_index =
275                        expert * weight_expert_stride + column * weight_row_stride + k;
276                    sum += input[input_index] * weights[weight_index];
277                }
278                out[output_row * output_row_stride + column] = sum;
279            }
280        }
281        token_start += expert_tokens;
282    }
283    out
284}
285#[cfg(feature = "dtype-f8")]
286
287pub fn ragged_block_scaled_bmm_f32(
288    lhs: &[u8],
289    rhs: &[u8],
290    lhs_scale: &[f32],
291    rhs_scale: &[f32],
292    row_indptr: &[i32],
293    batch: usize,
294    total_rows: usize,
295    columns: usize,
296    reduction: usize,
297    scale_block: usize,
298    lhs_scale_row_stride: usize,
299    rhs_scale_batch_stride: usize,
300    rhs_scale_row_stride: usize,
301) -> Vec<f32> {
302    let mut out = vec![0.0f32; total_rows * columns];
303    for batch_index in 0..batch {
304        let row_start = row_indptr[batch_index] as usize;
305        let row_end = row_indptr[batch_index + 1] as usize;
306        for global_row in row_start..row_end {
307            for column in 0..columns {
308                let mut sum = 0.0f32;
309                for k in 0..reduction {
310                    let scale_k = k / scale_block;
311                    let lhs_index = global_row * reduction + k;
312                    let rhs_index = batch_index * columns * reduction + column * reduction + k;
313                    let lhs_scale_index = global_row * lhs_scale_row_stride + scale_k;
314                    let rhs_scale_index = batch_index * rhs_scale_batch_stride
315                        + (column / scale_block) * rhs_scale_row_stride
316                        + scale_k;
317                    sum += f8e4m3_value(lhs[lhs_index])
318                        * f8e4m3_value(rhs[rhs_index])
319                        * lhs_scale[lhs_scale_index]
320                        * rhs_scale[rhs_scale_index];
321                }
322                out[global_row * columns + column] = sum;
323            }
324        }
325    }
326    out
327}
328
329#[cfg(feature = "dtype-f8")]
330pub fn f8e4m3_value(value: u8) -> f32 {
331    let sign = if value & 0x80 == 0 { 1.0 } else { -1.0 };
332    let exponent = (value >> 3) & 0x0f;
333    let mantissa = value & 0x07;
334    if exponent == 0x0f && mantissa == 0x07 {
335        f32::NAN
336    } else if exponent == 0 {
337        if mantissa == 0 {
338            sign * 0.0
339        } else {
340            sign * (mantissa as f32 / 8.0) * 2f32.powi(-6)
341        }
342    } else {
343        sign * (1.0 + mantissa as f32 / 8.0) * 2f32.powi(exponent as i32 - 7)
344    }
345}
346
347pub fn bmm_f64(
348    lhs: &[f64],
349    rhs: &[f64],
350    batch: usize,
351    rows: usize,
352    columns: usize,
353    reduction: usize,
354    lhs_batch_stride: usize,
355    lhs_row_stride: usize,
356    rhs_batch_stride: usize,
357    rhs_row_stride: usize,
358    transpose_lhs: bool,
359    transpose_rhs: bool,
360) -> Vec<f64> {
361    let mut out = vec![0.0f64; batch * rows * columns];
362    for batch_index in 0..batch {
363        for row in 0..rows {
364            for column in 0..columns {
365                let mut sum = 0.0f64;
366                for k in 0..reduction {
367                    let lhs_index = if transpose_lhs {
368                        batch_index * lhs_batch_stride + k * lhs_row_stride + row
369                    } else {
370                        batch_index * lhs_batch_stride + row * lhs_row_stride + k
371                    };
372                    let rhs_index = if transpose_rhs {
373                        batch_index * rhs_batch_stride + column * rhs_row_stride + k
374                    } else {
375                        batch_index * rhs_batch_stride + k * rhs_row_stride + column
376                    };
377                    sum += lhs[lhs_index] * rhs[rhs_index];
378                }
379                out[batch_index * rows * columns + row * columns + column] = sum;
380            }
381        }
382    }
383    out
384}