1use std::{ops::Range, sync::Arc};
4
5use algebra::{
6 MuAlgebra,
7 module::{
8 Module,
9 homomorphism::{ModuleHomomorphism, MuFreeModuleHomomorphism},
10 },
11};
12use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
13use fp::{
14 matrix::Matrix,
15 vector::{FpSliceMut, FpVector},
16};
17use maybe_rayon::prelude::*;
18use once::OnceBiVec;
19use sseq::coordinates::{Bidegree, BidegreeGenerator, BidegreeRange};
20
21use crate::{
22 chain_complex::{AugmentedChainComplex, BoundedChainComplex, ChainComplex, FreeChainComplex},
23 save::{SaveDirectory, SaveKind},
24};
25
26pub type ResolutionHomomorphism<CC1, CC2> = MuResolutionHomomorphism<false, CC1, CC2>;
27pub type UnstableResolutionHomomorphism<CC1, CC2> = MuResolutionHomomorphism<true, CC1, CC2>;
28
29pub struct MuResolutionHomomorphism<const U: bool, CC1, CC2>
32where
33 CC1: FreeChainComplex<U>,
34 CC1::Algebra: MuAlgebra<U>,
35 CC2: ChainComplex<Algebra = CC1::Algebra>,
36{
37 name: String,
38 pub source: Arc<CC1>,
39 pub target: Arc<CC2>,
40 maps: OnceBiVec<Arc<MuFreeModuleHomomorphism<U, CC2::Module>>>,
41 pub shift: Bidegree,
42 save_dir: SaveDirectory,
43}
44
45impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
46where
47 CC1: FreeChainComplex<U>,
48 CC1::Algebra: MuAlgebra<U>,
49 CC2: ChainComplex<Algebra = CC1::Algebra>,
50{
51 pub fn new(name: String, source: Arc<CC1>, target: Arc<CC2>, shift: Bidegree) -> Self {
52 let save_dir = if source.save_dir().is_some() && !name.is_empty() {
53 let mut save_dir = source.save_dir().clone();
54 save_dir.push(format!("products/{name}"));
55 SaveKind::ChainMap
56 .create_dir(save_dir.write().unwrap())
57 .unwrap();
58 save_dir
59 } else {
60 SaveDirectory::None
61 };
62
63 Self {
64 name,
65 source,
66 target,
67 maps: OnceBiVec::new(shift.s()),
68 shift,
69 save_dir,
70 }
71 }
72
73 pub fn name(&self) -> &str {
74 &self.name
75 }
76
77 pub fn algebra(&self) -> Arc<CC1::Algebra> {
78 self.source.algebra()
79 }
80
81 pub fn next_homological_degree(&self) -> i32 {
82 self.maps.len()
83 }
84
85 fn get_map_ensure_length(&self, input_s: i32) -> &MuFreeModuleHomomorphism<U, CC2::Module> {
86 self.maps.extend(input_s, |input_s| {
87 let output_s = input_s - self.shift.s();
88 Arc::new(MuFreeModuleHomomorphism::new(
89 self.source.module(input_s),
90 self.target.module(output_s),
91 self.shift.t(),
92 ))
93 });
94 &self.maps[input_s]
95 }
96
97 pub fn get_map(&self, input_s: i32) -> Arc<MuFreeModuleHomomorphism<U, CC2::Module>> {
99 Arc::clone(&self.maps[input_s])
100 }
101
102 pub fn save_dir(&self) -> &SaveDirectory {
103 &self.save_dir
104 }
105}
106
107impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
108where
109 CC1: FreeChainComplex<U>,
110 CC1::Algebra: MuAlgebra<U>,
111 CC2: ChainComplex<Algebra = CC1::Algebra>,
112{
113 #[tracing::instrument(fields(self = self.name, %max))]
120 pub fn extend(&self, max: Bidegree) {
121 self.extend_profile(BidegreeRange::new(&(), max.s() + 1, &|_, _| max.t() + 1))
122 }
123
124 #[tracing::instrument(fields(self = self.name, %max))]
131 pub fn extend_through_stem(&self, max: Bidegree) {
132 self.extend_profile(BidegreeRange::new(&(), max.s() + 1, &|_, s| {
133 max.n() + s + 1
134 }))
135 }
136
137 #[tracing::instrument(fields(self = self.name))]
144 pub fn extend_all(&self) {
145 self.extend_profile(BidegreeRange::new(
146 self,
147 std::cmp::min(
148 self.target.next_homological_degree() + self.shift.s(),
149 self.source.next_homological_degree(),
150 ),
151 &|selff, s| {
152 std::cmp::min(
153 selff
154 .target
155 .module(s - selff.shift.s())
156 .max_computed_degree()
157 + selff.shift.t(),
158 selff.source.module(s).max_computed_degree(),
159 ) + 1
160 },
161 ));
162 }
163
164 pub fn extend_profile<AUX: Sync>(&self, max: BidegreeRange<AUX>) {
177 self.get_map_ensure_length(max.s() - 1);
178
179 sseq::coordinates::iter_s_t(
180 &|b| self.extend_step_raw(b, None),
181 Bidegree::s_t(
182 self.shift.s(),
183 self.get_map_ensure_length(self.shift.s()).min_degree(),
184 ),
185 max,
186 );
187
188 for s in self.shift.s()..max.s() {
189 assert_eq!(
190 Vec::<i32>::new(),
191 self.maps[s].ooo_outputs(),
192 "Map {s} has out of order elements"
193 );
194 }
195 }
196
197 #[tracing::instrument(skip(self, extra_images), fields(self = self.name, %input))]
205 pub fn extend_step_raw(
206 &self,
207 input: Bidegree,
208 extra_images: Option<Vec<FpVector>>,
209 ) -> Range<i32> {
210 let output = input - self.shift;
211 assert!(self.target.has_computed_bidegree(output));
212 assert!(self.source.has_computed_bidegree(input));
213 assert!(input.s() >= self.shift.s());
214
215 let f_cur = self.get_map_ensure_length(input.s());
216 if input.t() < f_cur.next_degree() {
217 assert!(extra_images.is_none());
218 return input.t()..input.t() + 1;
220 }
221
222 let p = self.source.prime();
223
224 let num_gens = f_cur.source().number_of_gens_in_degree(input.t());
225 let fx_dimension = f_cur.target().dimension(output.t());
226
227 if num_gens == 0 || fx_dimension == 0 {
228 return f_cur.add_generators_from_rows_ooo(
229 input.t(),
230 vec![FpVector::new(p, fx_dimension); num_gens],
231 );
232 }
233
234 if let Some(dir) = self.save_dir.read() {
235 let mut outputs = Vec::with_capacity(num_gens);
236
237 if let Some(mut f) = self
238 .source
239 .save_file(SaveKind::ChainMap, input)
240 .open_file(dir.to_owned())
241 {
242 let fx_dimension = f.read_u64::<LittleEndian>().unwrap() as usize;
243 for _ in 0..num_gens {
244 outputs.push(FpVector::from_bytes(p, fx_dimension, &mut f).unwrap());
245 }
246 return f_cur.add_generators_from_rows_ooo(input.t(), outputs);
247 }
248 }
249
250 if output.s() == 0 {
251 let outputs =
252 extra_images.unwrap_or_else(|| vec![FpVector::new(p, fx_dimension); num_gens]);
253
254 if let Some(dir) = self.save_dir.write() {
255 let mut f = self
256 .source
257 .save_file(SaveKind::ChainMap, input)
258 .create_file(dir.clone(), false);
259 f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
260 for row in &outputs {
261 row.to_bytes(&mut f).unwrap();
262 }
263 }
264
265 return f_cur.add_generators_from_rows_ooo(input.t(), outputs);
266 }
267 let mut outputs = vec![FpVector::new(p, fx_dimension); num_gens];
268 let d_source = self.source.differential(input.s());
269 let d_target = self.target.differential(output.s());
270 let f_prev = self.get_map(input.s() - 1);
271 assert!(Arc::ptr_eq(&d_source.source(), &f_cur.source()));
272 assert!(Arc::ptr_eq(&d_source.target(), &f_prev.source()));
273 assert!(Arc::ptr_eq(&d_target.source(), &f_cur.target()));
274 assert!(Arc::ptr_eq(&d_target.target(), &f_prev.target()));
275 let fdx_dimension = f_prev.target().dimension(output.t());
276
277 let mut extra_image_row = 0;
279 for (k, output_row) in outputs.iter_mut().enumerate() {
280 if d_source.output(input.t(), k).is_zero() {
281 let extra_image_matrix = extra_images.as_ref().expect("Missing extra image rows");
282 output_row.assign(&extra_image_matrix[extra_image_row]);
283 extra_image_row += 1;
284 }
285 }
286
287 d_target.compute_auxiliary_data_through_degree(output.t());
289
290 let compute_fdx_vector = |k| {
291 let dx_vector = d_source.output(input.t(), k);
292 if dx_vector.is_zero() {
293 None
294 } else {
295 let mut fdx_vector = FpVector::new(p, fdx_dimension);
296 f_prev.apply(
297 fdx_vector.as_slice_mut(),
298 1,
299 input.t(),
300 dx_vector.as_slice(),
301 );
302 Some(fdx_vector)
303 }
304 };
305
306 let fdx_vectors: Vec<FpVector> = (0..num_gens)
307 .into_maybe_par_iter()
308 .filter_map(compute_fdx_vector)
309 .collect();
310
311 let mut qi_outputs: Vec<_> = outputs
312 .iter_mut()
313 .enumerate()
314 .filter_map(|(k, v)| {
315 if d_source.output(input.t(), k).is_zero() {
316 None
317 } else {
318 Some(v.as_slice_mut())
319 }
320 })
321 .collect();
322
323 if !fdx_vectors.is_empty() {
324 assert!(
325 self.target
326 .apply_quasi_inverse(&mut qi_outputs, output, &fdx_vectors)
327 );
328 }
329
330 if let Some(dir) = self.save_dir.write() {
331 let mut f = self
332 .source
333 .save_file(SaveKind::ChainMap, input)
334 .create_file(dir.clone(), false);
335 f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
336 for row in &outputs {
337 row.to_bytes(&mut f).unwrap();
338 }
339 }
340 f_cur.add_generators_from_rows_ooo(input.t(), outputs)
341 }
342}
343
344impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
345where
346 CC1: FreeChainComplex<U>,
347 CC1::Algebra: MuAlgebra<U>,
348 CC2: AugmentedChainComplex<Algebra = CC1::Algebra>,
349{
350 pub fn from_class(
351 name: String,
352 source: Arc<CC1>,
353 target: Arc<CC2>,
354 shift: Bidegree,
355 class: &[u32],
356 ) -> Self {
357 let result = Self::new(name, source, target, shift);
358
359 let num_gens = result
360 .source
361 .module(shift.s())
362 .number_of_gens_in_degree(shift.t());
363 assert_eq!(num_gens, class.len());
364
365 let mut matrix = Matrix::new(result.source.prime(), num_gens, 1);
366 for (k, &v) in class.iter().enumerate() {
367 matrix.row_mut(k).set_entry(0, v);
368 }
369
370 result.extend_step(shift, Some(&matrix));
371 result
372 }
373
374 pub fn extend_step(&self, input: Bidegree, extra_images: Option<&Matrix>) -> Range<i32> {
383 self.extend_step_raw(
384 input,
385 extra_images.map(|m| {
386 let p = self.target.prime();
387 let output = input - self.shift;
388
389 let mut outputs =
390 vec![
391 FpVector::new(p, self.target.module(output.s()).dimension(output.t()));
392 m.rows()
393 ];
394 let chain_map = self.target.chain_map(output.s());
395 chain_map.compute_auxiliary_data_through_degree(output.t());
396 for (output_vec, input) in std::iter::zip(&mut outputs, m.iter()) {
397 assert!(chain_map.apply_quasi_inverse(
398 output_vec.as_slice_mut(),
399 output.t(),
400 input,
401 ));
402 }
403 outputs
404 }),
405 )
406 }
407}
408
409impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
410where
411 CC1: AugmentedChainComplex + FreeChainComplex<U>,
412 CC1::Algebra: MuAlgebra<U>,
413 CC2: AugmentedChainComplex<Algebra = CC1::Algebra>,
414 CC1::TargetComplex: BoundedChainComplex,
415 CC2::TargetComplex: BoundedChainComplex,
416{
417 pub fn from_module_homomorphism(
419 name: String,
420 source: Arc<CC1>,
421 target: Arc<CC2>,
422 f: &impl ModuleHomomorphism<
423 Source = <<CC1 as AugmentedChainComplex>::TargetComplex as ChainComplex>::Module,
424 Target = <<CC2 as AugmentedChainComplex>::TargetComplex as ChainComplex>::Module,
425 >,
426 ) -> Self {
427 assert_eq!(source.target().max_s(), 1);
428 assert_eq!(target.target().max_s(), 1);
429
430 let source_module = source.target().module(0);
431 let target_module = target.target().module(0);
432 assert!(Arc::ptr_eq(&source_module, &f.source()));
433 assert!(Arc::ptr_eq(&target_module, &f.target()));
434
435 let p = source.prime();
436 let shift = Bidegree::s_t(0, f.degree_shift());
437
438 let max_degree = source_module.max_generator_degree().expect(
439 "MuResolutionHomomorphism::from_module_homomorphism requires finite \
440 max_generator_degree",
441 );
442
443 let hom = Self::new(name, source, target, shift);
444
445 source_module.compute_basis(max_degree);
446 target_module.compute_basis(shift.t() + max_degree);
447
448 let max = Bidegree::s_t(0, max_degree);
449 hom.source.compute_through_bidegree(max);
450 hom.target.compute_through_bidegree(max + shift);
451
452 for t in source_module.min_degree()..=max_degree {
453 let mut m = Matrix::new(
454 p,
455 source_module.dimension(t),
456 target_module.dimension(t + shift.t()),
457 );
458
459 f.get_matrix(m.as_slice_mut(), t);
460 hom.extend_step(Bidegree::s_t(0, t), Some(&m));
461 }
462 hom
463 }
464}
465
466impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
467where
468 CC1: FreeChainComplex<U>,
469 CC1::Algebra: MuAlgebra<U>,
470 CC2: FreeChainComplex<U, Algebra = CC1::Algebra>,
471{
472 pub fn act(&self, mut result: FpSliceMut, coef: u32, g: BidegreeGenerator) {
476 let source = g.degree() + self.shift;
477
478 assert_eq!(
479 result.as_slice().len(),
480 self.source
481 .module(source.s())
482 .number_of_gens_in_degree(source.t())
483 );
484
485 let target_module = self.target.module(g.s());
486
487 let map = self.get_map(source.s());
488 let j = target_module.operation_generator_to_index(0, 0, g.t(), g.idx());
489 for i in 0..result.as_slice().len() {
490 result.add_basis_element(i, coef * map.output(source.t(), i).entry(j));
491 }
492 }
493}