Skip to content

Commit 8eefc34

Browse files
author
maxtremblay
committed
Fix linear equation solve
1 parent e6e5310 commit 8eefc34

File tree

3 files changed

+143
-26
lines changed

3 files changed

+143
-26
lines changed

src/matrix/gauss_jordan.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use super::SparseBinMat;
22
use crate::SparseBinSlice;
33

44
pub(super) struct GaussJordan {
5-
number_of_rows: usize,
65
number_of_columns: usize,
76
active_column: usize,
87
rows: Vec<Vec<usize>>,
@@ -11,7 +10,6 @@ pub(super) struct GaussJordan {
1110
impl GaussJordan {
1211
pub(super) fn new(matrix: &SparseBinMat) -> Self {
1312
Self {
14-
number_of_rows: matrix.number_of_rows(),
1513
number_of_columns: matrix.number_of_columns(),
1614
active_column: 0,
1715
rows: matrix
@@ -26,11 +24,9 @@ impl GaussJordan {
2624
}
2725

2826
pub(super) fn echelon_form(self) -> SparseBinMat {
29-
let number_of_rows = self.number_of_rows;
3027
let number_of_columns = self.number_of_columns;
3128
let mut rows = self.unsorted_echeloned_rows();
3229
rows.sort_by_key(|row| row[0]);
33-
rows.extend_from_slice(&vec![Vec::new(); number_of_rows - rows.len()]);
3430
SparseBinMat::new(number_of_columns, rows)
3531
}
3632

@@ -157,8 +153,6 @@ mod test {
157153
vec![1, 2, 3],
158154
vec![3, 4, 6],
159155
vec![5, 6],
160-
vec![],
161-
vec![],
162156
],
163157
);
164158
assert_eq!(echelon_form, expected);

src/matrix/mod.rs

Lines changed: 143 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl SparseBinMat {
236236
pub fn empty() -> Self {
237237
Self {
238238
column_indices: Vec::new(),
239-
row_ranges: Vec::new(),
239+
row_ranges: Vec::new(),
240240
number_of_columns: 0,
241241
}
242242
}
@@ -290,6 +290,28 @@ impl SparseBinMat {
290290
self.number_of_ones() == 0
291291
}
292292

293+
/// Inserts a new row at the end the matrix.
294+
///
295+
/// This update the matrix inplace.
296+
///
297+
/// # Example
298+
///
299+
/// ```
300+
/// # use sparse_bin_mat::SparseBinMat;
301+
/// let rows = vec![vec![0, 1], vec![1, 2]];
302+
/// let mut matrix = SparseBinMat::new(3, rows);
303+
/// matrix.push_row(&[0, 2]);
304+
///
305+
/// let rows = vec![vec![0, 1], vec![1, 2], vec![0, 2]];
306+
/// let expected = SparseBinMat::new(3, rows);
307+
///
308+
/// assert_eq!(matrix, expected);
309+
/// ```
310+
pub fn push_row(&mut self, row: &[usize]) {
311+
self.row_ranges.push(self.number_of_ones() + row.len());
312+
self.column_indices.extend_from_slice(row);
313+
}
314+
293315
/// Returns the value at the given row and column
294316
/// or None if one of the index is out of bound.
295317
///
@@ -536,7 +558,7 @@ impl SparseBinMat {
536558
/// ```
537559
/// # use sparse_bin_mat::SparseBinMat;
538560
/// let matrix = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![0], vec![1, 2], vec![0, 2]]);
539-
/// let expected = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![1], vec![2], vec![]]);
561+
/// let expected = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![1], vec![2]]);
540562
/// assert_eq!(matrix.echelon_form(), expected);
541563
/// ```
542564
pub fn echelon_form(&self) -> Self {
@@ -559,28 +581,16 @@ impl SparseBinMat {
559581
/// # use sparse_bin_mat::SparseBinMat;
560582
/// let matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
561583
///
562-
/// let rhs = SparseBinMat::new(3, vec![vec![0, 2]]);
563-
/// let expected = SparseBinMat::new(2, vec![vec![0, 1]]);
584+
/// let rhs = SparseBinMat::new(3, vec![vec![0, 1], vec![0, 2]]);
585+
/// let expected = SparseBinMat::new(2, vec![vec![0], vec![0, 1]]);
564586
/// assert_eq!(matrix.solve(&rhs), Some(expected));
565587
///
566588
/// let rhs = SparseBinMat::new(3, vec![vec![0]]);
567589
/// assert!(matrix.solve(&rhs).is_none());
568590
/// ```
569591
pub fn solve(&self, rhs: &SparseBinMat) -> Option<Self> {
570-
// Compute combined echelon form and transpose for easier iteration.
571-
let matrix = self
572-
.vertical_concat_with(&rhs)
573-
.transposed()
574-
.echelon_form()
575-
.transposed();
576-
// lhs are the rows corresponding to self.
577-
let lhs = matrix
578-
.keep_only_rows(&(0..self.number_of_rows()).collect_vec())
579-
.unwrap();
580-
// We iterate throught the remaining rows (corresponding to rhs).
581-
matrix
582-
.rows()
583-
.skip(self.number_of_rows())
592+
let (lhs, rhs) = self.prepare_matrices_to_solve(rhs);
593+
rhs.rows()
584594
.map(|row| lhs.solve_vec(row))
585595
.fold_options(Vec::new(), |mut acc, row| {
586596
acc.push(row);
@@ -589,6 +599,22 @@ impl SparseBinMat {
589599
.map(|rows| Self::new(self.number_of_rows(), rows))
590600
}
591601

602+
fn prepare_matrices_to_solve(&self, rhs: &SparseBinMat) -> (Self, Self) {
603+
// Compute combined echelon form and transpose for easier iteration.
604+
let mut matrix = self.vertical_concat_with(&rhs).transposed().echelon_form();
605+
if let Some(first) = matrix.row(matrix.number_of_rows() - 1).and_then(|row| {
606+
row.as_slice()
607+
.first()
608+
.filter(|first| **first < self.number_of_rows() - 1)
609+
.cloned()
610+
}) {
611+
for _ in 0..(self.number_of_rows() - first - 1) {
612+
matrix.push_row(&[]);
613+
}
614+
}
615+
matrix.transposed().split_rows(self.number_of_rows())
616+
}
617+
592618
/// Solves self * x = vec and returns x.
593619
fn solve_vec(&self, vec: SparseBinSlice) -> Option<Vec<usize>> {
594620
let mut vec = vec.to_vec();
@@ -888,6 +914,104 @@ impl SparseBinMat {
888914
self.keep_only_rows(&to_keep)
889915
}
890916

917+
/// Returns two matrices where the first one contains
918+
/// all rows until split and the second one contains all
919+
/// rows starting from split.
920+
///
921+
/// # Example
922+
///
923+
/// ```
924+
/// # use sparse_bin_mat::SparseBinMat;
925+
/// let matrix = SparseBinMat::new(5, vec![
926+
/// vec![0, 1, 2],
927+
/// vec![2, 3, 4],
928+
/// vec![0, 2, 4],
929+
/// vec![1, 3],
930+
/// vec![0, 1, 3],
931+
/// ]);
932+
/// let (top, bottom) = matrix.split_rows(2);
933+
///
934+
/// let expected_top = SparseBinMat::new(5, vec![
935+
/// vec![0, 1, 2],
936+
/// vec![2, 3, 4],
937+
/// ]);
938+
/// assert_eq!(top, expected_top);
939+
///
940+
/// let expected_bottom = SparseBinMat::new(5, vec![
941+
/// vec![0, 2, 4],
942+
/// vec![1, 3],
943+
/// vec![0, 1, 3],
944+
/// ]);
945+
/// assert_eq!(bottom, expected_bottom);
946+
/// ```
947+
pub fn split_rows(&self, split: usize) -> (Self, Self) {
948+
(self.split_rows_top(split), self.split_rows_bottom(split))
949+
}
950+
951+
/// Returns a matrix that contains all rows until split.
952+
///
953+
///
954+
/// # Example
955+
///
956+
/// ```
957+
/// # use sparse_bin_mat::SparseBinMat;
958+
/// let matrix = SparseBinMat::new(5, vec![
959+
/// vec![0, 1, 2],
960+
/// vec![2, 3, 4],
961+
/// vec![0, 2, 4],
962+
/// vec![1, 3],
963+
/// vec![0, 1, 3],
964+
/// ]);
965+
/// let top = matrix.split_rows_top(1);
966+
///
967+
/// let expected_top = SparseBinMat::new(5, vec![
968+
/// vec![0, 1, 2],
969+
/// ]);
970+
/// assert_eq!(top, expected_top);
971+
/// ```
972+
pub fn split_rows_top(&self, split: usize) -> Self {
973+
let row_ranges = self.row_ranges[..=split].to_vec();
974+
let column_indices =
975+
self.column_indices[..row_ranges.last().cloned().unwrap_or(0)].to_vec();
976+
Self {
977+
row_ranges,
978+
column_indices,
979+
number_of_columns: self.number_of_columns,
980+
}
981+
}
982+
983+
/// Returns a matrix that contains all rows starting from split.
984+
///
985+
/// # Example
986+
///
987+
/// ```
988+
/// # use sparse_bin_mat::SparseBinMat;
989+
/// let matrix = SparseBinMat::new(5, vec![
990+
/// vec![0, 1, 2],
991+
/// vec![2, 3, 4],
992+
/// vec![0, 2, 4],
993+
/// vec![1, 3],
994+
/// vec![0, 1, 3],
995+
/// ]);
996+
/// let bottom = matrix.split_rows_bottom(3);
997+
///
998+
/// let expected_bottom = SparseBinMat::new(5, vec![
999+
/// vec![1, 3],
1000+
/// vec![0, 1, 3],
1001+
/// ]);
1002+
/// assert_eq!(bottom, expected_bottom);
1003+
/// ```
1004+
pub fn split_rows_bottom(&self, split: usize) -> Self {
1005+
let row_ranges = self.row_ranges[split..].to_vec();
1006+
let column_indices =
1007+
self.column_indices[row_ranges.first().cloned().unwrap_or(0)..].to_vec();
1008+
Self {
1009+
row_ranges: row_ranges.iter().map(|r| r - row_ranges[0]).collect(),
1010+
column_indices,
1011+
number_of_columns: self.number_of_columns,
1012+
}
1013+
}
1014+
8911015
/// Returns a new matrix keeping only the given columns or an error
8921016
/// if columns are out of bound, unsorted or not unique.
8931017
///
@@ -1120,7 +1244,7 @@ mod test {
11201244
);
11211245
let b = SparseBinMat::new(6, vec![vec![0], vec![1, 5]]);
11221246
let solution = a.solve(&b).unwrap();
1123-
let expected = SparseBinMat::new(9, vec![vec![0, 1, 2, 4, 5], vec![1, 4, 7]]);
1247+
let expected = SparseBinMat::new(9, vec![vec![0, 1, 2, 4, 5], vec![1, 4, 6]]);
11241248
assert_eq!(solution, expected);
11251249
}
11261250

src/matrix/transpose.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use super::SparseBinMat;
22

33
pub(super) fn transpose(matrix: &SparseBinMat) -> SparseBinMat {
44
if matrix.is_empty() {
5-
println!("{}", matrix.number_of_columns());
65
return SparseBinMat::new(0, vec![vec![]; matrix.number_of_columns()]);
76
}
87
let mut transposed = vec![Vec::new(); matrix.number_of_columns()];

0 commit comments

Comments
 (0)