use crate::Matrix;
impl Matrix {
pub fn transpose(&self) -> Matrix {
let mut data = vec![0.0; self.rows() * self.cols()];
for r in 0..self.rows() {
for c in 0..self.cols() {
data[c * self.rows() + r] = self.get(r, c);
}
}
Matrix::from_vec(data, self.cols(), self.rows())
}
pub fn matmul(&self, other: &Matrix) -> Matrix {
let m = self.rows();
let k = self.cols();
let n = other.cols();
assert_eq!(
k,
other.rows(),
"matmul dimension mismatch: {}x{} @ {}x{}",
m,
k,
other.rows(),
n
);
let mut result = vec![0.0; m * n];
unsafe {
matrixmultiply::dgemm(
m,
k,
n,
1.0,
self.as_slice().as_ptr(),
k as isize,
1,
other.as_slice().as_ptr(),
n as isize,
1,
0.0,
result.as_mut_ptr(),
n as isize,
1,
);
}
Matrix::from_vec(result, m, n)
}
pub fn vecmul(&self, vec: &[f64]) -> Vec<f64> {
let m = self.rows();
let k = self.cols();
assert_eq!(
k,
vec.len(),
"vecmul dimension mismatch: {}x{} @ {}",
m,
k,
vec.len()
);
let mut result = vec![0.0; m];
unsafe {
matrixmultiply::dgemm(
m,
k,
1,
1.0,
self.as_slice().as_ptr(),
k as isize,
1,
vec.as_ptr(),
1,
1,
0.0,
result.as_mut_ptr(),
1,
1,
);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_transpose() {
let m = Matrix::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
let t = m.transpose();
assert_eq!(t.shape(), (3, 2));
assert_eq!(t.row(0), &[1.0, 4.0]);
assert_eq!(t.row(1), &[2.0, 5.0]);
assert_eq!(t.row(2), &[3.0, 6.0]);
}
#[test]
fn test_transpose_roundtrip() {
let m = Matrix::from_fn(3, 4, |r, c| (r * 4 + c) as f64);
let tt = m.transpose().transpose();
assert_eq!(m, tt);
}
#[test]
fn test_transpose_shape_swap() {
let m = Matrix::zeros(3, 7);
let t = m.transpose();
assert_eq!(t.shape(), (7, 3));
}
#[test]
fn test_matmul_identity() {
let a = Matrix::from_rows(&[&[1.0, 2.0], &[3.0, 4.0]]);
let eye = Matrix::from_fn(2, 2, |r, c| if r == c { 1.0 } else { 0.0 });
let result = a.matmul(&eye);
assert_eq!(result, a);
}
#[test]
fn test_matmul_2x3_3x2() {
let a = Matrix::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
let b = Matrix::from_rows(&[&[7.0, 8.0], &[9.0, 10.0], &[11.0, 12.0]]);
let c = a.matmul(&b);
assert_eq!(c.shape(), (2, 2));
assert_relative_eq!(c.get(0, 0), 58.0);
assert_relative_eq!(c.get(0, 1), 64.0);
assert_relative_eq!(c.get(1, 0), 139.0);
assert_relative_eq!(c.get(1, 1), 154.0);
}
#[test]
#[should_panic(expected = "matmul dimension mismatch")]
fn test_matmul_dimension_mismatch() {
let a = Matrix::zeros(2, 3);
let b = Matrix::zeros(4, 2);
a.matmul(&b);
}
#[test]
fn test_matmul_transpose_property() {
let a = Matrix::from_fn(2, 3, |r, c| (r * 3 + c + 1) as f64);
let b = Matrix::from_fn(3, 4, |r, c| (r * 4 + c + 1) as f64);
let ab_t = a.matmul(&b).transpose();
let bt_at = b.transpose().matmul(&a.transpose());
for r in 0..ab_t.rows() {
for c in 0..ab_t.cols() {
assert_relative_eq!(ab_t.get(r, c), bt_at.get(r, c), epsilon = 1e-10);
}
}
}
#[test]
fn test_vecmul() {
let m = Matrix::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
let v = vec![1.0, 0.0, 1.0];
let result = m.vecmul(&v);
assert_eq!(result.len(), 2);
assert_relative_eq!(result[0], 4.0); assert_relative_eq!(result[1], 10.0); }
#[test]
fn test_vecmul_filterbank() {
let filterbank = Matrix::from_fn(128, 1025, |r, c| {
if c >= r * 8 && c < (r + 1) * 8 {
1.0
} else {
0.0
}
});
let spectrum: Vec<f64> = (0..1025).map(|i| i as f64).collect();
let result = filterbank.vecmul(&spectrum);
assert_eq!(result.len(), 128);
assert_relative_eq!(result[0], 28.0);
}
#[test]
#[should_panic(expected = "vecmul dimension mismatch")]
fn test_vecmul_dimension_mismatch() {
let m = Matrix::zeros(2, 3);
let v = vec![1.0, 2.0];
m.vecmul(&v);
}
}