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}