1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! Implements necessary methods on *square* matrices.
use std::{
ops::{
Add,
Index,
IndexMut,
},
};
use super::Vector;
#[derive(Clone, Copy, PartialEq, Debug)]
/// Abstracts over a square matrix of arbitrary dimension.
pub struct Matrix<const N: usize> {
/// Contains the values of this matrix.
values: [[f64; N]; N],
}
/// Implements necessary behaviors of a matrix.
impl<const N: usize> Matrix<N> {
/// Constructs a zero matrix.
pub fn zero() -> Self {
Self {
values: [[0.0; N]; N],
}
}
/// Constructs an identity matrix.
pub fn identity() -> Self {
let mut values = [[0.0; N]; N];
for i in 0..N {
values[i][i] = 1.0;
}
Self {
values
}
}
/// Constructs a matrix of provided values.
pub fn new(values: [[f64; N]; N]) -> Self {
Self {
values,
}
}
/// Right-multiplies this matrix by the provided vector, returning the result.
pub fn mult(&self, vector: Vector<N>) -> Vector<N> {
let mut output = Vector::<N>::zero();
for i in 0..N {
for j in 0..N {
output[i] += self[(i, j)] * vector[j];
}
}
output
}
/// Swap rows `i` and `j`.
fn swaprow(&mut self, i: usize, j: usize) {
let temp = self.values[i];
self.values[i] = self.values[j];
self.values[j] = temp;
}
/// Scale row `i` by factor `s`.
fn scalerow(&mut self, i: usize, s: f64) {
for j in 0..N {
self[(i, j)] *= s;
}
}
/// Subtract `s` times row `j` from row `i`.
fn subrow(&mut self, i: usize, j: usize, s: f64) {
for k in 0..N {
self[(i, k)] -= s * self[(j, k)];
}
}
/// Returns the inverse of this matrix.
pub fn inverse(&self) -> Self {
let mut output = *self;
let mut inverse = Self::identity();
for i in 0..N {
// Determine the index of the row with the largest pivot
// Start from the working row
let mut j = i;
for k in i..N {
if output[(k, i)] > output[(i, i)] {
j = k;
}
}
// Swap largest pivot to working row
output.swaprow(i, j);
inverse.swaprow(i, j);
// Normalize this row
let s = 1.0 / output[(i, i)];
output.scalerow(i, s);
inverse.scalerow(i, s);
// Subtract this row from all lower rows
for k in (i + 1)..N {
let s = output[(k, i)];
output.subrow(k, i, s);
inverse.subrow(k, i, s);
}
}
// We're now in upper triangular, let's get to GJ normal form
for i in 0..N {
for j in (i + 1)..N {
let s = output[(i, j)];
output.subrow(i, j, s);
inverse.subrow(i, j, s);
}
}
inverse
}
/// Scales a matrix by a provided scalar, returning the new matrix.
pub fn scale(&self, scalar: f64) -> Self {
let mut newvalues = [[0.0; N]; N];
for i in 0..N {
for j in 0..N {
newvalues[i][j] = scalar * self[(i, j)];
}
}
Self {
values: newvalues,
}
}
}
impl<const N: usize> Index<(usize, usize)> for Matrix<N> {
type Output = f64;
fn index(&self, idx: (usize, usize)) -> &Self::Output {
&self.values[idx.0][idx.1]
}
}
impl<const N: usize> IndexMut<(usize, usize)> for Matrix<N> {
fn index_mut(&mut self, idx: (usize, usize)) -> &mut Self::Output {
&mut self.values[idx.0][idx.1]
}
}
impl<const N: usize> Add for Matrix<N> {
type Output = Self;
fn add(self, other: Self) -> Self {
let mut new = Self::zero();
for i in 0..N {
for j in 0..N {
new[(i, j)] = self[(i, j)] + other[(i, j)];
}
}
new
}
}