1use 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#[derive(Eq, PartialEq, Clone, Debug)]
36pub struct LUDecomposition<F> {
37 row_permutation: FullPermutation,
41 column_permutation: FullPermutation,
45 lower_triangular: Vec<Vec<(usize, F)>>,
49 upper_triangular: Vec<Vec<(usize, F)>>,
53 upper_diagonal: Vec<F>,
55
56 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 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 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 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 0
146 } else {
147 pivot_column_index - 1
149 };
150 self.upper_triangular[disappearing_column_index] = spike;
152
153 self.upper_triangular[disappearing_column_index..].rotate_left(1);
155 self.upper_diagonal[pivot_column_index..].rotate_left(1);
156
157 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 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 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 .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.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 self.left_multiply_by_basis_inverse(original_column).into_column().get(i).cloned()
247 }
248
249 fn should_refactor(&self) -> bool {
250 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 }
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; if column != self.m() - 1 {
295 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 for j in 0..column {
356 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 for j in (column + 1)..self.m() {
382 let column = &self.upper_triangular[j - 1];
383 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#[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 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 #[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 #[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 #[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 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 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 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 #[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 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 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 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}