arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/arith/src/matrix.rs
arith/src/matrix.rsRaw
  1use anyhow::{anyhow, Result};
  2use std::ops::{Add, Mul};
  3
  4#[derive(Debug, Clone, PartialEq)]
  5pub struct Matrix<T>(pub Vec<Vec<T>>);
  6
  7impl<T> Matrix<T>
  8where
  9    T: Copy + Add<Output = T> + Mul<Output = T> + Default + std::fmt::Debug,
 10{
 11    // TODO maybe rm this method, move it to tests only
 12    pub fn new(rows: usize, cols: usize, value: T) -> Self {
 13        Matrix(vec![vec![value; cols]; rows])
 14    }
 15
 16    pub fn add(&self, other: &Matrix<T>) -> Result<Matrix<T>> {
 17        if self.0.len() != other.0.len() || self.0[0].len() != other.0[0].len() {
 18            return Err(anyhow!("dimensions don't match"));
 19        }
 20
 21        let r = self
 22            .0
 23            .iter()
 24            .zip(&other.0)
 25            .map(|(row1, row2)| {
 26                row1.iter()
 27                    .zip(row2)
 28                    .map(|(a, b)| *a + *b)
 29                    .collect::<Vec<T>>()
 30            })
 31            .collect::<Vec<Vec<T>>>();
 32
 33        Ok(Matrix(r))
 34    }
 35
 36    pub fn mul(&self, other: &Matrix<T>) -> Result<Matrix<T>> {
 37        let rows_a = self.0.len();
 38        let cols_a = self.0[0].len();
 39        let rows_b = other.0.len();
 40        let cols_b = other.0[0].len();
 41
 42        if cols_a != rows_b {
 43            return Err(anyhow!("self.n_cols != other.n_rows"));
 44        }
 45
 46        let mut r = vec![vec![T::default(); cols_b]; rows_a];
 47
 48        for i in 0..rows_a {
 49            for j in 0..cols_b {
 50                for k in 0..cols_a {
 51                    r[i][j] = r[i][j] + self.0[i][k] * other.0[k][j];
 52                }
 53            }
 54        }
 55
 56        Ok(Matrix(r))
 57    }
 58    pub fn mul_vec(&self, v: &Vec<T>) -> Result<Vec<T>> {
 59        let rows = self.0.len();
 60        let cols = self.0[0].len();
 61
 62        if cols != v.len() {
 63            return Err(anyhow!(
 64                "Number of columns in matrix does not match the length of the vector"
 65            ));
 66        }
 67
 68        let mut r = vec![T::default(); rows];
 69
 70        for i in 0..rows {
 71            for j in 0..cols {
 72                r[i] = r[i] + self.0[i][j] * v[j];
 73            }
 74        }
 75        Ok(r)
 76    }
 77
 78    pub fn transpose(&self) -> Matrix<T> {
 79        let rows = self.0.len();
 80        let cols = self.0[0].len();
 81        let mut r = vec![vec![T::default(); rows]; cols];
 82
 83        for i in 0..rows {
 84            for j in 0..cols {
 85                r[j][i] = self.0[i][j];
 86            }
 87        }
 88
 89        Matrix(r)
 90    }
 91
 92    pub fn scalar_mul(&self, scalar: T) -> Matrix<T> {
 93        let r = self
 94            .0
 95            .iter()
 96            .map(|row| row.iter().map(|&val| val * scalar).collect::<Vec<T>>())
 97            .collect::<Vec<Vec<T>>>();
 98
 99        Matrix(r)
100    }
101}
102
103// WIP. Currently uses ndarray, ndarray_linalg, num_complex to solve a system of
104// linear equations A*x=b for x.
105use crate::C;
106impl Matrix<C<f64>> {
107    pub fn solve(&self, b: &Vec<C<f64>>) -> Result<Vec<C<f64>>> {
108        use ndarray::{Array1, Array2};
109        use ndarray_linalg::Solve;
110        use num_complex::Complex64;
111
112        let m: Array2<Complex64> = Array2::from_shape_vec(
113            (self.0.len(), self.0[0].len()),
114            self.0
115                .clone()
116                .into_iter()
117                .flatten()
118                .map(|e| Complex64::new(e.re, e.im))
119                .collect(),
120        )
121        .unwrap();
122        let v: Array1<Complex64> = Array1::from_shape_vec(
123            b.len(),
124            b.iter().map(|e| Complex64::new(e.re, e.im)).collect(),
125        )
126        .unwrap();
127        let r = m.solve(&v)?;
128        let r: Vec<C<f64>> = r.into_iter().map(|e| C::<f64>::new(e.re, e.im)).collect();
129        Ok(r)
130    }
131}
132impl Matrix<f64> {
133    // tmp (rm)
134    pub fn solve(&self, b: Vec<f64>) -> Result<Vec<f64>> {
135        use ndarray::{Array1, Array2};
136        use ndarray_linalg::Solve;
137
138        let m: Array2<f64> = Array2::from_shape_vec(
139            (self.0.len(), self.0[0].len()),
140            self.0.clone().into_iter().flatten().collect(),
141        )
142        .unwrap();
143        let v: Array1<f64> = Array1::from_shape_vec(b.len(), b).unwrap();
144        let r = m.solve(&v)?;
145        let r: Vec<f64> = r.into_iter().map(|e| e).collect();
146        Ok(r)
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_add() -> Result<()> {
156        let a = Matrix::new(2, 3, 1);
157        let b = Matrix::new(2, 3, 2);
158        let expected = Matrix::new(2, 3, 3);
159        assert_eq!(a.add(&b).unwrap(), expected);
160        Ok(())
161    }
162
163    #[test]
164    fn test_mul() -> Result<()> {
165        let a = Matrix::new(2, 3, 1);
166        let b = Matrix::new(3, 2, 1);
167        let expected = Matrix::new(2, 2, 3); // 2x3 * 3x2 = 2x2 matrix (with all values 3)
168        assert_eq!(a.mul(&b).unwrap(), expected);
169        Ok(())
170    }
171
172    #[test]
173    fn test_transpose() -> Result<()> {
174        let a = Matrix::new(2, 3, 1);
175        let expected = Matrix::new(3, 2, 1);
176        assert_eq!(a.transpose(), expected);
177        Ok(())
178    }
179
180    #[test]
181    fn test_scalar_mul() -> Result<()> {
182        let a = Matrix::new(2, 3, 1);
183        let expected = Matrix::new(2, 3, 3);
184        assert_eq!(a.scalar_mul(3), expected);
185        Ok(())
186    }
187}