mirror of
https://github.com/drewcassidy/vector-victor.git
synced 2024-09-01 14:58:35 +00:00
More work on fleshing out the Matrix class
LU decomposition doesnt work yet :sad:
This commit is contained in:
parent
53b1f5fdfa
commit
0cc31266e9
@ -3,6 +3,5 @@ extern crate core;
|
|||||||
pub mod index;
|
pub mod index;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
mod matrix_traits;
|
|
||||||
|
|
||||||
pub use matrix::{Matrix, Vector};
|
pub use matrix::{Matrix, Vector};
|
||||||
|
286
src/matrix.rs
286
src/matrix.rs
@ -1,10 +1,13 @@
|
|||||||
use crate::impl_matrix_op;
|
use crate::impl_matrix_op;
|
||||||
use crate::index::Index2D;
|
use crate::index::Index2D;
|
||||||
use itertools::Itertools;
|
|
||||||
use num_traits::{Num, One, Zero};
|
use num_traits::real::Real;
|
||||||
|
use num_traits::{Num, NumOps, One, Signed, Zero};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::iter::{zip, Flatten, Product, Sum};
|
use std::iter::{zip, Flatten, Product, Sum};
|
||||||
|
|
||||||
use std::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
use std::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
||||||
|
|
||||||
/// A Scalar that a [Matrix] can be made up of.
|
/// A Scalar that a [Matrix] can be made up of.
|
||||||
///
|
///
|
||||||
/// This trait has no associated functions and can be implemented on any type that is [Default] and
|
/// This trait has no associated functions and can be implemented on any type that is [Default] and
|
||||||
@ -34,39 +37,37 @@ where
|
|||||||
/// An alias for a [Matrix] with a single column
|
/// An alias for a [Matrix] with a single column
|
||||||
pub type Vector<T, const N: usize> = Matrix<T, N, 1>;
|
pub type Vector<T, const N: usize> = Matrix<T, N, 1>;
|
||||||
|
|
||||||
pub trait MatrixDepth {
|
pub trait MatrixLike {
|
||||||
const DEPTH: usize = 1;
|
type Scalar: Copy;
|
||||||
|
const WIDTH: usize;
|
||||||
|
const HEIGHT: usize;
|
||||||
|
}
|
||||||
|
impl<T: Copy, const M: usize, const N: usize> MatrixLike for Matrix<T, M, N> {
|
||||||
|
type Scalar = T;
|
||||||
|
const WIDTH: usize = N;
|
||||||
|
const HEIGHT: usize = M;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Dot<R> {
|
pub trait SquareMatrix {}
|
||||||
type Output;
|
impl<T: Copy, const M: usize> SquareMatrix for Matrix<T, M, M> {}
|
||||||
#[must_use]
|
|
||||||
fn dot(&self, rhs: &R) -> Self::Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Cross<R> {
|
// pub trait Dot<R>: MatrixLike {
|
||||||
#[must_use]
|
// #[must_use]
|
||||||
fn cross_r(&self, rhs: &R) -> Self;
|
// fn dot(&self, rhs: &R) -> <Self as MatrixLike>::Scalar;
|
||||||
#[must_use]
|
// }
|
||||||
fn cross_l(&self, rhs: &R) -> Self;
|
//
|
||||||
}
|
// pub trait Cross<R> {
|
||||||
|
// #[must_use]
|
||||||
pub trait MMul<R> {
|
// fn cross_r(&self, rhs: &R) -> Self;
|
||||||
type Output;
|
// #[must_use]
|
||||||
#[must_use]
|
// fn cross_l(&self, rhs: &R) -> Self;
|
||||||
fn mmul(&self, rhs: &R) -> Self::Output;
|
// }
|
||||||
}
|
//
|
||||||
|
// pub trait MMul<R> {
|
||||||
pub trait Identity {
|
// type Output;
|
||||||
#[must_use]
|
// #[must_use]
|
||||||
fn identity() -> Self;
|
// fn mmul(&self, rhs: &R) -> Self::Output;
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub trait Determinant {
|
|
||||||
type Output;
|
|
||||||
#[must_use]
|
|
||||||
fn determinant(&self) -> Self::Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple access functions that only require T be copyable
|
// Simple access functions that only require T be copyable
|
||||||
impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
||||||
@ -235,15 +236,12 @@ impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn row(&self, m: usize) -> Vector<T, N> {
|
pub fn row(&self, m: usize) -> Option<Vector<T, N>> {
|
||||||
assert!(
|
if m < M {
|
||||||
m < M,
|
Some(Vector::<T, N>::vec(self.data[m]))
|
||||||
"Row index {} out of bounds for {}x{} matrix",
|
} else {
|
||||||
m,
|
None
|
||||||
M,
|
}
|
||||||
N
|
|
||||||
);
|
|
||||||
Vector::<T, N>::vec(self.data[m])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -260,6 +258,12 @@ impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pivot_row(&mut self, m1: usize, m2: usize) {
|
||||||
|
let tmp = self.row(m2).expect("Invalid row index");
|
||||||
|
self.set_row(m2, &self.row(m1).expect("Invalid row index"));
|
||||||
|
self.set_row(m1, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn col(&self, n: usize) -> Option<Vector<T, M>> {
|
pub fn col(&self, n: usize) -> Option<Vector<T, M>> {
|
||||||
@ -285,9 +289,15 @@ impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pivot_col(&mut self, n1: usize, n2: usize) {
|
||||||
|
let tmp = self.col(n2).expect("Invalid column index");
|
||||||
|
self.set_col(n2, &self.col(n1).expect("Invalid column index"));
|
||||||
|
self.set_col(n1, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn rows<'a>(&'a self) -> impl Iterator<Item = Vector<T, N>> + 'a {
|
pub fn rows<'a>(&'a self) -> impl Iterator<Item = Vector<T, N>> + 'a {
|
||||||
(0..M).map(|m| self.row(m))
|
(0..M).map(|m| self.row(m).expect("invalid row reached while iterating"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -302,6 +312,18 @@ impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
|||||||
Matrix::<T, N, M>::from_rows(self.cols())
|
Matrix::<T, N, M>::from_rows(self.cols())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abs(&self) -> Self
|
||||||
|
where
|
||||||
|
T: Default + PartialOrd + Zero + Neg<Output = T>,
|
||||||
|
{
|
||||||
|
self.elements()
|
||||||
|
.map(|&x| match x > T::zero() {
|
||||||
|
true => x,
|
||||||
|
false => -x,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
// pub fn mmul<const P: usize, R, O>(&self, rhs: &Matrix<R, P, N>) -> Matrix<T, P, M>
|
// pub fn mmul<const P: usize, R, O>(&self, rhs: &Matrix<R, P, N>) -> Matrix<T, P, M>
|
||||||
// where
|
// where
|
||||||
// R: Num,
|
// R: Num,
|
||||||
@ -344,25 +366,22 @@ impl<T: Copy, const M: usize> Vector<T, M> {
|
|||||||
data: data.map(|e| [e]),
|
data: data.map(|e| [e]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Copy, R: Copy, const M: usize> Dot<Vector<R, M>> for Vector<T, M>
|
pub fn dot<R>(&self, rhs: &R) -> T
|
||||||
where
|
where
|
||||||
for<'a> T: Sum<&'a T>,
|
for<'s> &'s Self: Mul<&'s R, Output = Self>,
|
||||||
for<'b> &'b Self: Mul<&'b Vector<R, M>, Output = Self>,
|
T: Sum<T>,
|
||||||
{
|
{
|
||||||
type Output = T;
|
(self * rhs).elements().cloned().sum()
|
||||||
fn dot(&self, rhs: &Matrix<R, M, 1>) -> Self::Output {
|
|
||||||
(self * rhs).elements().sum::<Self::Output>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy, R: Copy> Cross<Vector<R, 3>> for Vector<T, 3>
|
// Cross Product
|
||||||
where
|
impl<T: Copy> Vector<T, 3> {
|
||||||
T: Mul<R, Output = T> + Sub<T, Output = T>,
|
pub fn cross_r<R: Copy>(&self, rhs: &Vector<R, 3>) -> Self
|
||||||
Self: Neg<Output = Self>,
|
where
|
||||||
{
|
T: NumOps<R> + NumOps,
|
||||||
fn cross_r(&self, rhs: &Vector<R, 3>) -> Self {
|
{
|
||||||
Self::vec([
|
Self::vec([
|
||||||
(self[1] * rhs[2]) - (self[2] * rhs[1]),
|
(self[1] * rhs[2]) - (self[2] * rhs[1]),
|
||||||
(self[2] * rhs[0]) - (self[0] * rhs[2]),
|
(self[2] * rhs[0]) - (self[0] * rhs[2]),
|
||||||
@ -370,22 +389,21 @@ where
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross_l(&self, rhs: &Vector<R, 3>) -> Self {
|
pub fn cross_l<R: Copy>(&self, rhs: &Vector<R, 3>) -> Self
|
||||||
|
where
|
||||||
|
T: NumOps<R> + NumOps + Neg<Output = T>,
|
||||||
|
{
|
||||||
-self.cross_r(rhs)
|
-self.cross_r(rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Matrix Multiplication
|
//Matrix Multiplication
|
||||||
impl<T: Copy, R: Copy, const M: usize, const N: usize, const P: usize> MMul<Matrix<R, N, P>>
|
impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
|
||||||
for Matrix<T, M, N>
|
pub fn mmul<R: Copy, const P: usize>(&self, rhs: &Matrix<R, N, P>) -> Matrix<T, M, P>
|
||||||
where
|
where
|
||||||
T: Default,
|
T: Default + NumOps<R> + Sum,
|
||||||
Vector<T, N>: Dot<Vector<R, N>, Output = T>,
|
{
|
||||||
{
|
let mut result: Matrix<T, M, P> = Default::default();
|
||||||
type Output = Matrix<T, M, P>;
|
|
||||||
|
|
||||||
fn mmul(&self, rhs: &Matrix<R, N, P>) -> Self::Output {
|
|
||||||
let mut result = Self::Output::default();
|
|
||||||
|
|
||||||
for (m, a) in self.rows().enumerate() {
|
for (m, a) in self.rows().enumerate() {
|
||||||
for (n, b) in rhs.cols().enumerate() {
|
for (n, b) in rhs.cols().enumerate() {
|
||||||
@ -397,41 +415,97 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity
|
// Matrix Decomposition and related functions
|
||||||
impl<T: Copy + Zero + One, const M: usize> Identity for Matrix<T, M, M> {
|
impl<T: Copy, const N: usize> Matrix<T, N, N>
|
||||||
fn identity() -> Self {
|
where
|
||||||
|
T: Real + Default,
|
||||||
|
{
|
||||||
|
pub fn lu(&self) -> Option<(Self, Vector<usize, N>, T)> {
|
||||||
|
let mut lu = self.clone();
|
||||||
|
let mut idx: Vector<usize, N> = Default::default();
|
||||||
|
let mut d = T::one();
|
||||||
|
|
||||||
|
let mut vv: Vector<T, N> = self
|
||||||
|
.rows()
|
||||||
|
.map(|row| {
|
||||||
|
let m = row.elements().cloned().reduce(|acc, x| acc.max(x.abs()))?;
|
||||||
|
match m < T::epsilon() {
|
||||||
|
true => None,
|
||||||
|
false => Some(T::one() / m),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Option<_>>()?; // get the inverse maxabs value in each row
|
||||||
|
|
||||||
|
for k in 0..N {
|
||||||
|
// search for the pivot element and its index
|
||||||
|
let ipivot = (lu.col(k)? * vv)
|
||||||
|
.abs()
|
||||||
|
.elements()
|
||||||
|
.enumerate()
|
||||||
|
.skip(k) // below the diagonal
|
||||||
|
.reduce(|(imax, xmax), (i, x)| match x > xmax {
|
||||||
|
// Is the figure of merit for the pivot better than the best so far?
|
||||||
|
true => (i, x),
|
||||||
|
false => (imax, xmax),
|
||||||
|
})?
|
||||||
|
.0;
|
||||||
|
|
||||||
|
// do we need to interchange rows?
|
||||||
|
if k != ipivot {
|
||||||
|
lu.pivot_row(ipivot, k); // yes, we do
|
||||||
|
d = -d; // change parity of d
|
||||||
|
vv[ipivot] = vv[k] //interchange scale factor
|
||||||
|
}
|
||||||
|
idx[k] = ipivot;
|
||||||
|
|
||||||
|
let pivot = lu[(k, k)];
|
||||||
|
if pivot.abs() < T::epsilon() {
|
||||||
|
// if the pivot is zero, the matrix is singular
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in (k + 1)..N {
|
||||||
|
// divide by the pivot element
|
||||||
|
let dpivot = lu[(i, k)] / pivot;
|
||||||
|
lu[(i, k)] = dpivot;
|
||||||
|
for j in (k + 1)..N {
|
||||||
|
// reduce remaining submatrix
|
||||||
|
lu[(i, j)] = lu[(i, j)] - dpivot * lu[(k, j)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some((lu, idx, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(&self) -> Option<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn det(&self) -> Self::Scalar {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square matrices
|
||||||
|
impl<T: Copy, const N: usize> Matrix<T, N, N> {
|
||||||
|
pub fn identity() -> Self
|
||||||
|
where
|
||||||
|
T: Zero + One,
|
||||||
|
{
|
||||||
let mut result = Self::zero();
|
let mut result = Self::zero();
|
||||||
for i in 0..M {
|
for i in 0..N {
|
||||||
result[(i, i)] = T::one();
|
result[(i, i)] = T::one();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Determinant
|
pub fn diagonals<'s>(&'s self) -> impl Iterator<Item = T> + 's {
|
||||||
impl<T: Copy, const M: usize> Determinant for Matrix<T, M, M>
|
(0..N).map(|n| self[(n, n)])
|
||||||
where
|
}
|
||||||
for<'a> T: Product<T> + Sum<T> + Mul<&'a i32, Output = T>,
|
|
||||||
{
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn determinant(&self) -> Self::Output {
|
pub fn subdiagonals<'s>(&'s self) -> impl Iterator<Item = T> + 's {
|
||||||
// Leibniz formula
|
(0..N - 1).map(|n| self[(n, n + 1)])
|
||||||
|
|
||||||
// alternating 1,-1,1,-1...
|
|
||||||
let signs = [1, -1].iter().cycle();
|
|
||||||
// all permutations of 0..M
|
|
||||||
let column_permutations = (0..M).permutations(M);
|
|
||||||
|
|
||||||
// Calculating the determinant is done by summing up M steps,
|
|
||||||
// each with a different permutation of columns and alternating sign
|
|
||||||
// Each step involves a product of the components being operated on
|
|
||||||
let summand = |(columns, sign)| -> T {
|
|
||||||
zip(0..M, columns).map(|(r, c)| self[(r, c)]).product::<T>() * sign
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now sum up all steps
|
|
||||||
zip(column_permutations, signs).map(summand).sum()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,31 +623,19 @@ where
|
|||||||
|
|
||||||
impl<T: Copy + AddAssign, const M: usize, const N: usize> Sum for Matrix<T, M, N>
|
impl<T: Copy + AddAssign, const M: usize, const N: usize> Sum for Matrix<T, M, N>
|
||||||
where
|
where
|
||||||
Self: Zero + AddAssign,
|
Self: Zero + Add<Output = Self>,
|
||||||
{
|
{
|
||||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let mut sum = Self::zero();
|
iter.fold(Self::zero(), Self::add)
|
||||||
|
|
||||||
for m in iter {
|
|
||||||
sum += m;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + MulAssign, const M: usize, const N: usize> Product for Matrix<T, M, N>
|
impl<T: Copy + MulAssign, const M: usize, const N: usize> Product for Matrix<T, M, N>
|
||||||
where
|
where
|
||||||
Self: One + MulAssign,
|
Self: One + Mul<Output = Self>,
|
||||||
{
|
{
|
||||||
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let mut prod = Self::one();
|
iter.fold(Self::one(), Self::mul)
|
||||||
|
|
||||||
for m in iter {
|
|
||||||
prod *= m;
|
|
||||||
}
|
|
||||||
|
|
||||||
prod
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
tests/ops.rs
11
tests/ops.rs
@ -1,6 +1,9 @@
|
|||||||
use generic_parameterize::parameterize;
|
use generic_parameterize::parameterize;
|
||||||
|
use std::convert::identity;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
use vector_victor::Matrix;
|
use vector_victor::Matrix;
|
||||||
|
|
||||||
#[parameterize(S = (i32, f32, u32), M = [1,4], N = [1,4])]
|
#[parameterize(S = (i32, f32, u32), M = [1,4], N = [1,4])]
|
||||||
@ -16,3 +19,11 @@ where
|
|||||||
assert_eq!(*ci, S::from(4));
|
assert_eq!(*ci, S::from(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lu() {
|
||||||
|
// let a: Matrix<f32, 3, 3> = Matrix::<f32, 3, 3>::identity();
|
||||||
|
let a = Matrix::new([[1.0, 2.0], [3.0, 4.0]]);
|
||||||
|
let (lu, _idx, _d) = a.lu().expect("What");
|
||||||
|
println!("{:?}", lu);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user