Skip to main content

qualia_core_db/solvers/linear_algebra/
vector.rs

1//! Dynamic element-wise vector operations — the foundational rank-1 linear algebra the
2//! transformer is built from beyond GEMM: the **residual connection** (`x + sublayer(x)`) is
3//! vector addition, and the **gated activation** (SwiGLU) uses the **Hadamard product**
4//! (element-wise multiply). Plain arithmetic; caller-owned, zero allocation.
5
6use crate::solvers::SolversError;
7
8/// `c[i] = a[i] + b[i]` — vector addition (the residual connection `x + sublayer(x)`).
9pub fn add_into(a: &[f64], b: &[f64], c: &mut [f64]) -> Result<(), SolversError> {
10    if a.len() != b.len() || a.len() != c.len() {
11        return Err(SolversError::InvalidDimension);
12    }
13    for i in 0..a.len() {
14        c[i] = a[i] + b[i];
15    }
16    Ok(())
17}
18
19/// `a[i] += b[i]` — in-place residual add.
20pub fn add_assign(a: &mut [f64], b: &[f64]) -> Result<(), SolversError> {
21    if a.len() != b.len() {
22        return Err(SolversError::InvalidDimension);
23    }
24    for i in 0..a.len() {
25        a[i] += b[i];
26    }
27    Ok(())
28}
29
30/// `c[i] = a[i] · b[i]` — the Hadamard (element-wise) product, e.g. the SwiGLU gate `silu(g) ⊙ u`.
31pub fn hadamard_into(a: &[f64], b: &[f64], c: &mut [f64]) -> Result<(), SolversError> {
32    if a.len() != b.len() || a.len() != c.len() {
33        return Err(SolversError::InvalidDimension);
34    }
35    for i in 0..a.len() {
36        c[i] = a[i] * b[i];
37    }
38    Ok(())
39}
40
41/// `a[i] *= b[i]` — in-place Hadamard product.
42pub fn hadamard_assign(a: &mut [f64], b: &[f64]) -> Result<(), SolversError> {
43    if a.len() != b.len() {
44        return Err(SolversError::InvalidDimension);
45    }
46    for i in 0..a.len() {
47        a[i] *= b[i];
48    }
49    Ok(())
50}
51
52/// `a[i] *= s` — scalar scaling.
53pub fn scale(a: &mut [f64], s: f64) {
54    for v in a.iter_mut() {
55        *v *= s;
56    }
57}
58
59/// `y[i] += α·x[i]` — the BLAS `axpy`.
60pub fn axpy(alpha: f64, x: &[f64], y: &mut [f64]) -> Result<(), SolversError> {
61    if x.len() != y.len() {
62        return Err(SolversError::InvalidDimension);
63    }
64    for i in 0..x.len() {
65        y[i] += alpha * x[i];
66    }
67    Ok(())
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn add_is_vector_addition() {
76        let a = [1.0, 2.0, 3.0];
77        let b = [10.0, 20.0, 30.0];
78        let mut c = [0.0; 3];
79        add_into(&a, &b, &mut c).unwrap();
80        assert_eq!(c, [11.0, 22.0, 33.0]);
81        let mut x = a;
82        add_assign(&mut x, &b).unwrap();
83        assert_eq!(x, [11.0, 22.0, 33.0]);
84    }
85
86    #[test]
87    fn hadamard_is_elementwise_product() {
88        let a = [2.0, 3.0, 4.0];
89        let b = [5.0, 0.0, -1.0];
90        let mut c = [0.0; 3];
91        hadamard_into(&a, &b, &mut c).unwrap();
92        assert_eq!(c, [10.0, 0.0, -4.0]);
93        let mut x = a;
94        hadamard_assign(&mut x, &b).unwrap();
95        assert_eq!(x, [10.0, 0.0, -4.0]);
96    }
97
98    #[test]
99    fn scale_and_axpy() {
100        let mut a = [1.0, 2.0, 3.0];
101        scale(&mut a, 2.0);
102        assert_eq!(a, [2.0, 4.0, 6.0]);
103        let x = [1.0, 1.0, 1.0];
104        let mut y = [10.0, 20.0, 30.0];
105        axpy(0.5, &x, &mut y).unwrap();
106        assert_eq!(y, [10.5, 20.5, 30.5]);
107    }
108
109    #[test]
110    fn rejects_length_mismatch() {
111        let a = [1.0, 2.0];
112        let b = [1.0];
113        let mut c = [0.0; 2];
114        assert!(matches!(
115            add_into(&a, &b, &mut c),
116            Err(SolversError::InvalidDimension)
117        ));
118    }
119}