Skip to main content

embedded_dsp/
matrix.rs

1//! Matrix operations (addition, subtraction, multiplication, scale, transpose, inverse, complex matrix multiplication).
2
3use crate::types::*;
4
5/// Matrix structure wrapping a slice of data in row-major order.
6#[derive(Debug, Clone, Copy)]
7pub struct MatrixInstance<'a, T> {
8    pub num_rows: u16,
9    pub num_cols: u16,
10    pub data: &'a [T],
11}
12
13impl<'a, T> MatrixInstance<'a, T> {
14    pub fn new(num_rows: u16, num_cols: u16, data: &'a [T]) -> Self {
15        Self {
16            num_rows,
17            num_cols,
18            data,
19        }
20    }
21}
22
23/// Mutable Matrix structure wrapping a mutable slice of data in row-major order.
24#[derive(Debug)]
25pub struct MatrixInstanceMut<'a, T> {
26    pub num_rows: u16,
27    pub num_cols: u16,
28    pub data: &'a mut [T],
29}
30
31impl<'a, T> MatrixInstanceMut<'a, T> {
32    pub fn new(num_rows: u16, num_cols: u16, data: &'a mut [T]) -> Self {
33        Self {
34            num_rows,
35            num_cols,
36            data,
37        }
38    }
39}
40
41// --- Matrix Addition ---
42
43pub fn mat_add_f32(
44    a: &MatrixInstance<f32>,
45    b: &MatrixInstance<f32>,
46    out: &mut MatrixInstanceMut<f32>,
47) -> Status {
48    if a.num_rows != b.num_rows
49        || a.num_cols != b.num_cols
50        || a.num_rows != out.num_rows
51        || a.num_cols != out.num_cols
52    {
53        return Status::SizeMismatch;
54    }
55    let total = (a.num_rows as usize) * (a.num_cols as usize);
56    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
57        return Status::LengthError;
58    }
59    for i in 0..total {
60        out.data[i] = a.data[i] + b.data[i];
61    }
62    Status::Success
63}
64
65pub fn mat_add_q31(
66    a: &MatrixInstance<q31>,
67    b: &MatrixInstance<q31>,
68    out: &mut MatrixInstanceMut<q31>,
69) -> Status {
70    if a.num_rows != b.num_rows
71        || a.num_cols != b.num_cols
72        || a.num_rows != out.num_rows
73        || a.num_cols != out.num_cols
74    {
75        return Status::SizeMismatch;
76    }
77    let total = (a.num_rows as usize) * (a.num_cols as usize);
78    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
79        return Status::LengthError;
80    }
81    for i in 0..total {
82        out.data[i] = a.data[i].saturating_add(b.data[i]);
83    }
84    Status::Success
85}
86
87pub fn mat_add_q15(
88    a: &MatrixInstance<q15>,
89    b: &MatrixInstance<q15>,
90    out: &mut MatrixInstanceMut<q15>,
91) -> Status {
92    if a.num_rows != b.num_rows
93        || a.num_cols != b.num_cols
94        || a.num_rows != out.num_rows
95        || a.num_cols != out.num_cols
96    {
97        return Status::SizeMismatch;
98    }
99    let total = (a.num_rows as usize) * (a.num_cols as usize);
100    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
101        return Status::LengthError;
102    }
103    for i in 0..total {
104        out.data[i] = a.data[i].saturating_add(b.data[i]);
105    }
106    Status::Success
107}
108
109// --- Matrix Subtraction ---
110
111pub fn mat_sub_f32(
112    a: &MatrixInstance<f32>,
113    b: &MatrixInstance<f32>,
114    out: &mut MatrixInstanceMut<f32>,
115) -> Status {
116    if a.num_rows != b.num_rows
117        || a.num_cols != b.num_cols
118        || a.num_rows != out.num_rows
119        || a.num_cols != out.num_cols
120    {
121        return Status::SizeMismatch;
122    }
123    let total = (a.num_rows as usize) * (a.num_cols as usize);
124    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
125        return Status::LengthError;
126    }
127    for i in 0..total {
128        out.data[i] = a.data[i] - b.data[i];
129    }
130    Status::Success
131}
132
133pub fn mat_sub_q31(
134    a: &MatrixInstance<q31>,
135    b: &MatrixInstance<q31>,
136    out: &mut MatrixInstanceMut<q31>,
137) -> Status {
138    if a.num_rows != b.num_rows
139        || a.num_cols != b.num_cols
140        || a.num_rows != out.num_rows
141        || a.num_cols != out.num_cols
142    {
143        return Status::SizeMismatch;
144    }
145    let total = (a.num_rows as usize) * (a.num_cols as usize);
146    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
147        return Status::LengthError;
148    }
149    for i in 0..total {
150        out.data[i] = a.data[i].saturating_sub(b.data[i]);
151    }
152    Status::Success
153}
154
155pub fn mat_sub_q15(
156    a: &MatrixInstance<q15>,
157    b: &MatrixInstance<q15>,
158    out: &mut MatrixInstanceMut<q15>,
159) -> Status {
160    if a.num_rows != b.num_rows
161        || a.num_cols != b.num_cols
162        || a.num_rows != out.num_rows
163        || a.num_cols != out.num_cols
164    {
165        return Status::SizeMismatch;
166    }
167    let total = (a.num_rows as usize) * (a.num_cols as usize);
168    if a.data.len() < total || b.data.len() < total || out.data.len() < total {
169        return Status::LengthError;
170    }
171    for i in 0..total {
172        out.data[i] = a.data[i].saturating_sub(b.data[i]);
173    }
174    Status::Success
175}
176
177// --- Matrix Multiplication ---
178
179pub fn mat_mult_f32(
180    a: &MatrixInstance<f32>,
181    b: &MatrixInstance<f32>,
182    out: &mut MatrixInstanceMut<f32>,
183) -> Status {
184    if a.num_cols != b.num_rows || a.num_rows != out.num_rows || b.num_cols != out.num_cols {
185        return Status::SizeMismatch;
186    }
187    let rows_a = a.num_rows as usize;
188    let cols_a = a.num_cols as usize;
189    let cols_b = b.num_cols as usize;
190
191    for r in 0..rows_a {
192        for c in 0..cols_b {
193            let mut sum = 0.0f32;
194            for k in 0..cols_a {
195                sum += a.data[r * cols_a + k] * b.data[k * cols_b + c];
196            }
197            out.data[r * cols_b + c] = sum;
198        }
199    }
200    Status::Success
201}
202
203pub fn mat_mult_q31(
204    a: &MatrixInstance<q31>,
205    b: &MatrixInstance<q31>,
206    out: &mut MatrixInstanceMut<q31>,
207) -> Status {
208    if a.num_cols != b.num_rows || a.num_rows != out.num_rows || b.num_cols != out.num_cols {
209        return Status::SizeMismatch;
210    }
211    let rows_a = a.num_rows as usize;
212    let cols_a = a.num_cols as usize;
213    let cols_b = b.num_cols as usize;
214
215    for r in 0..rows_a {
216        for c in 0..cols_b {
217            let mut sum: i64 = 0;
218            for k in 0..cols_a {
219                sum += (a.data[r * cols_a + k] as i64 * b.data[k * cols_b + c] as i64) >> 31;
220            }
221            out.data[r * cols_b + c] = sum.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
222        }
223    }
224    Status::Success
225}
226
227pub fn mat_mult_q15(
228    a: &MatrixInstance<q15>,
229    b: &MatrixInstance<q15>,
230    out: &mut MatrixInstanceMut<q15>,
231) -> Status {
232    if a.num_cols != b.num_rows || a.num_rows != out.num_rows || b.num_cols != out.num_cols {
233        return Status::SizeMismatch;
234    }
235    let rows_a = a.num_rows as usize;
236    let cols_a = a.num_cols as usize;
237    let cols_b = b.num_cols as usize;
238
239    for r in 0..rows_a {
240        for c in 0..cols_b {
241            let mut sum: i32 = 0;
242            for k in 0..cols_a {
243                sum += (a.data[r * cols_a + k] as i32 * b.data[k * cols_b + c] as i32) >> 15;
244            }
245            out.data[r * cols_b + c] = sum.clamp(i16::MIN as i32, i16::MAX as i32) as q15;
246        }
247    }
248    Status::Success
249}
250
251// --- Matrix Scale ---
252
253pub fn mat_scale_f32(
254    src: &MatrixInstance<f32>,
255    scale: f32,
256    out: &mut MatrixInstanceMut<f32>,
257) -> Status {
258    if src.num_rows != out.num_rows || src.num_cols != out.num_cols {
259        return Status::SizeMismatch;
260    }
261    let total = (src.num_rows as usize) * (src.num_cols as usize);
262    for i in 0..total {
263        out.data[i] = src.data[i] * scale;
264    }
265    Status::Success
266}
267
268pub fn mat_scale_q31(
269    src: &MatrixInstance<q31>,
270    scale_fract: q31,
271    shift: i8,
272    out: &mut MatrixInstanceMut<q31>,
273) -> Status {
274    if src.num_rows != out.num_rows || src.num_cols != out.num_cols {
275        return Status::SizeMismatch;
276    }
277    let total = (src.num_rows as usize) * (src.num_cols as usize);
278    crate::basic_math::scale_q31(
279        &src.data[..total],
280        scale_fract,
281        shift,
282        &mut out.data[..total],
283    );
284    Status::Success
285}
286
287pub fn mat_scale_q15(
288    src: &MatrixInstance<q15>,
289    scale_fract: q15,
290    shift: i8,
291    out: &mut MatrixInstanceMut<q15>,
292) -> Status {
293    if src.num_rows != out.num_rows || src.num_cols != out.num_cols {
294        return Status::SizeMismatch;
295    }
296    let total = (src.num_rows as usize) * (src.num_cols as usize);
297    crate::basic_math::scale_q15(
298        &src.data[..total],
299        scale_fract,
300        shift,
301        &mut out.data[..total],
302    );
303    Status::Success
304}
305
306// --- Matrix Transpose ---
307
308pub fn mat_trans_f32(src: &MatrixInstance<f32>, out: &mut MatrixInstanceMut<f32>) -> Status {
309    if src.num_rows != out.num_cols || src.num_cols != out.num_rows {
310        return Status::SizeMismatch;
311    }
312    let rows = src.num_rows as usize;
313    let cols = src.num_cols as usize;
314
315    for r in 0..rows {
316        for c in 0..cols {
317            out.data[c * rows + r] = src.data[r * cols + c];
318        }
319    }
320    Status::Success
321}
322
323pub fn mat_trans_q31(src: &MatrixInstance<q31>, out: &mut MatrixInstanceMut<q31>) -> Status {
324    if src.num_rows != out.num_cols || src.num_cols != out.num_rows {
325        return Status::SizeMismatch;
326    }
327    let rows = src.num_rows as usize;
328    let cols = src.num_cols as usize;
329
330    for r in 0..rows {
331        for c in 0..cols {
332            out.data[c * rows + r] = src.data[r * cols + c];
333        }
334    }
335    Status::Success
336}
337
338pub fn mat_trans_q15(src: &MatrixInstance<q15>, out: &mut MatrixInstanceMut<q15>) -> Status {
339    if src.num_rows != out.num_cols || src.num_cols != out.num_rows {
340        return Status::SizeMismatch;
341    }
342    let rows = src.num_rows as usize;
343    let cols = src.num_cols as usize;
344
345    for r in 0..rows {
346        for c in 0..cols {
347            out.data[c * rows + r] = src.data[r * cols + c];
348        }
349    }
350    Status::Success
351}
352
353// --- Matrix Inverse (f32 Gauss-Jordan Elimination with partial pivoting) ---
354
355pub fn mat_inverse_f32(src: &MatrixInstance<f32>, out: &mut MatrixInstanceMut<f32>) -> Status {
356    if src.num_rows != src.num_cols || out.num_rows != out.num_cols || src.num_rows != out.num_rows
357    {
358        return Status::SizeMismatch;
359    }
360    let n = src.num_rows as usize;
361    if n == 0 {
362        return Status::SizeMismatch;
363    }
364
365    // Stack-allocated scratch buffer for n <= 16, or array for n x 2n augmented matrix
366    // Gauss-Jordan elimination
367    let mut aug = [0.0f32; 16 * 32];
368    if n > 16 {
369        return Status::ArgumentError; // Limit to 16x16 without heap allocation in no_std
370    }
371
372    for r in 0..n {
373        for c in 0..n {
374            aug[r * 2 * n + c] = src.data[r * n + c];
375            aug[r * 2 * n + n + c] = if r == c { 1.0 } else { 0.0 };
376        }
377    }
378
379    for i in 0..n {
380        // Pivot selection
381        let mut max_row = i;
382        let mut max_val = aug[i * 2 * n + i].abs();
383        for r in (i + 1)..n {
384            let val = aug[r * 2 * n + i].abs();
385            if val > max_val {
386                max_val = val;
387                max_row = r;
388            }
389        }
390
391        if max_val < 1e-12 {
392            return Status::Singular;
393        }
394
395        // Swap rows
396        if max_row != i {
397            for c in 0..(2 * n) {
398                aug.swap(i * 2 * n + c, max_row * 2 * n + c);
399            }
400        }
401
402        let pivot = aug[i * 2 * n + i];
403        for c in 0..(2 * n) {
404            aug[i * 2 * n + c] /= pivot;
405        }
406
407        for r in 0..n {
408            if r != i {
409                let factor = aug[r * 2 * n + i];
410                for c in 0..(2 * n) {
411                    let sub = factor * aug[i * 2 * n + c];
412                    aug[r * 2 * n + c] -= sub;
413                }
414            }
415        }
416    }
417
418    for r in 0..n {
419        for c in 0..n {
420            out.data[r * n + c] = aug[r * 2 * n + n + c];
421        }
422    }
423
424    Status::Success
425}