use std::ops::{Add, Sub, Neg, Mul};
use super::kernels;
use super::FixedVector;
use crate::fixed_point::fixed_num::{Fixed, FixedNum};
use crate::fixed_point::width::Storage;
#[derive(Clone, Debug)]
pub struct FixedMatrix<T> {
rows: usize,
cols: usize,
data: Vec<T>,
}
impl<S: Storage, const F: u32> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn new(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
data: vec![Fixed::ZERO; rows * cols],
}
}
#[inline]
pub fn rows(&self) -> usize {
self.rows
}
#[inline]
pub fn cols(&self) -> usize {
self.cols
}
#[inline]
pub fn get(&self, row: usize, col: usize) -> Fixed<S, F> {
assert!(row < self.rows && col < self.cols, "FixedMatrix<Fixed<S, F>>: index out of bounds");
self.data[row * self.cols + col]
}
#[inline]
pub fn set(&mut self, row: usize, col: usize, value: Fixed<S, F>) {
assert!(row < self.rows && col < self.cols, "FixedMatrix<Fixed<S, F>>: index out of bounds");
self.data[row * self.cols + col] = value;
}
}
impl<S: Storage, const F: u32> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn from_slice(rows: usize, cols: usize, values: &[Fixed<S, F>]) -> Self {
assert_eq!(values.len(), rows * cols,
"FixedMatrix::from_slice: expected {} elements, got {}", rows * cols, values.len());
Self {
rows,
cols,
data: values.to_vec(),
}
}
pub fn from_fn(rows: usize, cols: usize, f: impl Fn(usize, usize) -> Fixed<S, F>) -> Self {
let mut data = Vec::with_capacity(rows * cols);
for r in 0..rows {
for c in 0..cols {
data.push(f(r, c));
}
}
Self { rows, cols, data }
}
pub fn identity(n: usize) -> Self {
Self::from_fn(n, n, |r, c| {
if r == c { Fixed::one() } else { Fixed::ZERO }
})
}
pub fn diagonal(v: &FixedVector<Fixed<S, F>>) -> Self {
let n = v.len();
Self::from_fn(n, n, |r, c| {
if r == c { v[r] } else { Fixed::ZERO }
})
}
}
impl<S: Storage, const F: u32> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn transpose(&self) -> Self {
let mut data = Vec::with_capacity(self.rows * self.cols);
for c in 0..self.cols {
for r in 0..self.rows {
data.push(self.data[r * self.cols + c]);
}
}
Self { rows: self.cols, cols: self.rows, data }
}
pub fn trace(&self) -> Fixed<S, F> {
assert_eq!(self.rows, self.cols, "FixedMatrix::trace: matrix must be square");
let mut sum = Fixed::ZERO;
for i in 0..self.rows {
sum += self.get(i, i);
}
sum
}
#[inline]
pub fn is_square(&self) -> bool {
self.rows == self.cols
}
pub fn row(&self, r: usize) -> FixedVector<Fixed<S, F>> {
assert!(r < self.rows, "FixedMatrix::row: index out of bounds");
let start = r * self.cols;
FixedVector::<Fixed<S, F>>::from_slice(&self.data[start..start + self.cols])
}
pub fn col(&self, c: usize) -> FixedVector<Fixed<S, F>> {
assert!(c < self.cols, "FixedMatrix::col: index out of bounds");
let mut v = FixedVector::<Fixed<S, F>>::new(self.rows);
for r in 0..self.rows {
v[r] = self.get(r, c);
}
v
}
}
impl<S: Storage, const F: u32> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn mul_vector(&self, v: &FixedVector<Fixed<S, F>>) -> FixedVector<Fixed<S, F>> {
assert_eq!(self.cols, v.len(),
"FixedMatrix::mul_vector: cols ({}) != vector len ({})", self.cols, v.len());
let v_raw: Vec<S> = v.as_slice().iter().map(|fp| fp.raw()).collect();
let mut result = FixedVector::<Fixed<S, F>>::new(self.rows);
for r in 0..self.rows {
let row_start = r * self.cols;
let row_raw: Vec<S> = self.data[row_start..row_start + self.cols]
.iter().map(|fp| fp.raw()).collect();
result[r] = Fixed::from_raw(kernels::dot_raw::<Fixed<S, F>>(&row_raw, &v_raw));
}
result
}
}
impl<S: Storage, const F: u32> Mul for FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
fn mul(self, rhs: Self) -> Self {
mat_mul(&self, &rhs)
}
}
impl<S: Storage, const F: u32> Mul for &FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn mul(self, rhs: &FixedMatrix<Fixed<S, F>>) -> FixedMatrix<Fixed<S, F>> {
mat_mul(self, rhs)
}
}
fn mat_mul<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>, b: &FixedMatrix<Fixed<S, F>>) -> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
assert_eq!(a.cols, b.rows,
"FixedMatrix::mul: A.cols ({}) != B.rows ({})", a.cols, b.rows);
let k = a.cols;
let b_cols_raw: Vec<Vec<S>> = (0..b.cols)
.map(|c| (0..b.rows).map(|r| b.data[r * b.cols + c].raw()).collect())
.collect();
let mut result = FixedMatrix::<Fixed<S, F>>::new(a.rows, b.cols);
for r in 0..a.rows {
let row_start = r * k;
let a_row_raw: Vec<S> = a.data[row_start..row_start + k]
.iter().map(|fp| fp.raw()).collect();
for c in 0..b.cols {
let dot = kernels::dot_raw::<Fixed<S, F>>(&a_row_raw, &b_cols_raw[c]);
result.data[r * b.cols + c] = Fixed::from_raw(dot);
}
}
result
}
impl<S: Storage, const F: u32> Add for FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self {
assert_eq!((self.rows, self.cols), (rhs.rows, rhs.cols),
"FixedMatrix::add: dimension mismatch");
Self {
rows: self.rows,
cols: self.cols,
data: self.data.iter().zip(rhs.data.iter())
.map(|(&a, &b)| a + b).collect(),
}
}
}
impl<'a, 'b, S: Storage, const F: u32> Add<&'b FixedMatrix<Fixed<S, F>>> for &'a FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn add(self, rhs: &'b FixedMatrix<Fixed<S, F>>) -> FixedMatrix<Fixed<S, F>> {
assert_eq!((self.rows, self.cols), (rhs.rows, rhs.cols),
"FixedMatrix::add: dimension mismatch");
FixedMatrix::<Fixed<S, F>> {
rows: self.rows,
cols: self.cols,
data: self.data.iter().zip(rhs.data.iter())
.map(|(&a, &b)| a + b).collect(),
}
}
}
impl<S: Storage, const F: u32> Sub for FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self {
assert_eq!((self.rows, self.cols), (rhs.rows, rhs.cols),
"FixedMatrix::sub: dimension mismatch");
Self {
rows: self.rows,
cols: self.cols,
data: self.data.iter().zip(rhs.data.iter())
.map(|(&a, &b)| a - b).collect(),
}
}
}
impl<'a, 'b, S: Storage, const F: u32> Sub<&'b FixedMatrix<Fixed<S, F>>> for &'a FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn sub(self, rhs: &'b FixedMatrix<Fixed<S, F>>) -> FixedMatrix<Fixed<S, F>> {
assert_eq!((self.rows, self.cols), (rhs.rows, rhs.cols),
"FixedMatrix::sub: dimension mismatch");
FixedMatrix::<Fixed<S, F>> {
rows: self.rows,
cols: self.cols,
data: self.data.iter().zip(rhs.data.iter())
.map(|(&a, &b)| a - b).collect(),
}
}
}
impl<S: Storage, const F: u32> Neg for FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
fn neg(self) -> Self {
Self {
rows: self.rows,
cols: self.cols,
data: self.data.iter().map(|&v| -v).collect(),
}
}
}
impl<S: Storage, const F: u32> Neg for &FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn neg(self) -> FixedMatrix<Fixed<S, F>> {
FixedMatrix::<Fixed<S, F>> {
rows: self.rows,
cols: self.cols,
data: self.data.iter().map(|&v| -v).collect(),
}
}
}
impl<S: Storage, const F: u32> Mul<FixedMatrix<Fixed<S, F>>> for Fixed<S, F>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn mul(self, rhs: FixedMatrix<Fixed<S, F>>) -> FixedMatrix<Fixed<S, F>> {
FixedMatrix::<Fixed<S, F>> {
rows: rhs.rows,
cols: rhs.cols,
data: rhs.data.iter().map(|&v| self * v).collect(),
}
}
}
impl<S: Storage, const F: u32> Mul<Fixed<S, F>> for FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
fn mul(self, rhs: Fixed<S, F>) -> Self {
Self {
rows: self.rows,
cols: self.cols,
data: self.data.iter().map(|&v| v * rhs).collect(),
}
}
}
impl<S: Storage, const F: u32> Mul<Fixed<S, F>> for &FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = FixedMatrix<Fixed<S, F>>;
fn mul(self, rhs: Fixed<S, F>) -> FixedMatrix<Fixed<S, F>> {
FixedMatrix::<Fixed<S, F>> {
rows: self.rows,
cols: self.cols,
data: self.data.iter().map(|&v| v * rhs).collect(),
}
}
}
impl<S: Storage, const F: u32> FixedMatrix<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
pub fn submatrix(&self, row: usize, col: usize, rows: usize, cols: usize) -> Self {
assert!(row + rows <= self.rows && col + cols <= self.cols,
"FixedMatrix::submatrix: extends beyond bounds");
Self::from_fn(rows, cols, |r, c| self.get(row + r, col + c))
}
pub fn set_submatrix(&mut self, row: usize, col: usize, sub: &FixedMatrix<Fixed<S, F>>) {
assert!(row + sub.rows <= self.rows && col + sub.cols <= self.cols,
"FixedMatrix::set_submatrix: extends beyond bounds");
for r in 0..sub.rows {
for c in 0..sub.cols {
self.set(row + r, col + c, sub.get(r, c));
}
}
}
pub fn kronecker(&self, other: &FixedMatrix<Fixed<S, F>>) -> Self {
let out_rows = self.rows * other.rows;
let out_cols = self.cols * other.cols;
Self::from_fn(out_rows, out_cols, |r, c| {
let ai = r / other.rows;
let bi = r % other.rows;
let aj = c / other.cols;
let bj = c % other.cols;
self.get(ai, aj) * other.get(bi, bj)
})
}
#[inline]
pub(crate) fn data_slice(&self) -> &[Fixed<S, F>] {
&self.data
}
pub fn swap_rows(&mut self, i: usize, j: usize) {
if i == j { return; }
assert!(i < self.rows && j < self.rows, "FixedMatrix::swap_rows: index out of bounds");
for c in 0..self.cols {
self.data.swap(i * self.cols + c, j * self.cols + c);
}
}
pub(crate) fn row_raw_range(&self, r: usize, col_start: usize, col_end: usize) -> Vec<S> {
self.data[r * self.cols + col_start..r * self.cols + col_end]
.iter().map(|fp| fp.raw()).collect()
}
}
impl<T: PartialEq> PartialEq for FixedMatrix<T> {
fn eq(&self, other: &Self) -> bool {
self.rows == other.rows && self.cols == other.cols && self.data == other.data
}
}
impl<T> Default for FixedMatrix<T> {
fn default() -> Self {
Self {
rows: 0,
cols: 0,
data: Vec::new(),
}
}
}