initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
186
Cargo.lock
generated
Normal file
186
Cargo.lock
generated
Normal file
@@ -0,0 +1,186 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opimps"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "857dabe64a7afe2e51ac9962dc3c008e74ae050dd47e21a7e7b1fc69a67a0229"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmath"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"opimps",
|
||||
"rand",
|
||||
"sealed",
|
||||
"trait-group",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sealed"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trait-group"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1b362975c6f0f21a41fbb9ca91fe5dcb7e01e12331360374347476b45f5cb9c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
28
Cargo.toml
Normal file
28
Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "rmath"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Walter Oggioni <oggioni.walter@gmail.com>"]
|
||||
license = "MIT"
|
||||
rust-version = "1.60"
|
||||
|
||||
[dependencies]
|
||||
trait-group = "0.1.0"
|
||||
sealed = "0.5"
|
||||
num-traits = "0.2.17"
|
||||
opimps = "0.2.2"
|
||||
|
||||
[dev-dependencies]
|
||||
num-bigint = "0.4.4"
|
||||
rand = "0.8.5"
|
||||
|
||||
[lib]
|
||||
name = "rmath"
|
||||
crate-type = ["lib"]
|
||||
bench = false
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
debug-assertions = false
|
||||
codegen-units = 1
|
42
examples/benchmark.rs
Normal file
42
examples/benchmark.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::env;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
use rmath::HMatrix;
|
||||
use rmath::Rational;
|
||||
use rmath::NumericalMatrix;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use rand::RngCore;
|
||||
|
||||
fn main( ) {
|
||||
let size = env::args().skip(1).map(|it| {
|
||||
it.parse::<u32>().unwrap()
|
||||
}).take(1).collect::<Vec<u32>>()[0];
|
||||
let mut rand = {
|
||||
let seed = [
|
||||
1,0,1,3,
|
||||
2,5,0,0,
|
||||
200,1,0,0,
|
||||
210,30,0,0,
|
||||
78,134,31,0,
|
||||
253,11,7,0,
|
||||
120,169,89,48,
|
||||
200,0,202,0
|
||||
];
|
||||
StdRng::from_seed(seed)
|
||||
};
|
||||
let mtx = HMatrix::<Rational<BigInt>>::new(
|
||||
usize::try_from(size).unwrap(),usize::try_from(size).unwrap(), |_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % (size * 20)) - size * 10, BigInt::one())
|
||||
});
|
||||
let b = HMatrix::<Rational<BigInt>>::new(
|
||||
usize::try_from(size).unwrap(),usize::try_from(size).unwrap(), |_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % (size * 10)) - (size * 5), BigInt::one())
|
||||
});
|
||||
let lu = mtx.clone().lu().unwrap();
|
||||
let x = lu.solve(&b).unwrap();
|
||||
|
||||
assert!((mtx * x - b).is_zero());
|
||||
}
|
51
examples/matrix_example.rs
Normal file
51
examples/matrix_example.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use rmath::HMatrix;
|
||||
use rmath::SMatrix;
|
||||
|
||||
fn main() {
|
||||
let mut acc = 0;
|
||||
let mut m1 = HMatrix::new(3, 3, |_| {
|
||||
let res = acc;
|
||||
acc += 1;
|
||||
res
|
||||
});
|
||||
println!("{}", m1);
|
||||
println!("{}", &m1 + &m1);
|
||||
println!("{}", m1.clone() - m1.clone());
|
||||
println!("{}", &m1 * &m1);
|
||||
|
||||
acc = 0;
|
||||
let mut m2: SMatrix<i32, 3, 3> = SMatrix::new(|_| {
|
||||
let res = acc;
|
||||
acc += 1;
|
||||
res
|
||||
});
|
||||
println!("{}", m2);
|
||||
println!("{}", m2 + m2);
|
||||
println!("{}", m2 - m2);
|
||||
println!("{}", m2 * m2);
|
||||
|
||||
for (i, j, v) in m2 {
|
||||
println!("{} {} {}", i, j, v);
|
||||
}
|
||||
for (i, j, v) in &mut m2 {
|
||||
println!("{} {} {}", i, j, v);
|
||||
*v = 5;
|
||||
}
|
||||
println!("{}", m2);
|
||||
println!("{}", m1 == m1);
|
||||
println!("{}", m1 == &m1 + &m1);
|
||||
println!("{}", m2 == m2);
|
||||
println!("{}", m2 == m2 + m2);
|
||||
|
||||
for (i, j, v) in &mut m1 {
|
||||
println!("{} {} {}", i, j, v);
|
||||
*v = 0;
|
||||
}
|
||||
|
||||
let a: SMatrix<i32, 3, 2> =
|
||||
SMatrix::new(|(i, j)| i32::try_from(i).unwrap() - i32::try_from(j).unwrap());
|
||||
let b: SMatrix<i32, 2, 3> =
|
||||
SMatrix::new(|(i, j)| i32::try_from(i).unwrap() - i32::try_from(j).unwrap());
|
||||
println!("{}", a * b);
|
||||
println!("{}", b * a);
|
||||
}
|
50
src/err.rs
Normal file
50
src/err.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait RmathError {
|
||||
fn msg(&self) -> String;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SingularMatrixError {}
|
||||
|
||||
impl Debug for SingularMatrixError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.msg())
|
||||
}
|
||||
}
|
||||
|
||||
impl RmathError for SingularMatrixError {
|
||||
fn msg(&self) -> String {
|
||||
String::from("Matrix is singular")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizeError {
|
||||
msg: String,
|
||||
}
|
||||
|
||||
impl Debug for dyn RmathError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.msg())
|
||||
}
|
||||
}
|
||||
|
||||
impl RmathError for SizeError {
|
||||
fn msg(&self) -> String {
|
||||
self.msg.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SizeError {
|
||||
pub fn new(msg: &str) -> SizeError {
|
||||
SizeError {
|
||||
msg: String::from(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SizeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.msg())
|
||||
}
|
||||
}
|
215
src/hludecomposition.rs
Normal file
215
src/hludecomposition.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
use std::ops::Add;
|
||||
use std::ops::Div;
|
||||
use std::ops::DivAssign;
|
||||
use std::ops::Mul;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use crate::err::RmathError;
|
||||
|
||||
use super::hpivot::HPivot;
|
||||
use super::matrix::LUDecompositionImpl;
|
||||
use super::matrix::NumericalMatrixImpl;
|
||||
use super::HMatrix;
|
||||
use super::LUDecomposition;
|
||||
use super::Matrix;
|
||||
use super::NumericalMatrix;
|
||||
use super::Pivot;
|
||||
use super::SingularMatrixError;
|
||||
|
||||
use super::misc::EnhancedOption;
|
||||
|
||||
pub struct HLUDecomposition<TYPE> {
|
||||
matrix: HMatrix<TYPE>,
|
||||
pivot: HPivot,
|
||||
}
|
||||
|
||||
impl<TYPE> HLUDecomposition<TYPE>
|
||||
where TYPE : Zero + SubAssign<TYPE> + Clone,
|
||||
for<'a> &'a TYPE : Mul<&'a TYPE, Output = TYPE>,
|
||||
for<'a> TYPE : DivAssign<&'a TYPE> {
|
||||
pub (crate) fn new(matrix: HMatrix<TYPE>, pivot: Option<HPivot>) -> Self {
|
||||
let size = matrix.rows();
|
||||
HLUDecomposition {
|
||||
matrix,
|
||||
pivot: pivot.otherwise(|| HPivot::new(size)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve(&self, b : &HMatrix<TYPE>) -> Result<HMatrix<TYPE>, Box<dyn RmathError>> {
|
||||
let mut x = HMatrix::<TYPE>::new(b.rows(), b.columns(), |_| TYPE::zero());
|
||||
for n in 0..b.columns() {
|
||||
for i in 0..self.matrix.rows() {
|
||||
x[(i, n)] = b[(self.pivot[i], n)].clone();
|
||||
for k in 0..i {
|
||||
let sub = &self.matrix[(i, k)] * &x[(k, n)];
|
||||
x[(i, n)] -= sub;
|
||||
}
|
||||
}
|
||||
for i in (0..self.matrix.rows()).rev() {
|
||||
for k in (i + 1)..self.matrix.rows() {
|
||||
let sub = &self.matrix[(i, k)] * &x[(k, n)];
|
||||
x[(i, n)] -= sub;
|
||||
}
|
||||
if self.matrix[(i, i)].is_zero() {
|
||||
return Err(Box::<SingularMatrixError>::default());
|
||||
} else {
|
||||
x[(i, n)] /= &self.matrix[(i, i)];
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> LUDecompositionImpl<TYPE, HMatrix<TYPE>, HPivot> for HLUDecomposition<TYPE>
|
||||
where
|
||||
for<'a> TYPE: 'a + One + MulAssign<&'a TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn get_matrix(&self) -> &HMatrix<TYPE> {
|
||||
&self.matrix
|
||||
}
|
||||
|
||||
fn get_pivot(&self) -> &HPivot {
|
||||
&self.pivot
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> LUDecomposition<TYPE, HMatrix<TYPE>, HPivot> for HLUDecomposition<TYPE>
|
||||
where
|
||||
for<'b> TYPE: 'b
|
||||
+ Clone
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ Add<&'b TYPE, Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>
|
||||
+ SubAssign<TYPE>
|
||||
+ SubAssign<&'b TYPE>
|
||||
+ DivAssign<TYPE>
|
||||
+ DivAssign<&'b TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'c> &'c TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn l(&self) -> HMatrix<TYPE> {
|
||||
self.matrix.clone().tril_replace(TYPE::one())
|
||||
}
|
||||
|
||||
fn u(&self) -> HMatrix<TYPE> {
|
||||
self.matrix.clone().triu()
|
||||
}
|
||||
|
||||
fn invert(&self) -> HMatrix<TYPE> {
|
||||
let mut result =
|
||||
HMatrix::<TYPE>::new(self.matrix.rows(), self.matrix.columns(), |_| TYPE::zero());
|
||||
NumericalMatrixImpl::<TYPE, HPivot, HLUDecomposition<TYPE>>::lu_invert(
|
||||
&self.matrix,
|
||||
&mut result,
|
||||
&mut self.pivot.clone(),
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
fn pivot(&self, m: HMatrix<TYPE>) -> HMatrix<TYPE> {
|
||||
&self.pivot * m
|
||||
}
|
||||
|
||||
// fn p<'a>(&'a self) -> &'a HPivot {
|
||||
// &self.pivot
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::rngs::StdRng;
|
||||
use rand::RngCore;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::HMatrix;
|
||||
use num_traits::Zero;
|
||||
|
||||
use rand;
|
||||
|
||||
#[test]
|
||||
fn solve_linear_system() {
|
||||
let mut rand = {
|
||||
let seed = [
|
||||
1,0,1,3,
|
||||
2,5,0,0,
|
||||
200,1,0,0,
|
||||
210,30,0,0,
|
||||
78,134,31,0,
|
||||
253,11,7,0,
|
||||
120,169,89,48,
|
||||
200,0,202,0
|
||||
];
|
||||
StdRng::from_seed(seed)
|
||||
};
|
||||
let mtx = HMatrix::<Rational<i64>>::new(5, 5, |_| {
|
||||
Rational::new(i64::try_from(rand.next_u32() % 40).unwrap() - 20, 1)
|
||||
});
|
||||
let b = HMatrix::<Rational<i64>>::new(5, 5, |_| {
|
||||
Rational::new(i64::try_from(rand.next_u32() % 20).unwrap() - 10, 1)
|
||||
});
|
||||
let lu = mtx.clone().lu().unwrap();
|
||||
let x = lu.solve(&b).unwrap();
|
||||
assert!((mtx * x - b).is_zero());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_big_int {
|
||||
use num_traits::Zero;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::RngCore;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::HMatrix;
|
||||
|
||||
use rand;
|
||||
|
||||
#[test]
|
||||
fn solve_linear_system() {
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::One;
|
||||
|
||||
let mut rand = {
|
||||
let seed = [
|
||||
1,0,1,3,
|
||||
2,5,0,0,
|
||||
200,1,0,0,
|
||||
210,30,0,0,
|
||||
78,134,31,0,
|
||||
253,11,7,0,
|
||||
120,169,89,48,
|
||||
200,0,202,0
|
||||
];
|
||||
StdRng::from_seed(seed)
|
||||
};
|
||||
let mtx = HMatrix::<Rational<BigInt>>::new(10, 10, |_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % 200) - 100, BigInt::one())
|
||||
});
|
||||
let b = HMatrix::<Rational<BigInt>>::new(10, 10, |_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % 100) - 50, BigInt::one())
|
||||
});
|
||||
let lu = mtx.clone().lu().unwrap();
|
||||
let x = lu.solve(&b).unwrap();
|
||||
|
||||
assert!((mtx * x - b).is_zero());
|
||||
|
||||
}
|
||||
|
||||
}
|
1057
src/hmatrix.rs
Normal file
1057
src/hmatrix.rs
Normal file
File diff suppressed because it is too large
Load Diff
87
src/hpivot.rs
Normal file
87
src/hpivot.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
use std::ops::Mul;
|
||||
|
||||
use super::hmatrix::HMatrix;
|
||||
use super::matrix::MatrixImpl;
|
||||
use super::Matrix;
|
||||
use super::Pivot;
|
||||
|
||||
pub struct HPivot {
|
||||
data: Vec<usize>,
|
||||
permutations: usize,
|
||||
}
|
||||
|
||||
impl Index<usize> for HPivot {
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.data[index]
|
||||
}
|
||||
|
||||
type Output = usize;
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for HPivot {
|
||||
fn index_mut(&mut self, index: usize) -> &mut <Self as Index<usize>>::Output {
|
||||
&mut self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Pivot for HPivot {
|
||||
fn swap(&mut self, i1: usize, i2: usize) {
|
||||
self.data.swap(i1, i2);
|
||||
self.permutations += 1;
|
||||
}
|
||||
|
||||
fn permutations(&self) -> usize {
|
||||
self.permutations
|
||||
}
|
||||
|
||||
fn new(size: usize) -> Self {
|
||||
HPivot {
|
||||
data: (0..size).collect(),
|
||||
permutations: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Mul<HMatrix<TYPE>> for HPivot
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn mul(self, matrix: HMatrix<TYPE>) -> Self::Output {
|
||||
&self * matrix
|
||||
}
|
||||
|
||||
type Output = HMatrix<TYPE>;
|
||||
}
|
||||
|
||||
impl<TYPE> Mul<HMatrix<TYPE>> for &HPivot
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn mul(self, matrix: HMatrix<TYPE>) -> Self::Output {
|
||||
if self.data.len() != matrix.rows() {
|
||||
panic!("Pivot len must equal matrix rows")
|
||||
}
|
||||
let mut result = matrix.clone();
|
||||
let mut pclone = self.clone();
|
||||
for i in 0..matrix.rows() {
|
||||
while i != pclone[i] {
|
||||
let j = pclone[i];
|
||||
result.swap_rows_pivot(&mut pclone, i, j);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
type Output = HMatrix<TYPE>;
|
||||
}
|
||||
|
||||
impl Clone for HPivot {
|
||||
fn clone(&self) -> Self {
|
||||
HPivot {
|
||||
data: self.data.clone(),
|
||||
permutations: self.permutations,
|
||||
}
|
||||
}
|
||||
}
|
97
src/lib.rs
Normal file
97
src/lib.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
#[macro_use]
|
||||
extern crate trait_group;
|
||||
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
|
||||
mod rational;
|
||||
pub use self::rational::Rational;
|
||||
|
||||
mod matrix;
|
||||
pub use self::matrix::LUDecomposition;
|
||||
|
||||
mod hpivot;
|
||||
pub use hpivot::HPivot;
|
||||
|
||||
mod hludecomposition;
|
||||
pub use hludecomposition::HLUDecomposition;
|
||||
|
||||
mod hmatrix;
|
||||
pub use self::hmatrix::HMatrix;
|
||||
|
||||
mod spivot;
|
||||
pub use spivot::SPivot;
|
||||
|
||||
mod sludecomposition;
|
||||
pub use sludecomposition::SLUDecomposition;
|
||||
|
||||
mod smatrix;
|
||||
pub use self::smatrix::SMatrix;
|
||||
|
||||
mod err;
|
||||
pub use self::err::SingularMatrixError;
|
||||
pub use self::err::SizeError;
|
||||
|
||||
mod misc;
|
||||
|
||||
pub trait Pivot: Index<usize, Output = usize> + IndexMut<usize, Output = usize> + Sized {
|
||||
fn swap(&mut self, i1: usize, i2: usize);
|
||||
fn permutations(&self) -> usize;
|
||||
fn new(size: usize) -> Self;
|
||||
}
|
||||
|
||||
|
||||
pub trait Vector<TYPE> : Index<usize, Output = TYPE> + IndexMut<usize, Output = TYPE> {
|
||||
fn new<F : FnMut(usize) -> TYPE>(size : usize, cb : F) -> Self;
|
||||
fn length(&self) -> usize;
|
||||
}
|
||||
|
||||
pub trait NumericalVector<TYPE> : Vector<TYPE> {
|
||||
fn norm() -> TYPE;
|
||||
}
|
||||
|
||||
pub trait Matrix<TYPE>
|
||||
where
|
||||
Self: Index<(usize, usize), Output = TYPE> + IndexMut<(usize, usize), Output = TYPE> + Sized,
|
||||
{
|
||||
fn rows(&self) -> usize;
|
||||
fn columns(&self) -> usize;
|
||||
|
||||
fn size(&self) -> (usize, usize) {
|
||||
(self.rows(), self.columns())
|
||||
}
|
||||
|
||||
fn transpose(self) -> Result<Self, SizeError>;
|
||||
}
|
||||
|
||||
pub trait NumericalMatrix<TYPE, PIVOT: Pivot, LU: LUDecomposition<TYPE, Self, PIVOT>>:
|
||||
Matrix<TYPE>
|
||||
where
|
||||
for<'a> TYPE: Clone + One + Zero + MulAssign<&'a TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn identity(size: usize) -> Self;
|
||||
|
||||
fn tril(self) -> Self;
|
||||
|
||||
fn triu(self) -> Self;
|
||||
|
||||
fn tril_replace(self, diag_replacement: TYPE) -> Self;
|
||||
|
||||
fn triu_replace(self, diag_replacement: TYPE) -> Self;
|
||||
|
||||
fn gauss_jordan_high(self) -> Self;
|
||||
|
||||
fn gauss_jordan_low(self) -> Self;
|
||||
|
||||
fn det(self) -> TYPE;
|
||||
|
||||
fn invert(&mut self) -> Self;
|
||||
|
||||
fn lup(self) -> Result<LU, SingularMatrixError>;
|
||||
|
||||
fn lu(self) -> Result<LU, SingularMatrixError>;
|
||||
}
|
489
src/matrix.rs
Normal file
489
src/matrix.rs
Normal file
@@ -0,0 +1,489 @@
|
||||
use super::Matrix;
|
||||
use super::Pivot;
|
||||
use super::SingularMatrixError;
|
||||
use super::SizeError;
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
use std::ops::Add;
|
||||
use std::ops::Div;
|
||||
use std::ops::DivAssign;
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
use std::ops::Mul;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
use std::ops::SubAssign;
|
||||
|
||||
pub trait MatrixImpl<'a, TYPE>: Matrix<TYPE>
|
||||
where
|
||||
TYPE: 'a,
|
||||
Self: Index<(usize, usize), Output = TYPE>
|
||||
+ IndexMut<(usize, usize), Output = TYPE>
|
||||
+ IntoIterator<Item = (usize, usize, TYPE), IntoIter = Self::IteratorType>
|
||||
+ 'a,
|
||||
&'a Self: IntoIterator<Item = (usize, usize, &'a TYPE), IntoIter = Self::RefIteratorType<'a>>,
|
||||
&'a mut Self:
|
||||
IntoIterator<Item = (usize, usize, &'a mut TYPE), IntoIter = Self::MutRefIteratorType<'a>>,
|
||||
Self::IteratorType: Iterator<Item = (usize, usize, TYPE)>,
|
||||
Self::RefIteratorType<'a>: Iterator<Item = (usize, usize, &'a TYPE)>,
|
||||
Self::MutRefIteratorType<'a>: Iterator<Item = (usize, usize, &'a mut TYPE)>,
|
||||
{
|
||||
fn swap(&mut self, idx1: (usize, usize), idx2: (usize, usize)) {
|
||||
unsafe {
|
||||
// Can't take two mutable loans from one matrix, so instead just cast
|
||||
// them to their raw pointers to do the swap
|
||||
let pa: *mut TYPE = &mut self[idx1];
|
||||
let pb: *mut TYPE = &mut self[idx2];
|
||||
std::ptr::swap(pa, pb);
|
||||
}
|
||||
}
|
||||
|
||||
fn transpose_in_place(&mut self) -> Result<(), SizeError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
for j in 0..self.columns() {
|
||||
self.swap((i, j), (j, i));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn swap_rows(&mut self, row1: usize, row2: usize) {
|
||||
for i in 0..self.columns() {
|
||||
self.swap((row1, i), (row2, i))
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_rows_pivot<P: Pivot>(&mut self, pivot: &mut P, row1: usize, row2: usize) {
|
||||
self.swap_rows(row1, row2);
|
||||
pivot.swap(row1, row2);
|
||||
}
|
||||
|
||||
fn swap_rows_mul_pivot<P: Pivot>(
|
||||
&mut self,
|
||||
other: &mut Self,
|
||||
pivot: &mut P,
|
||||
row1: usize,
|
||||
row2: usize,
|
||||
) {
|
||||
self.swap_rows_pivot(pivot, row1, row2);
|
||||
other.swap_rows(row1, row2);
|
||||
}
|
||||
|
||||
fn swap_rows_mul(&mut self, other: &mut Self, row1: usize, row2: usize) {
|
||||
self.swap_rows(row1, row2);
|
||||
other.swap_rows(row1, row2);
|
||||
}
|
||||
|
||||
fn swap_columns(&mut self, col1: usize, col2: usize) {
|
||||
for i in 0..self.rows() {
|
||||
self.swap((i, col1), (i, col2))
|
||||
}
|
||||
}
|
||||
|
||||
type IteratorType;
|
||||
type RefIteratorType<'b>;
|
||||
type MutRefIteratorType<'b>;
|
||||
}
|
||||
|
||||
pub trait LUDecompositionImpl<TYPE, MATRIX, PIVOT: Pivot>
|
||||
where
|
||||
MATRIX: Matrix<TYPE>,
|
||||
for<'b> TYPE: One + MulAssign<&'b TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn get_matrix(&self) -> &MATRIX;
|
||||
|
||||
fn get_pivot(&self) -> &PIVOT;
|
||||
|
||||
fn det(&self) -> TYPE {
|
||||
let mut result = TYPE::one();
|
||||
for i in 0..self.get_matrix().rows() {
|
||||
result *= &self.get_matrix()[(i, i)];
|
||||
}
|
||||
if self.get_pivot().permutations() % 2 != 0 {
|
||||
result = -result;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LUDecomposition<TYPE, MATRIX: Matrix<TYPE>, PIVOT: Pivot>:
|
||||
LUDecompositionImpl<TYPE, MATRIX, PIVOT>
|
||||
where
|
||||
for<'b> TYPE: One + MulAssign<&'b TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn l(&self) -> MATRIX;
|
||||
|
||||
fn u(&self) -> MATRIX;
|
||||
|
||||
fn invert(&self) -> MATRIX;
|
||||
|
||||
fn pivot(&self, m: MATRIX) -> MATRIX;
|
||||
|
||||
fn det(&self) -> TYPE {
|
||||
LUDecompositionImpl::det(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NumericalMatrixImpl<'a, TYPE, PIVOT: Pivot, LU: LUDecomposition<TYPE, Self, PIVOT>>:
|
||||
MatrixImpl<'a, TYPE>
|
||||
where
|
||||
for<'b> TYPE: Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<TYPE, Output = TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'c> &'c TYPE: Mul<&'c TYPE, Output = TYPE>
|
||||
+ Add<&'c TYPE, Output = TYPE>
|
||||
+ Div<&'c TYPE, Output = TYPE>
|
||||
+ Neg<Output = TYPE>,
|
||||
Self: Sized,
|
||||
TYPE: 'a,
|
||||
Self: Index<(usize, usize), Output = TYPE>
|
||||
+ IndexMut<(usize, usize), Output = TYPE>
|
||||
+ IntoIterator<Item = (usize, usize, TYPE), IntoIter = Self::IteratorType>
|
||||
+ 'a,
|
||||
&'a Self: IntoIterator<Item = (usize, usize, &'a TYPE), IntoIter = Self::RefIteratorType<'a>>,
|
||||
&'a mut Self:
|
||||
IntoIterator<Item = (usize, usize, &'a mut TYPE), IntoIter = Self::MutRefIteratorType<'a>>,
|
||||
Self::IteratorType: Iterator<Item = (usize, usize, TYPE)>,
|
||||
Self::RefIteratorType<'a>: Iterator<Item = (usize, usize, &'a TYPE)>,
|
||||
Self::MutRefIteratorType<'a>: Iterator<Item = (usize, usize, &'a mut TYPE)>,
|
||||
{
|
||||
fn identity(size: usize) -> Self;
|
||||
|
||||
fn add_row(&mut self, source_index: usize, dest_index: usize, factor: &TYPE)
|
||||
where
|
||||
TYPE: Mul<Output = TYPE> + Add<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
for i in 0..self.columns() {
|
||||
let tmp = &(self[(source_index, i)]) * factor;
|
||||
self[(dest_index, i)] = &(self[(dest_index, i)]) + &tmp;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_row_mul(
|
||||
&mut self,
|
||||
other: &mut Self,
|
||||
source_index: usize,
|
||||
dest_index: usize,
|
||||
factor: &TYPE,
|
||||
) where
|
||||
TYPE: Add<Output = TYPE> + Mul<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
self.add_row(source_index, dest_index, factor);
|
||||
other.add_row(source_index, dest_index, factor);
|
||||
}
|
||||
|
||||
fn gauss_jordan_low<P: Pivot>(&mut self, pivot: &mut P)
|
||||
where
|
||||
TYPE: Zero
|
||||
+ PartialEq
|
||||
+ Add<Output = TYPE>
|
||||
+ Mul<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
if self[(i, i)] == TYPE::zero() {
|
||||
for j in (i + 1)..self.columns() {
|
||||
if self[(j, i)] != TYPE::zero() {
|
||||
self.swap_rows_pivot(pivot, i, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for j in (i + 1)..self.rows() {
|
||||
if self[(i, i)] != TYPE::zero() {
|
||||
let neg = -(&self[(j, i)]);
|
||||
let factor = &neg / &(self[(i, i)]);
|
||||
self.add_row(i, j, &factor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gauss_jordan_low_mul<P: Pivot>(&mut self, other: &mut Self, pivot: &mut P)
|
||||
where
|
||||
TYPE: Zero
|
||||
+ PartialEq
|
||||
+ Add<Output = TYPE>
|
||||
+ Mul<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
if self[(i, i)] == TYPE::zero() {
|
||||
for j in (i + 1)..self.columns() {
|
||||
if self[(j, i)] != TYPE::zero() {
|
||||
self.swap_rows_mul_pivot(other, pivot, i, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for j in (i + 1)..self.rows() {
|
||||
if self[(i, i)] != TYPE::zero() {
|
||||
let neg = -(&self[(j, i)]);
|
||||
let factor = &neg / &(self[(i, i)]);
|
||||
self.add_row_mul(other, i, j, &factor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gauss_jordan_high<P: Pivot>(&mut self, pivot: &mut P)
|
||||
where
|
||||
TYPE: Zero
|
||||
+ PartialEq
|
||||
+ Add<Output = TYPE>
|
||||
+ Mul<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
for i in (usize::zero()..self.rows()).rev() {
|
||||
if self[(i, i)] == TYPE::zero() {
|
||||
for j in (usize::zero()..i).rev() {
|
||||
if self[(j, i)] != TYPE::zero() {
|
||||
self.swap_rows_pivot(pivot, i, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for j in (usize::zero()..i).rev() {
|
||||
if self[(i, i)] != TYPE::zero() {
|
||||
let neg = -(&self[(j, i)]);
|
||||
let factor = &neg / &(self[(i, i)]);
|
||||
self.add_row(i, j, &factor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gauss_jordan_high_mul<P: Pivot>(&mut self, other: &mut Self, pivot: &mut P)
|
||||
where
|
||||
TYPE: Zero
|
||||
+ PartialEq
|
||||
+ Add<Output = TYPE>
|
||||
+ Mul<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
for i in (usize::zero()..self.rows()).rev() {
|
||||
if self[(i, i)] == TYPE::zero() {
|
||||
for j in i..usize::zero() {
|
||||
if self[(j, i)] != TYPE::zero() {
|
||||
self.swap_rows_mul_pivot(other, pivot, i, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for j in (usize::zero()..i).rev() {
|
||||
if self[(i, i)] != TYPE::zero() {
|
||||
let neg = -(&self[(j, i)]);
|
||||
let factor = &neg / &(self[(i, i)]);
|
||||
self.add_row_mul(other, i, j, &factor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn triu_impl(&'a mut self, diag_replacement: Option<TYPE>)
|
||||
where
|
||||
TYPE: Zero + Clone,
|
||||
{
|
||||
match diag_replacement {
|
||||
Some(replacement) => {
|
||||
for (i, j, value) in self {
|
||||
match &i.cmp(&j) {
|
||||
std::cmp::Ordering::Equal => *value = replacement.clone(),
|
||||
std::cmp::Ordering::Greater => *value = TYPE::zero(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for (i, j, value) in self {
|
||||
if i > j {
|
||||
*value = TYPE::zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tril_impl(&'a mut self, diag_replacement: Option<TYPE>)
|
||||
where
|
||||
TYPE: Zero + Clone,
|
||||
{
|
||||
match diag_replacement {
|
||||
Some(replacement) => {
|
||||
for (i, j, value) in self {
|
||||
match i.cmp(&j) {
|
||||
std::cmp::Ordering::Equal => *value = replacement.clone(),
|
||||
std::cmp::Ordering::Less => *value = TYPE::zero(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for (i, j, value) in self {
|
||||
if i < j {
|
||||
*value = TYPE::zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invert(&mut self, pivot: &mut PIVOT, result: &mut Self)
|
||||
where
|
||||
TYPE: Clone + Zero + One + PartialEq + Neg<Output = TYPE> + Div<Output = TYPE>,
|
||||
for<'f> &'f TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
self.gauss_jordan_low_mul(result, pivot);
|
||||
self.gauss_jordan_high_mul(result, pivot);
|
||||
for i in 0..result.rows() {
|
||||
let f = self[(i, i)].clone();
|
||||
for j in 0..result.columns() {
|
||||
result[(i, j)] = &(result[(i, j)]) / &f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn det(mut self, mut pivot: PIVOT) -> TYPE {
|
||||
self.gauss_jordan_low(&mut pivot);
|
||||
(0..self.rows())
|
||||
.map(|i| &self[(i, i)])
|
||||
.fold(TYPE::one(), |a, b| &a * b)
|
||||
}
|
||||
|
||||
fn lu_pivot(&mut self, i: usize, pivot: &mut PIVOT)
|
||||
where
|
||||
TYPE: PartialOrd + Zero + Clone,
|
||||
for<'f> &'f TYPE: Neg<Output = TYPE>,
|
||||
{
|
||||
let mut max = abs(&self[(i, i)]);
|
||||
let mut max_index = i;
|
||||
for j in (i + 1)..self.rows() {
|
||||
if abs(&self[(j, i)]) > max {
|
||||
max = abs(&self[(i, j)]);
|
||||
max_index = j;
|
||||
}
|
||||
}
|
||||
if max_index != i {
|
||||
self.swap_rows_pivot(pivot, i, max_index)
|
||||
}
|
||||
}
|
||||
|
||||
fn lu(&mut self) -> Result<(), SingularMatrixError>
|
||||
where
|
||||
TYPE: Zero + Clone,
|
||||
for<'b> &'b TYPE: Mul<&'b TYPE, Output = TYPE>,
|
||||
TYPE: SubAssign<TYPE> + DivAssign<TYPE>,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
self.lu_row(i)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lup(&mut self, p: &mut PIVOT) -> Result<(), SingularMatrixError>
|
||||
where
|
||||
TYPE: Zero + Clone + PartialOrd,
|
||||
for<'b> &'b TYPE: Mul<&'b TYPE, Output = TYPE> + Neg<Output = TYPE>,
|
||||
TYPE: SubAssign<TYPE> + DivAssign<TYPE>,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
self.lu_pivot(i, p);
|
||||
self.lu_row(i)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lu_invert(&self, result: &mut Self, p: &mut PIVOT)
|
||||
where
|
||||
TYPE: Zero + One + Clone + PartialOrd,
|
||||
for<'b> &'b TYPE: Mul<&'b TYPE, Output = TYPE>,
|
||||
for<'c> TYPE: DivAssign<&'c TYPE>,
|
||||
TYPE: SubAssign<TYPE>,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
for j in 0..self.rows() {
|
||||
if p[j] == i {
|
||||
result[(j, i)] = TYPE::one();
|
||||
} else {
|
||||
result[(j, i)] = TYPE::zero();
|
||||
}
|
||||
for k in 0..j {
|
||||
let delta = &self[(j, k)] * &result[(k, i)];
|
||||
result[(j, i)] -= delta;
|
||||
}
|
||||
}
|
||||
for j in (0..self.rows()).rev() {
|
||||
for k in (j + 1)..self.rows() {
|
||||
let delta = &self[(j, k)] * &result[(k, i)];
|
||||
result[(j, i)] -= delta;
|
||||
}
|
||||
result[(j, i)] /= &self[(j, j)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lu_row(&mut self, i: usize) -> Result<(), SingularMatrixError>
|
||||
where
|
||||
TYPE: Zero + Clone,
|
||||
for<'b> &'b TYPE: Mul<&'b TYPE, Output = TYPE>,
|
||||
TYPE: SubAssign<TYPE> + DivAssign<TYPE>,
|
||||
{
|
||||
if self[(i, i)].is_zero() {
|
||||
return Err(SingularMatrixError {});
|
||||
}
|
||||
for j in i..self.columns() {
|
||||
for k in 0..i {
|
||||
let delta = &self[(i, k)] * &self[(k, j)];
|
||||
self[(i, j)] -= delta;
|
||||
}
|
||||
}
|
||||
for j in (i + 1)..self.columns() {
|
||||
for k in 0..i {
|
||||
let delta = &self[(j, k)] * &self[(k, i)];
|
||||
self[(j, i)] -= delta;
|
||||
}
|
||||
let div = self[(i, i)].clone();
|
||||
self[(j, i)] /= div;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn squared_norm2(&'a self) -> TYPE {
|
||||
self.into_iter().fold(TYPE::zero(), move |acc, (_, _, v)| { acc + v * v })
|
||||
}
|
||||
}
|
||||
|
||||
fn abs<TYPE>(value: &TYPE) -> TYPE
|
||||
where
|
||||
TYPE: PartialOrd + Zero + Clone,
|
||||
for<'a> &'a TYPE: Neg<Output = TYPE>,
|
||||
{
|
||||
if value >= &TYPE::zero() {
|
||||
value.clone()
|
||||
} else {
|
||||
-value
|
||||
}
|
||||
}
|
49
src/misc.rs
Normal file
49
src/misc.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
|
||||
use crate::Matrix;
|
||||
|
||||
pub trait EnhancedOption<U> {
|
||||
fn otherwise<F>(self, cb: F) -> U
|
||||
where
|
||||
F: Fn() -> U;
|
||||
}
|
||||
|
||||
impl<T> EnhancedOption<T> for Option<T> {
|
||||
fn otherwise<F>(self, cb: F) -> T
|
||||
where
|
||||
F: Fn() -> T,
|
||||
{
|
||||
if let Some(value) = self {
|
||||
value
|
||||
} else {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_fmt<'a, M: Matrix<TYPE>, TYPE: 'a + Debug>(
|
||||
m: &M,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
) -> std::fmt::Result
|
||||
where
|
||||
M: Index<(usize, usize), Output = TYPE> + IndexMut<(usize, usize), Output = TYPE>,
|
||||
M: IntoIterator<Item = (usize, usize, TYPE)>,
|
||||
{
|
||||
write!(f, "[")?;
|
||||
for i in 0..m.rows() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "[")?;
|
||||
for j in 0..m.columns() {
|
||||
if j > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{:?}", m[(i, j)])?
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
791
src/rational.rs
Normal file
791
src/rational.rs
Normal file
@@ -0,0 +1,791 @@
|
||||
use num_traits::sign::Signed;
|
||||
use num_traits::Num;
|
||||
use num_traits::NumOps;
|
||||
use num_traits::One;
|
||||
use num_traits::Pow;
|
||||
use num_traits::Signed as SignedOps;
|
||||
use num_traits::Zero;
|
||||
use std::cmp::Ordering;
|
||||
use std::cmp::PartialEq;
|
||||
use std::cmp::PartialOrd;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Add;
|
||||
use std::ops::AddAssign;
|
||||
use std::ops::Div;
|
||||
use std::ops::DivAssign;
|
||||
use std::ops::Mul;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
use std::ops::Rem;
|
||||
use std::ops::Sub;
|
||||
use std::ops::SubAssign;
|
||||
|
||||
trait_group! {
|
||||
pub trait RationalInt
|
||||
: NumOps
|
||||
+ Debug
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ Zero<Output = Self>
|
||||
+ One<Output = Self>
|
||||
+ Pow<u32, Output = Self>
|
||||
+ Neg<Output = Self>
|
||||
+ Clone
|
||||
+ Default
|
||||
}
|
||||
|
||||
fn gcd<T>(a: &T, b: &T) -> T
|
||||
where
|
||||
T: Rem<Output = T> + Clone + Zero,
|
||||
{
|
||||
let mut n1 = a.clone();
|
||||
let mut n2 = b.clone();
|
||||
let mut tmp: T;
|
||||
while !n2.is_zero() {
|
||||
tmp = n1;
|
||||
n1 = n2.clone();
|
||||
n2 = tmp % n2;
|
||||
}
|
||||
n1
|
||||
}
|
||||
|
||||
fn mcm<'a, T>(n1: &'a T, n2: &'a T) -> T
|
||||
where
|
||||
T: Div<Output = T> + Rem<Output = T> + Clone + Zero,
|
||||
&'a T: Mul<Output = T>,
|
||||
{
|
||||
let den = gcd(n1, n2);
|
||||
n1 * n2 / den
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rational<TYPE> {
|
||||
num: TYPE,
|
||||
den: TYPE,
|
||||
}
|
||||
|
||||
impl<TYPE> Rational<TYPE>
|
||||
where
|
||||
TYPE: Zero + One + Clone + Rem<Output = TYPE> + PartialOrd + Neg<Output = TYPE>,
|
||||
for<'a> &'a TYPE: Div<&'a TYPE, Output = TYPE>,
|
||||
{
|
||||
fn simplify(&mut self) {
|
||||
if self.num.is_zero() {
|
||||
self.den = TYPE::one();
|
||||
} else if self.den.is_zero() {
|
||||
} else {
|
||||
(self.num, self.den) = {
|
||||
let n1_opt: TYPE;
|
||||
let n1 = if self.den < TYPE::zero() {
|
||||
n1_opt = -self.num.clone();
|
||||
&n1_opt
|
||||
} else {
|
||||
&self.num
|
||||
};
|
||||
let n2_opt: TYPE;
|
||||
let n2 = if self.den < TYPE::zero() {
|
||||
n2_opt = -self.den.clone();
|
||||
&n2_opt
|
||||
} else {
|
||||
&self.den
|
||||
};
|
||||
let num_abs_opt: TYPE;
|
||||
let num_abs = if self.num < TYPE::zero() {
|
||||
num_abs_opt = -self.num.clone();
|
||||
&num_abs_opt
|
||||
} else {
|
||||
&self.num
|
||||
};
|
||||
let gcd = gcd(num_abs, n2);
|
||||
(n1 / &gcd, n2 / &gcd)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
{
|
||||
pub fn new(num: TYPE, den: TYPE) -> Rational<TYPE> {
|
||||
let res = den.partial_cmp(&TYPE::zero());
|
||||
match res {
|
||||
Some(Ordering::Greater) => Rational { num, den },
|
||||
Some(Ordering::Less) => Rational {
|
||||
num: -num,
|
||||
den: -den,
|
||||
},
|
||||
_ => {
|
||||
panic!("Rational denominator cannot be zero or NaN")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[opimps::impl_op(Pow)]
|
||||
fn pow<TYPE>(self: Rational<TYPE>, exp: u32) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: Pow<u32, Output = TYPE>,
|
||||
{
|
||||
Rational {
|
||||
num: self.num.pow(exp),
|
||||
den: self.den.pow(exp),
|
||||
}
|
||||
}
|
||||
|
||||
#[opimps::impl_op(Pow)]
|
||||
fn pow<TYPE>(self: &Rational<TYPE>, exp: u32) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: Pow<u32, Output = TYPE>,
|
||||
{
|
||||
Rational {
|
||||
num: (&self.num).pow(exp),
|
||||
den: (&self.den).pow(exp),
|
||||
}
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Add)]
|
||||
fn add<TYPE>(self: Rational<TYPE>, rhs: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
let den = mcm::<TYPE>(&self.den, &rhs.den);
|
||||
let a = &self.num * &den;
|
||||
let b = &rhs.num * &den;
|
||||
let num = &a / &self.den + &b / &rhs.den;
|
||||
let mut result: Rational<TYPE> = Rational { num, den };
|
||||
result.simplify();
|
||||
result
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Sub)]
|
||||
fn sub<TYPE>(self: Rational<TYPE>, rhs: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
let den = mcm::<TYPE>(&self.den, &rhs.den);
|
||||
let a = &self.num * &den;
|
||||
let b = &rhs.num * &den;
|
||||
let num = &a / &self.den - &b / &rhs.den;
|
||||
let mut result: Rational<TYPE> = Rational { num, den };
|
||||
result.simplify();
|
||||
result
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Mul)]
|
||||
fn mul<TYPE>(self: Rational<TYPE>, rhs: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
let num = &self.num * &rhs.num;
|
||||
let den = &self.den * &rhs.den;
|
||||
let mut result = Rational { num, den };
|
||||
result.simplify();
|
||||
result
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Div)]
|
||||
fn div<TYPE>(self: Rational<TYPE>, rhs: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
let den = &self.den * &rhs.num;
|
||||
let num = &self.num * &rhs.den;
|
||||
let mut result = Rational::new(num, den);
|
||||
result.simplify();
|
||||
result
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Rem)]
|
||||
fn rem<TYPE>(self: Rational<TYPE>, rhs: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
match self.partial_cmp(&rhs) {
|
||||
Some(std::cmp::Ordering::Less) => rhs.clone(),
|
||||
Some(std::cmp::Ordering::Equal) => Rational::<TYPE>::zero(),
|
||||
Some(std::cmp::Ordering::Greater) => {
|
||||
let div = self / rhs;
|
||||
let num = &div.num / &div.den;
|
||||
div - Rational::new(num, TYPE::one())
|
||||
}
|
||||
None => {
|
||||
panic!("Cannot compare")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE> AddAssign<&'a Rational<TYPE>> for Rational<TYPE>
|
||||
where
|
||||
TYPE: Zero
|
||||
+ One
|
||||
+ Clone
|
||||
+ Rem<Output = TYPE>
|
||||
+ PartialOrd
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>,
|
||||
for<'b> &'b TYPE: Mul<&'b TYPE, Output = TYPE> + Div<&'b TYPE, Output = TYPE>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: &'a Self) {
|
||||
let den = mcm::<TYPE>(&self.den, &rhs.den);
|
||||
let a = &self.num * &den;
|
||||
let b = &rhs.num * &den;
|
||||
let num = &a / &self.den + &b / &rhs.den;
|
||||
self.num = num;
|
||||
self.den = den;
|
||||
self.simplify();
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> AddAssign for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self += &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE> SubAssign<&'a Rational<TYPE>> for Rational<TYPE>
|
||||
where
|
||||
TYPE: Div<Output = TYPE>
|
||||
+ Sub<Output = TYPE>
|
||||
+ Rem<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Clone
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialOrd,
|
||||
for<'c> &'c TYPE: Mul<&'c TYPE, Output = TYPE> + Div<&'c TYPE, Output = TYPE>,
|
||||
for<'b> TYPE: SubAssign<&'b TYPE>,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: &'a Self) {
|
||||
let den = mcm::<TYPE>(&self.den, &rhs.den);
|
||||
let a = &self.num * &den;
|
||||
let b = &rhs.num * &den;
|
||||
let num = &a / &self.den - &b / &rhs.den;
|
||||
self.num = num;
|
||||
self.den = den;
|
||||
self.simplify();
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> SubAssign for Rational<TYPE>
|
||||
where
|
||||
TYPE: Div<Output = TYPE>
|
||||
+ Sub<Output = TYPE>
|
||||
+ Rem<Output = TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Clone
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialOrd,
|
||||
for<'c> &'c TYPE: Mul<&'c TYPE, Output = TYPE> + Div<&'c TYPE, Output = TYPE>,
|
||||
for<'b> TYPE: SubAssign<&'b TYPE>,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self -= &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE> MulAssign<&'a Rational<TYPE>> for Rational<TYPE>
|
||||
where
|
||||
for<'b> TYPE: MulAssign<&'b TYPE>,
|
||||
TYPE: PartialOrd + One + Clone + Zero + Rem<Output = TYPE> + Neg<Output = TYPE>,
|
||||
for<'c> &'c TYPE: Div<&'c TYPE, Output = TYPE>,
|
||||
{
|
||||
fn mul_assign(&mut self, rhs: &'a Self) {
|
||||
self.num *= &rhs.num;
|
||||
self.den *= &rhs.den;
|
||||
self.simplify();
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> MulAssign for Rational<TYPE>
|
||||
where
|
||||
for<'a> TYPE: MulAssign<&'a TYPE>,
|
||||
TYPE: PartialOrd + One + Clone + Zero + Rem<Output = TYPE> + Neg<Output = TYPE>,
|
||||
for<'c> &'c TYPE: Div<&'c TYPE, Output = TYPE>,
|
||||
{
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self *= &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE> DivAssign<&'a Rational<TYPE>> for Rational<TYPE>
|
||||
where
|
||||
for<'b> TYPE: MulAssign<&'b TYPE>,
|
||||
TYPE: PartialOrd + One + Clone + Zero + Rem<Output = TYPE> + Neg<Output = TYPE>,
|
||||
for<'c> &'c TYPE: Div<&'c TYPE, Output = TYPE>,
|
||||
{
|
||||
fn div_assign(&mut self, rhs: &'a Self) {
|
||||
self.num *= &rhs.den;
|
||||
self.den *= &rhs.num;
|
||||
self.simplify()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> DivAssign for Rational<TYPE>
|
||||
where
|
||||
for<'a> TYPE: MulAssign<&'a TYPE>,
|
||||
TYPE: PartialOrd + One + Clone + Zero + Rem<Output = TYPE> + Neg<Output = TYPE>,
|
||||
for<'c> &'c TYPE: Div<&'c TYPE, Output = TYPE>,
|
||||
{
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
*self /= &rhs;
|
||||
self.simplify()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Display for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt + Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}/{}", self.num, self.den)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> PartialEq for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.partial_cmp(other) == Some(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Eq for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt + Eq,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<TYPE> PartialOrd for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
(&self.num * &other.den).partial_cmp(&(&other.num * &self.den))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Ord for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt + Ord,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(&self.num * &other.den).cmp(&(&other.num * &self.den))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Clone for Rational<TYPE>
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Rational {
|
||||
num: self.num.clone(),
|
||||
den: self.den.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Copy for Rational<TYPE> where TYPE: Copy {}
|
||||
|
||||
impl<TYPE> Zero for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn zero() -> Rational<TYPE> {
|
||||
Rational {
|
||||
num: TYPE::zero(),
|
||||
den: TYPE::one(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.num.is_zero() && !self.den.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> One for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt + Display,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn one() -> Self {
|
||||
Rational {
|
||||
num: TYPE::one(),
|
||||
den: TYPE::one(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_one(&self) -> bool {
|
||||
self.num == self.den
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE> Default for Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt + Display,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Rational::<TYPE>::zero()
|
||||
}
|
||||
}
|
||||
|
||||
#[opimps::impl_uni_op(Neg)]
|
||||
fn neg<TYPE>(self: Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
{
|
||||
Rational::new(-self.num, self.den)
|
||||
}
|
||||
|
||||
#[opimps::impl_uni_op(Neg)]
|
||||
fn neg<TYPE>(self: &Rational<TYPE>) -> Rational<TYPE>
|
||||
where
|
||||
TYPE: RationalInt,
|
||||
{
|
||||
Rational::new(-self.num.clone(), self.den.clone())
|
||||
}
|
||||
|
||||
impl<TYPE> Num for Rational<TYPE>
|
||||
where
|
||||
TYPE: Num + RationalInt + Display,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn from_str_radix(_: &str, _: u32) -> Result<Self, Self::FromStrRadixErr> {
|
||||
Result::Err(Rational::<TYPE>::default())
|
||||
}
|
||||
|
||||
type FromStrRadixErr = Self;
|
||||
}
|
||||
|
||||
impl<TYPE> SignedOps for Rational<TYPE>
|
||||
where
|
||||
TYPE: Signed + SignedOps + Display + RationalInt,
|
||||
for<'a> &'a TYPE: NumOps<&'a TYPE, TYPE>,
|
||||
{
|
||||
fn abs(&self) -> Self {
|
||||
Rational::new(self.num.abs(), self.den.abs())
|
||||
}
|
||||
|
||||
fn abs_sub(&self, other: &Self) -> Self {
|
||||
if self <= other {
|
||||
Rational::<TYPE>::zero()
|
||||
} else {
|
||||
self.clone() - other.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> bool {
|
||||
!self.num.is_zero() && self.num.is_negative() ^ self.den.is_negative()
|
||||
}
|
||||
|
||||
fn is_positive(&self) -> bool {
|
||||
!self.num.is_zero() && !(self.num.is_positive() ^ self.den.is_positive())
|
||||
}
|
||||
|
||||
fn signum(&self) -> Self {
|
||||
if self.is_positive() {
|
||||
Rational::<TYPE>::one()
|
||||
} else if self.is_negative() {
|
||||
-Rational::<TYPE>::one()
|
||||
} else {
|
||||
Rational::<TYPE>::zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[test]
|
||||
fn test_simplify() {
|
||||
let mut r1 = Rational::new(15, 105);
|
||||
assert_eq!(Rational::new(1, 7), r1);
|
||||
r1.simplify();
|
||||
assert_eq!(Rational::new(1, 7), r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(4, 10);
|
||||
let r3 = Rational::new(1, 7);
|
||||
let one: Rational<i32> = Rational::<i32>::one();
|
||||
assert_eq!(one, r1 + r2);
|
||||
assert_eq!(r2 + r1, r1 + r2);
|
||||
assert_eq!(r1 + r2 + r3, r3 + r2 + r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_assign() {
|
||||
let mut r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(4, 10);
|
||||
let r3 = Rational::<i32>::one();
|
||||
r1 += r2;
|
||||
assert_eq!(r3, r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(4, 10);
|
||||
let r3 = Rational::new(1, 7);
|
||||
assert_eq!(Rational::<i32>::zero(), r1 - r1);
|
||||
assert_eq!(Rational::new(-1, 5), r2 - r1);
|
||||
assert!(Rational::<i32>::zero() > r3 - r1);
|
||||
assert!(Rational::<i32>::zero() > r3 - r2);
|
||||
assert!(Rational::<i32>::zero() > r3 - r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub_assign() {
|
||||
let mut r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(4, 10);
|
||||
let r3 = Rational::new(1, 5);
|
||||
r1 -= r2;
|
||||
assert_eq!(r3, r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(-4, 10);
|
||||
let r3 = Rational::new(1, 7);
|
||||
assert_eq!(Rational::new(9, 25), r1 * r1);
|
||||
assert_eq!(Rational::new(-12, 50), r2 * r1);
|
||||
assert_eq!(Rational::new(2, -35), r2 * r3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_assign() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(-4, 10);
|
||||
let r3 = Rational::new(-12, 50);
|
||||
let mut r4 = r1.clone();
|
||||
r4 *= r1;
|
||||
assert_eq!(Rational::<i32>::new(9, 25), r4);
|
||||
let mut r5 = r1.clone();
|
||||
r5 *= r2;
|
||||
assert_eq!(r3, r5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(-4, 10);
|
||||
let r3 = Rational::new(1, 7);
|
||||
assert_eq!(Rational::<i32>::one(), r1 / r1);
|
||||
assert_eq!(Rational::new(-2, 3), r2 / r1);
|
||||
assert_eq!(Rational::new(28, -10), r2 / r3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_assign() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(-4, 10);
|
||||
let r3 = Rational::new(30, -20);
|
||||
let mut r4 = r1.clone();
|
||||
r4 /= r1;
|
||||
assert_eq!(Rational::<i32>::one(), r4);
|
||||
let mut r5 = r1.clone();
|
||||
r5 /= r2;
|
||||
assert_eq!(r3, r5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(6, 10);
|
||||
let r3 = Rational::new(300, 500);
|
||||
let r4 = Rational::new(300, -500);
|
||||
assert!(r1 == r2);
|
||||
assert!(r1 == r3);
|
||||
assert!(r1 != r4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord() {
|
||||
let r1 = Rational::new(3, 5);
|
||||
let r2 = Rational::new(7, 10);
|
||||
let r3 = Rational::new(301, 500);
|
||||
let r4 = Rational::new(300, -500);
|
||||
let values: Vec<Rational<i32>> = BTreeSet::from([r1, r2, r3, r4])
|
||||
.iter()
|
||||
.map(|it| it.clone())
|
||||
.collect();
|
||||
let expected_order = Vec::from([r4, r1, r3, r2]);
|
||||
assert_eq!(expected_order, values);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd() {
|
||||
let a = 14;
|
||||
let b = 21;
|
||||
assert_eq!(7, gcd(&a, &b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mcm() {
|
||||
let a = 14;
|
||||
let b = 21;
|
||||
assert_eq!(42, mcm::<i32>(&a, &b));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod big_int_tests {
|
||||
use super::*;
|
||||
use num_bigint::BigInt;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[test]
|
||||
fn test_gcd() {
|
||||
assert_eq!(6, gcd(&12, &18));
|
||||
assert_eq!(6, gcd(&18, &12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify() {
|
||||
let mut r1 = Rational::new(BigInt::from(15), BigInt::from(105));
|
||||
assert_eq!(Rational::new(BigInt::from(1), BigInt::from(7)), r1);
|
||||
r1.simplify();
|
||||
assert_eq!(Rational::new(BigInt::from(1), BigInt::from(7)), r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(1), BigInt::from(7));
|
||||
assert_eq!(Rational::<BigInt>::one(), &r1 + &r2);
|
||||
assert_eq!(&r2 + &r1, &r1 + &r2);
|
||||
assert_eq!(&r1 + &r2 + &r3, &r3 + &r2 + &r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_assign() {
|
||||
let mut r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(4), BigInt::from(10));
|
||||
r1 += r2;
|
||||
assert_eq!(Rational::<BigInt>::one(), r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(1), BigInt::from(7));
|
||||
assert_eq!(Rational::<BigInt>::zero(), &r1 - &r1);
|
||||
assert_eq!(Rational::new(BigInt::from(-1), BigInt::from(5)), &r2 - &r1);
|
||||
assert!(Rational::<BigInt>::zero() > &r3 - &r1);
|
||||
assert!(Rational::<BigInt>::zero() > &r3 - &r2);
|
||||
assert!(Rational::<BigInt>::zero() > r3 - r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub_assign() {
|
||||
let mut r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(4), BigInt::from(10));
|
||||
r1 -= r2;
|
||||
assert_eq!(Rational::<BigInt>::new(BigInt::one(), BigInt::from(5)), r1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(-4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(1), BigInt::from(7));
|
||||
assert_eq!(Rational::new(BigInt::from(9), BigInt::from(25)), &r1 * &r1);
|
||||
assert_eq!(
|
||||
Rational::new(BigInt::from(-12), BigInt::from(50)),
|
||||
&r2 * &r1
|
||||
);
|
||||
assert_eq!(Rational::new(BigInt::from(2), BigInt::from(-35)), r2 * r3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_assign() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(-4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(-12), BigInt::from(50));
|
||||
let mut r4 = r1.clone();
|
||||
r4 *= &r1;
|
||||
assert_eq!(
|
||||
Rational::<BigInt>::new(BigInt::from(9), BigInt::from(25)),
|
||||
r4
|
||||
);
|
||||
let mut r5 = r1.clone();
|
||||
r5 *= r2;
|
||||
assert_eq!(r3, r5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(-4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(1), BigInt::from(7));
|
||||
assert_eq!(Rational::<BigInt>::one(), &r1 / &r1);
|
||||
assert_eq!(Rational::new(BigInt::from(-2), BigInt::from(3)), &r2 / r1);
|
||||
assert_eq!(Rational::new(BigInt::from(28), BigInt::from(-10)), r2 / r3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_assign() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(-4), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(30), BigInt::from(-20));
|
||||
let mut r4 = r1.clone();
|
||||
r4 /= &r1;
|
||||
assert_eq!(Rational::<BigInt>::one(), r4);
|
||||
let mut r5 = r1.clone();
|
||||
r5 /= r2;
|
||||
assert_eq!(r3, r5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(6), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(300), BigInt::from(500));
|
||||
let r4 = Rational::new(BigInt::from(300), BigInt::from(-500));
|
||||
assert!(r1 == r2);
|
||||
assert!(r1 == r3);
|
||||
assert!(r1 != r4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord() {
|
||||
let r1 = Rational::new(BigInt::from(3), BigInt::from(5));
|
||||
let r2 = Rational::new(BigInt::from(7), BigInt::from(10));
|
||||
let r3 = Rational::new(BigInt::from(301), BigInt::from(500));
|
||||
let r4 = Rational::new(BigInt::from(300), BigInt::from(-500));
|
||||
let values: Vec<&Rational<BigInt>> = BTreeSet::from([&r1, &r2, &r3, &r4])
|
||||
.iter()
|
||||
.map(|it| *it)
|
||||
.collect();
|
||||
let expected_order = Vec::from([&r4, &r1, &r3, &r2]);
|
||||
assert_eq!(expected_order, values);
|
||||
}
|
||||
}
|
208
src/sludecomposition.rs
Normal file
208
src/sludecomposition.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
use std::ops::Add;
|
||||
use std::ops::Div;
|
||||
use std::ops::DivAssign;
|
||||
use std::ops::Mul;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use crate::Matrix;
|
||||
use crate::SingularMatrixError;
|
||||
|
||||
use super::matrix::LUDecompositionImpl;
|
||||
|
||||
use super::matrix::LUDecomposition;
|
||||
use super::matrix::NumericalMatrixImpl;
|
||||
use super::spivot::SPivot;
|
||||
use super::NumericalMatrix;
|
||||
use super::Pivot;
|
||||
use super::SMatrix;
|
||||
|
||||
use super::misc::EnhancedOption;
|
||||
|
||||
pub struct SLUDecomposition<TYPE, const SIZE: usize> {
|
||||
matrix: SMatrix<TYPE, SIZE, SIZE>,
|
||||
pivot: SPivot<SIZE>,
|
||||
}
|
||||
|
||||
impl<TYPE, const SIZE: usize> SLUDecomposition<TYPE, SIZE>
|
||||
where TYPE : Zero + SubAssign<TYPE> + Clone,
|
||||
for<'a> &'a TYPE : Mul<&'a TYPE, Output = TYPE>,
|
||||
for<'a> TYPE : DivAssign<&'a TYPE> {
|
||||
pub (crate) fn new(matrix: SMatrix<TYPE, SIZE, SIZE>, pivot: Option<SPivot<SIZE>>) -> Self {
|
||||
SLUDecomposition {
|
||||
matrix,
|
||||
pivot: pivot.otherwise(|| SPivot::new(SIZE)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve<const COLUMNS : usize>(&self, b : &SMatrix<TYPE, SIZE, COLUMNS>) -> Result<SMatrix<TYPE, SIZE, COLUMNS>, SingularMatrixError> {
|
||||
let mut x = SMatrix::<TYPE, SIZE, COLUMNS>::new(|_| TYPE::zero());
|
||||
for n in 0..b.columns() {
|
||||
for i in 0..self.matrix.rows() {
|
||||
x[(i, n)] = b[(self.pivot[i], n)].clone();
|
||||
for k in 0..i {
|
||||
let sub = &self.matrix[(i, k)] * &x[(k, n)];
|
||||
x[(i, n)] -= sub;
|
||||
}
|
||||
}
|
||||
for i in (0..self.matrix.rows()).rev() {
|
||||
for k in (i + 1)..self.matrix.rows() {
|
||||
let sub = &self.matrix[(i, k)] * &x[(k, n)];
|
||||
x[(i, n)] -= sub;
|
||||
}
|
||||
if self.matrix[(i, i)].is_zero() {
|
||||
return Err(SingularMatrixError::default());
|
||||
} else {
|
||||
x[(i, n)] /= &self.matrix[(i, i)];
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const SIZE: usize> LUDecomposition<TYPE, SMatrix<TYPE, SIZE, SIZE>, SPivot<SIZE>>
|
||||
for SLUDecomposition<TYPE, SIZE>
|
||||
where
|
||||
for<'b> TYPE: Clone
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>
|
||||
+ SubAssign<TYPE>
|
||||
+ SubAssign<&'b TYPE>
|
||||
+ DivAssign<TYPE>
|
||||
+ DivAssign<&'b TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'c> &'c TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn l(&self) -> SMatrix<TYPE, SIZE, SIZE> {
|
||||
self.matrix.clone().tril_replace(TYPE::one())
|
||||
}
|
||||
|
||||
fn u(&self) -> SMatrix<TYPE, SIZE, SIZE> {
|
||||
self.matrix.clone().triu()
|
||||
}
|
||||
|
||||
fn invert(&self) -> SMatrix<TYPE, SIZE, SIZE> {
|
||||
let mut result = SMatrix::<TYPE, SIZE, SIZE>::new(|_| TYPE::zero());
|
||||
self.matrix.lu_invert(&mut result, &mut self.pivot.clone());
|
||||
result
|
||||
}
|
||||
|
||||
fn pivot(&self, m: SMatrix<TYPE, SIZE, SIZE>) -> SMatrix<TYPE, SIZE, SIZE> {
|
||||
&self.pivot * m
|
||||
}
|
||||
// fn p<'a>(&'a self) -> &'a HPivot {
|
||||
// &self.pivot
|
||||
// }
|
||||
}
|
||||
|
||||
impl<TYPE, const SIZE: usize> LUDecompositionImpl<TYPE, SMatrix<TYPE, SIZE, SIZE>, SPivot<SIZE>>
|
||||
for SLUDecomposition<TYPE, SIZE>
|
||||
where
|
||||
for<'b> TYPE: One + MulAssign<&'b TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn get_matrix(&self) -> &SMatrix<TYPE, SIZE, SIZE> {
|
||||
&self.matrix
|
||||
}
|
||||
|
||||
fn get_pivot(&self) -> &SPivot<SIZE> {
|
||||
&self.pivot
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::rngs::StdRng;
|
||||
use rand::RngCore;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::SMatrix;
|
||||
use num_traits::Zero;
|
||||
|
||||
use rand;
|
||||
|
||||
#[test]
|
||||
fn solve_linear_system() {
|
||||
let mut rand = {
|
||||
let seed = [
|
||||
1,0,1,3,
|
||||
2,5,0,0,
|
||||
200,1,0,0,
|
||||
210,30,0,0,
|
||||
78,134,31,0,
|
||||
253,11,7,0,
|
||||
120,169,89,48,
|
||||
200,0,202,0
|
||||
];
|
||||
StdRng::from_seed(seed)
|
||||
};
|
||||
let mtx = SMatrix::<Rational<i64>, 5, 5>::new(|_| {
|
||||
Rational::new(i64::try_from(rand.next_u32() % 40).unwrap() - 20, 1)
|
||||
});
|
||||
let b = SMatrix::<Rational<i64>, 5, 5>::new(|_| {
|
||||
Rational::new(i64::try_from(rand.next_u32() % 20).unwrap() - 10, 1)
|
||||
});
|
||||
let lu = mtx.clone().lu().unwrap();
|
||||
let x = lu.solve(&b).unwrap();
|
||||
assert!((mtx * x - b).is_zero());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_big_int {
|
||||
use num_traits::Zero;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::RngCore;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::SMatrix;
|
||||
|
||||
use rand;
|
||||
|
||||
#[test]
|
||||
fn solve_linear_system() {
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::One;
|
||||
|
||||
let mut rand = {
|
||||
let seed = [
|
||||
1,0,1,3,
|
||||
2,5,0,0,
|
||||
200,1,0,0,
|
||||
210,30,0,0,
|
||||
78,134,31,0,
|
||||
253,11,7,0,
|
||||
120,169,89,48,
|
||||
200,0,202,0
|
||||
];
|
||||
StdRng::from_seed(seed)
|
||||
};
|
||||
let mtx = SMatrix::<Rational<BigInt>, 10, 10>::new(|_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % 200) - 100, BigInt::one())
|
||||
});
|
||||
let b = SMatrix::<Rational<BigInt>, 10, 10>::new(|_| {
|
||||
Rational::new(BigInt::from(rand.next_u32() % 100) - 50, BigInt::one())
|
||||
});
|
||||
let lu = mtx.clone().lu().unwrap();
|
||||
let x = lu.solve(&b).unwrap();
|
||||
|
||||
assert!((mtx * x - b).is_zero());
|
||||
|
||||
}
|
||||
|
||||
}
|
956
src/smatrix.rs
Normal file
956
src/smatrix.rs
Normal file
@@ -0,0 +1,956 @@
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
|
||||
use std::cmp::PartialEq;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::Display;
|
||||
use std::iter::Enumerate;
|
||||
use std::iter::Flatten;
|
||||
use std::iter::IntoIterator;
|
||||
use std::ops::Add;
|
||||
use std::ops::Div;
|
||||
use std::ops::DivAssign;
|
||||
use std::ops::FnMut;
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
use std::ops::Mul;
|
||||
use std::ops::MulAssign;
|
||||
use std::ops::Neg;
|
||||
use std::ops::Sub;
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use super::err::SizeError;
|
||||
|
||||
use super::matrix::MatrixImpl;
|
||||
use super::matrix::NumericalMatrixImpl;
|
||||
use super::misc::debug_fmt;
|
||||
use super::sludecomposition::SLUDecomposition;
|
||||
use super::Matrix;
|
||||
use super::NumericalMatrix;
|
||||
use super::Pivot;
|
||||
use super::SingularMatrixError;
|
||||
|
||||
use super::spivot::SPivot;
|
||||
|
||||
pub struct SMatrix<TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||
values: [[TYPE; COLUMNS]; ROWS],
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
pub fn transpose(&self) -> SMatrix<TYPE, COLUMNS, ROWS> {
|
||||
SMatrix::<TYPE, COLUMNS, ROWS>::new(|(i, j)| self[(j, i)].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const SIZE: usize> SMatrix<TYPE, SIZE, SIZE> {
|
||||
pub fn transpose_in_place(mut self)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
for i in 0..self.rows() {
|
||||
for j in 0..self.columns() {
|
||||
self.swap((i, j), (j, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Index<(usize, usize)>
|
||||
for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn index(&self, index: (usize, usize)) -> &TYPE {
|
||||
&self.values[index.0][index.1]
|
||||
}
|
||||
|
||||
type Output = TYPE;
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> IndexMut<(usize, usize)>
|
||||
for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn index_mut(&mut self, index: (usize, usize)) -> &mut TYPE {
|
||||
&mut self.values[index.0][index.1]
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Default for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new(|_| TYPE::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Clone for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
SMatrix::new(|it| self[it].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Copy for SMatrix<TYPE, ROWS, COLUMNS> where
|
||||
TYPE: Copy
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> MatrixImpl<'a, TYPE>
|
||||
for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: 'a,
|
||||
{
|
||||
type IteratorType = SMatrixIterator<TYPE, ROWS, COLUMNS>;
|
||||
type RefIteratorType<'b> = SMatrixIteratorRef<'a, TYPE, ROWS, COLUMNS>;
|
||||
type MutRefIteratorType<'b> = SMatrixIteratorMut<'a, TYPE, ROWS, COLUMNS>;
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Matrix<TYPE> for SMatrix<TYPE, ROWS, COLUMNS> {
|
||||
fn columns(&self) -> usize {
|
||||
COLUMNS
|
||||
}
|
||||
|
||||
fn rows(&self) -> usize {
|
||||
ROWS
|
||||
}
|
||||
|
||||
fn transpose(mut self) -> Result<Self, crate::SizeError> {
|
||||
if ROWS != COLUMNS {
|
||||
Err(SizeError::new("Matrix must be squared"))
|
||||
} else {
|
||||
self.transpose_in_place()?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Add)]
|
||||
fn add<TYPE, const ROWS: usize, const COLUMNS: usize>(
|
||||
self: SMatrix<TYPE, ROWS, COLUMNS>,
|
||||
rhs: SMatrix<TYPE, ROWS, COLUMNS>,
|
||||
) -> SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Add<Output = TYPE>,
|
||||
for<'a> &'a TYPE: Add<&'a TYPE, Output = TYPE>,
|
||||
{
|
||||
SMatrix::new(|position| &self[position] + &rhs[position])
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Sub)]
|
||||
fn sub<TYPE, const ROWS: usize, const COLUMNS: usize>(
|
||||
self: SMatrix<TYPE, ROWS, COLUMNS>,
|
||||
rhs: SMatrix<TYPE, ROWS, COLUMNS>,
|
||||
) -> SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Sub<Output = TYPE>,
|
||||
for<'a> &'a TYPE: Sub<&'a TYPE, Output = TYPE>,
|
||||
{
|
||||
SMatrix::new(|position| &self[position] - &rhs[position])
|
||||
}
|
||||
|
||||
#[opimps::impl_ops(Mul)]
|
||||
fn mul<
|
||||
TYPE,
|
||||
const L_ROWS: usize,
|
||||
const L_COLUMNS: usize,
|
||||
const R_ROWS: usize,
|
||||
const R_COLUMNS: usize,
|
||||
>(
|
||||
self: SMatrix<TYPE, L_ROWS, L_COLUMNS>,
|
||||
rhs: SMatrix<TYPE, R_ROWS, R_COLUMNS>,
|
||||
) -> SMatrix<TYPE, L_ROWS, R_COLUMNS>
|
||||
where
|
||||
TYPE: Mul<Output = TYPE> + Add<Output = TYPE> + Zero,
|
||||
for<'a> &'a TYPE: Add<&'a TYPE, Output = TYPE> + Mul<&'a TYPE, Output = TYPE>,
|
||||
{
|
||||
let mut result: SMatrix<TYPE, L_ROWS, R_COLUMNS> = SMatrix::new(|_| TYPE::zero());
|
||||
for i in 0..result.rows() {
|
||||
for j in 0..result.columns() {
|
||||
for k in 0..self.columns() {
|
||||
let prod = &self[(i, k)] * &rhs[(k, j)];
|
||||
result[(i, j)] = &result[(i, j)] + ∏
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Display for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let lim = self.rows() * self.columns() - 1;
|
||||
writeln!(f, "[")?;
|
||||
for i in 0..self.rows() {
|
||||
write!(f, " ")?;
|
||||
for j in 0..self.columns() {
|
||||
let index = i * self.columns() + j;
|
||||
write!(f, "{}", self[(i, j)])?;
|
||||
if index < lim {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> SMatrix<TYPE, ROWS, COLUMNS> {
|
||||
pub fn new<INITIALIZER>(mut cb: INITIALIZER) -> Self
|
||||
where
|
||||
INITIALIZER: FnMut((usize, usize)) -> TYPE,
|
||||
{
|
||||
let values: [[TYPE; COLUMNS]; ROWS] =
|
||||
std::array::from_fn(|i| std::array::from_fn(|j| cb((i, j))));
|
||||
SMatrix { values }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SMatrixIterator<TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||
it: Enumerate<Flatten<std::array::IntoIter<[TYPE; COLUMNS], ROWS>>>,
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Iterator
|
||||
for SMatrixIterator<TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it
|
||||
.next()
|
||||
.map(|(index, value)| (index / COLUMNS, index % COLUMNS, value))
|
||||
}
|
||||
type Item = (usize, usize, TYPE);
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> IntoIterator for SMatrix<TYPE, ROWS, COLUMNS> {
|
||||
fn into_iter(self) -> SMatrixIterator<TYPE, ROWS, COLUMNS> {
|
||||
SMatrixIterator {
|
||||
it: self.values.into_iter().flatten().enumerate(),
|
||||
}
|
||||
}
|
||||
|
||||
type IntoIter = SMatrixIterator<TYPE, ROWS, COLUMNS>;
|
||||
type Item = (usize, usize, TYPE);
|
||||
}
|
||||
|
||||
pub struct SMatrixIteratorRef<'a, TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||
it: Enumerate<Flatten<std::slice::Iter<'a, [TYPE; COLUMNS]>>>,
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> Iterator
|
||||
for SMatrixIteratorRef<'a, TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it
|
||||
.next()
|
||||
.map(|(index, value)| (index / COLUMNS, index % COLUMNS, value))
|
||||
}
|
||||
type Item = (usize, usize, &'a TYPE);
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> IntoIterator
|
||||
for &'a SMatrix<TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn into_iter(self) -> SMatrixIteratorRef<'a, TYPE, ROWS, COLUMNS> {
|
||||
SMatrixIteratorRef {
|
||||
it: (self.values).iter().flatten().enumerate(),
|
||||
}
|
||||
}
|
||||
|
||||
type IntoIter = SMatrixIteratorRef<'a, TYPE, ROWS, COLUMNS>;
|
||||
type Item = (usize, usize, &'a TYPE);
|
||||
}
|
||||
|
||||
pub struct SMatrixIteratorMut<'a, TYPE, const ROWS: usize, const COLUMNS: usize> {
|
||||
it: Enumerate<Flatten<std::slice::IterMut<'a, [TYPE; COLUMNS]>>>,
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> Iterator
|
||||
for SMatrixIteratorMut<'a, TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it
|
||||
.next()
|
||||
.map(|(index, value)| (index / COLUMNS, index % COLUMNS, value))
|
||||
}
|
||||
type Item = (usize, usize, &'a mut TYPE);
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const ROWS: usize, const COLUMNS: usize> IntoIterator
|
||||
for &'a mut SMatrix<TYPE, ROWS, COLUMNS>
|
||||
{
|
||||
fn into_iter(self) -> SMatrixIteratorMut<'a, TYPE, ROWS, COLUMNS> {
|
||||
SMatrixIteratorMut {
|
||||
it: self.values.iter_mut().flatten().enumerate(),
|
||||
}
|
||||
}
|
||||
|
||||
type IntoIter = SMatrixIteratorMut<'a, TYPE, ROWS, COLUMNS>;
|
||||
type Item = (usize, usize, &'a mut TYPE);
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> PartialEq for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(0..ROWS)
|
||||
.flat_map(|i| (0..COLUMNS).map(move |j| (i, j)))
|
||||
.all(|index| self[index] == other[index])
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Debug for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
debug_fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Zero for SMatrix<TYPE, ROWS, COLUMNS>
|
||||
where
|
||||
TYPE: PartialEq + One + Zero + Add<TYPE, Output = TYPE>,
|
||||
for<'a> &'a TYPE: Add<&'a TYPE, Output = TYPE>,
|
||||
{
|
||||
fn zero() -> Self {
|
||||
SMatrix::new(|_| TYPE::zero())
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
*self == Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TYPE, const SIZE: usize> One for SMatrix<TYPE, SIZE, SIZE>
|
||||
where
|
||||
for<'b> TYPE: Clone
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ DivAssign<TYPE>
|
||||
+ SubAssign<TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>
|
||||
+ SubAssign<&'b TYPE>
|
||||
+ DivAssign<&'b TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'a> &'a TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn one() -> Self {
|
||||
<Self as NumericalMatrix<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>>::identity(SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const SIZE: usize>
|
||||
NumericalMatrixImpl<'a, TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>
|
||||
for SMatrix<TYPE, SIZE, SIZE>
|
||||
where
|
||||
for<'b> TYPE: Clone
|
||||
+ 'a
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ DivAssign<TYPE>
|
||||
+ SubAssign<TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>
|
||||
+ SubAssign<&'b TYPE>
|
||||
+ DivAssign<&'b TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'c> &'c TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn identity(_: usize) -> Self {
|
||||
Self::new(|(i, j)| if i == j { TYPE::one() } else { TYPE::zero() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TYPE, const SIZE: usize> NumericalMatrix<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>
|
||||
for SMatrix<TYPE, SIZE, SIZE>
|
||||
where
|
||||
for<'b> TYPE: Clone
|
||||
+ 'a
|
||||
+ Zero
|
||||
+ One
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ DivAssign<TYPE>
|
||||
+ SubAssign<TYPE>
|
||||
+ Neg<Output = TYPE>
|
||||
+ Div<Output = TYPE>
|
||||
+ SubAssign<&'b TYPE>
|
||||
+ DivAssign<&'b TYPE>
|
||||
+ MulAssign<&'b TYPE>,
|
||||
for<'c> &'c TYPE:
|
||||
Mul<Output = TYPE> + Add<Output = TYPE> + Div<Output = TYPE> + Neg<Output = TYPE>,
|
||||
{
|
||||
fn identity(size: usize) -> Self {
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::identity(size)
|
||||
}
|
||||
|
||||
fn tril(mut self) -> Self {
|
||||
self.tril_impl(None);
|
||||
self
|
||||
}
|
||||
|
||||
fn triu(mut self) -> Self {
|
||||
self.triu_impl(None);
|
||||
self
|
||||
}
|
||||
|
||||
fn tril_replace(mut self, diag_replacement: TYPE) -> Self {
|
||||
self.tril_impl(Some(diag_replacement));
|
||||
self
|
||||
}
|
||||
|
||||
fn triu_replace(mut self, diag_replacement: TYPE) -> Self {
|
||||
self.triu_impl(Some(diag_replacement));
|
||||
self
|
||||
}
|
||||
|
||||
fn gauss_jordan_high(mut self) -> Self {
|
||||
let mut pivot = SPivot::<SIZE>::new(self.rows());
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::gauss_jordan_high(
|
||||
&mut self, &mut pivot,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
fn gauss_jordan_low(mut self) -> Self {
|
||||
let mut pivot = SPivot::<SIZE>::new(self.rows());
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::gauss_jordan_low(
|
||||
&mut self, &mut pivot,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
fn det(self) -> TYPE {
|
||||
let pivot = SPivot::new(self.rows());
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::det(self, pivot)
|
||||
}
|
||||
|
||||
fn invert(&mut self) -> Self {
|
||||
let mut result = NumericalMatrix::identity(self.rows());
|
||||
let mut pivot = SPivot::new(self.rows());
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::invert(
|
||||
self,
|
||||
&mut pivot,
|
||||
&mut result,
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
fn lup(mut self) -> Result<SLUDecomposition<TYPE, SIZE>, SingularMatrixError> {
|
||||
let mut pivot = SPivot::new(self.columns());
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::lup(
|
||||
&mut self, &mut pivot,
|
||||
)?;
|
||||
Ok(SLUDecomposition::<TYPE, SIZE>::new(self, Some(pivot)))
|
||||
}
|
||||
|
||||
fn lu(mut self) -> Result<SLUDecomposition<TYPE, SIZE>, SingularMatrixError> {
|
||||
NumericalMatrixImpl::<TYPE, SPivot<SIZE>, SLUDecomposition<TYPE, SIZE>>::lu(&mut self)?;
|
||||
Ok(SLUDecomposition::<TYPE, SIZE>::new(self, None))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::LUDecomposition;
|
||||
use crate::Matrix;
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::SMatrix;
|
||||
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
for (_, _, value) in m1 {
|
||||
assert_eq!(idx, value);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
for (_, _, value) in m1 {
|
||||
assert_eq!(idx, value);
|
||||
idx += 1;
|
||||
}
|
||||
let m2 = m1.clone();
|
||||
assert!(m1 == m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sum() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = m1 + m2;
|
||||
for i in 0..m1.rows() {
|
||||
for j in 0..m1.columns() {
|
||||
assert_eq!(m3[(i, j)], m1[(i, j)] + m2[(i, j)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = m1 - m2;
|
||||
for i in 0..m1.rows() {
|
||||
for j in 0..m1.columns() {
|
||||
assert_eq!(m3[(i, j)], m1[(i, j)] - m2[(i, j)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 3, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
let m2 = SMatrix::<i32, 5, 2>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = m1 * m2;
|
||||
let expected = {
|
||||
let mut idx = 0;
|
||||
let values = [60, 70, 160, 195, 260, 320];
|
||||
SMatrix::<i32, 3, 2>::new(|_| {
|
||||
let res = values[idx];
|
||||
idx += 1;
|
||||
res
|
||||
})
|
||||
};
|
||||
assert!(expected == m3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i32, 5, 5>::new(|_| {
|
||||
let res = idx;
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<i32, 5, 5>::one();
|
||||
let m3 = m1 * m2;
|
||||
assert!(m1 == m3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<i64>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(arr[idx], i64::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = NumericalMatrix::invert(&mut m1.clone());
|
||||
let m3 = m1 * m2;
|
||||
assert!(m3.is_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_det() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<i64>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(arr[idx], 1);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
assert_eq!(Rational::new(-9, 1), NumericalMatrix::det(m1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tril() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i64, 3, 3>::new(|_| {
|
||||
let res = arr[idx];
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let diag_value = 42;
|
||||
let lt = m1.tril_replace(diag_value);
|
||||
for (i, j, value) in lt {
|
||||
if i == j {
|
||||
assert_eq!(value, diag_value);
|
||||
} else if i < j {
|
||||
assert_eq!(value, i64::zero())
|
||||
} else {
|
||||
assert_eq!(value, i64::from(arr[i * 3 + j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triu() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<i64, 3, 3>::new(|_| {
|
||||
let res = arr[idx];
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let diag_value = 42;
|
||||
let lt = m1.triu_replace(diag_value);
|
||||
for (i, j, value) in lt {
|
||||
if i == j {
|
||||
assert_eq!(value, diag_value);
|
||||
} else if i > j {
|
||||
assert_eq!(value, i64::zero())
|
||||
} else {
|
||||
assert_eq!(value, i64::from(arr[i * 3 + j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_decomposition() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let orig = SMatrix::<Rational<i64>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(arr[idx], i64::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m1 = orig.clone();
|
||||
let lu = m1.lup().unwrap();
|
||||
let tril = lu.l();
|
||||
let triu = lu.u();
|
||||
let m2 = lu.pivot(tril * triu);
|
||||
assert_eq!(orig, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_invert() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let orig = SMatrix::<Rational<i64>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(arr[idx], i64::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m1 = orig.clone();
|
||||
let lud = m1.lup().unwrap();
|
||||
let m2 = lud.invert();
|
||||
let m3 = orig * m2;
|
||||
assert!(m3.is_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_det() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<i64>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(arr[idx], 1);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
assert_eq!(Rational::new(-9, 1), m1.lup().unwrap().det());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod big_int_tests {
|
||||
use num_bigint::BigInt;
|
||||
|
||||
use crate::LUDecomposition;
|
||||
use crate::Matrix;
|
||||
use crate::NumericalMatrix;
|
||||
use crate::Rational;
|
||||
use crate::SMatrix;
|
||||
|
||||
use num_traits::One;
|
||||
use num_traits::Zero;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
for (_, _, value) in m1 {
|
||||
assert_eq!(BigInt::from(idx), value);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
let m2 = m1.clone();
|
||||
assert!(m1 == m2);
|
||||
for (_, _, value) in &m1 {
|
||||
assert_eq!(BigInt::from(idx), *value);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sum() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = &m1 + &m2;
|
||||
for i in 0..m1.rows() {
|
||||
for j in 0..m1.columns() {
|
||||
assert_eq!(m3[(i, j)], &m1[(i, j)] + &m2[(i, j)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = &m1 - &m2;
|
||||
for i in 0..m1.rows() {
|
||||
for j in 0..m1.columns() {
|
||||
assert_eq!(m3[(i, j)], &m1[(i, j)] - &m2[(i, j)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
idx = 0;
|
||||
let m2 = SMatrix::<BigInt, 5, 2>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m3 = m1 * m2;
|
||||
let expected = {
|
||||
let mut idx = 0;
|
||||
let values = [60, 70, 160, 195, 260, 320];
|
||||
SMatrix::<BigInt, 3, 2>::new(|_| {
|
||||
let res = BigInt::from(values[idx]);
|
||||
idx += 1;
|
||||
res
|
||||
})
|
||||
};
|
||||
assert!(expected == m3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 5, 5>::new(|_| {
|
||||
let res = BigInt::from(idx);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = SMatrix::<BigInt, 5, 5>::one();
|
||||
let m3 = &m1 * &m2;
|
||||
assert!(m1 == m3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<BigInt>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(BigInt::from(arr[idx]), BigInt::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m2 = NumericalMatrix::invert(&mut m1.clone());
|
||||
let m3 = m1 * m2;
|
||||
assert!(m3.is_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_det() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<BigInt>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(BigInt::from(arr[idx]), BigInt::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
assert_eq!(
|
||||
Rational::new(BigInt::from(9), -BigInt::one()),
|
||||
NumericalMatrix::det(m1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tril() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 3>::new(|_| {
|
||||
let res = BigInt::from(arr[idx]);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let diag_value = BigInt::from(42);
|
||||
let lt = m1.tril_replace(diag_value.clone());
|
||||
for (i, j, value) in lt {
|
||||
if i == j {
|
||||
assert_eq!(value, diag_value);
|
||||
} else if i < j {
|
||||
assert_eq!(value, BigInt::zero())
|
||||
} else {
|
||||
assert_eq!(value, BigInt::from(arr[i * 3 + j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triu() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<BigInt, 3, 3>::new(|_| {
|
||||
let res = BigInt::from(arr[idx]);
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let diag_value = BigInt::from(42);
|
||||
let lt = m1.triu_replace(diag_value.clone());
|
||||
for (i, j, value) in lt {
|
||||
if i == j {
|
||||
assert_eq!(value, diag_value);
|
||||
} else if i > j {
|
||||
assert_eq!(value, BigInt::zero())
|
||||
} else {
|
||||
assert_eq!(value, BigInt::from(arr[i * 3 + j]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_decomposition() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let orig = SMatrix::<Rational<BigInt>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(BigInt::from(arr[idx]), BigInt::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m1 = orig.clone();
|
||||
let lu = m1.lup().unwrap();
|
||||
let tril = lu.l();
|
||||
let triu = lu.u();
|
||||
let m2 = lu.pivot(tril * triu);
|
||||
assert_eq!(orig, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_invert() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let orig = SMatrix::<Rational<BigInt>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(BigInt::from(arr[idx]), BigInt::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
let m1 = orig.clone();
|
||||
let lu = m1.lup().unwrap();
|
||||
let m2 = lu.invert();
|
||||
let m3 = orig * m2;
|
||||
assert!(m3.is_one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lu_det() {
|
||||
let arr = [1, 2, 3, 4, 5, 6, 8, 7, 9];
|
||||
let mut idx = 0;
|
||||
let m1 = SMatrix::<Rational<BigInt>, 3, 3>::new(|_| {
|
||||
let res = Rational::new(BigInt::from(arr[idx]), BigInt::one());
|
||||
idx += 1;
|
||||
res
|
||||
});
|
||||
assert_eq!(
|
||||
Rational::new(BigInt::from(-9), BigInt::one()),
|
||||
m1.lup().unwrap().det()
|
||||
);
|
||||
}
|
||||
}
|
94
src/spivot.rs
Normal file
94
src/spivot.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use std::ops::Index;
|
||||
use std::ops::IndexMut;
|
||||
use std::ops::Mul;
|
||||
|
||||
use super::matrix::MatrixImpl;
|
||||
use super::smatrix::SMatrix;
|
||||
use super::Matrix;
|
||||
use super::Pivot;
|
||||
|
||||
pub struct SPivot<const SIZE: usize> {
|
||||
data: [usize; SIZE],
|
||||
permutations: usize,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Index<usize> for SPivot<SIZE> {
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.data[index]
|
||||
}
|
||||
|
||||
type Output = usize;
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> IndexMut<usize> for SPivot<SIZE> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut <Self as Index<usize>>::Output {
|
||||
&mut self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Pivot for SPivot<SIZE> {
|
||||
fn swap(&mut self, i1: usize, i2: usize) {
|
||||
self.data.swap(i1, i2);
|
||||
self.permutations += 1;
|
||||
}
|
||||
|
||||
fn permutations(&self) -> usize {
|
||||
self.permutations
|
||||
}
|
||||
|
||||
fn new(_: usize) -> Self {
|
||||
SPivot {
|
||||
data: std::array::from_fn(|i| i),
|
||||
permutations: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const ROWS: usize, const COLUMNS: usize, TYPE> Mul<SMatrix<TYPE, ROWS, COLUMNS>>
|
||||
for SPivot<ROWS>
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn mul(self, matrix: SMatrix<TYPE, ROWS, COLUMNS>) -> Self::Output {
|
||||
let mut result = matrix.clone();
|
||||
let mut pclone = self.clone();
|
||||
for i in 0..ROWS {
|
||||
while i != pclone[i] {
|
||||
let j = pclone[i];
|
||||
result.swap_rows_pivot(&mut pclone, i, j);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
type Output = SMatrix<TYPE, ROWS, COLUMNS>;
|
||||
}
|
||||
|
||||
impl<TYPE, const ROWS: usize, const COLUMNS: usize> Mul<SMatrix<TYPE, ROWS, COLUMNS>>
|
||||
for &SPivot<ROWS>
|
||||
where
|
||||
TYPE: Clone,
|
||||
{
|
||||
fn mul(self, matrix: SMatrix<TYPE, ROWS, COLUMNS>) -> Self::Output {
|
||||
let mut result = matrix.clone();
|
||||
let mut pclone = self.clone();
|
||||
for i in 0..matrix.rows() {
|
||||
while i != pclone[i] {
|
||||
let j = pclone[i];
|
||||
result.swap_rows_pivot(&mut pclone, i, j);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
type Output = SMatrix<TYPE, ROWS, COLUMNS>;
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Clone for SPivot<SIZE> {
|
||||
fn clone(&self) -> Self {
|
||||
SPivot {
|
||||
data: self.data,
|
||||
permutations: self.permutations,
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user