1use std::collections::HashMap;
6
7use itertools::Itertools;
8#[derive(Debug)]
10#[derive(Default)]
11pub struct Matrix {
12 pub val: Vec<f64>,
14 pub ncols: usize,
16 pub nrows: usize
18}
19
20impl Clone for Matrix {
21 fn clone(&self) -> Self {
22 return Matrix {
23 val : self.val.to_vec(),
24 ncols: self.ncols,
25 nrows: self.nrows
26 }
27 }
28}
29
30impl PartialEq for Matrix {
31 fn eq(&self, other: &Self) -> bool {
32 (self.val == other.val) & (self.ncols == other.ncols) & (self.nrows == other.nrows)
33 }
34}
35
36pub fn identity_matrix(n: usize) -> Matrix {
38 let mut t: Vec<f64> = Vec::with_capacity(n*n);
39 for r in 0..n {
40 for i in 0..n {
41 if r == i {
42 t.push(1.);
43 } else {
44 t.push(0.);
45 }
46 }
47 }
48 Matrix { val: t, ncols: n, nrows: n}
49}
50
51pub fn dot(mat1: &Matrix, mat2: &Matrix) -> Matrix{
53 if !(mat1.ncols == mat2.nrows){
54 panic!("Dimensions does not match, cannot calculate the dot product between matrices of shapes ({},{}) and ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
55 }
56 let mut result: Vec<f64> = Vec::with_capacity(mat1.nrows*mat2.ncols);
57 for i in 0..mat1.nrows {
58 for j in 0..mat2.ncols {
59 result.push(mat1.val[i*mat1.ncols..(i+1)*mat1.ncols].to_vec().iter().zip(mat2.val.iter().skip(j).step_by(mat2.ncols)).map(|(x, y)| x * y).sum());
60 }
61 }
62 return Matrix {
63 val: result,
64 ncols: mat2.ncols,
65 nrows: mat1.nrows
66 }
67}
68
69pub fn divide(mat1: &Matrix, mat2: &Matrix) -> Matrix {
135 if !(mat1.ncols == mat2.ncols && (mat1.nrows == mat2.nrows || mat2.nrows == 1)) {
136 panic!("Dimensions does not match, cannot divide a matrix of shape ({},{}) with a matrix of shape ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
137 }
138 let mut result: Vec<f64> = Vec::with_capacity(mat1.ncols*mat1.nrows);
139 for i in 0..mat1.nrows{
140 for j in 0..mat1.ncols {
141 if mat2.nrows > 1 && mat2.val[i*mat1.ncols+j] != 0.0 {
142 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[i*mat1.ncols+j]);
143 } else if mat2.nrows == 1 && mat2.val[j] != 0.0 {
144 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[j]);
145 } else {
146 result.push(f64::MAX);
147 }
148 }
149 }
150 return Matrix { val: result, ncols: mat1.ncols, nrows: mat1.nrows }
151}
152
153pub fn ge_divide(mat1: &Matrix, mat2: &Matrix) -> Matrix {
186 if !(mat1.ncols == mat2.ncols && (mat1.nrows == mat2.nrows || mat2.nrows == 1)) {
187 panic!("Dimensions does not match, cannot divide a matrix of shape ({},{}) with a matrix of shape ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
188 }
189 let mut result: Vec<f64> = Vec::with_capacity(mat1.ncols*mat1.nrows);
190 for i in 0..mat1.nrows{
191 for j in 0..mat1.ncols {
192 if mat2.nrows > 1 && mat2.val[i*mat1.ncols+j] > 0.0 {
193 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[i*mat1.ncols+j]);
194 } else if mat2.nrows == 1 && mat2.val[j] > 0.0 {
195 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[j]);
196 } else {
197 result.push(f64::MAX);
198 }
199 }
200 }
201 return Matrix { val: result, ncols: mat1.ncols, nrows: mat1.nrows }
202}
203
204pub fn le_divide(mat1: &Matrix, mat2: &Matrix) -> Matrix {
236 if !(mat1.ncols == mat2.ncols && (mat1.nrows == mat2.nrows || mat2.nrows == 1)) {
237 panic!("Dimensions does not match, cannot divide a matrix of shape ({},{}) with a matrix of shape ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
238 }
239 let mut result: Vec<f64> = Vec::with_capacity(mat1.ncols*mat1.nrows);
240 for i in 0..mat1.nrows{
241 for j in 0..mat1.ncols {
242 if mat2.nrows > 1 && mat2.val[i*mat1.ncols+j] < 0.0 {
243 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[i*mat1.ncols+j]);
244 } else if mat2.nrows == 1 && mat2.val[j] < 0.0 {
245 result.push(mat1.val[i*mat1.ncols+j]/mat2.val[j]);
246 } else {
247 result.push(f64::MAX);
248 }
249 }
250 }
251 return Matrix { val: result, ncols: mat1.ncols, nrows: mat1.nrows }
252}
253
254pub fn add(mat1: &Matrix, mat2: &Matrix) -> Matrix {
319 if !(mat1.ncols == mat2.ncols && (mat1.nrows == mat2.nrows || mat2.nrows == 1)) {
320 panic!("Dimensions does not match, cannot add a matrix of shape ({},{}) with a matrix of shape ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
321 }
322 let mut result: Vec<f64> = Vec::with_capacity(mat1.ncols*mat1.nrows);
323 for i in 0..mat1.nrows{
324 for j in 0..mat1.ncols {
325 if mat2.nrows > 1 {
326 result.push(mat1.val[i*mat1.ncols+j]+mat2.val[i*mat1.ncols+j]);
327 } else {
328 result.push(mat1.val[i*mat1.ncols+j]+mat2.val[j]);
329 }
330 }
331 }
332 return Matrix { val: result, ncols: mat1.ncols, nrows: mat1.nrows }
333}
334
335pub fn subtract(mat1: &Matrix, mat2: &Matrix) -> Matrix {
400 if !(mat1.ncols == mat2.ncols && (mat1.nrows == mat2.nrows || mat2.nrows == 1)) {
401 panic!("Dimensions does not match, cannot subtract a matrix of shape ({},{}) with a matrix of shape ({},{})", mat1.nrows, mat1.ncols, mat2.nrows, mat2.ncols);
402 }
403 let mut result: Vec<f64> = Vec::with_capacity(mat1.ncols*mat1.nrows);
404 for i in 0..mat1.nrows{
405 for j in 0..mat1.ncols {
406 if mat2.nrows > 1 {
407 result.push(mat1.val[i*mat1.ncols+j]-mat2.val[i*mat1.ncols+j]);
408 } else {
409 result.push(mat1.val[i*mat1.ncols+j]-mat2.val[j]);
410 }
411 }
412 }
413 return Matrix { val: result, ncols: mat1.ncols, nrows: mat1.nrows }
414}
415
416pub fn transpose(mat: &Matrix) -> Matrix{
440 let mut result = Vec::with_capacity(mat.val.len());
441 for i in 0..mat.ncols{
442 for j in 0..mat.nrows{
443 result.push(mat.val[j*mat.ncols + i])
444 }
445 }
446 return Matrix{val: result, nrows: mat.ncols, ncols: mat.nrows}
447}
448
449pub fn gauss_elimination(mat: &Matrix, dst_row: usize, src_row: usize, col: usize) -> Matrix{
451 if mat.val[src_row*mat.ncols+col] == 0.0 {
452 panic!("Cannot perform Gauss elimination when 'col' coefficient in the 'src_row' is zero");
453 }
454 let corr = mat.val[dst_row*mat.ncols + col]/mat.val[src_row*mat.ncols+col];
455 row_subtraction(mat, dst_row, src_row, Some(corr))
456}
457
458pub fn row_addition(mat: &Matrix, dst_row: usize, src_row: usize, multiplier: Option<f64>) -> Matrix{
460 let mul: f64;
461 if multiplier.is_none() {
462 mul = 1.0;
463 } else {
464 mul = multiplier.unwrap();
465 }
466 let mut tmp = Vec::with_capacity(mat.val.len());
467 for i in 0..mat.val.len(){
468 if i >= dst_row*mat.ncols && i < dst_row*mat.ncols+mat.ncols{
469 tmp.push(mat.val[i]+mat.val[src_row*mat.ncols+i%mat.ncols]*mul);
470 } else {
471 tmp.push(mat.val[i]);
472 }
473 }
474 return Matrix{val: tmp, ncols: mat.ncols, nrows: mat.nrows}
475}
476
477pub fn row_subtraction(mat: &Matrix, dst_row: usize, src_row: usize, multiplier: Option<f64>) -> Matrix{
479 let mul: f64;
480 if multiplier.is_none() {
481 mul = 1.0;
482 } else {
483 mul = multiplier.unwrap();
484 }
485 let mut tmp = Vec::with_capacity(mat.val.len());
486 for i in 0..mat.val.len(){
487 if i >= dst_row*mat.ncols && i < dst_row*mat.ncols+mat.ncols{
488 tmp.push(mat.val[i]-mat.val[src_row*mat.ncols+i%mat.ncols]*mul);
489 } else {
490 tmp.push(mat.val[i]);
491 }
492 }
493 return Matrix{val: tmp, ncols: mat.ncols, nrows: mat.nrows}
494}
495
496pub fn get_columns(mat: &Matrix, ind: &Vec<usize>) -> Matrix {
498 let mut result: Vec<f64> = Vec::with_capacity(ind.len());
499 for i in 0..mat.nrows {
500 result.extend(ind.iter().map(|j| mat.val[i*mat.ncols+j]).collect::<Vec<f64>>());
501 }
502 return Matrix { val: result, ncols: ind.len(), nrows: mat.nrows }
503}
504
505pub fn update_column(mat: &Matrix, ind: usize, v: &Vec<f64>) -> Matrix{
507 if mat.nrows != v.len() {
508 panic!("Dimension does not match");
509 }
510 let mut result = mat.val.to_vec();
511 for i in 0..v.len() {
512 result[i*mat.ncols+ind] = v[i];
513 }
514 Matrix { val: result, ncols: mat.ncols, nrows: mat.nrows}
515}
516
517impl Matrix {
518 pub fn dot(&self, mat2: &Matrix) -> Matrix{
520 dot(self, mat2)
521 }
522 pub fn ge_divide(&self, mat2: &Matrix) -> Matrix {
524 ge_divide(self, mat2)
525 }
526 pub fn le_divide(&self, mat2: &Matrix) -> Matrix {
528 le_divide(self, mat2)
529 }
530 pub fn divide(&self, mat2: &Matrix) -> Matrix {
532 divide(self, mat2)
533 }
534 pub fn subtract(&self, mat2: &Matrix) -> Matrix {
536 subtract(self, mat2)
537 }
538 pub fn transpose(&self) -> Matrix{
540 transpose(self)
541 }
542 pub fn insert_column(&self, column: usize, elem: Vec<f64>) -> Matrix {
543 if column > self.ncols {
544 panic!("Cannot insert column at {} to matrix with dimensions {}, {}", column, self.nrows, self.ncols);
545 }
546 let mut result = Vec::new();
547 let mut j = 0;
548 for (i, e) in self.val.iter().enumerate() {
549 if (i + self.ncols - column) > 0 && (i + self.ncols - column) % self.ncols == 0 {
550 result.push(elem[j].clone());
551 j = j + 1;
552 }
553 result.push(e.clone());
554 }
555 if (j + 1) == elem.len() {
556 result.push(elem[j])
557 }
558 return Matrix { val: result, ncols: self.ncols+1, nrows: self.nrows }
559 }
560 pub fn gauss_elimination(&self, row: usize, by: usize, col: usize) -> Matrix{
561 gauss_elimination(self, row, by, col)
562 }
563
564 pub fn row_addition(&self, dst_row: usize, src_row: usize, multiplier: Option<f64>) -> Matrix{
565 row_addition(self, dst_row, src_row, multiplier)
566 }
567
568 pub fn get_columns(&self, ind: &Vec<usize>) -> Matrix {
569 get_columns(self, ind)
570 }
571
572 pub fn update_column(&self, ind: usize, v: &Vec<f64>) -> Matrix{
573 update_column(self, ind, v)
574 }
575}
576
577
578#[derive(Default, Clone, Debug, PartialEq)]
579pub struct CsrMatrix {
580 pub row: Vec<i64>,
582 pub col: Vec<i64>,
584 pub val: Vec<f64>
586}
587
588impl CsrMatrix {
589
590 pub fn to_matrix(&self) -> Matrix {
592 let unique_rows: Vec<&i64> = self.row.iter().unique().sorted().collect_vec();
593 let unique_cols: Vec<&i64> = self.col.iter().unique().sorted().collect_vec();
594 let n_unique_rows = unique_rows.len();
595 let m_unique_cols = unique_cols.len();
596
597 let rows_map: HashMap<&i64, usize> = unique_rows.into_iter().zip(0..n_unique_rows).collect();
598 let cols_map: HashMap<&i64, usize> = unique_cols.into_iter().zip(0..m_unique_cols).collect();
599
600 let mut val: Vec<f64> = vec![0.0; n_unique_rows*m_unique_cols];
601 for (i,(j,v)) in self.row.iter().zip(self.col.iter().zip(self.val.iter())) {
602 let i_mapped = rows_map.get(i).expect("could not find row i in mapping");
603 let j_mapped = cols_map.get(j).expect("could not find col j in mapping");
604 val[((*i_mapped as usize) * m_unique_cols) + (*j_mapped)] = *v;
605 }
606 Matrix {
607 nrows: n_unique_rows,
608 ncols: m_unique_cols,
609 val: val,
610 }
611 }
612}
613
614pub fn optimized_bit_allocation_64(v: &Vec<i64>) -> Vec<i64>{
625 let mut res: Vec<i64> = Vec::with_capacity(v.len());
626 let mut grp_size= 0;
627 let mut val_adj = 1;
628 let mut current_val = v[0];
629 for i in 0..v.len(){
630 if v[i] == current_val {
631 grp_size += 1;
632 } else {
633 current_val = v[i];
634 val_adj = val_adj*grp_size + val_adj;
635 grp_size = 1;
636 }
637 res.push(val_adj);
638 }
639 res
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645 #[test]
646 fn test_dot_product() {
647 let m1 = Matrix {
648 val: vec![1.0,2.0,3.0,4.0,5.0,6.0],
649 ncols: 2,
650 nrows: 3,
651 };
652 let m2 = Matrix {
653 val: vec![7.0,8.0,9.0,10.0],
654 ncols: 2,
655 nrows: 2,
656 };
657 assert_eq!(m1.dot(&m2).val, Matrix{val: vec![25.0, 28.0, 57.0, 64.0, 89.0, 100.0], ncols: 2, nrows: 3}.val);
658 }
659 #[test]
660 fn test_optimized_bit_allocation(){
661 assert_eq!(optimized_bit_allocation_64(&vec![1,2,3,4]), vec![1,2,4,8]);
662 assert_eq!(optimized_bit_allocation_64(&vec![1,1,1,2]), vec![1,1,1,4]);
663 assert_eq!(optimized_bit_allocation_64(&vec![2,1,1,1,2]), vec![1,2,2,2,8]);
664 assert_eq!(optimized_bit_allocation_64(&vec![3, 3, 2, 3]), vec![1,1,3,6])
665 }
666
667}