RosettaCodeData/Task/Multiple-regression/Rust/multiple-regression.rs

271 lines
6.7 KiB
Rust

use std::fmt;
#[derive(Debug, Clone)]
struct Matrix<const R: usize, const C: usize> {
data: [[f64; C]; R],
}
impl<const R: usize, const C: usize> Matrix<R, C> {
fn new() -> Self {
Self {
data: [[0.0; C]; R],
}
}
fn from_rows(values: &[&[f64]]) -> Self {
let mut matrix = Self::new();
for (i, row) in values.iter().enumerate().take(R) {
for (j, &val) in row.iter().enumerate().take(C) {
matrix.data[i][j] = val;
}
}
matrix
}
fn get(&self, row: usize, col: usize) -> f64 {
self.data[row][col]
}
fn set(&mut self, row: usize, col: usize, value: f64) {
self.data[row][col] = value;
}
fn get_row(&self, row: usize) -> [f64; C] {
self.data[row]
}
fn set_row(&mut self, row: usize, values: &[f64; C]) {
self.data[row] = *values;
}
fn multiply<const D: usize>(&self, rhs: &Matrix<C, D>) -> Matrix<R, D> {
let mut result = Matrix::<R, D>::new();
for i in 0..R {
for j in 0..D {
for k in 0..C {
let prod = self.get(i, k) * rhs.get(k, j);
result.set(i, j, result.get(i, j) + prod);
}
}
}
result
}
fn transpose(&self) -> Matrix<C, R> {
let mut trans = Matrix::<C, R>::new();
for i in 0..R {
for j in 0..C {
trans.set(j, i, self.data[i][j]);
}
}
trans
}
fn to_reduced_row_echelon_form(&mut self) {
let mut lead = 0;
for r in 0..R {
if C <= lead {
return;
}
let mut i = r;
while self.get(i, lead) == 0.0 {
i += 1;
if R == i {
i = r;
lead += 1;
if C == lead {
return;
}
}
}
// Swap rows
let temp = self.get_row(i);
self.set_row(i, &self.get_row(r));
self.set_row(r, &temp);
if self.get(r, lead) != 0.0 {
let div = self.get(r, lead);
for j in 0..C {
self.set(r, j, self.get(r, j) / div);
}
}
for k in 0..R {
if k != r {
let mult = self.get(k, lead);
for j in 0..C {
let prod = self.get(r, j) * mult;
self.set(k, j, self.get(k, j) - prod);
}
}
}
lead += 1;
}
}
}
impl<const N: usize> Matrix<N, N> {
fn inverse(&self) -> Result<Matrix<N, N>, &'static str> {
if N == 0 {
return Err("Cannot invert empty matrix");
}
// Create augmented matrix using Vec for dynamic sizing
let mut aug = vec![vec![0.0; 2 * N]; N];
// Copy original matrix to left side of augmented matrix
for i in 0..N {
for j in 0..N {
aug[i][j] = self.get(i, j);
}
// Add identity matrix to right side
aug[i][i + N] = 1.0;
}
// Perform Gauss-Jordan elimination
let mut lead = 0;
for r in 0..N {
if 2 * N <= lead {
return Err("Matrix is not invertible");
}
let mut i = r;
while aug[i][lead] == 0.0 {
i += 1;
if N == i {
i = r;
lead += 1;
if 2 * N == lead {
return Err("Matrix is not invertible");
}
}
}
// Swap rows
aug.swap(i, r);
if aug[r][lead] != 0.0 {
let div = aug[r][lead];
for j in 0..(2 * N) {
aug[r][j] /= div;
}
}
for k in 0..N {
if k != r {
let mult = aug[k][lead];
for j in 0..(2 * N) {
aug[k][j] -= aug[r][j] * mult;
}
}
}
lead += 1;
}
// Extract inverse from right side
let mut inv = Matrix::<N, N>::new();
for i in 0..N {
for j in 0..N {
inv.set(i, j, aug[i][j + N]);
}
}
Ok(inv)
}
}
impl<const R: usize, const C: usize> fmt::Display for Matrix<R, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for i in 0..R {
write!(f, "[")?;
for j in 0..C {
if j > 0 {
write!(f, ", ")?;
}
write!(f, "{}", self.get(i, j))?;
}
writeln!(f, "]")?;
}
Ok(())
}
}
fn multiple_regression<const R: usize, const C: usize>(
y: &[f64; C],
x: &Matrix<R, C>,
) -> Result<[f64; R], &'static str> {
let mut tm = Matrix::<1, C>::new();
for (i, &val) in y.iter().enumerate() {
tm.set(0, i, val);
}
let cy = tm.transpose();
let cx = x.transpose();
let x_cx = x.multiply(&cx);
let inv = x_cx.inverse()?;
let result = inv.multiply(&x.multiply(&cy)).transpose();
Ok(result.get_row(0))
}
fn case1() -> Result<(), &'static str> {
let y = [1.0, 2.0, 3.0, 4.0, 5.0];
let x = Matrix::<1, 5>::from_rows(&[&[2.0, 1.0, 3.0, 4.0, 5.0]]);
let v = multiple_regression(&y, &x)?;
println!("{:?}", v);
Ok(())
}
fn case2() -> Result<(), &'static str> {
let y = [3.0, 4.0, 5.0];
let x = Matrix::<2, 3>::from_rows(&[
&[1.0, 2.0, 1.0],
&[1.0, 1.0, 2.0],
]);
let v = multiple_regression(&y, &x)?;
println!("{:?}", v);
Ok(())
}
fn case3() -> Result<(), &'static str> {
let y = [
52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29,
63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46,
];
let a = [
1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65,
1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83,
];
let mut x = Matrix::<3, 15>::new();
// First row: all ones
for i in 0..15 {
x.set(0, i, 1.0);
}
// Second row: values from array 'a'
for (i, &val) in a.iter().enumerate() {
x.set(1, i, val);
}
// Third row: squared values from array 'a'
for (i, &val) in a.iter().enumerate() {
x.set(2, i, val * val);
}
let v = multiple_regression(&y, &x)?;
println!("{:?}", v);
Ok(())
}
fn main() -> Result<(), &'static str> {
case1()?;
case2()?;
case3()?;
Ok(())
}