relp/algorithm/two_phase/tableau/inverse_maintenance/carry/lower_upper/
mod.rs

1//! # LU decomposition
2use std::cmp::max;
3use std::cmp::Ordering;
4use std::collections::BTreeMap;
5use std::fmt;
6use std::fmt::Display;
7use std::iter;
8
9use num_traits::Zero;
10
11use crate::algorithm::two_phase::matrix_provider::column::{Column, ColumnIterator, ColumnNumber};
12use crate::algorithm::two_phase::tableau::inverse_maintenance::{ColumnComputationInfo, ops};
13use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::BasisInverse;
14use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::eta_file::EtaFile;
15use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::permutation::{FullPermutation, Permutation, RotateToBackPermutation};
16use crate::data::linear_algebra::traits::SparseElement;
17use crate::data::linear_algebra::vector::{SparseVector, Vector};
18
19mod decomposition;
20mod permutation;
21mod eta_file;
22
23/// Decompose a matrix `B` into `PBQ = LU` where
24///
25/// * `P` is a row permutation
26/// * `Q` is a column permutation
27/// * `L` is lower triangular with `1`'s on the diagonal
28/// * `U` is upper triangular
29///
30/// Note that permutations `P` and `Q` have the transpose equal to their inverse as they are
31/// orthogonal matrices.
32///
33/// `P` and `Q` are "full" permutations, not to be confused with the simpler "rotating" permutations
34/// in the `updates` field.
35#[derive(Eq, PartialEq, Clone, Debug)]
36pub struct LUDecomposition<F> {
37    /// Row permutation `P`.
38    ///
39    /// The `forward` application of the permutation to rows of `M` corresponds to `PM`.
40    row_permutation: FullPermutation,
41    /// Column permutation `Q`.
42    ///
43    /// The
44    column_permutation: FullPermutation,
45    /// Lower triangular matrix `L`.
46    ///
47    /// Column major, one's on diagonal implied. So the length of the vector is `m - 1`.
48    lower_triangular: Vec<Vec<(usize, F)>>,
49    /// Upper triangular matrix `U`.
50    ///
51    /// Column major, diagonal stored separately. So the length of the vector is `m - 1`.
52    upper_triangular: Vec<Vec<(usize, F)>>,
53    /// Diagonal of the upper triangular matrix. Length is `m`.
54    upper_diagonal: Vec<F>,
55
56    // TODO(ARCHITECTURE): Consider grouping pivot index and eta file in a single update struct.
57    updates: Vec<(EtaFile<F>, RotateToBackPermutation)>,
58}
59
60impl<F> BasisInverse for LUDecomposition<F>
61where
62    F: ops::Field + ops::FieldHR,
63{
64    type F = F;
65    type ColumnComputationInfo = ColumnAndSpike<Self::F>;
66
67    fn identity(m: usize) -> Self {
68        Self {
69            row_permutation: FullPermutation::identity(m),
70            column_permutation: FullPermutation::identity(m),
71            lower_triangular: vec![vec![]; m - 1],
72            upper_triangular: vec![vec![]; m - 1],
73            upper_diagonal: vec![F::one(); m],
74            updates: vec![],
75        }
76    }
77
78    fn invert<C: Column>(columns: impl ExactSizeIterator<Item=C>) -> Self
79    where
80        Self::F: ops::Column<C::F>,
81    {
82        let m = columns.len();
83        let mut rows = vec![Vec::new(); m];
84        for (j, column) in columns.into_iter().enumerate() {
85            for (i, value) in column {
86                rows[i].push((j, value.into()));
87            }
88        }
89        debug_assert!(rows.iter().all(|row| row.is_sorted_by_key(|&(j, _)| j)));
90
91        Self::rows(rows)
92    }
93
94    fn change_basis(
95        &mut self,
96        pivot_row_index: usize,
97        column: Self::ColumnComputationInfo,
98    ) -> SparseVector<Self::F, Self::F> {
99        let m = self.m();
100
101        let pivot_column_index = {
102            // Column with a pivot in `pivot_row_index` is leaving
103            let mut pivot_column_index = pivot_row_index;
104            self.column_permutation.forward_ref(&mut pivot_column_index);
105            for (_, q) in &self.updates {
106                Permutation::forward_ref(q, &mut pivot_column_index);
107            }
108            pivot_column_index
109        };
110
111        // Compute r
112        let (u_bar, indices_to_zero): (Vec<_>, Vec<_>) = ((pivot_column_index + 1)..m)
113            .filter_map(|j| {
114                let column = &self.upper_triangular[j - 1];
115                column
116                    .binary_search_by_key(&pivot_column_index, |&(i, _)| i).ok()
117                    .map(|data_index| (
118                        (j, column[data_index].1.clone()),
119                        (j, data_index),
120                    ))
121            })
122            .unzip();
123        let u_bar = u_bar.into_iter().collect();
124        let r = self.right_multiply_by_upper_inverse(u_bar);
125        let eta_file = EtaFile::new(r, pivot_column_index, self.m());
126
127        // We now have all information needed to recover the triangle, start modifying it
128        // Zero out part of a row that will be rotated to the bottom
129        for (j, data_index) in indices_to_zero {
130            self.upper_triangular[j - 1].remove(data_index);
131        }
132        let Self::ColumnComputationInfo { column, mut spike } = column;
133        debug_assert!(spike.windows(2).all(|w| w[0].0 < w[1].0));
134
135        eta_file.update_spike_pivot_value(&mut spike);
136        debug_assert!(
137            spike.binary_search_by_key(&pivot_column_index, |&(i, _)| i).is_ok(),
138            "This value should be present to avoid singularity because it will be the bottom corner value.",
139        );
140
141        let disappearing_column_index = if pivot_column_index == 0 {
142            // The first column is replaced, but this column is not explicitly represented in the
143            // `upper_triangular` field. The non-diagonal item of the second column, if it exists,
144            // will be zeroed. The diagonal item will be shifted separately.
145            0
146        } else {
147            // In the usual case, subtract one because the column indices are shifted
148            pivot_column_index - 1
149        };
150        // Insert the spike
151        self.upper_triangular[disappearing_column_index] = spike;
152
153        // Rotate the non-spike columns to the left, the spike ends up on the right
154        self.upper_triangular[disappearing_column_index..].rotate_left(1);
155        self.upper_diagonal[pivot_column_index..].rotate_left(1);
156
157        // Rotate the "empty except for one value" pivot row to the bottom
158        let q = RotateToBackPermutation::new(pivot_column_index, self.m());
159        for j in max(pivot_column_index, 1)..m {
160            q.forward_sorted(&mut self.upper_triangular[j - 1]);
161        }
162        let (corner_index, corner_value) = self.upper_triangular.last_mut().unwrap().pop().unwrap();
163        debug_assert_eq!(corner_index, self.m() - 1);
164        *self.upper_diagonal.last_mut().unwrap() = corner_value;
165
166        // `upper_triangular` is diagonal again
167        debug_assert!(self.upper_triangular.iter().all(|column| {
168            column.windows(2).all(|w| w[0].0 < w[1].0)
169        }));
170        debug_assert!(self.upper_triangular.iter().enumerate().all(|(data_j, column)| {
171            let j = data_j + 1;
172            column.last().map_or(true, |&(i, _)| i < j)
173        }));
174        self.updates.push((eta_file, q));
175
176        // return the column, unmodified
177        column
178    }
179
180    fn left_multiply_by_basis_inverse<'a, I: ColumnIterator<'a>>(&self, iter: I) -> Self::ColumnComputationInfo
181    where
182        Self::F: ops::Column<I::F>,
183    {
184        let rhs = iter
185            .map(|(i, v)| (self.row_permutation[i], v.into()))
186            // Also sorts after the row permutation
187            .collect::<BTreeMap<_, _>>();
188        let mut w = self.left_multiply_by_lower_inverse(rhs);
189
190        for (eta, q) in &self.updates {
191            eta.apply_right(&mut w);
192            // Q^-1 c = (c^T Q)^T because Q orthogonal matrix. Here we
193            q.forward_sorted(&mut w);
194        }
195
196        let spike = w.clone();
197
198        let mut column = self.left_multiply_by_upper_inverse(w.into_iter().collect());
199
200        for (_, q) in self.updates.iter().rev() {
201            q.backward_unsorted(&mut column);
202        }
203        self.column_permutation.backward_unsorted(&mut column);
204        column.sort_unstable_by_key(|&(i, _)| i);
205
206        Self::ColumnComputationInfo {
207            column: SparseVector::new(column, self.m()),
208            spike,
209        }
210    }
211
212    fn right_multiply_by_basis_inverse<'a, I: ColumnIterator<'a>>(
213        &self, row: I,
214    ) -> SparseVector<Self::F, Self::F>
215    where
216        Self::F: ops::Column<I::F>,
217    {
218        let mut lhs = row
219            .map(|(i, v)| (self.column_permutation[i], v.into()))
220            .collect::<Vec<_>>();
221
222        for (_, q) in &self.updates {
223            q.forward_unsorted(&mut lhs);
224        }
225
226        let mut lhs = self.right_multiply_by_upper_inverse(lhs.into_iter().collect());
227
228        for (eta, q) in self.updates.iter().rev() {
229            q.backward_sorted(&mut lhs);
230            eta.apply_left(&mut lhs);
231        }
232
233        let mut lhs = self.right_multiply_by_lower_inverse(lhs.into_iter().collect());
234        self.row_permutation.backward_sorted(&mut lhs);
235
236        SparseVector::new(lhs, self.m())
237    }
238
239    fn generate_element<'a, I: ColumnIterator<'a>>(
240        &'a self, i: usize, original_column: I,
241    ) -> Option<Self::F>
242    where
243        Self::F: ops::Column<I::F>,
244    {
245        // TODO(PERFORMANCE): Compute a single value only
246        self.left_multiply_by_basis_inverse(original_column).into_column().get(i).cloned()
247    }
248
249    fn should_refactor(&self) -> bool {
250        // TODO(ENHANCEMENT): What would be a good decision rule?
251        self.updates.len() > 30
252    }
253
254    fn basis_inverse_row(&self, mut row: usize) -> SparseVector<Self::F, Self::F> {
255        self.column_permutation.forward_ref(&mut row);
256        for (_, q) in &self.updates {
257            q.forward_ref(&mut row);
258        }
259
260        let initial_rhs = iter::once((row, Self::F::one())).collect();
261        let mut w = self.right_multiply_by_upper_inverse(initial_rhs);
262
263        for (eta, q) in self.updates.iter().rev() {
264            q.backward_sorted(&mut w);
265            eta.apply_left(&mut w);
266        }
267
268        let mut tuples = self.right_multiply_by_lower_inverse(w.into_iter().collect());
269        self.row_permutation.backward_sorted(&mut tuples);
270
271        SparseVector::new(tuples, self.m())
272    }
273
274    fn m(&self) -> usize {
275        self.row_permutation.len()
276        // == self.column_permutation.len()
277        // == self.upper_triangular.len()
278        // == self.lower_triangular.len() + 1
279    }
280}
281
282impl<F> LUDecomposition<F>
283where
284    F: ops::Field + ops::FieldHR,
285{
286    fn left_multiply_by_lower_inverse(&self, mut rhs: BTreeMap<usize, F>) -> Vec<(usize, F)> {
287        let mut result = Vec::new();
288
289        while let Some((row, rhs_value)) = rhs.pop_first() {
290            let column = row;
291
292            let result_value = rhs_value; // Implicit 1 on the diagonal.
293
294            if column != self.m() - 1 {
295                // Introduce new non zeros
296                for &(i, ref value) in &self.lower_triangular[column] {
297                    insert_or_shift_maybe_remove(i, &result_value * value, &mut rhs);
298                }
299            }
300
301            result.push((row, result_value));
302        }
303
304        result
305    }
306
307    fn left_multiply_by_upper_inverse(&self, mut rhs: BTreeMap<usize, F>) -> Vec<(usize, F)> {
308        let mut result = Vec::new();
309
310        while let Some((row, rhs_value)) = rhs.pop_last() {
311            let column = row;
312            let result_value = rhs_value / &self.upper_diagonal[column];
313            self.update_rhs(column, &result_value, &mut rhs);
314            result.push((row, result_value));
315        }
316
317        result.reverse();
318        debug_assert!(result.is_sorted_by_key(|&(i, _)| i));
319
320        result
321    }
322
323    fn left_multiply_by_upper_inverse_row(&self, mut rhs: BTreeMap<usize, F>, target_row: usize) -> Option<F> {
324        while let Some((row, rhs_value)) = rhs.pop_last() {
325            let column = row;
326            match row.cmp(&target_row) {
327                Ordering::Less => return None,
328                Ordering::Equal => return Some(rhs_value / &self.upper_diagonal[column]),
329                Ordering::Greater => {
330                    let result_value = rhs_value / &self.upper_diagonal[column];
331                    self.update_rhs(column, &result_value, &mut rhs);
332                },
333            }
334        }
335
336        None
337    }
338
339    fn update_rhs(&self, column: usize, result_value: &F, rhs: &mut BTreeMap<usize, F>) {
340        if column > 0 {
341            for &(row, ref value) in &self.upper_triangular[column - 1] {
342                insert_or_shift_maybe_remove(row, result_value * value, rhs);
343            }
344        }
345    }
346
347    fn right_multiply_by_lower_inverse(&self, mut rhs: BTreeMap<usize, F>) -> Vec<(usize, F)> {
348        let mut result = Vec::new();
349
350        while let Some((column, rhs_value)) = rhs.pop_last() {
351            let row = column;
352            let result_value = rhs_value;
353
354            // Introduce new nonzeros, by rows
355            for j in 0..column {
356                // TODO(PERFORMANCE): Avoid scanning all columns for row values
357                let has_row = self.lower_triangular[j].binary_search_by_key(&row, |&(i, _)| i);
358                if let Ok(data_index) = has_row {
359                    let value = &self.lower_triangular[j][data_index].1;
360                    insert_or_shift_maybe_remove(j, &result_value * value, &mut rhs);
361                }
362            }
363
364            result.push((row, result_value));
365        }
366
367        result.reverse();
368        debug_assert!(result.is_sorted_by_key(|&(i, _)| i));
369
370        result
371    }
372
373    fn right_multiply_by_upper_inverse(&self, mut rhs: BTreeMap<usize, F>) -> Vec<(usize, F)> {
374        let mut result = Vec::new();
375
376        while let Some((column, rhs_value)) = rhs.pop_first() {
377            let row = column;
378            let result_value = rhs_value / &self.upper_diagonal[column];
379
380            // Introduce new nonzeros, by rows
381            for j in (column + 1)..self.m() {
382                let column = &self.upper_triangular[j - 1];
383                // TODO(PERFORMANCE): Avoid scanning all columns for row values
384                let has_row = column.binary_search_by_key(&row, |&(i, _)| i);
385                if let Ok(data_index) = has_row {
386                    let value = &column[data_index].1;
387                    insert_or_shift_maybe_remove(j, &result_value * value, &mut rhs);
388                }
389            }
390
391            result.push((column, result_value));
392        }
393
394        debug_assert!(result.windows(2).all(|w| w[0].0 < w[1].0));
395
396        result
397    }
398}
399
400fn insert_or_shift_maybe_remove<F>(index: usize, change: F, rhs: &mut BTreeMap<usize, F>)
401where
402    F: ops::Field + ops::FieldHR + Zero,
403{
404    match rhs.get_mut(&index) {
405        None => {
406            rhs.insert(index, -change);
407        }
408        Some(existing) => {
409            *existing -= change;
410            if existing.is_zero() {
411                rhs.remove(&index);
412            }
413        }
414    }
415}
416
417/// The generated column and the spike that was generated in the process wrapped together.
418#[derive(Debug)]
419pub struct ColumnAndSpike<F> {
420    column: SparseVector<F, F>,
421    spike: Vec<(usize, F)>,
422}
423
424impl<F: SparseElement<F>> ColumnComputationInfo<F> for ColumnAndSpike<F> {
425    fn column(&self) -> &SparseVector<F, F> {
426        &self.column
427    }
428
429    fn into_column(self) -> SparseVector<F, F> {
430        self.column
431    }
432}
433
434impl<F> Display for LUDecomposition<F>
435where
436    F: ops::Field + ops::FieldHR,
437{
438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439        let width = 10;
440        let column_width = 3;
441
442        writeln!(f, "Lower:")?;
443        write!(f, "{:>width$} |", "", width = column_width)?;
444        for j in 0..self.m() {
445            write!(f, "{0:^width$}", j, width = width)?;
446        }
447        writeln!(f)?;
448        let total_width = column_width + 1 + 1 + self.m() * width;
449        writeln!(f, "{}", "-".repeat(total_width))?;
450
451        for i in 0..self.m() {
452            write!(f, "{0:>width$} |", i, width = column_width)?;
453            for j in 0..self.m() {
454                let value = match j.cmp(&i) {
455                    Ordering::Greater => "".to_string(),
456                    Ordering::Equal => "1".to_string(),
457                    Ordering::Less => {
458                        match self.lower_triangular[j].binary_search_by_key(&i, |&(i, _)| i) {
459                            Ok(index) => self.lower_triangular[j][index].1.to_string(),
460                            Err(_) => "0".to_string(),
461                        }
462                    }
463                };
464                write!(f, "{0:^width$}", value, width = width)?;
465            }
466            writeln!(f)?;
467        }
468        writeln!(f)?;
469
470        writeln!(f, "Upper:")?;
471        write!(f, "{:>width$} |", "", width = column_width)?;
472        for j in 0..self.m() {
473            write!(f, "{0:^width$}", j, width = width)?;
474        }
475        writeln!(f)?;
476        writeln!(f, "{}", "-".repeat(total_width))?;
477
478        for i in 0..self.m() {
479            write!(f, "{0:>width$} |", i, width = column_width)?;
480            for j in 0..self.m() {
481                let value = match j.cmp(&i) {
482                    Ordering::Less => "".to_string(),
483                    Ordering::Equal => self.upper_diagonal[j].to_string(),
484                    Ordering::Greater => {
485                        let column = &self.upper_triangular[j - 1];
486                        match column.binary_search_by_key(&i, |&(i, _)| i) {
487                            Ok(index) => column[index].1.to_string(),
488                            Err(_) => "0".to_string(),
489                        }
490                    },
491                };
492                write!(f, "{0:^width$}", value, width = width)?;
493            }
494            writeln!(f)?;
495        }
496        writeln!(f)?;
497
498        writeln!(f, "Row permutation:")?;
499        writeln!(f, "{:?}", self.row_permutation)?;
500        writeln!(f, "Column permutation:")?;
501        writeln!(f, "{:?}", self.column_permutation)?;
502
503        writeln!(f, "Updates:")?;
504        for (i, (eta, t)) in self.updates.iter().enumerate() {
505            writeln!(f, "Update {}: ", i)?;
506            writeln!(f, "R: {:?}", eta)?;
507            writeln!(f, "pivot index: {}", t)?;
508        }
509        writeln!(f)
510    }
511}
512
513#[cfg(test)]
514mod test {
515    use std::collections::BTreeMap;
516
517    use relp_num::{R64, RB};
518    use relp_num::{Rational64, RationalBig};
519
520    use crate::algorithm::two_phase::matrix_provider::column::Column as ColumnTrait;
521    use crate::algorithm::two_phase::matrix_provider::column::identity::IdentityColumn;
522    use crate::algorithm::two_phase::matrix_provider::matrix_data;
523    use crate::algorithm::two_phase::matrix_provider::matrix_data::Column;
524    use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::BasisInverse;
525    use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::ColumnAndSpike;
526    use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::LUDecomposition;
527    use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::permutation::FullPermutation;
528    use crate::algorithm::two_phase::tableau::inverse_maintenance::ColumnComputationInfo;
529    use crate::data::linear_algebra::vector::{SparseVector, Vector};
530
531    mod matmul {
532        use crate::algorithm::im_ops::Field;
533
534        use super::*;
535
536        #[test]
537        fn identity_empty() {
538            let identity = LUDecomposition::<RationalBig>::identity(2);
539
540            let column = BTreeMap::new();
541            let result = identity.left_multiply_by_upper_inverse(column);
542            assert!(result.is_empty());
543            let column = BTreeMap::new();
544            let result = identity.right_multiply_by_upper_inverse(column);
545            assert!(result.is_empty());
546            let column = BTreeMap::new();
547            let result = identity.left_multiply_by_lower_inverse(column);
548            assert!(result.is_empty());
549            let column = BTreeMap::new();
550            let result = identity.right_multiply_by_lower_inverse(column);
551            assert!(result.is_empty());
552        }
553
554        #[test]
555        fn identity_single() {
556            let identity = LUDecomposition::<RationalBig>::identity(2);
557
558            let column = vec![(0, RB!(1))];
559            let result = identity.left_multiply_by_upper_inverse(column.clone().into_iter().collect());
560            assert_eq!(result, column);
561            let column = vec![(0, RB!(1))];
562            let result = identity.right_multiply_by_upper_inverse(column.clone().into_iter().collect());
563            assert_eq!(result, column);
564            let column = vec![(0, RB!(1))];
565            let result = identity.left_multiply_by_lower_inverse(column.clone().into_iter().collect());
566            assert_eq!(result, column);
567            let column = vec![(0, RB!(1))];
568            let result = identity.right_multiply_by_lower_inverse(column.clone().into_iter().collect());
569            assert_eq!(result, column);
570
571            let column = vec![(1, RB!(1))];
572            let result = identity.left_multiply_by_upper_inverse(column.clone().into_iter().collect());
573            assert_eq!(result, column);
574            let column = vec![(1, RB!(1))];
575            let result = identity.right_multiply_by_upper_inverse(column.clone().into_iter().collect());
576            assert_eq!(result, column);
577            let column = vec![(1, RB!(1))];
578            let result = identity.left_multiply_by_lower_inverse(column.clone().into_iter().collect());
579            assert_eq!(result, column);
580            let column = vec![(1, RB!(1))];
581            let result = identity.right_multiply_by_lower_inverse(column.clone().into_iter().collect());
582            assert_eq!(result, column);
583        }
584
585        #[test]
586        fn identity_double() {
587            let identity = LUDecomposition::<RationalBig>::identity(2);
588
589            let column = vec![(0, RB!(1)), (1, RB!(1))];
590            let result = identity.left_multiply_by_upper_inverse(column.clone().into_iter().collect());
591            assert_eq!(result, column);
592            let column = vec![(0, RB!(1)), (1, RB!(1))];
593            let result = identity.right_multiply_by_upper_inverse(column.clone().into_iter().collect());
594            assert_eq!(result, column);
595            let column = vec![(0, RB!(1)), (1, RB!(1))];
596            let result = identity.left_multiply_by_lower_inverse(column.clone().into_iter().collect());
597            assert_eq!(result, column);
598            let column = vec![(0, RB!(1)), (1, RB!(1))];
599            let result = identity.right_multiply_by_lower_inverse(column.clone().into_iter().collect());
600            assert_eq!(result, column);
601        }
602
603        #[test]
604        fn offdiagonal_empty() {
605            let offdiag = LUDecomposition {
606                row_permutation: FullPermutation::identity(2),
607                column_permutation: FullPermutation::identity(2),
608                lower_triangular: vec![vec![(1, RB!(1))]],
609                upper_triangular: vec![vec![]],
610                upper_diagonal: vec![RB!(1), RB!(1)],
611                updates: vec![],
612            };
613
614            let column: Column<Rational64> = Column::Sparse {
615                constraint_values: vec![],
616                slack: None,
617            };
618            let result = offdiag.left_multiply_by_basis_inverse(column.iter());
619            assert_eq!(result.column, SparseVector::new(vec![], 2));
620        }
621
622        #[test]
623        fn offdiagonal_single() {
624            let offdiag = LUDecomposition {
625                row_permutation: FullPermutation::identity(2),
626                column_permutation: FullPermutation::identity(2),
627                lower_triangular: vec![vec![(1, RB!(1))]],
628                upper_triangular: vec![vec![]],
629                upper_diagonal: vec![RB!(1), RB!(1)],
630                updates: vec![],
631            };
632
633            let column = Column::Sparse {
634                constraint_values: vec![(0, R64!(1))],
635                slack: None,
636            };
637            let result = offdiag.left_multiply_by_basis_inverse(column.iter());
638            assert_eq!(result.column, SparseVector::new(vec![(0, RB!(1)), (1, RB!(-1))], 2));
639
640            let column = Column::Sparse {
641                constraint_values: vec![(1, R64!(1))],
642                slack: None,
643            };
644            let result = offdiag.left_multiply_by_basis_inverse(column.iter());
645            assert_eq!(result.column, SparseVector::new(vec![(1, RB!(1))], 2));
646        }
647
648        #[test]
649        fn dense() {
650            // [
651            //  [1, 2],
652            //  [3, 4],
653            // ]
654            //
655            // Inverse:
656            // [
657            //  [-2, 1],
658            //  [1.5, -0.5],
659            // ]
660            let dense = LUDecomposition {
661                row_permutation: FullPermutation::new(vec![1, 0]),
662                column_permutation: FullPermutation::new(vec![0, 1]),
663                lower_triangular: vec![vec![(1, RB!(1, 3))]],
664                upper_triangular: vec![vec![(0, RB!(4))]],
665                upper_diagonal: vec![RB!(3), RB!(2, 3)],
666                updates: vec![],
667            };
668
669            assert_eq!(
670                dense.left_multiply_by_basis_inverse(IdentityColumn::new(0).iter()).into_column(),
671                SparseVector::new(vec![(0, RB!(-2)), (1, RB!(3, 2))], 2),
672            );
673            assert_eq!(
674                dense.left_multiply_by_basis_inverse(IdentityColumn::new(1).iter()).into_column(),
675                SparseVector::new(vec![(0, RB!(1)), (1, RB!(-1, 2))], 2),
676            );
677            assert_eq!(
678                dense.right_multiply_by_basis_inverse(IdentityColumn::new(0).iter()).into_column(),
679                SparseVector::new(vec![(0, RB!(-2)), (1, RB!(1))], 2),
680            );
681            assert_eq!(
682                dense.right_multiply_by_basis_inverse(IdentityColumn::new(1).iter()).into_column(),
683                SparseVector::new(vec![(0, RB!(3, 2)), (1, RB!(-1, 2))], 2),
684            );
685        }
686    }
687
688    mod change_basis {
689        use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::eta_file::EtaFile;
690        use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::permutation::RotateToBackPermutation;
691
692        use super::*;
693
694        /// Spike is the column which is already there.
695        #[test]
696        fn no_change() {
697            let mut initial = LUDecomposition::<RationalBig>::identity(3);
698
699            let spike = vec![(1, RB!(1))];
700            let column_computation_info = ColumnAndSpike {
701                column: SparseVector::new(spike.clone(), 3),
702                spike,
703            };
704            initial.change_basis(1, column_computation_info);
705            let modified = initial;
706
707            let mut expected = LUDecomposition::<RationalBig>::identity(3);
708            expected.updates.push((
709                EtaFile::new(vec![], 1, 3),
710                RotateToBackPermutation::new(1, 3),
711            ));
712            assert_eq!(modified, expected);
713        }
714
715        #[test]
716        fn from_identity_2() {
717            let mut identity = LUDecomposition::<RationalBig>::identity(2);
718
719            let spike = vec![(0, RB!(1)), (1, RB!(1))];
720            let column_computation_info = ColumnAndSpike {
721                column: SparseVector::new(spike.clone(), 2),
722                spike,
723            };
724            identity.change_basis(0, column_computation_info);
725            let modified = identity;
726
727            let expected = LUDecomposition {
728                row_permutation: FullPermutation::identity(2),
729                column_permutation: FullPermutation::identity(2),
730                lower_triangular: vec![vec![]],
731                upper_triangular: vec![vec![(0, RB!(1))]],
732                upper_diagonal: vec![RB!(1), RB!(1)],
733                updates: vec![(
734                    EtaFile::new(vec![], 0, 2),
735                    RotateToBackPermutation::new(0, 2),
736                )],
737            };
738            assert_eq!(modified, expected);
739        }
740
741        /// Doesn't require any `r`, permutations only are sufficient
742        #[test]
743        fn from_5x5_identity_no_r() {
744            let m = 5;
745            let mut initial = LUDecomposition::<RationalBig>::identity(5);
746
747            let spike = vec![(0, RB!(2)), (1, RB!(3)), (2, RB!(5)), (3, RB!(7))];
748            let column_computation_info = ColumnAndSpike {
749                column: SparseVector::new(spike.clone(), m),
750                spike,
751            };
752            initial.change_basis(1, column_computation_info);
753            let modified = initial;
754
755            let expected = LUDecomposition {
756                row_permutation: FullPermutation::identity(m),
757                column_permutation: FullPermutation::identity(m),
758                lower_triangular: vec![vec![]; m - 1],
759                upper_triangular: vec![vec![], vec![], vec![], vec![(0, RB!(2)), (1, RB!(5)), (2, RB!(7))]],
760                upper_diagonal: vec![RB!(1), RB!(1), RB!(1), RB!(1), RB!(3)],
761                updates: vec![(
762                    EtaFile::new(vec![], 1, m),
763                    RotateToBackPermutation::new(1, m),
764                )],
765            };
766            assert_eq!(modified, expected);
767        }
768
769        /// Does require an `r`, permutations are not sufficient.
770        #[test]
771        fn from_4x4_identity() {
772            let m = 4;
773            let mut initial = LUDecomposition {
774                row_permutation: FullPermutation::identity(m),
775                column_permutation: FullPermutation::identity(m),
776                lower_triangular: vec![vec![]; m - 1],
777                upper_triangular: vec![vec![], vec![], vec![(1, RB!(5))]],
778                upper_diagonal: vec![RB!(1), RB!(1), RB!(4), RB!(6)],
779                updates: vec![],
780            };
781
782            let spike = vec![(1, RB!(2)), (2, RB!(3)), (3, RB!(4))];
783            let column_computation_info = ColumnAndSpike {
784                // They are the same in this case, row permutation and lower are identity
785                column: SparseVector::new(spike.clone(), m),
786                spike,
787            };
788            initial.change_basis(1, column_computation_info);
789            let modified = initial;
790
791            let expected = LUDecomposition {
792                row_permutation: FullPermutation::identity(m),
793                column_permutation: FullPermutation::identity(m),
794                lower_triangular: vec![vec![]; m - 1],
795                upper_triangular: vec![vec![], vec![], vec![(1, RB!(3)), (2, RB!(4))]],
796                upper_diagonal: vec![RB!(1), RB!(4), RB!(6), RB!(-8, 6)],
797                updates: vec![(
798                    EtaFile::new(vec![(3, RB!(5, 6))], 1, m),
799                    RotateToBackPermutation::new(1, m),
800                )],
801            };
802            assert_eq!(modified, expected);
803
804            // Columns
805            assert_eq!(
806                modified.left_multiply_by_basis_inverse(IdentityColumn::new(0).iter()).into_column(),
807                SparseVector::standard_basis_vector(0, m),
808            );
809            assert_eq!(
810                modified.left_multiply_by_basis_inverse(IdentityColumn::new(1).iter()).into_column(),
811                SparseVector::new(vec![(1, RB!(-3, 4)), (2, RB!(9, 16)), (3, RB!(1, 2))], m),
812            );
813            assert_eq!(
814                modified.left_multiply_by_basis_inverse(IdentityColumn::new(2).iter()).into_column(),
815                SparseVector::new(vec![(2, RB!(1, 4))], m),
816            );
817            assert_eq!(
818                modified.left_multiply_by_basis_inverse(IdentityColumn::new(3).iter()).into_column(),
819                SparseVector::new(vec![(1, RB!(5, 8)), (2, RB!(-15, 32)), (3, RB!(-1, 4))], m),
820            );
821
822            // Rows
823            assert_eq!(
824                modified.basis_inverse_row(0),
825                SparseVector::standard_basis_vector(0, m),
826            );
827            assert_eq!(
828                modified.basis_inverse_row(1),
829                SparseVector::new(vec![(1, RB!(-3, 4)), (3, RB!(5, 8))], m),
830            );
831            assert_eq!(
832                modified.basis_inverse_row(2),
833                SparseVector::new(vec![(1, RB!(9, 16)), (2, RB!(1, 4)), (3, RB!(-15, 32))], m),
834            );
835            assert_eq!(
836                modified.basis_inverse_row(3),
837                SparseVector::new(vec![(1, RB!(1, 2)), (3, RB!(-1, 4))], m),
838            );
839        }
840
841        /// From "A review of the LU update in the simplex algorithm" by Joseph M. Elble and
842        /// Nikolaes V. Sahinidis, Int. J. Mathematics in Operational Research, Vol. 4, No. 4, 2012.
843        #[test]
844        fn from_5x5_identity() {
845            let m = 5;
846            let mut initial = LUDecomposition {
847                row_permutation: FullPermutation::identity(m),
848                column_permutation: FullPermutation::identity(m),
849                lower_triangular: vec![vec![]; m - 1],
850                upper_triangular: vec![
851                    vec![(0, RB!(12))],
852                    vec![(0, RB!(13)), (1, RB!(23))],
853                    vec![(0, RB!(14)), (1, RB!(24)), (2, RB!(34))],
854                    vec![(0, RB!(15)), (1, RB!(25)), (2, RB!(35)), (3, RB!(45))],
855                ],
856                upper_diagonal: vec![RB!(11), RB!(22), RB!(33), RB!(44), RB!(55)],
857                updates: vec![],
858            };
859
860            let spike = vec![(0, RB!(12)), (1, RB!(22)), (2, RB!(32)), (3, RB!(42))];
861            let column_computation_info = ColumnAndSpike {
862                column: SparseVector::new(spike.clone(), m),
863                spike,
864            };
865            initial.change_basis(1, column_computation_info);
866            let modified = initial;
867
868            let expected = LUDecomposition {
869                row_permutation: FullPermutation::identity(m),
870                column_permutation: FullPermutation::identity(m),
871                lower_triangular: vec![vec![]; m - 1],
872                upper_triangular: vec![
873                    vec![(0, RB!(13))],
874                    vec![(0, RB!(14)), (1, RB!(34))],
875                    vec![(0, RB!(15)), (1, RB!(35)), (2, RB!(45))],
876                    vec![(0, RB!(12)), (1, RB!(32)), (2, RB!(42))],
877                ],
878                upper_diagonal: vec![RB!(11), RB!(33), RB!(44), RB!(55), RB!(-215, 363)],
879                updates: vec![(
880                    EtaFile::new(vec![
881                            (2, RB!(23, 33)),
882                            (3, RB!(24 * 33 - 34 * 23, 33 * 44)),
883                            (4, RB!(43, 7986)),
884                        ], 1, m),
885                    RotateToBackPermutation::new(1, m),
886                )],
887            };
888            assert_eq!(modified, expected);
889
890            // Columns
891            assert_eq!(
892                modified.left_multiply_by_basis_inverse(IdentityColumn::new(0).iter()).into_column(),
893                SparseVector::new(vec![(0, RB!(1, 11))], m),
894            );
895            assert_eq!(
896                modified.left_multiply_by_basis_inverse(IdentityColumn::new(1).iter()).into_column(),
897                SparseVector::new(vec![(0, RB!(-2, 11)), (1, RB!(-363, 215)), (2, RB!(-1, 43)), (3, RB!(693, 430))], m),
898            );
899            assert_eq!(
900                modified.left_multiply_by_basis_inverse(IdentityColumn::new(2).iter()).into_column(),
901                SparseVector::new(vec![(0, RB!(1, 11)), (1, RB!(253, 215)), (2, RB!(2, 43)), (3, RB!(-483, 430))], m),
902            );
903            assert_eq!(
904                modified.left_multiply_by_basis_inverse(IdentityColumn::new(3).iter()).into_column(),
905                SparseVector::new(vec![(1, RB!(1, 86)), (2, RB!(-1, 43)), (3, RB!(1, 86))], m),
906            );
907            assert_eq!(
908                modified.left_multiply_by_basis_inverse(IdentityColumn::new(4).iter()).into_column(),
909                SparseVector::new(vec![(1, RB!(1, 110)), (3, RB!(-3, 110)), (4, RB!(1, 55))], m),
910            );
911            // Sum of two
912            let iter = matrix_data::Column::TwoSlack((0, RB!(1)), (1, RB!(1)));
913            assert_eq!(
914                modified.left_multiply_by_basis_inverse(iter.iter()).into_column(),
915                SparseVector::new(vec![(0, RB!(-1, 11)), (1, RB!(-363, 215)), (2, RB!(-1, 43)), (3, RB!(693, 430))], m),
916            );
917
918            // Rows
919            assert_eq!(
920                modified.basis_inverse_row(0),
921                SparseVector::new(vec![(0, RB!(1, 11)), (1, RB!(-2, 11)), (2, RB!(1, 11))], m),
922            );
923            assert_eq!(
924                modified.basis_inverse_row(1),
925                SparseVector::new(vec![(1, RB!(-363, 215)), (2, RB!(253, 215)), (3, RB!(1, 86)), (4, RB!(1, 110))], m),
926            );
927            assert_eq!(
928                modified.basis_inverse_row(2),
929                SparseVector::new(vec![(1, RB!(-1, 43)), (2, RB!(2, 43)), (3, RB!(-1, 43))], m),
930            );
931            assert_eq!(
932                modified.basis_inverse_row(3),
933                SparseVector::new(vec![(1, RB!(693, 430)), (2, RB!(-483, 430)), (3, RB!(1, 86)), (4, RB!(-3, 110))], m),
934            );
935            assert_eq!(
936                modified.basis_inverse_row(4),
937                SparseVector::new(vec![(4, RB!(1, 55))], m),
938            );
939        }
940    }
941}