algebra/module/homomorphism/
hom_pullback.rs1use std::sync::Arc;
2
3use fp::{
4 matrix::{QuasiInverse, Subspace},
5 vector::FpSliceMut,
6};
7use once::OnceBiVec;
8
9use crate::module::{
10 FreeModule, HomModule, Module,
11 block_structure::GeneratorBasisEltPair,
12 homomorphism::{FreeModuleHomomorphism, ModuleHomomorphism},
13};
14
15pub struct HomPullback<M: Module> {
17 source: Arc<HomModule<M>>,
18 target: Arc<HomModule<M>>,
19 map: Arc<FreeModuleHomomorphism<FreeModule<M::Algebra>>>,
20 images: OnceBiVec<Subspace>,
21 kernels: OnceBiVec<Subspace>,
22 quasi_inverses: OnceBiVec<QuasiInverse>,
23}
24
25impl<M: Module> HomPullback<M> {
26 pub fn new(
27 source: Arc<HomModule<M>>,
28 target: Arc<HomModule<M>>,
29 map: Arc<FreeModuleHomomorphism<FreeModule<M::Algebra>>>,
30 ) -> Self {
31 assert!(Arc::ptr_eq(&source.source(), &map.target()));
32 assert!(Arc::ptr_eq(&target.source(), &map.source()));
33 assert!(Arc::ptr_eq(&source.target(), &target.target()));
34
35 let min_degree = source.min_degree();
36 Self {
37 source,
38 target,
39 map,
40 images: OnceBiVec::new(min_degree),
41 kernels: OnceBiVec::new(min_degree),
42 quasi_inverses: OnceBiVec::new(min_degree),
43 }
44 }
45}
46
47impl<M: Module> ModuleHomomorphism for HomPullback<M> {
48 type Source = HomModule<M>;
49 type Target = HomModule<M>;
50
51 fn source(&self) -> Arc<Self::Source> {
52 Arc::clone(&self.source)
53 }
54
55 fn target(&self) -> Arc<Self::Target> {
56 Arc::clone(&self.target)
57 }
58
59 fn degree_shift(&self) -> i32 {
60 -self.map.degree_shift()
61 }
62
63 fn min_degree(&self) -> i32 {
64 self.source().min_degree()
65 }
66
67 fn apply_to_basis_element(
68 &self,
69 mut result: FpSliceMut,
70 coeff: u32,
71 fn_degree: i32,
72 fn_idx: usize,
73 ) {
74 let GeneratorBasisEltPair {
75 generator_degree,
76 generator_index,
77 basis_index,
78 } = self.source.block_structures[fn_degree].index_to_generator_basis_elt(fn_idx);
79
80 let target_module = self.target.target(); let source_free_module = self.source.source();
82 let target_free_module = self.target.source();
83 let degree_shift = self.map.degree_shift();
84
85 let max_degree = fn_degree + degree_shift + target_module.max_degree().unwrap();
86 let min_degree = std::cmp::max(
87 *generator_degree + degree_shift,
88 fn_degree + degree_shift + target_module.min_degree(),
89 );
90
91 for (target_gen_deg, target_gen_idx) in target_free_module
92 .iter_gens(max_degree)
93 .filter(|(t, _)| *t >= min_degree)
94 {
95 let target_range = self.target.block_structures[fn_degree + degree_shift]
96 .generator_to_block(target_gen_deg, target_gen_idx);
97
98 let slice = source_free_module.slice_vector(
99 target_gen_deg - degree_shift,
100 *generator_degree,
101 *generator_index,
102 self.map.output(target_gen_deg, target_gen_idx).as_slice(),
103 );
104
105 if slice.is_empty() {
107 continue;
108 }
109 target_module.act_by_element_on_basis(
110 result.slice_mut(target_range.start, target_range.end),
111 coeff,
112 target_gen_deg - degree_shift - *generator_degree,
113 slice,
114 *generator_degree - fn_degree,
115 *basis_index,
116 );
117 }
118 }
119
120 fn compute_auxiliary_data_through_degree(&self, degree: i32) {
121 self.kernels.extend(degree, |i| {
122 let (image, kernel, qi) = self.auxiliary_data(i);
123 self.images.push_checked(image, i);
124 self.quasi_inverses.push_checked(qi, i);
125 kernel
126 });
127 }
128
129 fn quasi_inverse(&self, degree: i32) -> Option<&QuasiInverse> {
130 self.quasi_inverses.get(degree)
131 }
132
133 fn kernel(&self, degree: i32) -> Option<&Subspace> {
134 self.kernels.get(degree)
135 }
136
137 fn image(&self, degree: i32) -> Option<&Subspace> {
138 self.images.get(degree)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use bivec::BiVec;
145 use fp::{matrix::Matrix, vector::FpVector};
146
147 use super::*;
148 use crate::{MilnorAlgebra, module::FDModule};
149
150 #[test]
151 fn test_pullback_id() {
152 const SHIFT: i32 = 2;
153 const NUM_GENS: [usize; 3] = [1, 2, 1];
154
155 let p = fp::prime::TWO;
156
157 let algebra = Arc::new(MilnorAlgebra::new(p, false));
158 let f0 = Arc::new(FreeModule::new(Arc::clone(&algebra), "F0".to_string(), 0));
159 let f1 = Arc::new(FreeModule::new(
160 Arc::clone(&algebra),
161 "F1".to_string(),
162 SHIFT,
163 ));
164 let m = Arc::new(
165 FDModule::from_json(Arc::clone(&algebra), &crate::tests::joker_json()).unwrap(),
166 );
167
168 let d = Arc::new(FreeModuleHomomorphism::new(
169 Arc::clone(&f1),
170 Arc::clone(&f0),
171 SHIFT,
172 ));
173
174 f0.compute_basis(NUM_GENS.len() as i32);
175 f1.compute_basis(NUM_GENS.len() as i32 + SHIFT);
176
177 for (deg, num_gens) in NUM_GENS.into_iter().enumerate() {
178 f0.add_generators(deg as i32, num_gens, None);
179 f1.add_generators(deg as i32 + SHIFT, num_gens, None);
180 let mut rows = vec![FpVector::new(p, f0.dimension(deg as i32)); num_gens];
181 for (i, row) in rows.iter_mut().enumerate() {
182 row.add_basis_element(row.len() - num_gens + i, 1);
183 }
184 d.add_generators_from_rows(deg as i32 + SHIFT, rows);
185 }
186
187 let pb = HomPullback::new(
188 Arc::new(HomModule::new(f0, Arc::clone(&m))),
189 Arc::new(HomModule::new(f1, Arc::clone(&m))),
190 d,
191 );
192
193 pb.source.compute_basis(-2);
194 pb.target.compute_basis(-2 + SHIFT);
195
196 for deg in pb.source.min_degree()..=pb.source.max_computed_degree() {
197 let dim = pb.source.dimension(deg);
198 let mut matrix = Matrix::new(p, dim, dim);
199 pb.get_matrix(matrix.as_slice_mut(), deg);
200 assert_eq!(matrix, Matrix::identity(p, dim));
201 }
202 }
203
204 #[test]
205 fn test_pullback() {
206 const SHIFT: i32 = 3;
207 const NUM_GENS: [usize; 3] = [1, 1, 1];
208
209 let p = fp::prime::TWO;
210
211 let algebra = Arc::new(MilnorAlgebra::new(p, false));
212 let f0 = Arc::new(FreeModule::new(Arc::clone(&algebra), "F0".to_string(), 0));
213 let f1 = Arc::new(FreeModule::new(
214 Arc::clone(&algebra),
215 "F1".to_string(),
216 SHIFT,
217 ));
218 let m = Arc::new(
219 FDModule::from_json(Arc::clone(&algebra), &crate::tests::joker_json()).unwrap(),
220 );
221
222 let d = Arc::new(FreeModuleHomomorphism::new(
223 Arc::clone(&f1),
224 Arc::clone(&f0),
225 SHIFT,
226 ));
227
228 f0.compute_basis(NUM_GENS.len() as i32);
229 f1.compute_basis(NUM_GENS.len() as i32 + SHIFT);
230
231 for (deg, num_gens) in NUM_GENS.into_iter().enumerate() {
232 f0.add_generators(deg as i32, num_gens, None);
233 f1.add_generators(deg as i32 + SHIFT, num_gens, None);
234 }
235
236 d.add_generators_from_rows(SHIFT, vec![FpVector::from_slice(p, &[1])]);
237 d.add_generators_from_rows(SHIFT + 1, vec![FpVector::from_slice(p, &[0, 1])]);
238 d.add_generators_from_rows(SHIFT + 2, vec![FpVector::from_slice(p, &[1, 1, 1])]);
239
240 let pb = HomPullback::new(
241 Arc::new(HomModule::new(f0, Arc::clone(&m))),
242 Arc::new(HomModule::new(f1, Arc::clone(&m))),
243 d,
244 );
245
246 pb.source.compute_basis(-2);
247 pb.target.compute_basis(-2 + SHIFT);
248
249 let outputs = BiVec::from_vec(
250 -4,
251 vec![
252 Matrix::from_vec(p, &[vec![1]]),
253 Matrix::from_vec(p, &[vec![1, 0], vec![0, 1]]),
254 Matrix::from_vec(p, &[vec![1, 0, 1], vec![0, 1, 1], vec![0, 0, 1]]),
255 ],
256 );
257
258 for deg in pb.source.min_degree()..=pb.source.max_computed_degree() {
259 let dim = pb.source.dimension(deg);
260 let mut matrix = Matrix::new(p, dim, dim);
261 pb.get_matrix(matrix.as_slice_mut(), deg);
262 assert_eq!(matrix, outputs[deg]);
263 }
264 }
265}