sparse_bin_mat/matrix/mod.rs
1use crate::error::{
2 validate_positions, InvalidPositions, MatMatIncompatibleDimensions,
3 MatVecIncompatibleDimensions,
4};
5use crate::BinNum;
6use crate::{SparseBinSlice, SparseBinVec, SparseBinVecBase};
7use itertools::Itertools;
8use std::collections::HashMap;
9use std::ops::{Add, Deref, Mul};
10
11mod concat;
12use concat::{concat_horizontally, concat_vertically};
13
14mod constructor_utils;
15use constructor_utils::initialize_from;
16
17mod gauss_jordan;
18use gauss_jordan::GaussJordan;
19
20mod inplace_operations;
21use inplace_operations::{insert_one_at, remove_one_at};
22
23mod kronecker;
24use kronecker::kronecker_product;
25
26mod non_trivial_elements;
27pub use self::non_trivial_elements::NonTrivialElements;
28
29mod nullspace;
30use nullspace::{normal_form_from_echelon_form, nullspace};
31
32mod rows;
33pub use self::rows::Rows;
34
35mod transpose;
36use transpose::transpose;
37
38mod ser_de;
39
40/// A sparse binary matrix optimized for row operations.
41#[derive(Debug, PartialEq, Eq, Hash, Clone)]
42pub struct SparseBinMat {
43 row_ranges: Vec<usize>,
44 column_indices: Vec<usize>,
45 number_of_columns: usize,
46}
47
48impl SparseBinMat {
49 /// Creates a new matrix with the given number of columns
50 /// and list of rows .
51 ///
52 /// A row is a list of the positions where the elements have value 1.
53 ///
54 /// # Example
55 ///
56 /// ```
57 /// # use sparse_bin_mat::SparseBinMat;
58 /// let matrix = SparseBinMat::new(4, vec![vec![0, 1, 2], vec![0, 2, 3]]);
59 ///
60 /// assert_eq!(matrix.number_of_rows(), 2);
61 /// assert_eq!(matrix.number_of_columns(), 4);
62 /// assert_eq!(matrix.number_of_elements(), 8);
63 /// ```
64 ///
65 /// # Panic
66 ///
67 /// Panics if a position in a row is greater or equal the number of columns,
68 /// a row is unsorted or a row contains duplicate.
69 pub fn new(number_of_columns: usize, rows: Vec<Vec<usize>>) -> Self {
70 Self::try_new(number_of_columns, rows).unwrap()
71 }
72
73 /// Creates a new matrix with the given number of columns
74 /// and list of rows or returns an error if a position in a
75 /// row is greater or equal the number of columns, a row is unsorted
76 /// or a row contains duplicate.
77 ///
78 /// A row is a list of the positions where the elements have value 1.
79 /// All rows are sorted during insertion.
80 ///
81 /// # Example
82 ///
83 /// ```
84 /// # use sparse_bin_mat::SparseBinMat;
85 /// let first_matrix = SparseBinMat::try_new(4, vec![vec![0, 1, 2], vec![0, 2, 3]]);
86 /// let second_matrix = SparseBinMat::new(4, vec![vec![0, 1, 2], vec![0, 2, 3]]);
87 /// assert_eq!(first_matrix, Ok(second_matrix));
88 /// ```
89 pub fn try_new(
90 number_of_columns: usize,
91 rows: Vec<Vec<usize>>,
92 ) -> Result<Self, InvalidPositions> {
93 for row in rows.iter() {
94 validate_positions(number_of_columns, row)?;
95 }
96 Ok(Self::new_unchecked(number_of_columns, rows))
97 }
98
99 // Assumes rows are sorted, all unique and inbound.
100 pub(crate) fn new_unchecked(number_of_columns: usize, rows: Vec<Vec<usize>>) -> Self {
101 let (row_ranges, column_indices) = initialize_from(rows, None);
102 Self {
103 row_ranges,
104 column_indices,
105 number_of_columns,
106 }
107 }
108
109 /// Creates a new matrix with the given number of columns,
110 /// capacity and list of rows.
111 ///
112 /// A row is a list of the positions where the elements have value 1.
113 ///
114 /// The capacity is used to pre-allocate enough memory to store that
115 /// amount of 1s in the matrix.
116 /// This is mostly useful in combination with inplace operations modifying
117 /// the number of 1s in the matrix.
118 ///
119 /// # Example
120 ///
121 /// ```
122 /// # use sparse_bin_mat::SparseBinMat;
123 /// let matrix = SparseBinMat::with_capacity(4, 7, vec![vec![0, 1, 2], vec![0, 2, 3]]);
124 ///
125 /// assert_eq!(matrix.number_of_rows(), 2);
126 /// assert_eq!(matrix.number_of_columns(), 4);
127 /// assert_eq!(matrix.number_of_elements(), 8);
128 /// assert_eq!(matrix.capacity(), 7);
129 /// ```
130 ///
131 /// # Panic
132 ///
133 /// Panics if a position in a row is greater or equal the number of columns,
134 /// a row is unsorted or a row contains duplicate.
135 pub fn with_capacity(number_of_columns: usize, capacity: usize, rows: Vec<Vec<usize>>) -> Self {
136 Self::try_with_capacity(number_of_columns, capacity, rows).expect("[Error]")
137 }
138
139 /// Creates a new matrix with the given number of columns,
140 /// capacity and list of rows
141 /// Returns an error if a position in a
142 /// row is greater or equal the number of columns, a row is unsorted
143 /// or a row contains duplicate.
144 ///
145 /// A row is a list of the positions where the elements have value 1.
146 /// All rows are sorted during insertion.
147 ///
148 /// The capacity is used to pre-allocate enough memory to store that
149 /// amount of 1s in the matrix.
150 /// This is mostly useful in combination with inplace operations modifying
151 /// the number of 1s in the matrix.
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// # use sparse_bin_mat::SparseBinMat;
157 /// let matrix = SparseBinMat::new(4, vec![vec![0, 1, 2], vec![0, 2, 3]]);
158 /// let try_matrix = SparseBinMat::try_with_capacity(4, 7, vec![vec![0, 1 ,2], vec![0, 2, 3]]);
159 ///
160 /// assert_eq!(try_matrix, Ok(matrix));
161 /// assert_eq!(try_matrix.unwrap().capacity(), 7);
162 /// ```
163 pub fn try_with_capacity(
164 number_of_columns: usize,
165 capacity: usize,
166 rows: Vec<Vec<usize>>,
167 ) -> Result<Self, InvalidPositions> {
168 for row in rows.iter() {
169 validate_positions(number_of_columns, row)?;
170 }
171 let (row_ranges, column_indices) = initialize_from(rows, Some(capacity));
172 Ok(Self {
173 row_ranges,
174 column_indices,
175 number_of_columns,
176 })
177 }
178
179 /// Creates an identity matrix of the given length.
180 ///
181 /// # Example
182 ///
183 /// ```
184 /// # use sparse_bin_mat::SparseBinMat;
185 /// let matrix = SparseBinMat::identity(5);
186 ///
187 /// let identity_rows = (0..5).map(|x| vec![x]).collect();
188 /// let identity_matrix = SparseBinMat::new(5, identity_rows);
189 ///
190 /// assert_eq!(matrix, identity_matrix);
191 /// ```
192 pub fn identity(length: usize) -> Self {
193 Self {
194 column_indices: (0..length).collect(),
195 row_ranges: (0..length + 1).collect(),
196 number_of_columns: length,
197 }
198 }
199
200 /// Creates a matrix fill with zeros of the given dimensions.
201 ///
202 /// # Example
203 ///
204 /// ```
205 /// # use sparse_bin_mat::SparseBinMat;
206 /// let matrix = SparseBinMat::zeros(2, 3);
207 ///
208 /// assert_eq!(matrix.number_of_rows(), 2);
209 /// assert_eq!(matrix.number_of_columns(), 3);
210 /// assert_eq!(matrix.number_of_zeros(), 6);
211 /// assert_eq!(matrix.number_of_ones(), 0);
212 /// ```
213 pub fn zeros(number_of_rows: usize, number_of_columns: usize) -> Self {
214 Self::new_unchecked(number_of_columns, vec![Vec::new(); number_of_rows])
215 }
216
217 /// Creates an empty matrix.
218 ///
219 /// This allocate minimally, so it is a good placeholder.
220 ///
221 /// # Example
222 ///
223 /// ```
224 /// # use sparse_bin_mat::SparseBinMat;
225 /// let matrix = SparseBinMat::empty();
226 ///
227 /// assert_eq!(matrix.number_of_rows(), 0);
228 /// assert_eq!(matrix.number_of_columns(), 0); assert_eq!(matrix.number_of_elements(), 0);
229 /// // Note that these are not equal since new preallocate some space
230 /// // to store the data.
231 /// assert_ne!(SparseBinMat::new(0, Vec::new()), SparseBinMat::empty());
232 ///
233 /// // To test for emptyness, you should prefer the following.
234 /// assert!(matrix.is_empty());
235 /// ```
236 pub fn empty() -> Self {
237 Self {
238 column_indices: Vec::new(),
239 row_ranges: Vec::new(),
240 number_of_columns: 0,
241 }
242 }
243
244 /// Returns the maximum number of 1s the matrix can
245 /// store before reallocating.
246 pub fn capacity(&self) -> usize {
247 self.column_indices.capacity()
248 }
249
250 /// Returns the number of columns in the matrix.
251 pub fn number_of_columns(&self) -> usize {
252 self.number_of_columns
253 }
254
255 /// Returns the number of rows in the matrix
256 pub fn number_of_rows(&self) -> usize {
257 match self.row_ranges.len() {
258 0 => 0,
259 n => n - 1,
260 }
261 }
262
263 /// Returns the number of rows and columns in the matrix.
264 pub fn dimension(&self) -> (usize, usize) {
265 (self.number_of_rows(), self.number_of_columns())
266 }
267
268 /// Returns the number of elements in the matrix.
269 pub fn number_of_elements(&self) -> usize {
270 self.number_of_rows() * self.number_of_columns()
271 }
272
273 /// Returns the number of elements with value 0 in the matrix.
274 pub fn number_of_zeros(&self) -> usize {
275 self.number_of_elements() - self.number_of_ones()
276 }
277
278 /// Returns the number of elements with value 1 in the matrix.
279 pub fn number_of_ones(&self) -> usize {
280 self.column_indices.len()
281 }
282
283 /// Returns true if the number of elements in the matrix is 0.
284 pub fn is_empty(&self) -> bool {
285 self.number_of_elements() == 0
286 }
287
288 /// Returns true if the all elements of the matrix are 0.
289 pub fn is_zero(&self) -> bool {
290 self.number_of_ones() == 0
291 }
292
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
315 /// Returns the value at the given row and column
316 /// or None if one of the index is out of bound.
317 ///
318 /// # Example
319 ///
320 /// ```
321 /// # use sparse_bin_mat::SparseBinMat;
322 /// let rows = vec![vec![0, 1], vec![1, 2]];
323 /// let matrix = SparseBinMat::new(3, rows);
324 ///
325 /// assert_eq!(matrix.get(0, 0), Some(1.into()));
326 /// assert_eq!(matrix.get(1, 0), Some(0.into()));
327 /// assert_eq!(matrix.get(2, 0), None);
328 /// ```
329 pub fn get(&self, row: usize, column: usize) -> Option<BinNum> {
330 if column < self.number_of_columns() {
331 self.row(row).and_then(|row| row.get(column))
332 } else {
333 None
334 }
335 }
336
337 /// Inserts the given value at the given row and column.
338 ///
339 /// This operation is perform in place. That is, it take ownership of the matrix
340 /// and returns an updated matrix using the same memory.
341 ///
342 /// To avoid having to reallocate new memory,
343 /// it is reccommended to construct the matrix using
344 /// [`with_capacity`](SparseBinMat::with_capacity)
345 /// or
346 /// [`try_with_capacity`](SparseBinMat::try_with_capacity).
347 ///
348 /// # Example
349 ///
350 /// ```
351 /// # use sparse_bin_mat::SparseBinMat;
352 ///
353 /// let mut matrix = SparseBinMat::with_capacity(3, 4, vec![vec![0], vec![1, 2]]);
354 ///
355 /// // This doesn't change the matrix
356 /// matrix = matrix.emplace_at(1, 0, 0);
357 /// assert_eq!(matrix.number_of_ones(), 3);
358 ///
359 /// // Add a 1 in the first row.
360 /// matrix = matrix.emplace_at(1, 0, 2);
361 /// let expected = SparseBinMat::new(3, vec![vec![0, 2], vec![1, 2]]);
362 /// assert_eq!(matrix, expected);
363 ///
364 /// // Remove a 1 in the second row.
365 /// matrix = matrix.emplace_at(0, 1, 1);
366 /// let expected = SparseBinMat::new(3, vec![vec![0, 2], vec![2]]);
367 /// assert_eq!(matrix, expected);
368 /// ```
369 ///
370 /// # Panic
371 ///
372 /// Panics if either the row or column is out of bound.
373 pub fn emplace_at<B: Into<BinNum>>(self, value: B, row: usize, column: usize) -> Self {
374 match (self.get(row, column).map(|n| n.inner), value.into().inner) {
375 (None, _) => panic!(
376 "position ({}, {}) is out of bound for {} matrix",
377 row,
378 column,
379 dimension_to_string(self.dimension())
380 ),
381 (Some(0), 1) => insert_one_at(self, row, column),
382 (Some(1), 0) => remove_one_at(self, row, column),
383 _ => self,
384 }
385 }
386
387 /// Returns true if the value at the given row and column is 0
388 /// or None if one of the index is out of bound.
389 ///
390 /// # Example
391 ///
392 /// ```
393 /// # use sparse_bin_mat::SparseBinMat;
394 /// let rows = vec![vec![0, 1], vec![1, 2]];
395 /// let matrix = SparseBinMat::new(3, rows);
396 ///
397 /// assert_eq!(matrix.is_zero_at(0, 0), Some(false));
398 /// assert_eq!(matrix.is_zero_at(1, 0), Some(true));
399 /// assert_eq!(matrix.is_zero_at(2, 0), None);
400 /// ```
401 pub fn is_zero_at(&self, row: usize, column: usize) -> Option<bool> {
402 self.get(row, column).map(|value| value == 0.into())
403 }
404
405 /// Returns true if the value at the given row and column is 1
406 /// or None if one of the index is out of bound.
407 ///
408 /// # Example
409 ///
410 /// ```
411 /// # use sparse_bin_mat::SparseBinMat;
412 /// let rows = vec![vec![0, 1], vec![1, 2]];
413 /// let matrix = SparseBinMat::new(3, rows);
414 ///
415 /// assert_eq!(matrix.is_one_at(0, 0), Some(true));
416 /// assert_eq!(matrix.is_one_at(1, 0), Some(false));
417 /// assert_eq!(matrix.is_one_at(2, 0), None);
418 /// ```
419 pub fn is_one_at(&self, row: usize, column: usize) -> Option<bool> {
420 self.get(row, column).map(|value| value == 1.into())
421 }
422
423 /// Returns a reference to the given row of the matrix
424 /// or None if the row index is out of bound.
425 ///
426 /// # Example
427 ///
428 /// ```
429 /// # use sparse_bin_mat::{SparseBinSlice, SparseBinMat};
430 /// let rows = vec![vec![0, 1], vec![1, 2]];
431 /// let matrix = SparseBinMat::new(3, rows.clone());
432 ///
433 /// assert_eq!(matrix.row(0), Some(SparseBinSlice::new(3, &rows[0])));
434 /// assert_eq!(matrix.row(1), Some(SparseBinSlice::new(3, &rows[1])));
435 /// assert_eq!(matrix.row(2), None);
436 /// ```
437 pub fn row(&self, row: usize) -> Option<SparseBinSlice> {
438 let row_start = self.row_ranges.get(row)?;
439 let row_end = self.row_ranges.get(row + 1)?;
440 Some(
441 SparseBinSlice::try_new(
442 self.number_of_columns(),
443 &self.column_indices[*row_start..*row_end],
444 )
445 .unwrap(),
446 )
447 }
448
449 /// Returns an iterator yielding the rows of the matrix
450 /// as slice of non zero positions.
451 ///
452 /// # Example
453 ///
454 /// ```
455 /// # use sparse_bin_mat::{SparseBinSlice, SparseBinMat};
456 /// let rows = vec![vec![0, 1, 2, 5], vec![1, 3, 4], vec![2, 4, 5], vec![0, 5]];
457 /// let matrix = SparseBinMat::new(7, rows.clone());
458 ///
459 /// let mut iter = matrix.rows();
460 ///
461 /// assert_eq!(iter.next(), Some(SparseBinSlice::new(7, &rows[0])));
462 /// assert_eq!(iter.next(), Some(SparseBinSlice::new(7, &rows[1])));
463 /// assert_eq!(iter.next(), Some(SparseBinSlice::new(7, &rows[2])));
464 /// assert_eq!(iter.next(), Some(SparseBinSlice::new(7, &rows[3])));
465 /// assert_eq!(iter.next(), None);
466 /// ```
467 pub fn rows(&self) -> Rows {
468 Rows::from(self)
469 }
470
471 /// Returns an iterator yielding the positions of the non
472 /// trivial elements.
473 ///
474 /// # Example
475 ///
476 /// ```
477 /// # use sparse_bin_mat::{SparseBinSlice, SparseBinMat};
478 /// let rows = vec![vec![0, 2], vec![1], vec![0, 2], vec![1]];
479 /// let matrix = SparseBinMat::new(3, rows);
480 ///
481 /// let mut iter = matrix.non_trivial_elements();
482 ///
483 /// assert_eq!(iter.next(), Some((0, 0)));
484 /// assert_eq!(iter.next(), Some((0, 2)));
485 /// assert_eq!(iter.next(), Some((1, 1)));
486 /// assert_eq!(iter.next(), Some((2, 0)));
487 /// assert_eq!(iter.next(), Some((2, 2)));
488 /// assert_eq!(iter.next(), Some((3, 1)));
489 /// assert_eq!(iter.next(), None);
490 /// ```
491 pub fn non_trivial_elements(&self) -> NonTrivialElements {
492 NonTrivialElements::new(self)
493 }
494
495 /// Returns an iterator yielding the number
496 /// of non zero elements in each row of the matrix.
497 ///
498 /// # Example
499 ///
500 /// ```
501 /// # use sparse_bin_mat::SparseBinMat;
502 /// let rows = vec![vec![0, 1, 2, 5], vec![1, 3, 4], vec![2, 4, 5], vec![0, 5]];
503 /// let matrix = SparseBinMat::new(7, rows);
504 ///
505 /// assert_eq!(matrix.row_weights().collect::<Vec<usize>>(), vec![4, 3, 3, 2]);
506 /// ```
507 pub fn row_weights<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
508 self.rows().map(|row| row.weight())
509 }
510
511 /// Gets the transposed version of the matrix
512 /// by swapping the columns with the rows.
513 ///
514 /// # Example
515 ///
516 /// ```
517 /// # use sparse_bin_mat::SparseBinMat;
518 ///
519 /// let rows = vec![vec![0, 1, 2], vec![1, 3], vec![0, 2, 3]];
520 /// let matrix = SparseBinMat::new(4, rows);
521 ///
522 /// let transposed_matrix = matrix.transposed();
523 ///
524 /// let expected_rows = vec![vec![0, 2], vec![0, 1], vec![0, 2], vec![1, 2]];
525 /// let expected_matrix = SparseBinMat::new(3, expected_rows);
526 ///
527 /// assert_eq!(transposed_matrix, expected_matrix);
528 /// ```
529 pub fn transposed(&self) -> Self {
530 transpose(self)
531 }
532
533 /// Computes the rank of the matrix.
534 /// That is, the number of linearly independent rows or columns.
535 ///
536 /// # Example
537 ///
538 /// ```
539 /// # use sparse_bin_mat::SparseBinMat;
540 ///
541 /// let rows = vec![vec![0, 1], vec![1, 2], vec![0, 2]];
542 /// let matrix = SparseBinMat::new(3, rows);
543 ///
544 /// assert_eq!(matrix.rank(), 2);
545 /// ```
546 pub fn rank(&self) -> usize {
547 GaussJordan::new(self).rank()
548 }
549
550 /// Returns an echeloned version of the matrix.
551 ///
552 /// A matrix in echelon form as the property that
553 /// all rows have a 1 at their first non trivial
554 /// position. Also, all rows are linearly independent.
555 ///
556 /// # Example
557 ///
558 /// ```
559 /// # use sparse_bin_mat::SparseBinMat;
560 /// let matrix = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![0], vec![1, 2], vec![0, 2]]);
561 /// let expected = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![1], vec![2]]);
562 /// assert_eq!(matrix.echelon_form(), expected);
563 /// ```
564 pub fn echelon_form(&self) -> Self {
565 GaussJordan::new(self).echelon_form()
566 }
567
568 /// Solves a linear system of equations define from this matrix.
569 ///
570 /// One solution is provided for each column of rhs.
571 /// That is, for each row r of rhs, this solves
572 /// the system A*x = r and returns x into a matrix where the row i
573 /// is the solution for the column i of rhs.
574 ///
575 /// Returns None if at least one equation is not solvable and returns
576 /// an arbitrary solution if it is not unique.
577 ///
578 /// # Example
579 ///
580 /// ```
581 /// # use sparse_bin_mat::SparseBinMat;
582 /// let matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
583 ///
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]]);
586 /// assert_eq!(matrix.solve(&rhs), Some(expected));
587 ///
588 /// let rhs = SparseBinMat::new(3, vec![vec![0]]);
589 /// assert!(matrix.solve(&rhs).is_none());
590 /// ```
591 pub fn solve(&self, rhs: &SparseBinMat) -> Option<Self> {
592 let (lhs, rhs) = self.prepare_matrices_to_solve(rhs);
593 rhs.rows()
594 .map(|row| lhs.solve_vec(row))
595 .fold_options(Vec::new(), |mut acc, row| {
596 acc.push(row);
597 acc
598 })
599 .map(|rows| Self::new(self.number_of_rows(), rows))
600 }
601
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
618 /// Solves self * x = vec and returns x.
619 fn solve_vec(&self, vec: SparseBinSlice) -> Option<Vec<usize>> {
620 let mut vec = vec.to_vec();
621 let mut solution = Vec::new();
622 let mut col_idx = self.number_of_rows();
623 for vec_idx in (0..vec.len()).rev() {
624 if col_idx == 0 {
625 return None;
626 }
627 col_idx -= 1;
628 if vec.is_one_at(vec_idx).unwrap() {
629 while self
630 .row(col_idx)
631 .and_then(|row| row.is_zero_at(vec_idx))
632 .unwrap_or(false)
633 {
634 if col_idx == 0 {
635 return None;
636 }
637 col_idx -= 1;
638 }
639 vec = &vec + &self.row(col_idx).unwrap();
640 solution.push(col_idx);
641 }
642 }
643 solution.reverse();
644 vec.is_zero().then(|| solution)
645 }
646
647 /// Returns a matrix for which the rows are the generators
648 /// of the nullspace of the original matrix.
649 ///
650 /// The nullspace of a matrix M is the set of vectors N such that
651 /// Mx = 0 for all x in N.
652 /// Therefore, if N is the nullspace matrix obtain from this function,
653 /// we have that M * N^T = 0.
654 ///
655 /// # Example
656 ///
657 /// ```
658 /// # use sparse_bin_mat::SparseBinMat;
659 /// let matrix = SparseBinMat::new(
660 /// 6,
661 /// vec![vec![0, 1, 3, 5], vec![2, 3, 4], vec![2, 5], vec![0, 1, 3]],
662 /// );
663 ///
664 /// let expected = SparseBinMat::new(6, vec![vec![0, 3, 4,], vec![0, 1]]);
665 /// let nullspace = matrix.nullspace();
666 ///
667 /// assert_eq!(nullspace, expected);
668 /// assert_eq!(&matrix * &nullspace.transposed(), SparseBinMat::zeros(4, 2));
669 /// ```
670 pub fn nullspace(&self) -> Self {
671 nullspace(self)
672 }
673
674 pub fn normal_form(&self) -> (Self, Vec<usize>) {
675 normal_form_from_echelon_form(&self.echelon_form())
676 }
677
678 pub fn permute_columns(self: &Self, permutation: &[usize]) -> SparseBinMat {
679 let inverse = Self::inverse_permutation(&permutation);
680 let rows = self
681 .rows()
682 .map(|row| {
683 row.non_trivial_positions()
684 .map(|column| inverse[column])
685 .sorted()
686 .collect()
687 })
688 .collect();
689 SparseBinMat::new(self.number_of_columns(), rows)
690 }
691
692 fn inverse_permutation(permutation: &[usize]) -> Vec<usize> {
693 let mut inverse = vec![0; permutation.len()];
694 for (index, position) in permutation.iter().enumerate() {
695 inverse[*position] = index;
696 }
697 inverse
698 }
699
700 /// Returns the horizontal concatenation of two matrices.
701 ///
702 /// If the matrix have different number of rows, the smallest
703 /// one is padded with empty rows.
704 ///
705 /// # Example
706 ///
707 /// ```
708 /// # use sparse_bin_mat::SparseBinMat;
709 /// let left_matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
710 /// let right_matrix = SparseBinMat::new(4, vec![vec![1, 2, 3], vec![0, 1], vec![2, 3]]);
711 ///
712 /// let concatened = left_matrix.horizontal_concat_with(&right_matrix);
713 ///
714 /// let expected = SparseBinMat::new(7, vec![vec![0, 1, 4, 5, 6], vec![1, 2, 3, 4], vec![5, 6]]);
715 ///
716 /// assert_eq!(concatened, expected);
717 /// ```
718 pub fn horizontal_concat_with(&self, other: &SparseBinMat) -> SparseBinMat {
719 concat_horizontally(self, other)
720 }
721
722 /// Returns the vertical concatenation of two matrices.
723 ///
724 /// If the matrix have different number of columns, the smallest
725 /// one is padded with empty columns.
726 ///
727 /// # Example
728 ///
729 /// ```
730 /// # use sparse_bin_mat::SparseBinMat;
731 /// let left_matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
732 /// let right_matrix = SparseBinMat::identity(3);
733 ///
734 /// let concatened = left_matrix.vertical_concat_with(&right_matrix);
735 /// let expected = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2], vec![0], vec![1], vec![2]]);
736 ///
737 /// assert_eq!(concatened, expected);
738 /// ```
739 pub fn vertical_concat_with(&self, other: &SparseBinMat) -> Self {
740 concat_vertically(self, other)
741 }
742
743 /// Returns the dot product between a matrix and a vector or an error
744 /// if the number of columns in the matrix is not equal to the length of
745 /// the vector.
746 ///
747 /// Use the Mul (*) operator for a version that panics instead of
748 /// returning a `Result`.
749 ///
750 /// # Example
751 ///
752 /// ```
753 /// # use sparse_bin_mat::{SparseBinMat, SparseBinVec};
754 /// let matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
755 /// let vector = SparseBinVec::new(3, vec![0, 1]);
756 /// let result = SparseBinVec::new(2, vec![1]);
757 ///
758 /// assert_eq!(matrix.dot_with_vector(&vector), Ok(result));
759 /// ```
760 pub fn dot_with_vector<T>(
761 &self,
762 vector: &SparseBinVecBase<T>,
763 ) -> Result<SparseBinVec, MatVecIncompatibleDimensions>
764 where
765 T: std::ops::Deref<Target = [usize]>,
766 {
767 if self.number_of_columns() != vector.len() {
768 return Err(MatVecIncompatibleDimensions::new(
769 self.dimension(),
770 vector.len(),
771 ));
772 }
773 let positions = self
774 .rows()
775 .map(|row| row.dot_with(vector).unwrap())
776 .positions(|product| product == 1.into())
777 .collect();
778 Ok(SparseBinVec::new_unchecked(
779 self.number_of_rows(),
780 positions,
781 ))
782 }
783
784 /// Returns the dot product between two matrices or an error
785 /// if the number of columns in the first matrix is not equal
786 /// to the number of rows in the second matrix.
787 ///
788 /// Use the Mul (*) operator for a version that panics instead of
789 /// returning a `Result`.
790 ///
791 /// # Example
792 ///
793 /// ```
794 /// # use sparse_bin_mat::SparseBinMat;
795 /// let matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
796 /// let other_matrix = SparseBinMat::new(4, vec![vec![1], vec![2], vec![3]]);
797 /// let result = SparseBinMat::new(4, vec![vec![1, 2], vec![2, 3]]);
798 ///
799 /// assert_eq!(matrix.dot_with_matrix(&other_matrix), Ok(result));
800 /// ```
801 pub fn dot_with_matrix(
802 &self,
803 other: &Self,
804 ) -> Result<SparseBinMat, MatMatIncompatibleDimensions> {
805 if self.number_of_columns() != other.number_of_rows() {
806 return Err(MatMatIncompatibleDimensions::new(
807 self.dimension(),
808 other.dimension(),
809 ));
810 }
811 let transposed = other.transposed();
812 let rows = self
813 .rows()
814 .map(|row| {
815 transposed
816 .rows()
817 .positions(|column| row.dot_with(&column).unwrap() == 1.into())
818 .collect()
819 })
820 .collect();
821 Ok(Self::new_unchecked(other.number_of_columns, rows))
822 }
823
824 /// Returns the bitwise xor sum of two matrices or an error
825 /// if the matrices have different dimensions.
826 ///
827 /// Use the Add (+) operator for a version that panics instead
828 /// of returning a `Result`.
829 ///
830 /// # Example
831 ///
832 /// ```
833 /// # use sparse_bin_mat::{SparseBinMat, SparseBinVec};
834 /// let matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
835 /// let other_matrix = SparseBinMat::new(3, vec![vec![0, 1, 2], vec![0, 1, 2]]);
836 /// let result = SparseBinMat::new(3, vec![vec![2], vec![0]]);
837 ///
838 /// assert_eq!(matrix.bitwise_xor_with(&other_matrix), Ok(result));
839 /// ```
840 pub fn bitwise_xor_with(&self, other: &Self) -> Result<Self, MatMatIncompatibleDimensions> {
841 if self.dimension() != other.dimension() {
842 return Err(MatMatIncompatibleDimensions::new(
843 self.dimension(),
844 other.dimension(),
845 ));
846 }
847 let rows = self
848 .rows()
849 .zip(other.rows())
850 .map(|(row, other_row)| row.bitwise_xor_with(&other_row).unwrap().to_positions_vec())
851 .collect();
852 Ok(SparseBinMat::new_unchecked(self.number_of_columns(), rows))
853 }
854
855 /// Returns a new matrix keeping only the given rows or an error
856 /// if rows are out of bound, unsorted or not unique.
857 ///
858 /// # Example
859 ///
860 /// ```
861 /// use sparse_bin_mat::SparseBinMat;
862 /// let matrix = SparseBinMat::new(5, vec![
863 /// vec![0, 1, 2],
864 /// vec![2, 3, 4],
865 /// vec![0, 2, 4],
866 /// vec![1, 3],
867 /// ]);
868 ///
869 /// let truncated = SparseBinMat::new(5, vec![
870 /// vec![0, 1, 2],
871 /// vec![0, 2, 4],
872 /// ]);
873 ///
874 /// assert_eq!(matrix.keep_only_rows(&[0, 2]), Ok(truncated));
875 /// assert_eq!(matrix.keep_only_rows(&[0, 2, 3]).unwrap().number_of_rows(), 3);
876 /// ```
877 pub fn keep_only_rows(&self, rows: &[usize]) -> Result<Self, InvalidPositions> {
878 validate_positions(self.number_of_rows(), rows)?;
879 let rows = self
880 .rows()
881 .enumerate()
882 .filter(|(index, _)| rows.contains(index))
883 .map(|(_, row)| row.non_trivial_positions().collect())
884 .collect();
885 Ok(Self::new_unchecked(self.number_of_columns(), rows))
886 }
887
888 /// Returns a truncated matrix where the given rows are removed or an error
889 /// if rows are out of bound or unsorted.
890 ///
891 /// # Example
892 ///
893 /// ```
894 /// # use sparse_bin_mat::SparseBinMat;
895 /// let matrix = SparseBinMat::new(5, vec![
896 /// vec![0, 1, 2],
897 /// vec![2, 3, 4],
898 /// vec![0, 2, 4],
899 /// vec![1, 3],
900 /// ]);
901 ///
902 /// let truncated = SparseBinMat::new(5, vec![
903 /// vec![2, 3, 4],
904 /// vec![1, 3],
905 /// ]);
906 ///
907 /// assert_eq!(matrix.without_rows(&[0, 2]), Ok(truncated));
908 /// assert_eq!(matrix.without_rows(&[1, 2, 3]).unwrap().number_of_rows(), 1);
909 /// ```
910 pub fn without_rows(&self, rows: &[usize]) -> Result<Self, InvalidPositions> {
911 let to_keep: Vec<usize> = (0..self.number_of_rows())
912 .filter(|x| !rows.contains(x))
913 .collect();
914 self.keep_only_rows(&to_keep)
915 }
916
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
1015 /// Returns a new matrix keeping only the given columns or an error
1016 /// if columns are out of bound, unsorted or not unique.
1017 ///
1018 /// Columns are relabeled to the fit new number of columns.
1019 ///
1020 /// # Example
1021 ///
1022 /// ```
1023 /// use sparse_bin_mat::SparseBinMat;
1024 /// let matrix = SparseBinMat::new(5, vec![
1025 /// vec![0, 1, 2],
1026 /// vec![2, 3, 4],
1027 /// vec![0, 2, 4],
1028 /// vec![1, 3],
1029 /// ]);
1030 ///
1031 /// let truncated = SparseBinMat::new(3, vec![
1032 /// vec![0, 1],
1033 /// vec![2],
1034 /// vec![0, 2],
1035 /// vec![1],
1036 /// ]);
1037 ///
1038 /// assert_eq!(matrix.keep_only_columns(&[0, 1, 4]), Ok(truncated));
1039 /// assert_eq!(matrix.keep_only_columns(&[1, 2]).unwrap().number_of_columns(), 2);
1040 /// ```
1041 pub fn keep_only_columns(&self, columns: &[usize]) -> Result<Self, InvalidPositions> {
1042 validate_positions(self.number_of_columns(), columns)?;
1043 let old_to_new_column_map = columns
1044 .iter()
1045 .enumerate()
1046 .map(|(new, old)| (old, new))
1047 .collect::<HashMap<_, _>>();
1048 let rows = self
1049 .rows()
1050 .map(|row| {
1051 row.non_trivial_positions()
1052 .filter_map(|column| old_to_new_column_map.get(&column).cloned())
1053 .collect()
1054 })
1055 .collect();
1056 Ok(Self::new_unchecked(columns.len(), rows))
1057 }
1058
1059 /// Returns a truncated matrix where the given columns are removed or
1060 /// an error if columns are out of bound or unsorted.
1061 ///
1062 /// Columns are relabeled to fit the new number of columns.
1063 ///
1064 /// # Example
1065 ///
1066 /// ```
1067 /// # use sparse_bin_mat::SparseBinMat;
1068 /// let matrix = SparseBinMat::new(5, vec![
1069 /// vec![0, 1, 2],
1070 /// vec![2, 3, 4],
1071 /// vec![0, 2, 4],
1072 /// vec![1, 3],
1073 /// ]);
1074 ///
1075 /// let truncated = SparseBinMat::new(3, vec![
1076 /// vec![0],
1077 /// vec![1, 2],
1078 /// vec![2],
1079 /// vec![0, 1],
1080 /// ]);
1081 ///
1082 /// assert_eq!(matrix.without_columns(&[0, 2]), Ok(truncated));
1083 /// ```
1084 pub fn without_columns(&self, columns: &[usize]) -> Result<Self, InvalidPositions> {
1085 let to_keep: Vec<usize> = (0..self.number_of_columns)
1086 .filter(|x| !columns.contains(x))
1087 .collect();
1088 self.keep_only_columns(&to_keep)
1089 }
1090
1091 /// Returns the Kronecker product of two matrices.
1092 ///
1093 /// # Example
1094 ///
1095 /// ```
1096 /// # use sparse_bin_mat::SparseBinMat;
1097 /// let left_matrix = SparseBinMat::new(2, vec![vec![1], vec![0]]);
1098 /// let right_matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
1099 ///
1100 /// let product = left_matrix.kron_with(&right_matrix);
1101 /// let expected = SparseBinMat::new(6, vec![vec![3, 4], vec![4, 5], vec![0, 1], vec![1, 2]]);
1102 ///
1103 /// assert_eq!(product, expected);
1104 /// ```
1105 pub fn kron_with(&self, other: &Self) -> Self {
1106 kronecker_product(self, other)
1107 }
1108
1109 /// Returns a json string for the matrix.
1110 pub fn as_json(&self) -> Result<String, serde_json::Error> {
1111 serde_json::to_string(self)
1112 }
1113}
1114
1115impl Add<&SparseBinMat> for &SparseBinMat {
1116 type Output = SparseBinMat;
1117
1118 fn add(self, other: &SparseBinMat) -> SparseBinMat {
1119 self.bitwise_xor_with(other).expect(&format!(
1120 "{} and {} matrices can't be added",
1121 dimension_to_string(self.dimension()),
1122 dimension_to_string(other.dimension()),
1123 ))
1124 }
1125}
1126
1127impl Mul<&SparseBinMat> for &SparseBinMat {
1128 type Output = SparseBinMat;
1129
1130 fn mul(self, other: &SparseBinMat) -> SparseBinMat {
1131 self.dot_with_matrix(other).expect(&format!(
1132 "{} and {} matrices can't be multiplied",
1133 dimension_to_string(self.dimension()),
1134 dimension_to_string(other.dimension()),
1135 ))
1136 }
1137}
1138
1139impl<T: Deref<Target = [usize]>> Mul<&SparseBinVecBase<T>> for &SparseBinMat {
1140 type Output = SparseBinVec;
1141
1142 fn mul(self, other: &SparseBinVecBase<T>) -> SparseBinVec {
1143 self.dot_with_vector(other).expect(&format!(
1144 "{} matrix can't be multiplied with vector of length {}",
1145 dimension_to_string(self.dimension()),
1146 other.len()
1147 ))
1148 }
1149}
1150
1151fn dimension_to_string(dimension: (usize, usize)) -> String {
1152 format!("({} x {})", dimension.0, dimension.1)
1153}
1154
1155impl std::fmt::Display for SparseBinMat {
1156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1157 for row in self.rows() {
1158 writeln!(f, "{}", row)?;
1159 }
1160 Ok(())
1161 }
1162}
1163
1164#[cfg(test)]
1165mod test {
1166 use std::vec;
1167
1168 use super::*;
1169
1170 #[test]
1171 #[should_panic]
1172 fn panics_on_construction_if_rows_are_out_of_bound() {
1173 let rows = vec![vec![0, 1, 5], vec![2, 3, 4]];
1174 SparseBinMat::new(5, rows);
1175 }
1176
1177 #[test]
1178 fn addition() {
1179 let first_matrix = SparseBinMat::new(6, vec![vec![0, 2, 4], vec![1, 3, 5]]);
1180 let second_matrix = SparseBinMat::new(6, vec![vec![0, 1, 2], vec![3, 4, 5]]);
1181 let sum = SparseBinMat::new(6, vec![vec![1, 4], vec![1, 4]]);
1182 assert_eq!(&first_matrix + &second_matrix, sum);
1183 }
1184
1185 #[test]
1186 fn panics_on_addition_if_different_dimensions() {
1187 let matrix_6_2 = SparseBinMat::new(6, vec![vec![0, 2, 4], vec![1, 3, 5]]);
1188 let matrix_6_3 = SparseBinMat::new(6, vec![vec![0, 1, 2], vec![3, 4, 5], vec![0, 3]]);
1189 let matrix_2_2 = SparseBinMat::new(2, vec![vec![0], vec![1]]);
1190
1191 let result = std::panic::catch_unwind(|| &matrix_6_2 + &matrix_6_3);
1192 assert!(result.is_err());
1193
1194 let result = std::panic::catch_unwind(|| &matrix_6_2 + &matrix_2_2);
1195 assert!(result.is_err());
1196
1197 let result = std::panic::catch_unwind(|| &matrix_6_3 + &matrix_2_2);
1198 assert!(result.is_err());
1199 }
1200
1201 #[test]
1202 fn multiplication_with_other_matrix() {
1203 let first_matrix = SparseBinMat::new(3, vec![vec![0, 1], vec![1, 2]]);
1204 let second_matrix = SparseBinMat::new(5, vec![vec![0, 2], vec![1, 3], vec![2, 4]]);
1205 let product = SparseBinMat::new(5, vec![vec![0, 1, 2, 3], vec![1, 2, 3, 4]]);
1206 assert_eq!(&first_matrix * &second_matrix, product);
1207 }
1208
1209 #[test]
1210 fn panics_on_matrix_multiplication_if_wrong_dimension() {
1211 let matrix_6_3 = SparseBinMat::new(6, vec![vec![0, 1, 2], vec![3, 4, 5], vec![0, 3]]);
1212 let matrix_2_2 = SparseBinMat::new(2, vec![vec![0], vec![1]]);
1213 let result = std::panic::catch_unwind(|| &matrix_6_3 * &matrix_2_2);
1214 assert!(result.is_err());
1215 }
1216
1217 #[test]
1218 fn solve_simple_matrix() {
1219 let a = SparseBinMat::new(
1220 4,
1221 vec![vec![0, 1, 3], vec![1, 2], vec![2, 3], vec![], vec![0, 2]],
1222 );
1223 let b = SparseBinMat::new(4, vec![vec![0], vec![1, 3], vec![2]]);
1224 let solution = a.solve(&b).unwrap();
1225 let expected = SparseBinMat::new(5, vec![vec![0, 1, 2], vec![1, 2], vec![0, 1, 2, 4]]);
1226 assert_eq!(solution, expected);
1227 }
1228
1229 #[test]
1230 fn solve_surface_code() {
1231 let a = SparseBinMat::new(
1232 6,
1233 vec![
1234 vec![0, 1, 4],
1235 vec![1, 2],
1236 vec![3, 4],
1237 vec![],
1238 vec![2],
1239 vec![3],
1240 vec![5],
1241 vec![5],
1242 vec![],
1243 ],
1244 );
1245 let b = SparseBinMat::new(6, vec![vec![0], vec![1, 5]]);
1246 let solution = a.solve(&b).unwrap();
1247 let expected = SparseBinMat::new(9, vec![vec![0, 1, 2, 4, 5], vec![1, 4, 6]]);
1248 assert_eq!(solution, expected);
1249 }
1250
1251 #[test]
1252 fn solve_surface_code_horizontal_only() {
1253 let a = SparseBinMat::new(
1254 5,
1255 vec![
1256 vec![],
1257 vec![],
1258 vec![],
1259 vec![0, 1],
1260 vec![1, 2],
1261 vec![0, 3],
1262 vec![],
1263 vec![2, 4],
1264 vec![3, 4],
1265 ],
1266 );
1267 let b = SparseBinMat::new(5, vec![vec![2, 3]]);
1268 let solution = a.solve(&b).unwrap();
1269 let expected = SparseBinMat::new(9, vec![vec![3, 4, 5]]);
1270 assert_eq!(solution, expected);
1271 }
1272
1273 #[test]
1274 fn solve_surface_code_no_solution() {
1275 let a = SparseBinMat::new(
1276 5,
1277 vec![
1278 vec![],
1279 vec![],
1280 vec![],
1281 vec![0, 1],
1282 vec![1, 2],
1283 vec![0, 3],
1284 vec![],
1285 vec![2, 4],
1286 vec![3, 4],
1287 ],
1288 );
1289 let b = SparseBinMat::new(5, vec![vec![2, 3], vec![1]]);
1290 let solution = a.solve(&b);
1291 assert!(solution.is_none());
1292 }
1293
1294 #[test]
1295 fn permutation() {
1296 let permutation = vec![0, 2, 4, 1, 3, 5, 6];
1297 let inverse = vec![0, 3, 1, 4, 2, 5, 6];
1298 assert_eq!(SparseBinMat::inverse_permutation(&permutation), inverse);
1299 }
1300
1301 #[test]
1302 fn column_permutation() {
1303 let matrix = SparseBinMat::new(
1304 6,
1305 vec![
1306 vec![0, 1, 2],
1307 vec![3, 4, 5],
1308 vec![0, 2, 4],
1309 vec![1, 3, 5],
1310 vec![0, 5],
1311 ],
1312 );
1313 let permutation = vec![1, 0, 2, 4, 5, 3];
1314 let expected = SparseBinMat::new(
1315 6,
1316 vec![
1317 vec![0, 1, 2],
1318 vec![3, 4, 5],
1319 vec![1, 2, 3],
1320 vec![0, 4, 5],
1321 vec![1, 4],
1322 ],
1323 );
1324 assert_eq!(matrix.permute_columns(&permutation), expected);
1325 }
1326}