├── .gitignore ├── .vscode └── settings.json ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "mtrx" 4 | ] 5 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mtrx" 3 | version = "0.1.0" 4 | authors = ["Brendan McGuire "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Provides type-safe matrix operations using const generics" 7 | homepage = "https://github.com/MayorMonty/mtrx" 8 | repository = "https://github.com/MayorMonty/mtrx" 9 | documentation = "https://docs.rs/mtrx" 10 | readme = "README.md" 11 | keywords = ["matrix", "const-generics"] 12 | categories = ["mathematics"] 13 | edition = "2021" 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mtrx 2 | 3 | Matrix operations using Rust's new const generics feature. Matrix sizes are determined at compile 4 | time, allowing better type checking. 5 | 6 | Supported Operations 7 | - Addition 8 | - Subtraction 9 | - Scalar Multiplication 10 | - Matrix Multiplication 11 | - Matrix Vector Product 12 | - Transposition 13 | - Matrix Powers 14 | 15 | ```Rust 16 | let matrix_a = Matrix::new([[1, 2, 3], [4, 5, 6]]); 17 | let matrix_b = Matrix::new([[7, 8], [9, 10], [11, 12]]); 18 | 19 | let result: Matrix = matrix_a.multiply(matrix_b); 20 | assert_eq!(result.inner, [[58, 64], [139, 154]]); 21 | 22 | let result = matrix_a * matrix_b; 23 | assert_eq!(result.inner, [[58, 64], [139, 154]]); 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; 2 | 3 | /// The requirements for a type to be a Matrix Cell. Numeric types fulfill these 4 | /// requirements, and many of them can be derived as needed 5 | pub trait MatrixCell: 6 | Add + Mul + AddAssign + SubAssign + Copy + From 7 | { 8 | } 9 | impl + Mul + AddAssign + SubAssign + Copy + From> MatrixCell 10 | for T 11 | { 12 | } 13 | 14 | /// Uses const generics to represent a mathematical matrix 15 | #[derive(Copy, Clone)] 16 | pub struct Matrix { 17 | pub inner: [[T; C]; R], 18 | } 19 | 20 | impl Matrix { 21 | /// Creates a new Matrix of the given size 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * 'inner' - The initial value of the matrix, defines the dimensions of the matrix 26 | /// 27 | /// # Examples 28 | /// 29 | /// ``` 30 | /// use mtrx::Matrix; 31 | /// 32 | /// let matrix = Matrix::new([[1, 2], [3, 4]]); 33 | /// 34 | /// ``` 35 | pub fn new(inner: [[T; C]; R]) -> Self { 36 | Matrix { inner } 37 | } 38 | 39 | /// Returns a 2D array representation of the matrix 40 | pub fn inner(&self) -> [[T; C]; R] { 41 | self.inner 42 | } 43 | 44 | /// Returns true if the matrix is a square matrix 45 | /// 46 | /// # Examples 47 | /// ``` 48 | /// use mtrx::Matrix; 49 | /// 50 | /// let a = Matrix::new([[1, 2], [3, 4]]); 51 | /// let b = Matrix::new([[1, 2, 3], [3, 4, 5]]); 52 | /// assert!(a.is_square()); 53 | /// assert!(!b.is_square()); 54 | /// 55 | /// ``` 56 | pub fn is_square(&self) -> bool { 57 | R == C 58 | } 59 | 60 | /// Multiples the matrix by a scalar value, and returns a new matrix with the scaled values 61 | /// 62 | /// # Arguments 63 | /// 64 | /// * 'scalar' - Value to multiply the matrix by 65 | /// 66 | /// Returns a matrix with dimensions R×C (unchanged) 67 | /// 68 | /// # Examples 69 | /// ``` 70 | /// use mtrx::Matrix; 71 | /// 72 | /// let matrix = Matrix::new( 73 | /// [[1, 1], [2, 2]] 74 | /// ); 75 | /// 76 | /// let result = matrix.multiply_scalar(2); 77 | /// assert_eq!(result.inner(), [[2, 2], [4, 4]]); 78 | /// ``` 79 | pub fn multiply_scalar(&self, scalar: T) -> Matrix { 80 | // Use the default value 81 | let mut inner = [[0i8.into(); C]; R]; 82 | 83 | for r in 0..R { 84 | for c in 0..C { 85 | inner[r][c] = scalar * self.inner[r][c]; 86 | } 87 | } 88 | 89 | Matrix { inner } 90 | } 91 | 92 | /// Performs the dot product with the row of this matrix and the column of the given matrix, 93 | /// used in matrix multiplication 94 | fn dot_product(&self, row: usize, matrix: Matrix, col: usize) -> T { 95 | // Initialize sum with the first value so that we never have to initialize it with zero, which can be difficult with generic numeric types 96 | let mut sum: T = self.inner[row][0] * matrix.inner[0][col]; 97 | 98 | // Add the remainder of the n-tuple 99 | for i in 1..C { 100 | sum += self.inner[row][i] * matrix.inner[i][col] 101 | } 102 | 103 | // Return the sum 104 | sum 105 | } 106 | 107 | /// Performs matrix multiplication with the given matrix, returns the resultant matrix 108 | /// 109 | /// # Arguments 110 | /// 111 | /// * 'matrix' Matrix of dimensions C×K 112 | /// 113 | /// Returns a matrix with dimensions R×K 114 | /// 115 | /// # Examples 116 | /// ``` 117 | /// use mtrx::Matrix; 118 | /// 119 | /// let matrix_a = Matrix::new( 120 | /// [[1, 2, 3], 121 | /// [4, 5, 6]] 122 | /// ); 123 | /// 124 | /// let matrix_b = Matrix::new( 125 | /// [[7, 8], 126 | /// [9, 10], 127 | /// [11, 12]] 128 | /// ); 129 | /// 130 | /// let result = matrix_a.multiply_matrix(matrix_b); 131 | /// assert_eq!(result.inner, [[58, 64], [139, 154]]); 132 | /// 133 | /// ``` 134 | /// 135 | pub fn multiply_matrix(&self, matrix: Matrix) -> Matrix { 136 | // Initialize a default array (the default values are just placeholders) 137 | let mut inner = [[0i8.into(); K]; R]; 138 | 139 | // Perform the multiplication 140 | for r in 0..R { 141 | for c in 0..K { 142 | inner[r][c] = self.dot_product(r, matrix, c); 143 | } 144 | } 145 | 146 | Matrix { inner } 147 | } 148 | 149 | /// Returns the transposed matrix. 150 | /// 151 | /// Matrix transposition is the process of "rotating" the matrix 90 degrees, essentially 152 | /// swapping rows and columns. For example, 153 | /// 154 | /// 155 | /// | 1 2 | 156 | /// | 3 4 | 157 | /// | 5 6 | 158 | /// 159 | /// becomes 160 | /// 161 | /// | 1 3 5 | 162 | /// | 2 4 6 | 163 | /// 164 | /// Returns the transposed Matrix 165 | /// 166 | /// # Examples 167 | /// 168 | /// ``` 169 | /// use mtrx::Matrix; 170 | /// 171 | /// let matrix = Matrix::new( 172 | /// [[1, 2, 3], 173 | /// [4, 5, 6]] 174 | /// ); 175 | /// 176 | /// let transposed = matrix.transpose(); 177 | /// 178 | /// assert_eq!(transposed.inner, [[1, 4], [2, 5], [3, 6]]) 179 | /// 180 | /// ``` 181 | /// 182 | pub fn transpose(&self) -> Matrix { 183 | let mut inner = [[0i8.into(); R]; C]; 184 | 185 | for r in 0..R { 186 | for c in 0..C { 187 | inner[c][r] = self.inner[r][c]; 188 | } 189 | } 190 | 191 | Matrix { inner } 192 | } 193 | 194 | /// Adds two matrices of the same size and returns the sum matrix (also the same size). 195 | /// Additionally you can also use the + operator to add matrices together; 196 | /// 197 | /// # Arguments 198 | /// 199 | /// * 'other' - Same same sized matrix to add 200 | /// 201 | /// # Examples 202 | /// 203 | /// ``` 204 | /// use mtrx::Matrix; 205 | /// 206 | /// let matrix_a = Matrix::new([[1, 2], [3, 4]]); 207 | /// let matrix_b = Matrix::new([[3, 2], [1, 0]]); 208 | /// 209 | /// let sum = matrix_a.add_matrix(matrix_b); 210 | /// assert_eq!(sum.inner, [[4, 4], [4, 4]]); 211 | /// 212 | /// let sum = matrix_a + matrix_b; 213 | /// assert_eq!(sum.inner, [[4, 4], [4, 4]]); 214 | /// 215 | /// ``` 216 | pub fn add_matrix(&self, other: Matrix) -> Matrix { 217 | let mut inner = self.inner.clone(); 218 | 219 | for r in 0..R { 220 | for c in 0..C { 221 | inner[r][c] += other.inner[r][c]; 222 | } 223 | } 224 | 225 | Matrix { inner } 226 | } 227 | 228 | /// Adds a single value to all cells in the matrix and returns the sum matrix. Additionally, you 229 | /// can use the plus operator to add a value to the matrix 230 | /// 231 | /// # Arguments 232 | /// 233 | /// * 'other' - The value T to add to all the cell 234 | /// 235 | /// # Examples 236 | /// 237 | /// ``` 238 | /// use mtrx::Matrix; 239 | /// let matrix_a = Matrix::new([[1, 2], [3, 4]]); 240 | /// 241 | /// let sum = matrix_a.add_value(10); 242 | /// assert_eq!(sum.inner, [[11, 12], [13, 14]]); 243 | /// 244 | /// let sum = matrix_a + 10; 245 | /// assert_eq!(sum.inner, [[11, 12], [13, 14]]); 246 | /// 247 | /// ``` 248 | pub fn add_value(&self, other: T) -> Matrix { 249 | let mut inner = self.inner.clone(); 250 | 251 | for r in 0..R { 252 | for c in 0..C { 253 | inner[r][c] += other 254 | } 255 | } 256 | 257 | Matrix { inner } 258 | } 259 | 260 | /// Subtracts two matrices of the same size and returns the difference matrix (also the same size). 261 | /// Additionally you can also use the - operator to subtract matrices. 262 | /// 263 | /// # Arguments 264 | /// 265 | /// * 'other' - Same same sized matrix to subtract 266 | /// 267 | /// # Examples 268 | /// 269 | /// ``` 270 | /// use mtrx::Matrix; 271 | /// 272 | /// let matrix_a = Matrix::new([[1, 2], [3, 4]]); 273 | /// let matrix_b = Matrix::new([[0, 1], [2, 3]]); 274 | /// 275 | /// let difference = matrix_a.sub_matrix(matrix_b); 276 | /// assert_eq!(difference.inner, [[1, 1], [1, 1]]); 277 | /// 278 | /// let difference = matrix_a - matrix_b; 279 | /// assert_eq!(difference.inner, [[1, 1], [1, 1]]); 280 | /// 281 | /// ``` 282 | pub fn sub_matrix(&self, other: Matrix) -> Matrix { 283 | let mut inner = self.inner.clone(); 284 | 285 | for r in 0..R { 286 | for c in 0..C { 287 | inner[r][c] -= other.inner[r][c]; 288 | } 289 | } 290 | 291 | Matrix { inner } 292 | } 293 | 294 | /// Subtracts a single value to all cells in the matrix and returns the difference matrix. Additionally, you 295 | /// can use the - operator to add a value to the matrix 296 | /// 297 | /// # Arguments 298 | /// 299 | /// * 'other' - The value T to subtract from each cell 300 | /// 301 | /// # Examples 302 | /// 303 | /// ``` 304 | /// use mtrx::Matrix; 305 | /// let matrix_a = Matrix::new([[1, 2], [3, 4]]); 306 | /// 307 | /// let sum = matrix_a.sub_value(1); 308 | /// assert_eq!(sum.inner, [[0, 1], [2, 3]]); 309 | /// 310 | /// let sum = matrix_a - 1; 311 | /// assert_eq!(sum.inner, [[0, 1], [2, 3]]); 312 | /// 313 | /// ``` 314 | pub fn sub_value(&self, other: T) -> Matrix { 315 | let mut inner = self.inner.clone(); 316 | 317 | for r in 0..R { 318 | for c in 0..C { 319 | inner[r][c] -= other 320 | } 321 | } 322 | 323 | Matrix { inner } 324 | } 325 | 326 | /// Multiplies a matrix by a mathematical vector (const-sized array) and returns the matrix 327 | /// vector product. 328 | /// 329 | /// # Arguments 330 | /// 331 | /// * 'other' - Mathematical vector to multiply the matrix by 332 | /// 333 | /// # Examples 334 | /// 335 | /// ``` 336 | /// use mtrx::Matrix; 337 | /// 338 | /// let matrix = Matrix::new([ 339 | /// [1, -1, 2], 340 | /// [0, -3, 1] 341 | /// ]); 342 | /// 343 | /// let vector = [2, 1, 0]; 344 | /// 345 | /// let product = matrix.vector_product(vector); 346 | /// assert_eq!(product, [1, -3]); 347 | /// 348 | /// let product = matrix * vector; 349 | /// assert_eq!(product, [1, -3]) 350 | /// 351 | /// 352 | /// ``` 353 | /// 354 | pub fn vector_product(&self, other: [T; C]) -> [T; R] { 355 | let mut values = [0i8.into(); R]; 356 | 357 | for r in 0..R { 358 | for c in 0..C { 359 | values[r] += self.inner[r][c] * other[c] 360 | } 361 | } 362 | 363 | values 364 | } 365 | 366 | /// Returns a non-mutable reference to the cell at the specified row and column 367 | /// 368 | /// Note: typical mathematical notation for matrices is for 1-indexing. However, in order to be 369 | /// consistent, this function is zero-indexed. 370 | /// 371 | /// # Arguments 372 | /// 373 | /// * 'row' - Must be within 0..R 374 | /// * 'col' - Must be within 0..C 375 | /// 376 | /// # Examples 377 | /// 378 | /// ``` 379 | /// use mtrx::Matrix; 380 | /// 381 | /// let matrix = Matrix::new([ 382 | /// [1, 2], 383 | /// [3, 4] 384 | /// ]); 385 | /// 386 | /// assert_eq!(matrix.get(0, 0), Some(&1)); 387 | /// assert_eq!(matrix.get(0, 1), Some(&2)); 388 | /// assert_eq!(matrix.get(1, 0), Some(&3)); 389 | /// assert_eq!(matrix.get(1, 1), Some(&4)); 390 | /// 391 | /// assert_eq!(matrix.get(2, 2), None); 392 | /// 393 | /// ``` 394 | /// 395 | pub fn get(&self, row: usize, col: usize) -> Option<&T> { 396 | if row < R && col < C { 397 | Some(&self.inner[row][col]) 398 | } else { 399 | None 400 | } 401 | } 402 | 403 | /// Returns a mutable reference to the cell at the specified row and column. 404 | /// 405 | /// Note: typical mathematical notation for matrices is for 1-indexing. However, in order to be 406 | /// consistent, this function is zero-indexed. 407 | /// 408 | /// # Arguments 409 | /// 410 | /// * 'row' - Must be within 0..R 411 | /// * 'col' - Must be within 0..C 412 | /// 413 | /// # Examples 414 | /// 415 | /// ``` 416 | /// use mtrx::Matrix; 417 | /// 418 | /// let mut matrix = Matrix::new([ 419 | /// [1, 2], 420 | /// [3, 4] 421 | /// ]); 422 | /// 423 | /// 424 | /// assert_eq!(matrix.get_mut(0, 0), Some(&mut 1)); 425 | /// assert_eq!(matrix.get_mut(0, 1), Some(&mut 2)); 426 | /// assert_eq!(matrix.get_mut(1, 0), Some(&mut 3)); 427 | /// assert_eq!(matrix.get_mut(1, 1), Some(&mut 4)); 428 | /// 429 | /// assert_eq!(matrix.get_mut(2, 2), None); 430 | /// 431 | /// ``` 432 | /// 433 | pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut T> { 434 | if row < R && col < C { 435 | Some(&mut self.inner[row][col]) 436 | } else { 437 | None 438 | } 439 | } 440 | 441 | /// Sets the value of the cell at the given dimensions 442 | /// 443 | /// Note: typical mathematical notation for matrices is for 1-indexing. However, in order to be 444 | /// consistent, this function is zero-indexed. 445 | /// 446 | /// # Arguments 447 | /// 448 | /// * 'row' - Must be within 0..R 449 | /// * 'col' - Must be within 0..C 450 | /// * 'value' - The value to set at row, col 451 | /// 452 | /// # Examples 453 | /// 454 | /// ``` 455 | /// use mtrx::Matrix; 456 | /// 457 | /// let mut matrix = Matrix::new([ 458 | /// [1, 2], 459 | /// [3, 4] 460 | /// ]); 461 | /// 462 | /// matrix.set(0, 0, 0); 463 | /// assert_eq!(matrix.get(0, 0), Some(&0)); 464 | /// 465 | /// ``` 466 | /// 467 | pub fn set(&mut self, row: usize, col: usize, value: T) -> bool { 468 | if let Some(cell) = self.get_mut(row, col) { 469 | *cell = value; 470 | true 471 | } else { 472 | false 473 | } 474 | } 475 | } 476 | 477 | /// Some matrix operations are only valid for square matrices 478 | impl Matrix { 479 | /// Returns the identity matrix for a RxR matrix. The identity matrix is the matrix with 1 in a 480 | /// diagonal line down the matrix, and a zero everywhere else. For example, the 3x3 identity 481 | /// matrix is: 482 | /// 483 | /// 1 0 0 484 | /// 485 | /// 0 1 0 486 | /// 487 | /// 0 0 1 488 | /// 489 | /// # Examples 490 | /// 491 | /// ``` 492 | /// use mtrx::Matrix; 493 | /// 494 | /// let identity: Matrix = Matrix::identity(); 495 | /// assert_eq!(identity.inner, [[1, 0], [0, 1]]); 496 | /// 497 | /// ``` 498 | /// 499 | /// Identity matrices cannot be created with non-square const generic sizes: 500 | /// 501 | /// ```compile_fail 502 | /// use mtrx::Matrix; 503 | /// 504 | /// let identity: Matrix = Matrix::identity(); // Compiler Error! 505 | /// 506 | /// ``` 507 | pub fn identity() -> Matrix { 508 | let mut inner = [[0i8.into(); R]; R]; 509 | 510 | for r in 0..R { 511 | for c in 0..R { 512 | if r == c { 513 | inner[r][c] = 1i8.into(); 514 | } 515 | } 516 | } 517 | 518 | Matrix { inner } 519 | } 520 | 521 | /// Raises a square matrix to a power (essentially, multiplying itself exp times). Raising a 522 | /// matrix to the zeroth power returns [Matrix::identity] 523 | /// 524 | /// # Arguments 525 | /// 526 | /// * 'exp' - Exponent 527 | /// 528 | /// # Examples 529 | /// 530 | /// ``` 531 | /// use mtrx::Matrix; 532 | /// 533 | /// let matrix = Matrix::new([[1, -3], [2, 5]]); 534 | /// let result = matrix.pow(2); 535 | /// 536 | /// assert_eq!(result.inner, [[-5, -18], [12, 19]]); 537 | /// 538 | /// let result = matrix.pow(0); 539 | /// assert_eq!(result.inner, [[1, 0], [0, 1]]); 540 | /// 541 | /// 542 | /// ``` 543 | pub fn pow(&self, exp: usize) -> Matrix { 544 | // By convention matrix to the power of zero is the identity matrix 545 | if exp == 0 { 546 | Matrix::identity() 547 | 548 | // Otherwise, multiply the matrix by itself exp times 549 | } else { 550 | let mut matrix = self.clone(); 551 | 552 | for _ in 1..exp { 553 | matrix = matrix * matrix; 554 | } 555 | 556 | matrix 557 | } 558 | } 559 | } 560 | 561 | /// Trait Implementations 562 | /// See method descriptions above for more details 563 | 564 | impl Add for Matrix { 565 | type Output = Matrix; 566 | 567 | fn add(self, other: Self) -> Self { 568 | self.add_matrix(other) 569 | } 570 | } 571 | 572 | impl Add for Matrix { 573 | type Output = Matrix; 574 | 575 | fn add(self, other: T) -> Self { 576 | self.add_value(other) 577 | } 578 | } 579 | 580 | impl Sub for Matrix { 581 | type Output = Matrix; 582 | 583 | fn sub(self, other: Self) -> Self { 584 | self.sub_matrix(other) 585 | } 586 | } 587 | 588 | impl Sub for Matrix { 589 | type Output = Matrix; 590 | 591 | fn sub(self, other: T) -> Self { 592 | self.sub_value(other) 593 | } 594 | } 595 | 596 | impl Mul> 597 | for Matrix 598 | { 599 | type Output = Matrix; 600 | 601 | fn mul(self, other: Matrix) -> Matrix { 602 | self.multiply_matrix(other) 603 | } 604 | } 605 | 606 | impl Mul for Matrix { 607 | type Output = Matrix; 608 | 609 | fn mul(self, other: T) -> Matrix { 610 | self.multiply_scalar(other) 611 | } 612 | } 613 | 614 | impl Mul<[T; C]> for Matrix { 615 | type Output = [T; R]; 616 | 617 | fn mul(self, other: [T; C]) -> [T; R] { 618 | self.vector_product(other) 619 | } 620 | } 621 | --------------------------------------------------------------------------------