1use std::sync::{Arc, Mutex, mpsc};
4
5use algebra::{
6 Algebra, MuAlgebra,
7 module::{
8 Module, MuFreeModule,
9 homomorphism::{ModuleHomomorphism, MuFreeModuleHomomorphism},
10 },
11};
12use anyhow::Context;
13use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
14use dashmap::DashMap;
15use fp::{
16 matrix::{AugmentedMatrix, QuasiInverse, Subspace},
17 vector::{FpSlice, FpSliceMut, FpVector},
18};
19use itertools::Itertools;
20use once::{OnceBiVec, OnceVec};
21use sseq::coordinates::{Bidegree, BidegreeGenerator};
22
23use crate::{
24 chain_complex::{AugmentedChainComplex, ChainComplex},
25 save::{SaveDirectory, SaveKind},
26 utils::parallel::ParallelGuard,
27};
28
29struct SenderData {
33 b: Bidegree,
34 new: bool,
36 retry: bool,
38 sender: mpsc::Sender<Self>,
43}
44
45impl SenderData {
46 fn send(b: Bidegree, new: bool, sender: mpsc::Sender<Self>) {
47 sender
48 .send(Self {
49 b,
50 new,
51 retry: false,
52 sender: sender.clone(),
53 })
54 .unwrap()
55 }
56
57 fn send_retry(b: Bidegree, sender: mpsc::Sender<Self>) {
58 sender
59 .send(Self {
60 b,
61 new: false,
62 retry: true,
63 sender: sender.clone(),
64 })
65 .unwrap()
66 }
67}
68
69const MAX_NEW_GENS: usize = 10;
74
75pub type Resolution<CC> = MuResolution<false, CC>;
76pub type UnstableResolution<CC> = MuResolution<true, CC>;
77
78pub struct MuResolution<const U: bool, CC: ChainComplex>
81where
82 CC::Algebra: MuAlgebra<U>,
83{
84 name: String,
85 lock: Mutex<()>,
86 complex: Arc<CC>,
87 modules: OnceBiVec<Arc<MuFreeModule<U, CC::Algebra>>>,
88 zero_module: Arc<MuFreeModule<U, CC::Algebra>>,
89 chain_maps: OnceBiVec<Arc<MuFreeModuleHomomorphism<U, CC::Module>>>,
90 differentials: OnceVec<Arc<MuFreeModuleHomomorphism<U, MuFreeModule<U, CC::Algebra>>>>,
91
92 kernels: DashMap<Bidegree, Subspace>,
96 save_dir: SaveDirectory,
97
98 pub should_save: bool,
101
102 pub load_quasi_inverse: bool,
113}
114
115impl<const U: bool, CC: ChainComplex> MuResolution<U, CC>
116where
117 CC::Algebra: MuAlgebra<U>,
118{
119 pub fn new(complex: Arc<CC>) -> Self {
120 Self::new_with_save(complex, None).unwrap()
122 }
123
124 pub fn new_with_save(
125 complex: Arc<CC>,
126 save_dir: impl Into<SaveDirectory>,
127 ) -> anyhow::Result<Self> {
128 let save_dir = save_dir.into();
129 let algebra = complex.algebra();
130 let min_degree = complex.min_degree();
131 let zero_module = Arc::new(MuFreeModule::new(algebra, "F_{-1}".to_string(), min_degree));
132
133 if let Some(p) = save_dir.write() {
134 for subdir in SaveKind::resolution_data() {
135 subdir.create_dir(p)?;
136 }
137 }
138
139 Ok(Self {
140 name: String::new(),
141 complex,
142 zero_module,
143 should_save: save_dir.is_some(),
144 save_dir,
145 lock: Mutex::new(()),
146
147 chain_maps: OnceBiVec::new(0),
148 modules: OnceBiVec::new(0),
149 differentials: OnceVec::new(),
150 kernels: DashMap::new(),
151 load_quasi_inverse: true,
152 })
153 }
154
155 pub fn set_name(&mut self, name: String) {
156 self.name = name;
157 }
158
159 pub fn name(&self) -> &str {
160 &self.name
161 }
162
163 fn add_generators(&self, b: Bidegree, num_new_gens: usize) {
164 let gen_names = (0..num_new_gens)
165 .map(|idx| format!("x_{:#}", BidegreeGenerator::new(b, idx)))
166 .collect();
167 self.module(b.s())
168 .add_generators(b.t(), num_new_gens, Some(gen_names));
169 }
170
171 fn extend_through_degree(&self, max_s: i32) {
175 let min_degree = self.min_degree();
176
177 for i in self.modules.len()..=max_s {
178 self.modules.push(Arc::new(MuFreeModule::new(
179 Arc::clone(&self.algebra()),
180 format!("F{i}"),
181 min_degree,
182 )));
183 self.chain_maps.push(Arc::new(MuFreeModuleHomomorphism::new(
184 Arc::clone(&self.modules[i]),
185 Arc::clone(&self.complex.module(i)),
186 0,
187 )));
188 }
189
190 if self.differentials.is_empty() {
191 self.differentials
192 .push(Arc::new(MuFreeModuleHomomorphism::new(
193 Arc::clone(&self.modules[0]),
194 Arc::clone(&self.zero_module),
195 0,
196 )));
197 }
198
199 for i in self.differentials.len() as i32..=max_s {
200 self.differentials
201 .push(Arc::new(MuFreeModuleHomomorphism::new(
202 Arc::clone(&self.modules[i]),
203 Arc::clone(&self.modules[i - 1]),
204 0,
205 )));
206 }
207 }
208
209 #[tracing::instrument(skip(self), fields(%b))]
214 fn get_kernel(&self, b: Bidegree) -> Subspace {
215 if let Some((_, v)) = self.kernels.remove(&b) {
216 return v;
217 }
218
219 if b.s() == 0 {
220 self.zero_module.extend_by_zero(b.t());
221 }
222
223 let p = self.prime();
224
225 if let Some(dir) = self.save_dir.read()
226 && let Some(mut f) = self.save_file(SaveKind::Kernel, b).open_file(dir.clone())
227 {
228 return Subspace::from_bytes(p, &mut f)
229 .with_context(|| format!("Failed to read kernel at {b}"))
230 .unwrap();
231 }
232
233 let complex = self.target();
234 complex.compute_through_bidegree(b);
235
236 let current_differential = self.differential(b.s());
237 let current_chain_map = self.chain_map(b.s());
238
239 let source = self.module(b.s());
240 let target_cc = complex.module(b.s());
241 let target_res = current_differential.target(); source.compute_basis(b.t());
244 target_res.compute_basis(b.t());
245
246 let source_dimension = source.dimension(b.t());
247 let target_cc_dimension = target_cc.dimension(b.t());
248 let target_res_dimension = target_res.dimension(b.t());
249
250 let mut matrix = AugmentedMatrix::<3>::new(
251 p,
252 source_dimension,
253 [target_cc_dimension, target_res_dimension, source_dimension],
254 );
255
256 {
257 let _guard = ParallelGuard::new();
258 current_chain_map.get_matrix(matrix.segment(0, 0), b.t());
259 current_differential.get_matrix(matrix.segment(1, 1), b.t());
260 }
261 matrix.segment(2, 2).add_identity();
262 matrix.row_reduce();
263
264 let kernel = matrix.compute_kernel();
265
266 if self.should_save
267 && let Some(dir) = self.save_dir.write()
268 {
269 let mut f = self
270 .save_file(SaveKind::Kernel, b)
271 .create_file(dir.clone(), true);
272 kernel
273 .to_bytes(&mut f)
274 .with_context(|| format!("Failed to write kernel at {b}"))
275 .unwrap();
276 }
277 kernel
278 }
279
280 #[tracing::instrument(skip(self), fields(%b, num_new_gens, density))]
350 fn step_resolution(&self, b: Bidegree) {
351 if b.s() == 0 {
352 self.zero_module.extend_by_zero(b.t());
353 }
354
355 let p = self.prime();
356
357 let complex = self.target();
365 complex.compute_through_bidegree(b);
366
367 let current_differential = self.differential(b.s());
368 let current_chain_map = self.chain_map(b.s());
369 let complex_cur_differential = complex.differential(b.s());
370
371 match current_differential.next_degree().cmp(&b.t()) {
372 std::cmp::Ordering::Greater => {
373 return;
375 }
376 std::cmp::Ordering::Less => {
377 panic!("We're not ready to compute bidegree {b} yet.");
379 }
380 std::cmp::Ordering::Equal => (),
381 };
382
383 let source = self.module(b.s());
384 let target_cc = complex.module(b.s());
385 let target_res = current_differential.target(); source.compute_basis(b.t());
388
389 let source_dimension = source.dimension(b.t());
395 let target_cc_dimension = target_cc.dimension(b.t());
396 target_res.compute_basis(b.t());
397 let target_res_dimension = target_res.dimension(b.t());
398
399 if let Some(dir) = self.save_dir.read()
400 && let Some(mut f) = self
401 .save_file(SaveKind::Differential, b)
402 .open_file(dir.clone())
403 {
404 let num_new_gens = f.read_u64::<LittleEndian>().unwrap() as usize;
405 let saved_target_res_dimension = f.read_u64::<LittleEndian>().unwrap() as usize;
410 assert_eq!(
411 target_cc_dimension,
412 f.read_u64::<LittleEndian>().unwrap() as usize,
413 "Malformed data: mismatched augmentation target dimension"
414 );
415
416 self.add_generators(b, num_new_gens);
417
418 let mut d_targets = Vec::with_capacity(num_new_gens);
419 let mut a_targets = Vec::with_capacity(num_new_gens);
420
421 for _ in 0..num_new_gens {
422 d_targets
423 .push(FpVector::from_bytes(p, saved_target_res_dimension, &mut f).unwrap());
424 }
425 for _ in 0..num_new_gens {
426 a_targets.push(FpVector::from_bytes(p, target_cc_dimension, &mut f).unwrap());
427 }
428 drop(f);
429 current_differential.add_generators_from_rows(b.t(), d_targets);
430 current_chain_map.add_generators_from_rows(b.t(), a_targets);
431
432 if self.load_quasi_inverse {
434 if let Some(mut f) = self.save_file(SaveKind::ResQi, b).open_file(dir.clone()) {
435 let res_qi = QuasiInverse::from_bytes(p, &mut f).unwrap();
436
437 assert_eq!(
438 res_qi.source_dimension(),
439 source_dimension + num_new_gens,
440 "Malformed data: mismatched source dimension in resolution qi at {b}"
441 );
442
443 current_differential.set_quasi_inverse(b.t(), Some(res_qi));
444 } else {
445 current_differential.set_quasi_inverse(b.t(), None);
446 }
447 } else {
448 current_differential.set_quasi_inverse(b.t(), None);
449 }
450
451 if let Some(mut f) = self
452 .save_file(SaveKind::AugmentationQi, b)
453 .open_file(dir.clone())
454 {
455 let cm_qi = QuasiInverse::from_bytes(p, &mut f).unwrap();
456
457 assert_eq!(
458 cm_qi.target_dimension(),
459 target_cc_dimension,
460 "Malformed data: mismatched augmentation target dimension in qi at {b}"
461 );
462 assert_eq!(
463 cm_qi.source_dimension(),
464 source_dimension + num_new_gens,
465 "Malformed data: mismatched source dimension in augmentation qi at {b}"
466 );
467
468 current_chain_map.set_quasi_inverse(b.t(), Some(cm_qi));
469 } else {
470 current_chain_map.set_quasi_inverse(b.t(), None);
471 }
472
473 current_differential.set_kernel(b.t(), None);
474 current_differential.set_image(b.t(), None);
475
476 current_chain_map.set_kernel(b.t(), None);
477 current_chain_map.set_image(b.t(), None);
478 return;
479 }
480
481 let mut matrix = AugmentedMatrix::<3>::new_with_capacity(
482 p,
483 source_dimension,
484 &[target_cc_dimension, target_res_dimension, source_dimension],
485 source_dimension + MAX_NEW_GENS,
486 MAX_NEW_GENS,
487 );
488 {
491 let _guard = ParallelGuard::new();
492 current_chain_map.get_matrix(matrix.segment(0, 0), b.t());
493 current_differential.get_matrix(matrix.segment(1, 1), b.t());
494 }
495 matrix.segment(2, 2).add_identity();
496
497 matrix.row_reduce();
498
499 if !self.has_computed_bidegree(b + Bidegree::s_t(1, 0)) {
500 let kernel = matrix.compute_kernel();
501 if self.should_save
502 && let Some(dir) = self.save_dir.write()
503 {
504 let mut f = self
505 .save_file(SaveKind::Kernel, b)
506 .create_file(dir.clone(), true);
507
508 kernel
509 .to_bytes(&mut f)
510 .with_context(|| format!("Failed to write kernel at {b}"))
511 .unwrap();
512 }
513
514 self.kernels.insert(b, kernel);
515 }
516
517 let cc_new_gens = matrix.extend_to_surjection(0, target_cc_dimension, MAX_NEW_GENS);
523
524 let mut res_new_gens = Vec::new();
525
526 if b.s() > 0 {
527 if !cc_new_gens.is_empty() {
528 let prev_chain_map = self.chain_map(b.s() - 1);
532 let quasi_inverse = prev_chain_map.quasi_inverse(b.t()).unwrap();
533
534 let dfx_dim = complex_cur_differential.target().dimension(b.t());
535 let mut dfx = FpVector::new(self.prime(), dfx_dim);
536
537 for (i, &column) in cc_new_gens.iter().enumerate() {
538 complex_cur_differential.apply_to_basis_element(
539 dfx.as_slice_mut(),
540 1,
541 b.t(),
542 column,
543 );
544 quasi_inverse.apply(
545 matrix.row_segment_mut(source_dimension + i, 1, 1),
546 1,
547 dfx.as_slice(),
548 );
549 dfx.set_to_zero();
550 }
551 }
552
553 res_new_gens = matrix.inner.extend_image(
560 matrix.start[1],
561 matrix.end[1],
562 &self.get_kernel(b - Bidegree::s_t(1, 0)),
563 MAX_NEW_GENS,
564 );
565 }
566 let num_new_gens = cc_new_gens.len() + res_new_gens.len();
567 self.add_generators(b, num_new_gens);
568
569 let new_rows = source_dimension + num_new_gens;
570
571 current_chain_map.add_generators_from_matrix_rows(
572 b.t(),
573 matrix.segment(0, 0).row_slice(source_dimension, new_rows),
574 );
575 current_differential.add_generators_from_matrix_rows(
576 b.t(),
577 matrix.segment(1, 1).row_slice(source_dimension, new_rows),
578 );
579
580 if num_new_gens > 0 {
581 let columns = matrix.columns();
583 matrix.extend_column_dimension(columns + num_new_gens);
584
585 for i in source_dimension..new_rows {
586 matrix.inner.row_mut(i).set_entry(matrix.start[2] + i, 1);
587 }
588
589 for k in source_dimension..source_dimension + cc_new_gens.len() {
600 for column in matrix.start[1]..matrix.end[1] {
601 let row = matrix.pivots()[column];
602 if row < 0 {
603 continue;
604 }
605 let row = row as usize;
606 unsafe {
607 matrix.row_op(k, row, column, p);
608 }
609 }
610 }
611
612 let first_res_row = source_dimension + cc_new_gens.len();
614 for (source_row, &pivot_col) in res_new_gens.iter().enumerate() {
615 for target_row in 0..first_res_row {
616 unsafe {
617 matrix.row_op(target_row, source_row + first_res_row, pivot_col, p);
618 }
619 }
620 }
621
622 let mut new_gens = cc_new_gens.into_iter().chain(res_new_gens).enumerate();
624 let (mut next_new_row, mut next_new_col) = new_gens.next().unwrap();
625 let mut next_old_row = 0;
626
627 for old_col in 0..matrix.columns() {
628 if old_col == next_new_col {
629 matrix.rotate_down(next_old_row..source_dimension + next_new_row + 1, 1);
630 matrix.pivots_mut()[old_col] = next_old_row as isize;
631 match new_gens.next() {
632 Some((x, y)) => {
633 next_new_row = x;
634 next_new_col = y;
635 }
636 None => {
637 for entry in &mut matrix.pivots_mut()[old_col + 1..] {
638 if *entry >= 0 {
639 *entry += next_new_row as isize + 1;
640 }
641 }
642 break;
643 }
644 }
645 next_old_row += 1;
646 } else if matrix.pivots()[old_col] >= 0 {
647 matrix.pivots_mut()[old_col] += next_new_row as isize;
648 next_old_row += 1;
649 }
650 }
651 }
652 let (cm_qi, res_qi) = matrix.compute_quasi_inverses();
653
654 tracing::Span::current().record("num_new_gens", num_new_gens);
655 tracing::Span::current().record(
656 "density",
657 current_differential.differential_density(b.t()) * 100.0,
658 );
659
660 if self.should_save
661 && let Some(dir) = self.save_dir.write()
662 {
663 let mut f = self
669 .save_file(SaveKind::Differential, b)
670 .create_file(dir.clone(), false);
671
672 res_qi
674 .to_bytes(
675 &mut self
676 .save_file(SaveKind::ResQi, b)
677 .create_file(dir.clone(), true),
678 )
679 .unwrap();
680
681 cm_qi
683 .to_bytes(
684 &mut self
685 .save_file(SaveKind::AugmentationQi, b)
686 .create_file(dir.clone(), true),
687 )
688 .unwrap();
689
690 f.write_u64::<LittleEndian>(num_new_gens as u64).unwrap();
692 f.write_u64::<LittleEndian>(target_res_dimension as u64)
693 .unwrap();
694 f.write_u64::<LittleEndian>(target_cc_dimension as u64)
695 .unwrap();
696
697 for n in 0..num_new_gens {
698 current_differential
699 .output(b.t(), n)
700 .to_bytes(&mut f)
701 .unwrap();
702 }
703 for n in 0..num_new_gens {
704 current_chain_map.output(b.t(), n).to_bytes(&mut f).unwrap();
705 }
706 drop(f);
707
708 if b.s() > 0 {
710 self.save_file(SaveKind::Kernel, b - Bidegree::s_t(1, 0))
711 .delete_file(dir.clone())
712 .unwrap();
713 }
714 }
715
716 if self.load_quasi_inverse {
717 current_differential.set_quasi_inverse(b.t(), Some(res_qi));
718 } else {
719 current_differential.set_quasi_inverse(b.t(), None);
720 }
721
722 current_chain_map.set_quasi_inverse(b.t(), Some(cm_qi));
725 current_chain_map.set_kernel(b.t(), None);
726 current_chain_map.set_image(b.t(), None);
727
728 current_differential.set_kernel(b.t(), None);
729 current_differential.set_image(b.t(), None);
730 }
731
732 pub fn compute_through_bidegree_with_callback(
733 &self,
734 max: Bidegree,
735 mut cb: impl FnMut(Bidegree),
736 ) {
737 let min_degree = self.min_degree();
738 let _lock = self.lock.lock();
739
740 self.target().compute_through_bidegree(max);
741 self.extend_through_degree(max.s());
742 self.algebra().compute_basis(max.t() - min_degree);
743
744 let tracing_span = tracing::Span::current();
745 maybe_rayon::in_place_scope(|scope| {
746 let _tracing_guard = tracing_span.enter();
747
748 let mut progress: Vec<i32> = vec![min_degree - 1; max.s() as usize + 1];
750 progress[0] = min_degree - 2;
753
754 let (sender, receiver) = mpsc::channel();
755 SenderData::send(Bidegree::s_t(0, min_degree - 1), false, sender);
756
757 let f = |b, sender| {
758 if self.has_computed_bidegree(b) {
759 SenderData::send(b, false, sender);
760 } else {
761 let tracing_span = tracing_span.clone();
762 scope.spawn(move |_| {
763 let _tracing_guard = tracing_span.enter();
764 if crate::utils::parallel::is_in_parallel() {
765 SenderData::send_retry(b, sender);
766 return;
767 }
768 self.step_resolution(b);
769 SenderData::send(b, true, sender);
770 });
771 }
772 };
773
774 while let Ok(SenderData {
775 b,
776 new,
777 retry,
778 sender,
779 }) = receiver.recv()
780 {
781 if retry {
782 f(b, sender);
783 continue;
784 }
785 assert!(progress[b.s() as usize] == b.t() - 1);
786 progress[b.s() as usize] = b.t();
787
788 if b.t() < max.t() && (b.s() == 0 || progress[b.s() as usize - 1] > b.t()) {
789 f(b + Bidegree::s_t(0, 1), sender.clone());
791 }
792 if b.s() < max.s() && progress[b.s() as usize + 1] == b.t() - 1 {
793 f(b + Bidegree::s_t(1, 0), sender);
794 }
795 if new {
796 cb(b);
797 }
798 }
799 });
800 }
801
802 #[tracing::instrument(skip(self), fields(self = self.name, %max))]
804 pub fn compute_through_stem(&self, max: Bidegree) {
805 self.compute_through_stem_with_callback(max, |_| ());
806 }
807
808 pub fn compute_through_stem_with_callback(&self, max: Bidegree, mut cb: impl FnMut(Bidegree)) {
809 let min_degree = self.min_degree();
810 let _lock = self.lock.lock();
811
812 self.target().compute_through_bidegree(max);
813 self.extend_through_degree(max.s());
814 self.algebra().compute_basis(max.t() - min_degree);
815
816 let tracing_span = tracing::Span::current();
817 maybe_rayon::in_place_scope(|scope| {
818 let _tracing_guard = tracing_span.enter();
819
820 let mut progress: Vec<i32> = vec![min_degree - 1; max.s() as usize + 1];
822 progress[0] = min_degree - 2;
825
826 let (sender, receiver) = mpsc::channel();
827 SenderData::send(Bidegree::s_t(0, min_degree - 1), false, sender);
828
829 let f = |b, sender| {
830 if self.has_computed_bidegree(b) {
831 SenderData::send(b, false, sender);
832 } else {
833 let tracing_span = tracing_span.clone();
834 scope.spawn(move |_| {
835 let _tracing_guard = tracing_span.enter();
836 if crate::utils::parallel::is_in_parallel() {
837 SenderData::send_retry(b, sender);
838 return;
839 }
840 self.step_resolution(b);
841 SenderData::send(b, true, sender);
842 });
843 }
844 };
845
846 while let Ok(SenderData {
847 b,
848 new,
849 retry,
850 sender,
851 }) = receiver.recv()
852 {
853 if retry {
854 f(b, sender);
855 continue;
856 }
857 assert!(progress[b.s() as usize] == b.t() - 1);
858 progress[b.s() as usize] = b.t();
859
860 let distance = max.n() - b.n() + 1;
862
863 if b.s() < max.s() && progress[b.s() as usize + 1] == b.t() - 1 {
864 f(b + Bidegree::s_t(1, 0), sender.clone());
865 }
866
867 if distance > 1 && (b.s() == 0 || progress[b.s() as usize - 1] > b.t()) {
868 f(b + Bidegree::s_t(0, 1), sender);
870 } else if distance == 1 && b.s() < max.s() {
871 let next_b = b + Bidegree::s_t(0, 1);
873 if !self.has_computed_bidegree(b + Bidegree::s_t(1, 1))
874 && (self.save_dir.is_none()
875 || !self
876 .save_file(SaveKind::Differential, b + Bidegree::s_t(1, 1))
877 .exists(self.save_dir.read().cloned().unwrap()))
878 {
879 scope.spawn(move |_| {
880 self.kernels.insert(next_b, self.get_kernel(next_b));
881 SenderData::send(next_b, false, sender);
882 });
883 } else {
884 SenderData::send(next_b, false, sender);
885 }
886 }
887 if new {
888 cb(b);
889 }
890 }
891 });
892 }
893}
894
895impl<const U: bool, CC: ChainComplex> ChainComplex for MuResolution<U, CC>
896where
897 CC::Algebra: MuAlgebra<U>,
898{
899 type Algebra = CC::Algebra;
900 type Homomorphism = MuFreeModuleHomomorphism<U, MuFreeModule<U, Self::Algebra>>;
901 type Module = MuFreeModule<U, Self::Algebra>;
902
903 fn algebra(&self) -> Arc<Self::Algebra> {
904 self.target().algebra()
905 }
906
907 fn module(&self, s: i32) -> Arc<Self::Module> {
908 Arc::clone(&self.modules[s])
909 }
910
911 fn zero_module(&self) -> Arc<Self::Module> {
912 Arc::clone(&self.zero_module)
913 }
914
915 fn min_degree(&self) -> i32 {
916 self.target().min_degree()
917 }
918
919 fn has_computed_bidegree(&self, b: Bidegree) -> bool {
920 self.differentials.len() > b.s() as usize && self.differential(b.s()).next_degree() > b.t()
921 }
922
923 fn differential(&self, s: i32) -> Arc<Self::Homomorphism> {
924 Arc::clone(&self.differentials[s as usize])
925 }
926
927 #[tracing::instrument(skip(self), fields(self = self.name, %b))]
928 fn compute_through_bidegree(&self, b: Bidegree) {
929 self.compute_through_bidegree_with_callback(b, |_| ())
930 }
931
932 fn next_homological_degree(&self) -> i32 {
933 self.modules.len()
934 }
935
936 fn apply_quasi_inverse<T, S>(&self, results: &mut [T], b: Bidegree, inputs: &[S]) -> bool
937 where
938 for<'a> &'a mut T: Into<FpSliceMut<'a>>,
939 for<'a> &'a S: Into<FpSlice<'a>>,
940 {
941 assert_eq!(results.len(), inputs.len());
942
943 if let Some(qi) = self.differential(b.s()).quasi_inverse(b.t()) {
944 for (input, result) in inputs.iter().zip_eq(results) {
945 qi.apply(result.into(), 1, input.into());
946 }
947 true
948 } else if let Some(dir) = self.save_dir.read() {
949 if let Some(mut f) = self.save_file(SaveKind::ResQi, b).open_file(dir.clone()) {
950 QuasiInverse::stream_quasi_inverse(self.prime(), &mut f, results, inputs).unwrap();
951 true
952 } else {
953 false
954 }
955 } else {
956 false
957 }
958 }
959
960 fn save_dir(&self) -> &SaveDirectory {
961 &self.save_dir
962 }
963}
964
965impl<const U: bool, CC: ChainComplex> AugmentedChainComplex for MuResolution<U, CC>
966where
967 CC::Algebra: MuAlgebra<U>,
968{
969 type ChainMap = MuFreeModuleHomomorphism<U, CC::Module>;
970 type TargetComplex = CC;
971
972 fn target(&self) -> Arc<Self::TargetComplex> {
973 Arc::clone(&self.complex)
974 }
975
976 fn chain_map(&self, s: i32) -> Arc<Self::ChainMap> {
977 Arc::clone(&self.chain_maps[s])
978 }
979}
980
981#[cfg(test)]
982mod tests {
983 use expect_test::expect;
984
985 use super::*;
986 use crate::{chain_complex::FreeChainComplex, utils::construct_standard};
987
988 #[test]
989 fn test_restart_stem() {
990 let res = construct_standard::<false, _, _>("S_2", None).unwrap();
991 res.compute_through_stem(Bidegree::n_s(14, 8));
992 res.compute_through_bidegree(Bidegree::s_t(5, 19));
993
994 expect![[r#"
995 ·
996 · ·
997 · · · ·
998 · · · ·
999 · · · · ·
1000 · · · · · · ·
1001 · · · · · · · · ·
1002 · · · · ·
1003 ·
1004 "#]]
1005 .assert_eq(&res.graded_dimension_string());
1006 }
1007
1008 #[test]
1009 fn test_apply_quasi_inverse() {
1010 let tempdir = tempfile::TempDir::new().unwrap();
1011
1012 let mut res =
1013 construct_standard::<false, _, _>("S_2", Some(tempdir.path().into())).unwrap();
1014 res.load_quasi_inverse = false;
1015
1016 let b = Bidegree::s_t(8, 8);
1017 res.compute_through_bidegree(b);
1018
1019 assert!(res.differential(8).quasi_inverse(8).is_none());
1020
1021 let v = FpVector::new(res.prime(), res.module(7).dimension(8));
1022 let mut w = FpVector::new(res.prime(), res.module(8).dimension(8));
1023
1024 assert!(res.apply_quasi_inverse(&mut [w.as_slice_mut()], b, &[v.as_slice()]));
1025 assert!(w.is_zero());
1026 }
1027}