1use std::{
16 fmt::Display,
17 io,
18 sync::{Arc, Mutex, mpsc},
19};
20
21use algebra::{
22 Algebra, combinatorics,
23 milnor_algebra::{MilnorAlgebra, PPartEntry},
24 module::{
25 FreeModule, GeneratorData, Module, ZeroModule,
26 homomorphism::{FreeModuleHomomorphism, FullModuleHomomorphism, ModuleHomomorphism},
27 },
28};
29use anyhow::anyhow;
30use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
31use fp::{
32 matrix::{AugmentedMatrix, Matrix},
33 prime::{TWO, ValidPrime},
34 vector::{FpSlice, FpSliceMut, FpVector},
35};
36use itertools::Itertools;
37use once::OnceBiVec;
38use sseq::coordinates::{Bidegree, BidegreeGenerator};
39
40use crate::{
41 chain_complex::{AugmentedChainComplex, ChainComplex, FiniteChainComplex, FreeChainComplex},
42 save::{SaveDirectory, SaveKind},
43 utils::{LogWriter, parallel::ParallelGuard},
44};
45
46struct SenderData {
48 b: Bidegree,
49 retry: bool,
50 sender: mpsc::Sender<Self>,
51}
52
53impl SenderData {
54 pub(crate) fn send(b: Bidegree, sender: mpsc::Sender<Self>) {
55 sender
56 .send(Self {
57 b,
58 retry: false,
59 sender: sender.clone(),
60 })
61 .unwrap()
62 }
63
64 pub(crate) fn send_retry(b: Bidegree, sender: mpsc::Sender<Self>) {
65 sender
66 .send(Self {
67 b,
68 retry: true,
69 sender: sender.clone(),
70 })
71 .unwrap()
72 }
73}
74
75const MAX_NEW_GENS: usize = 10;
76
77#[derive(Clone)]
84struct MilnorSubalgebra {
85 profile: Vec<u8>,
86}
87
88impl MilnorSubalgebra {
89 #[allow(dead_code)]
91 const INFINITY: u8 = (std::mem::size_of::<PPartEntry>() * 4 - 1) as u8;
92
93 fn new(profile: Vec<u8>) -> Self {
94 Self { profile }
95 }
96
97 fn zero_algebra() -> Self {
99 Self { profile: vec![] }
100 }
101
102 fn has_signature(&self, ppart: &[PPartEntry], signature: &[PPartEntry]) -> bool {
104 for (i, (&profile, &signature)) in self.profile.iter().zip(signature).enumerate() {
105 let ppart = ppart.get(i).copied().unwrap_or(0);
106 if ppart & ((1 << profile) - 1) != signature {
107 return false;
108 }
109 }
110 true
111 }
112
113 fn zero_signature(&self) -> Vec<PPartEntry> {
114 vec![0; self.profile.len()]
115 }
116
117 fn signature_mask<'a>(
121 &'a self,
122 algebra: &'a MilnorAlgebra,
123 module: &'a FreeModule<MilnorAlgebra>,
124 degree: i32,
125 signature: &'a [PPartEntry],
126 ) -> impl Iterator<Item = usize> + 'a {
127 module.iter_gen_offsets([degree]).flat_map(
128 move |GeneratorData {
129 gen_deg,
130 start: [offset],
131 end: _,
132 }| {
133 algebra
134 .ppart_table(degree - gen_deg)
135 .iter()
136 .enumerate()
137 .filter_map(move |(n, op)| {
138 if self.has_signature(op, signature) {
139 Some(offset + n)
140 } else {
141 None
142 }
143 })
144 },
145 )
146 }
147
148 fn signature_matrix(
151 &self,
152 hom: &FreeModuleHomomorphism<FreeModule<MilnorAlgebra>>,
153 degree: i32,
154 signature: &[PPartEntry],
155 ) -> Matrix {
156 let p = hom.prime();
157 let source = hom.source();
158 let target = hom.target();
159 let algebra = target.algebra();
160 let target_degree = degree - hom.degree_shift();
161
162 let target_mask: Vec<usize> = self
163 .signature_mask(&algebra, &target, degree - hom.degree_shift(), signature)
164 .collect();
165
166 let source_mask: Vec<usize> = self
167 .signature_mask(&algebra, &source, degree, signature)
168 .collect();
169
170 let mut scratch = FpVector::new(p, target.dimension(target_degree));
171 let mut result = Matrix::new(p, source_mask.len(), target_mask.len());
172
173 for (mut row, &masked_index) in std::iter::zip(result.iter_mut(), &source_mask) {
174 scratch.set_to_zero();
175 hom.apply_to_basis_element(scratch.as_slice_mut(), 1, degree, masked_index);
176
177 row.add_masked(scratch.as_slice(), 1, &target_mask);
178 }
179 result
180 }
181
182 fn iter_signatures(&self, degree: i32) -> impl Iterator<Item = Vec<PPartEntry>> + '_ {
185 SignatureIterator::new(self, degree)
186 }
187
188 fn top_degree(&self) -> i32 {
189 self.profile
190 .iter()
191 .map(|&entry| (1 << entry) - 1)
192 .enumerate()
193 .map(|(idx, entry)| ((1 << (idx + 1)) - 1) * entry)
194 .sum()
195 }
196
197 fn optimal_for(b: Bidegree) -> Self {
198 let b_is_in_vanishing_region = |subalgebra: &Self| {
199 let coeff = (1 << subalgebra.profile.len()) - 1;
200 b.t() >= coeff * (b.s() + 1) + subalgebra.top_degree()
201 };
202 SubalgebraIterator::new()
203 .take_while(b_is_in_vanishing_region)
204 .last()
205 .unwrap_or(Self::zero_algebra())
206 }
207
208 fn to_bytes(&self, buffer: &mut impl io::Write) -> io::Result<()> {
209 buffer.write_u64::<LittleEndian>(self.profile.len() as u64)?;
210 buffer.write_all(&self.profile)?;
211
212 let len = self.profile.len();
213 let zeros = [0; 8];
214 let padding = len - ((len / 8) * 8);
215 buffer.write_all(&zeros[0..padding])
216 }
217
218 fn from_bytes(data: &mut impl io::Read) -> io::Result<Self> {
219 let len = data.read_u64::<LittleEndian>()? as usize;
220 let mut profile = vec![0; len];
221
222 data.read_exact(&mut profile)?;
223
224 let padding = len - ((len / 8) * 8);
225 if padding > 0 {
226 let mut buf: [u8; 8] = [0; 8];
227 data.read_exact(&mut buf[0..padding])?;
228 assert_eq!(buf, [0; 8]);
229 }
230 Ok(Self { profile })
231 }
232
233 fn signature_to_bytes(signature: &[PPartEntry], buffer: &mut impl io::Write) -> io::Result<()> {
234 if cfg!(target_endian = "little") && std::mem::size_of::<PPartEntry>() == 2 {
235 unsafe {
236 let buf: &[u8] = std::slice::from_raw_parts(
237 signature.as_ptr() as *const u8,
238 signature.len() * 2,
239 );
240 buffer.write_all(buf).unwrap();
241 }
242 } else {
243 for &entry in signature {
244 buffer.write_u16::<LittleEndian>(entry as u16)?;
245 }
246 }
247
248 let len = signature.len();
249 let zeros = [0; 8];
250 let padding = len - ((len / 4) * 4);
251
252 if padding > 0 {
253 buffer.write_all(&zeros[0..padding * 2])?;
254 }
255 Ok(())
256 }
257
258 fn signature_from_bytes(&self, data: &mut impl io::Read) -> io::Result<Vec<PPartEntry>> {
259 let len = self.profile.len();
260 let mut signature: Vec<PPartEntry> = vec![0; len];
261
262 if cfg!(target_endian = "little") && std::mem::size_of::<PPartEntry>() == 2 {
263 unsafe {
264 let buf: &mut [u8] =
265 std::slice::from_raw_parts_mut(signature.as_mut_ptr() as *mut u8, len * 2);
266 data.read_exact(buf).unwrap();
267 }
268 } else {
269 for entry in &mut signature {
270 *entry = data.read_u16::<LittleEndian>()? as PPartEntry;
271 }
272 }
273
274 let padding = len - ((len / 4) * 4);
275 if padding > 0 {
276 let mut buffer: [u8; 8] = [0; 8];
277 data.read_exact(&mut buffer[0..padding * 2])?;
278 assert_eq!(buffer, [0; 8]);
279 }
280 Ok(signature)
281 }
282}
283
284impl Display for MilnorSubalgebra {
285 fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
286 if self.profile.is_empty() {
287 write!(out, "F_2")
288 } else if self.profile.len() as u8 == self.profile[0] {
289 write!(out, "A({})", self.profile.len() - 1)
290 } else {
291 write!(out, "Algebra with profile {:?}", self.profile)
292 }
293 }
294}
295
296struct SubalgebraIterator {
300 current: MilnorSubalgebra,
301}
302
303impl SubalgebraIterator {
304 fn new() -> Self {
305 Self {
306 current: MilnorSubalgebra::new(vec![]),
307 }
308 }
309}
310
311impl Iterator for SubalgebraIterator {
312 type Item = MilnorSubalgebra;
313
314 fn next(&mut self) -> Option<Self::Item> {
315 if self.current.profile.is_empty()
316 || self.current.profile[0] == self.current.profile.len() as u8
317 {
318 self.current.profile.push(1);
320 Some(self.current.clone())
321 } else {
322 if let Some((_, entry)) = self
324 .current
325 .profile
326 .iter_mut()
327 .rev()
328 .enumerate()
329 .find(|(idx, entry)| **entry == *idx as u8)
330 {
331 *entry += 1;
332 }
333 Some(self.current.clone())
334 }
335 }
336}
337
338struct SignatureIterator<'a> {
340 subalgebra: &'a MilnorSubalgebra,
341 current: Vec<PPartEntry>,
342 signature_degree: i32,
343 degree: i32,
344}
345
346impl<'a> SignatureIterator<'a> {
347 fn new(subalgebra: &'a MilnorSubalgebra, degree: i32) -> Self {
348 Self {
349 current: vec![0; subalgebra.profile.len()],
350 degree,
351 subalgebra,
352 signature_degree: 0,
353 }
354 }
355}
356
357impl Iterator for SignatureIterator<'_> {
358 type Item = Vec<PPartEntry>;
359
360 fn next(&mut self) -> Option<Self::Item> {
361 let xi_degrees = combinatorics::xi_degrees(TWO);
362 let len = self.current.len();
363 for (i, current) in self.current.iter_mut().enumerate() {
364 *current += 1;
365 self.signature_degree += xi_degrees[i];
366
367 if self.signature_degree > self.degree || *current == 1 << self.subalgebra.profile[i] {
368 self.signature_degree -= xi_degrees[i] * *current as i32;
369 *current = 0;
370 if i + 1 == len {
371 return None;
372 }
373 } else {
374 return Some(self.current.clone());
375 }
376 }
377 assert!(self.current.is_empty());
379 None
380 }
381}
382
383enum Magic {
385 End = -1,
386 Signature = -2,
387 Fix = -3,
388}
389
390pub struct Resolution<M: ZeroModule<Algebra = MilnorAlgebra>> {
397 lock: Mutex<()>,
398 name: String,
399 max_degree: i32,
400 modules: OnceBiVec<Arc<FreeModule<MilnorAlgebra>>>,
401 zero_module: Arc<FreeModule<MilnorAlgebra>>,
402 differentials: OnceBiVec<Arc<FreeModuleHomomorphism<FreeModule<MilnorAlgebra>>>>,
403 target: Arc<FiniteChainComplex<M>>,
404 chain_maps: OnceBiVec<Arc<FreeModuleHomomorphism<M>>>,
405 save_dir: SaveDirectory,
406}
407
408impl<M: ZeroModule<Algebra = MilnorAlgebra>> Resolution<M> {
409 pub fn name(&self) -> &str {
410 &self.name
411 }
412
413 pub fn set_name(&mut self, name: String) {
414 self.name = name;
415 }
416
417 pub fn new(module: Arc<M>) -> Self {
418 Self::new_with_save(module, None).unwrap()
419 }
420
421 pub fn new_with_save(
422 module: Arc<M>,
423 save_dir: impl Into<SaveDirectory>,
424 ) -> anyhow::Result<Self> {
425 let save_dir = save_dir.into();
426 let max_degree = module
427 .max_degree()
428 .ok_or_else(|| anyhow!("Nassau's algorithm requires bounded module"))?;
429 let target = Arc::new(FiniteChainComplex::ccdz(module));
430
431 if let Some(p) = save_dir.write() {
432 for subdir in SaveKind::nassau_data() {
433 subdir.create_dir(p)?;
434 }
435 }
436
437 Ok(Self {
438 lock: Mutex::new(()),
439 zero_module: Arc::new(FreeModule::new(target.algebra(), "F_{-1}".to_string(), 0)),
440 name: String::new(),
441 modules: OnceBiVec::new(0),
442 differentials: OnceBiVec::new(0),
443 chain_maps: OnceBiVec::new(0),
444 target,
445 max_degree,
446 save_dir,
447 })
448 }
449
450 fn add_generators(&self, b: Bidegree, num_new_gens: usize) {
451 let gen_names = (0..num_new_gens)
452 .map(|idx| format!("x_{:#}", BidegreeGenerator::new(b, idx)))
453 .collect();
454 self.module(b.s())
455 .add_generators(b.t(), num_new_gens, Some(gen_names));
456 }
457
458 fn extend_through_degree(&self, max_s: i32) {
462 let min_degree = self.min_degree();
463
464 self.modules.extend(max_s, |i| {
465 Arc::new(FreeModule::new(
466 Arc::clone(&self.algebra()),
467 format!("F{i}"),
468 min_degree,
469 ))
470 });
471
472 self.differentials.extend(0, |_| {
473 Arc::new(FreeModuleHomomorphism::new(
474 Arc::clone(&self.modules[0]),
475 Arc::clone(&self.zero_module),
476 0,
477 ))
478 });
479
480 self.differentials.extend(max_s, |i| {
481 Arc::new(FreeModuleHomomorphism::new(
482 Arc::clone(&self.modules[i]),
483 Arc::clone(&self.modules[i - 1]),
484 0,
485 ))
486 });
487
488 self.chain_maps.extend(max_s, |i| {
489 Arc::new(FreeModuleHomomorphism::new(
490 Arc::clone(&self.modules[i]),
491 self.target.module(i),
492 0,
493 ))
494 });
495 }
496
497 #[tracing::instrument(skip_all, fields(throughput))]
498 fn write_qi(
499 f: &mut Option<impl io::Write>,
500 scratch: &mut FpVector,
501 signature: &[PPartEntry],
502 next_mask: &[usize],
503 full_matrix: &Matrix,
504 masked_matrix: &AugmentedMatrix<2>,
505 ) -> io::Result<()> {
506 let f = match f {
507 Some(f) => f,
508 None => return Ok(()),
509 };
510
511 let mut own_f = LogWriter::new(f);
512 let f = &mut own_f;
513
514 let pivots = &masked_matrix.pivots()[0..masked_matrix.end[0]];
515 if !pivots.iter().any(|&x| x >= 0) {
516 return Ok(());
517 }
518
519 if signature.iter().any(|&x| x > 0) {
521 f.write_u64::<LittleEndian>(Magic::Signature as u64)?;
522 MilnorSubalgebra::signature_to_bytes(signature, f)?;
523 }
524
525 for (col, &row) in pivots.iter().enumerate() {
527 if row < 0 {
528 continue;
529 }
530 f.write_u64::<LittleEndian>(next_mask[col] as u64)?;
531 let preimage = masked_matrix.row_segment(row as usize, 1, 1);
532 scratch.set_scratch_vector_size(preimage.len());
533 scratch.as_slice_mut().assign(preimage);
534 scratch.to_bytes(f)?;
535
536 scratch.set_scratch_vector_size(full_matrix.columns());
537 for (i, _) in preimage.iter_nonzero() {
538 scratch.as_slice_mut().add(full_matrix.row(i), 1);
539 }
540 scratch.to_bytes(f)?;
541 }
542
543 tracing::Span::current().record(
544 "throughput",
545 tracing::field::display(own_f.into_throughput()),
546 );
547 Ok(())
548 }
549
550 fn write_differential(
551 &self,
552 b: Bidegree,
553 num_new_gens: usize,
554 target_dim: usize,
555 ) -> anyhow::Result<()> {
556 if let Some(dir) = self.save_dir.write() {
557 let mut f = self
558 .save_file(SaveKind::NassauDifferential, b)
559 .create_file(dir.clone(), false);
560 f.write_u64::<LittleEndian>(num_new_gens as u64)?;
561 f.write_u64::<LittleEndian>(target_dim as u64)?;
562
563 for n in 0..num_new_gens {
564 self.differential(b.s()).output(b.t(), n).to_bytes(&mut f)?;
565 }
566 }
567 Ok(())
568 }
569
570 #[tracing::instrument(skip(self), fields(%b, %subalgebra, num_new_gens, density))]
571 fn step_resolution_with_subalgebra(
572 &self,
573 b: Bidegree,
574 subalgebra: MilnorSubalgebra,
575 ) -> anyhow::Result<()> {
576 let end = || {
577 tracing::Span::current().record("num_new_gens", self.number_of_gens_in_bidegree(b));
578 tracing::Span::current().record(
579 "density",
580 self.differentials[b.s()].differential_density(b.t()) * 100.0,
581 );
582 };
583
584 let p = self.prime();
585 let mut scratch = FpVector::new(p, 0);
586
587 let target = &*self.modules[b.s() - 1];
588 let algebra = target.algebra();
589
590 let zero_sig = subalgebra.zero_signature();
591 let target_dim = target.dimension(b.t());
592 let target_mask: Vec<usize> = subalgebra
593 .signature_mask(&algebra, target, b.t(), &zero_sig)
594 .collect();
595 let target_masked_dim = target_mask.len();
596
597 let next = &self.modules[b.s() - 2];
598 next.compute_basis(b.t());
599
600 let mut f = if let Some(dir) = self.save_dir().write() {
601 let mut f = self
602 .save_file(SaveKind::NassauQi, b - Bidegree::s_t(1, 0))
603 .create_file(dir.to_owned(), true);
604 f.write_u64::<LittleEndian>(next.dimension(b.t()) as u64)?;
605 f.write_u64::<LittleEndian>(target_masked_dim as u64)?;
606 subalgebra.to_bytes(&mut f)?;
607 Some(f)
608 } else {
609 None
610 };
611
612 let guard = tracing::info_span!("step", signature = ?zero_sig).entered();
613 let next_mask: Vec<usize> = subalgebra
614 .signature_mask(&algebra, &self.modules[b.s() - 2], b.t(), &zero_sig)
615 .collect();
616 let next_masked_dim = next_mask.len();
617
618 let full_matrix = {
619 let _guard = ParallelGuard::new();
620 self.differentials[b.s() - 1].get_partial_matrix(b.t(), &target_mask)
621 };
622 let mut masked_matrix =
623 AugmentedMatrix::new(p, target_masked_dim, [next_masked_dim, target_masked_dim]);
624
625 masked_matrix
626 .segment(0, 0)
627 .add_masked(&full_matrix, &next_mask);
628 masked_matrix.segment(1, 1).add_identity();
629 masked_matrix.row_reduce();
630 let kernel = masked_matrix.compute_kernel();
631
632 Self::write_qi(
633 &mut f,
634 &mut scratch,
635 &zero_sig,
636 &next_mask,
637 &full_matrix,
638 &masked_matrix,
639 )?;
640
641 if let Some(f) = &mut f
642 && target.max_computed_degree() < b.t()
643 {
644 f.write_u64::<LittleEndian>(Magic::Fix as u64)?;
645 }
646
647 let mut n = subalgebra.signature_matrix(&self.differentials[b.s()], b.t(), &zero_sig);
649 n.row_reduce();
650 let next_row = n.rows();
651
652 let num_new_gens = n.extend_image(0, n.columns(), &kernel, 0).len();
653
654 if b.t() < b.s() {
655 assert_eq!(num_new_gens, 0, "Adding generators at {b}");
656 }
657
658 self.add_generators(b, num_new_gens);
659
660 let mut xs = vec![FpVector::new(p, target_dim); num_new_gens];
661 let mut dxs = vec![FpVector::new(p, next.dimension(b.t())); num_new_gens];
662
663 for ((x, x_masked), dx) in xs
664 .iter_mut()
665 .zip_eq(n.iter().skip(next_row))
666 .zip_eq(&mut dxs)
667 {
668 x.as_slice_mut().add_unmasked(x_masked, 1, &target_mask);
669 for (i, _) in x_masked.iter_nonzero() {
670 dx.as_slice_mut().add(full_matrix.row(i), 1);
671 }
672 }
673
674 let mut target_mask: Vec<usize> = Vec::new();
676 let mut next_mask: Vec<usize> = Vec::new();
677
678 drop(guard);
679
680 for signature in subalgebra.iter_signatures(b.t()) {
681 let _guard = tracing::info_span!("step", ?signature).entered();
682 target_mask.clear();
683 next_mask.clear();
684 target_mask.extend(subalgebra.signature_mask(&algebra, target, b.t(), &signature));
685 next_mask.extend(subalgebra.signature_mask(&algebra, next, b.t(), &signature));
686
687 let full_matrix = {
688 let _guard = ParallelGuard::new();
689 self.differential(b.s() - 1)
690 .get_partial_matrix(b.t(), &target_mask)
691 };
692
693 let mut masked_matrix =
694 AugmentedMatrix::new(p, target_mask.len(), [next_mask.len(), target_mask.len()]);
695 masked_matrix
696 .segment(0, 0)
697 .add_masked(&full_matrix, &next_mask);
698 masked_matrix.segment(1, 1).add_identity();
699 masked_matrix.row_reduce();
700
701 let qi = masked_matrix.compute_quasi_inverse();
702 let pivots = qi.pivots().unwrap();
703 let preimage = qi.preimage();
704
705 for (x, dx) in xs.iter_mut().zip(&mut dxs) {
706 scratch.set_scratch_vector_size(target_mask.len());
707 let mut row = 0;
708 for (i, &v) in next_mask.iter().enumerate() {
709 if pivots[i] < 0 {
710 continue;
711 }
712 if dx.entry(v) != 0 {
713 scratch.as_slice_mut().add(preimage.row(row), 1);
714 }
715 row += 1;
716 }
717 for (i, _) in scratch.iter_nonzero() {
718 x.add_basis_element(target_mask[i], 1);
719 dx.as_slice_mut().add(full_matrix.row(i), 1);
720 }
721 }
722 Self::write_qi(
723 &mut f,
724 &mut scratch,
725 &signature,
726 &next_mask,
727 &full_matrix,
728 &masked_matrix,
729 )?;
730 }
731 for dx in &dxs {
732 assert!(dx.is_zero(), "dx non-zero at {b}");
733 }
734 self.differential(b.s()).add_generators_from_rows(b.t(), xs);
735
736 end();
737
738 if let Some(f) = &mut f {
739 f.write_u64::<LittleEndian>(Magic::End as u64)?;
740 }
741
742 self.write_differential(b, num_new_gens, target_dim)?;
743 Ok(())
744 }
745
746 #[tracing::instrument(skip(self))]
748 fn step0(&self, t: i32) {
749 self.zero_module.extend_by_zero(t);
750
751 let source_module = &self.modules[0];
752 let target_module = self.target.module(0);
753
754 let chain_map = &self.chain_maps[0];
755 let d = &self.differentials[0];
756
757 let source_dim = source_module.dimension(t);
758 let target_dim = target_module.dimension(t);
759
760 source_module.compute_basis(t);
761 target_module.compute_basis(t);
762
763 if target_dim == 0 {
764 source_module.extend_by_zero(t);
765 chain_map.extend_by_zero(t);
766 } else {
767 let mut matrix = AugmentedMatrix::<2>::new_with_capacity(
768 self.prime(),
769 source_dim,
770 &[target_dim, source_dim],
771 source_dim + target_dim,
772 0,
773 );
774 {
775 let _guard = ParallelGuard::new();
776 chain_map.get_matrix(matrix.segment(0, 0), t);
777 }
778 matrix.segment(1, 1).add_identity();
779
780 matrix.row_reduce();
781
782 let num_new_gens = matrix.extend_to_surjection(0, target_dim, 0).len();
783
784 self.add_generators(Bidegree::s_t(0, t), num_new_gens);
785
786 chain_map.add_generators_from_matrix_rows(
787 t,
788 matrix
789 .segment(0, 0)
790 .row_slice(source_dim, source_dim + num_new_gens),
791 );
792 }
793 chain_map.compute_auxiliary_data_through_degree(t);
794
795 d.set_kernel(t, None);
796 d.set_image(t, None);
797 d.set_quasi_inverse(t, None);
798 d.extend_by_zero(t);
799 }
800
801 #[tracing::instrument(skip(self))]
803 fn step1(&self, t: i32) -> anyhow::Result<()> {
804 let p = self.prime();
805
806 let source_module = &self.modules[1];
807 let target_module = &self.modules[0];
808 let cc_module = self.target.module(0);
809
810 let source_dim = source_module.dimension(t);
811 let target_dim = target_module.dimension(t);
812
813 let mut matrix =
814 AugmentedMatrix::<2>::new(p, target_dim, [cc_module.dimension(t), target_dim]);
815 {
816 let _guard = ParallelGuard::new();
817 self.chain_maps[0].get_matrix(matrix.segment(0, 0), t);
818 }
819 matrix.segment(1, 1).add_identity();
820 matrix.row_reduce();
821 let desired_image = matrix.compute_kernel();
822
823 let mut matrix = AugmentedMatrix::<2>::new_with_capacity(
824 p,
825 source_dim,
826 &[target_dim, source_dim],
827 source_dim + MAX_NEW_GENS,
828 0,
829 );
830 {
831 let _guard = ParallelGuard::new();
832 self.differentials[1].get_matrix(matrix.segment(0, 0), t);
833 }
834 matrix.segment(1, 1).add_identity();
835 matrix.row_reduce();
836
837 let num_new_gens = matrix.extend_image(0, target_dim, &desired_image, 0).len();
838
839 self.add_generators(Bidegree::s_t(1, t), num_new_gens);
840
841 self.differentials[1].add_generators_from_matrix_rows(
842 t,
843 matrix
844 .segment(0, 0)
845 .row_slice(source_dim, source_dim + num_new_gens),
846 );
847
848 self.write_differential(Bidegree::s_t(1, t), num_new_gens, target_dim)?;
849 Ok(())
850 }
851
852 fn step_resolution_with_result(&self, b: Bidegree) -> anyhow::Result<()> {
853 let p = self.prime();
854 let set_data = || {
855 let d = &self.differentials[b.s()];
856 let c = &self.chain_maps[b.s()];
857
858 d.set_kernel(b.t(), None);
859 d.set_image(b.t(), None);
860 d.set_quasi_inverse(b.t(), None);
861
862 c.set_kernel(b.t(), None);
863 c.set_image(b.t(), None);
864 c.set_quasi_inverse(b.t(), None);
865 };
866 self.modules[b.s()].compute_basis(b.t());
867 if b.s() > 0 {
868 self.modules[b.s() - 1].compute_basis(b.t());
869 }
870
871 if b.s() == 0 {
872 self.step0(b.t());
873 return Ok(());
874 }
875
876 if let Some(dir) = self.save_dir.read()
877 && let Some(mut f) = self
878 .save_file(SaveKind::NassauDifferential, b)
879 .open_file(dir.clone())
880 {
881 tracing::info!(%b, "Loading differential");
882
883 let num_new_gens = f.read_u64::<LittleEndian>()? as usize;
884 let saved_target_res_dimension = f.read_u64::<LittleEndian>()? as usize;
889
890 self.add_generators(b, num_new_gens);
891
892 let mut d_targets = Vec::with_capacity(num_new_gens);
893
894 for _ in 0..num_new_gens {
895 d_targets.push(FpVector::from_bytes(p, saved_target_res_dimension, &mut f)?);
896 }
897
898 self.differentials[b.s()].add_generators_from_rows(b.t(), d_targets);
899
900 set_data();
901
902 return Ok(());
903 }
904
905 if b.s() == 1 {
906 self.step1(b.t())?;
907 set_data();
908 return Ok(());
909 }
910
911 self.step_resolution_with_subalgebra(
912 b,
913 MilnorSubalgebra::optimal_for(b - Bidegree::s_t(0, self.max_degree)),
914 )?;
915 self.chain_maps[b.s()].extend_by_zero(b.t());
916
917 set_data();
918 Ok(())
919 }
920
921 fn step_resolution(&self, b: Bidegree) {
922 self.step_resolution_with_result(b)
923 .unwrap_or_else(|e| panic!("Error computing bidegree {b}: {e}"));
924 }
925
926 #[tracing::instrument(skip(self), fields(self = self.name, %max))]
928 pub fn compute_through_stem(&self, max: Bidegree) {
929 let _lock = self.lock.lock();
930
931 self.extend_through_degree(max.s());
932 self.algebra().compute_basis(max.t());
933
934 let tracing_span = tracing::Span::current();
935 maybe_rayon::in_place_scope(|scope| {
936 let _tracing_guard = tracing_span.enter();
937
938 let mut progress: Vec<i32> = vec![-1; max.s() as usize + 1];
945 progress[0] = -2;
948
949 let (sender, receiver) = mpsc::channel();
950 SenderData::send(Bidegree::s_t(0, -1), sender);
951
952 let f = |b, sender| {
953 if self.has_computed_bidegree(b) {
954 SenderData::send(b, sender);
955 } else {
956 let tracing_span = tracing_span.clone();
957 scope.spawn(move |_| {
958 let _tracing_guard = tracing_span.enter();
959 if crate::utils::parallel::is_in_parallel() {
960 SenderData::send_retry(b, sender);
961 return;
962 }
963 self.step_resolution(b);
964 SenderData::send(b, sender);
965 });
966 }
967 };
968
969 while let Ok(SenderData { b, retry, sender }) = receiver.recv() {
970 if retry {
971 f(b, sender);
972 continue;
973 }
974 assert!(progress[b.s() as usize] == b.t() - 1);
975 progress[b.s() as usize] = b.t();
976
977 let distance = max.n() - b.n() + 1;
979
980 if b.s() < max.s() && progress[b.s() as usize + 1] == b.t() - 1 {
981 f(b + Bidegree::s_t(1, 0), sender.clone());
982 }
983
984 if distance > 1 && (b.s() == 0 || progress[b.s() as usize - 1] > b.t()) {
985 f(b + Bidegree::s_t(0, 1), sender);
987 } else if distance == 1 && b.s() < max.s() {
988 SenderData::send(b + Bidegree::s_t(0, 1), sender);
989 }
990 }
991 });
992 }
993}
994
995impl<M: ZeroModule<Algebra = MilnorAlgebra>> ChainComplex for Resolution<M> {
996 type Algebra = MilnorAlgebra;
997 type Homomorphism = FreeModuleHomomorphism<FreeModule<Self::Algebra>>;
998 type Module = FreeModule<Self::Algebra>;
999
1000 fn prime(&self) -> ValidPrime {
1001 TWO
1002 }
1003
1004 fn algebra(&self) -> Arc<Self::Algebra> {
1005 self.zero_module.algebra()
1006 }
1007
1008 fn module(&self, s: i32) -> Arc<Self::Module> {
1009 Arc::clone(&self.modules[s])
1010 }
1011
1012 fn zero_module(&self) -> Arc<Self::Module> {
1013 Arc::clone(&self.zero_module)
1014 }
1015
1016 fn min_degree(&self) -> i32 {
1017 0
1018 }
1019
1020 fn has_computed_bidegree(&self, b: Bidegree) -> bool {
1021 self.differentials.len() > b.s() && self.differential(b.s()).next_degree() > b.t()
1022 }
1023
1024 fn differential(&self, s: i32) -> Arc<Self::Homomorphism> {
1025 Arc::clone(&self.differentials[s])
1026 }
1027
1028 #[tracing::instrument(skip(self), fields(self = self.name, %max))]
1029 fn compute_through_bidegree(&self, max: Bidegree) {
1030 let _lock = self.lock.lock();
1031
1032 self.extend_through_degree(max.s());
1033 self.algebra().compute_basis(max.t());
1034
1035 for t in 0..=max.t() {
1036 for s in 0..=max.s() {
1037 let b = Bidegree::s_t(s, t);
1038 if self.has_computed_bidegree(b) {
1039 continue;
1040 }
1041 self.step_resolution(b);
1042 }
1043 }
1044 }
1045
1046 fn next_homological_degree(&self) -> i32 {
1047 self.modules.len()
1048 }
1049
1050 fn save_dir(&self) -> &SaveDirectory {
1051 &self.save_dir
1052 }
1053
1054 fn apply_quasi_inverse<T, S>(&self, results: &mut [T], b: Bidegree, inputs: &[S]) -> bool
1055 where
1056 for<'a> &'a mut T: Into<FpSliceMut<'a>>,
1057 for<'a> &'a S: Into<FpSlice<'a>>,
1058 {
1059 let mut f = if let Some(dir) = self.save_dir.read() {
1060 if let Some(f) = self.save_file(SaveKind::NassauQi, b).open_file(dir.clone()) {
1061 f
1062 } else {
1063 return false;
1064 }
1065 } else {
1066 return false;
1067 };
1068
1069 let p = self.prime();
1070
1071 let target_dim = f.read_u64::<LittleEndian>().unwrap() as usize;
1072 let zero_mask_dim = f.read_u64::<LittleEndian>().unwrap() as usize;
1073 let subalgebra = MilnorSubalgebra::from_bytes(&mut f).unwrap();
1074 let source = &self.modules[b.s()];
1075 let target = &self.modules[b.s() - 1];
1076 let algebra = target.algebra();
1077
1078 let mut inputs: Vec<FpVector> = inputs.iter().map(|x| x.into().to_owned()).collect();
1079 let mut mask: Vec<usize> = Vec::with_capacity(zero_mask_dim + 8);
1080 mask.extend(subalgebra.signature_mask(
1081 &algebra,
1082 source,
1083 b.t(),
1084 &subalgebra.zero_signature(),
1085 ));
1086
1087 let mut scratch0 = FpVector::new(p, zero_mask_dim);
1088 let mut scratch1 = FpVector::new(p, target_dim);
1089
1090 let (mut target_zero_mask, mut dx_matrix) = if zero_mask_dim != mask.len() {
1109 let num_new_gens = source.number_of_gens_in_degree(b.t());
1110 assert_eq!(mask.len(), zero_mask_dim + num_new_gens);
1111
1112 let target_zero_mask: Vec<usize> = subalgebra
1113 .signature_mask(&algebra, target, b.t(), &subalgebra.zero_signature())
1114 .collect();
1115 let mut matrix = AugmentedMatrix::<3>::new(
1116 p,
1117 num_new_gens,
1118 [target_zero_mask.len(), target.dimension(b.t()), mask.len()],
1119 );
1120
1121 for i in 0..num_new_gens {
1122 let dx = self.differentials[b.s()].output(b.t(), i);
1123 matrix
1124 .row_segment_mut(i, 1, 1)
1125 .slice_mut(0, dx.len())
1126 .add(dx.as_slice(), 1);
1127 matrix
1128 .row_segment_mut(i, 2, 2)
1129 .add_basis_element(zero_mask_dim + i, 1);
1130 }
1131
1132 (target_zero_mask, matrix)
1133 } else {
1134 (Vec::new(), AugmentedMatrix::<3>::new(p, 0, [0, 0, 0]))
1135 };
1136
1137 loop {
1138 let col = f.read_u64::<LittleEndian>().unwrap() as usize;
1139 if col == Magic::End as usize {
1140 break;
1141 } else if col == Magic::Signature as usize {
1142 let signature = subalgebra.signature_from_bytes(&mut f).unwrap();
1143
1144 mask.clear();
1145 mask.extend(subalgebra.signature_mask(&algebra, source, b.t(), &signature));
1146 scratch0.set_scratch_vector_size(mask.len());
1147 } else if col == Magic::Fix as usize {
1148 for (j, &k) in target_zero_mask.iter().enumerate() {
1153 for i in 0..dx_matrix.rows() {
1154 if dx_matrix.row_segment(i, 1, 1).entry(k) != 0 {
1155 dx_matrix.row_segment_mut(i, 0, 0).add_basis_element(j, 1);
1156 }
1157 }
1158 }
1159 dx_matrix.row_reduce();
1160
1161 for i in 0..dx_matrix.rows() {
1163 let masked_col = dx_matrix.row(i).first_nonzero().unwrap().0;
1164 assert_eq!(dx_matrix.pivots()[masked_col], i as isize);
1165 let col = target_zero_mask[masked_col];
1166
1167 for (input, output) in inputs.iter_mut().zip(results.iter_mut()) {
1168 let entry = input.entry(col);
1169 if entry != 0 {
1170 output
1171 .into()
1172 .add_unmasked(dx_matrix.row_segment(i, 2, 2), 1, &mask);
1173 input.as_slice_mut().add(dx_matrix.row_segment(i, 1, 1), 1);
1174 }
1175 }
1176 }
1177
1178 target_zero_mask = Vec::new();
1180 dx_matrix = AugmentedMatrix::<3>::new(p, 0, [0, 0, 0]);
1181 } else {
1182 scratch0.update_from_bytes(&mut f).unwrap();
1183 scratch1.update_from_bytes(&mut f).unwrap();
1184 for (input, output) in inputs.iter_mut().zip(results.iter_mut()) {
1185 let entry = input.entry(col);
1186 if entry != 0 {
1187 output.into().add_unmasked(scratch0.as_slice(), 1, &mask);
1188 input
1190 .slice_mut(0, scratch1.len())
1191 .add(scratch1.as_slice(), 1);
1192 }
1193 }
1194
1195 if !target_zero_mask.is_empty() {
1197 for i in 0..dx_matrix.rows() {
1198 if dx_matrix.row_segment(i, 1, 1).entry(col) != 0 {
1199 dx_matrix
1200 .row_segment_mut(i, 2, 2)
1201 .slice_mut(0, zero_mask_dim)
1202 .add(scratch0.as_slice(), 1);
1203 dx_matrix
1204 .row_segment_mut(i, 1, 1)
1205 .slice_mut(0, target_dim)
1206 .add(scratch1.as_slice(), 1);
1207 }
1208 }
1209 }
1210 }
1211 }
1212 drop(f);
1214
1215 for dx in inputs {
1216 assert!(
1217 dx.is_zero(),
1218 "remainder non-zero at {b}\nAlgebra: {subalgebra}\ndx: {}",
1219 target.element_to_string(b.t(), dx.as_slice())
1220 );
1221 }
1222 true
1223 }
1224}
1225
1226impl<M: ZeroModule<Algebra = MilnorAlgebra>> AugmentedChainComplex for Resolution<M> {
1227 type ChainMap = FreeModuleHomomorphism<M>;
1228 type TargetComplex = FiniteChainComplex<M, FullModuleHomomorphism<M, M>>;
1229
1230 fn target(&self) -> Arc<Self::TargetComplex> {
1231 Arc::clone(&self.target)
1232 }
1233
1234 fn chain_map(&self, s: i32) -> Arc<Self::ChainMap> {
1235 Arc::clone(&self.chain_maps[s])
1236 }
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241 use expect_test::expect;
1242
1243 use super::*;
1244
1245 #[test]
1246 fn test_restart_stem() {
1247 let res = crate::utils::construct_nassau("S_2", None).unwrap();
1248 res.compute_through_stem(Bidegree::n_s(14, 8));
1249 res.compute_through_bidegree(Bidegree::s_t(5, 19));
1250
1251 expect![[r#"
1252 ·
1253 · ·
1254 · · · ·
1255 · · · ·
1256 · · · · ·
1257 · · · · · · ·
1258 · · · · · · · · ·
1259 · · · · ·
1260 ·
1261 "#]]
1262 .assert_eq(&res.graded_dimension_string());
1263 }
1264
1265 #[test]
1266 fn test_signature_iterator() {
1267 let subalgebra = MilnorSubalgebra::new(vec![2, 1]);
1268 assert_eq!(
1269 subalgebra.iter_signatures(6).collect::<Vec<_>>(),
1270 vec![
1271 vec![1, 0],
1272 vec![2, 0],
1273 vec![3, 0],
1274 vec![0, 1],
1275 vec![1, 1],
1276 vec![2, 1],
1277 vec![3, 1],
1278 ]
1279 );
1280
1281 assert_eq!(
1282 subalgebra.iter_signatures(5).collect::<Vec<_>>(),
1283 vec![
1284 vec![1, 0],
1285 vec![2, 0],
1286 vec![3, 0],
1287 vec![0, 1],
1288 vec![1, 1],
1289 vec![2, 1],
1290 ]
1291 );
1292 assert_eq!(
1293 subalgebra.iter_signatures(4).collect::<Vec<_>>(),
1294 vec![vec![1, 0], vec![2, 0], vec![3, 0], vec![0, 1], vec![1, 1],]
1295 );
1296 assert_eq!(
1297 subalgebra.iter_signatures(3).collect::<Vec<_>>(),
1298 vec![vec![1, 0], vec![2, 0], vec![3, 0], vec![0, 1],]
1299 );
1300 assert_eq!(
1301 subalgebra.iter_signatures(2).collect::<Vec<_>>(),
1302 vec![vec![1, 0], vec![2, 0],]
1303 );
1304 assert_eq!(
1305 subalgebra.iter_signatures(1).collect::<Vec<_>>(),
1306 vec![vec![1, 0],]
1307 );
1308 assert_eq!(
1309 subalgebra.iter_signatures(0).collect::<Vec<_>>(),
1310 Vec::<Vec<PPartEntry>>::new()
1311 );
1312 }
1313
1314 #[test]
1315 fn test_signature_iterator_large() {
1316 let subalgebra = MilnorSubalgebra::new(vec![
1317 0,
1318 MilnorSubalgebra::INFINITY,
1319 MilnorSubalgebra::INFINITY,
1320 MilnorSubalgebra::INFINITY,
1321 ]);
1322 assert_eq!(
1323 subalgebra.iter_signatures(7).collect::<Vec<_>>(),
1324 vec![vec![0, 1, 0, 0], vec![0, 2, 0, 0], vec![0, 0, 1, 0],]
1325 );
1326 }
1327}