algebra/module/homomorphism/
free_module_homomorphism.rs1use std::sync::Arc;
2
3use fp::{
4 matrix::{MatrixSliceMut, QuasiInverse, Subspace},
5 vector::{FpSlice, FpSliceMut, FpVector},
6};
7use once::OnceBiVec;
8
9use crate::{
10 algebra::MuAlgebra,
11 module::{
12 Module, MuFreeModule,
13 free_module::OperationGeneratorPair,
14 homomorphism::{ModuleHomomorphism, ZeroHomomorphism},
15 },
16};
17
18pub type FreeModuleHomomorphism<M> = MuFreeModuleHomomorphism<false, M>;
19pub type UnstableFreeModuleHomomorphism<M> = MuFreeModuleHomomorphism<true, M>;
20
21pub struct MuFreeModuleHomomorphism<const U: bool, M: Module>
22where
23 M::Algebra: MuAlgebra<U>,
24{
25 source: Arc<MuFreeModule<U, M::Algebra>>,
26 target: Arc<M>,
27 outputs: OnceBiVec<Vec<FpVector>>, pub images: OnceBiVec<Option<Subspace>>,
29 pub kernels: OnceBiVec<Option<Subspace>>,
30 pub quasi_inverses: OnceBiVec<Option<QuasiInverse>>,
31 min_degree: i32,
32 degree_shift: i32,
34}
35
36impl<const U: bool, M: Module> ModuleHomomorphism for MuFreeModuleHomomorphism<U, M>
37where
38 M::Algebra: MuAlgebra<U>,
39{
40 type Source = MuFreeModule<U, M::Algebra>;
41 type Target = M;
42
43 fn source(&self) -> Arc<Self::Source> {
44 Arc::clone(&self.source)
45 }
46
47 fn target(&self) -> Arc<Self::Target> {
48 Arc::clone(&self.target)
49 }
50
51 fn degree_shift(&self) -> i32 {
52 self.degree_shift
53 }
54
55 fn apply_to_basis_element(
56 &self,
57 result: FpSliceMut,
58 coeff: u32,
59 input_degree: i32,
60 input_index: usize,
61 ) {
62 assert!(input_degree >= self.source.min_degree());
63 assert!(input_index < self.source.dimension(input_degree));
64 let output_degree = input_degree - self.degree_shift;
65 assert_eq!(
66 self.target.dimension(output_degree),
67 result.as_slice().len()
68 );
69 let OperationGeneratorPair {
70 operation_degree,
71 generator_degree,
72 operation_index,
73 generator_index,
74 } = *self.source.index_to_op_gen(input_degree, input_index);
75
76 if generator_degree >= self.min_degree() {
77 let output_on_generator = self.output(generator_degree, generator_index);
78 self.target.act(
79 result,
80 coeff,
81 operation_degree,
82 operation_index,
83 generator_degree - self.degree_shift,
84 output_on_generator.as_slice(),
85 );
86 }
87 }
88
89 fn quasi_inverse(&self, degree: i32) -> Option<&QuasiInverse> {
90 self.quasi_inverses.get(degree).and_then(Option::as_ref)
91 }
92
93 fn kernel(&self, degree: i32) -> Option<&Subspace> {
94 self.kernels.get(degree).and_then(Option::as_ref)
95 }
96
97 fn image(&self, degree: i32) -> Option<&Subspace> {
98 self.images.get(degree).and_then(Option::as_ref)
99 }
100
101 fn compute_auxiliary_data_through_degree(&self, degree: i32) {
102 self.kernels.extend(degree, |i| {
103 let (image, kernel, qi) = self.auxiliary_data(i);
104 self.images.push_checked(Some(image), i);
105 self.quasi_inverses.push_checked(Some(qi), i);
106 Some(kernel)
107 });
108 }
109}
110
111impl<const U: bool, M: Module> MuFreeModuleHomomorphism<U, M>
112where
113 M::Algebra: MuAlgebra<U>,
114{
115 pub fn new(
116 source: Arc<MuFreeModule<U, M::Algebra>>,
117 target: Arc<M>,
118 degree_shift: i32,
119 ) -> Self {
120 let min_degree = std::cmp::max(source.min_degree(), target.min_degree() + degree_shift);
121 let outputs = OnceBiVec::new(min_degree);
122 let kernels = OnceBiVec::new(min_degree);
123 let images = OnceBiVec::new(min_degree);
124 let quasi_inverses = OnceBiVec::new(min_degree);
125 Self {
126 source,
127 target,
128 outputs,
129 images,
130 kernels,
131 quasi_inverses,
132 min_degree,
133 degree_shift,
134 }
135 }
136
137 pub fn degree_shift(&self) -> i32 {
138 self.degree_shift
139 }
140
141 pub fn min_degree(&self) -> i32 {
142 self.min_degree
143 }
144
145 pub fn next_degree(&self) -> i32 {
146 self.outputs.len()
147 }
148
149 pub fn output(&self, generator_degree: i32, generator_index: usize) -> &FpVector {
150 assert!(
151 generator_degree >= self.min_degree(),
152 "generator_degree {} less than min degree {}",
153 generator_degree,
154 self.min_degree()
155 );
156 assert!(
157 generator_index < self.source.number_of_gens_in_degree(generator_degree),
158 "generator_index {} greater than number of generators {}",
159 generator_index,
160 self.source.number_of_gens_in_degree(generator_degree)
161 );
162 &self.outputs[generator_degree][generator_index]
163 }
164
165 pub fn differential_density(&self, degree: i32) -> f32 {
166 let outputs = &self.outputs[degree];
167 if outputs.is_empty() {
168 f32::NAN
169 } else {
170 outputs.iter().map(FpVector::density).sum::<f32>() / outputs.len() as f32
171 }
172 }
173
174 pub fn extend_by_zero(&self, degree: i32) {
175 let p = self.prime();
176 self.outputs.extend(degree, |i| {
177 let num_gens = self.source.number_of_gens_in_degree(i);
178 let dimension = self.target.dimension(i - self.degree_shift);
179 let mut new_outputs: Vec<FpVector> = Vec::with_capacity(num_gens);
180 for _ in 0..num_gens {
181 new_outputs.push(FpVector::new(p, dimension));
182 }
183 new_outputs
184 });
185 }
186
187 pub fn add_generators_from_big_vector(&self, degree: i32, outputs_vectors: FpSlice) {
188 let p = self.prime();
189 let new_generators = self.source.number_of_gens_in_degree(degree);
190 let target_dimension = self.target.dimension(degree - self.degree_shift);
191 let mut new_outputs: Vec<FpVector> = Vec::with_capacity(new_generators);
192 for _ in 0..new_generators {
193 new_outputs.push(FpVector::new(p, target_dimension));
194 }
195 if target_dimension == 0 {
196 self.outputs.push_checked(new_outputs, degree);
197 return;
198 }
199 for (i, new_output) in new_outputs.iter_mut().enumerate() {
200 new_output
201 .as_slice_mut()
202 .assign(outputs_vectors.restrict(target_dimension * i, target_dimension * (i + 1)));
203 }
204 self.outputs.push_checked(new_outputs, degree);
205 }
206
207 pub fn add_generators_from_matrix_rows(&self, degree: i32, mut matrix: MatrixSliceMut) {
209 let p = self.prime();
210 let new_generators = self.source.number_of_gens_in_degree(degree);
211 let target_dimension = self.target.dimension(degree - self.degree_shift);
212
213 let mut new_outputs: Vec<FpVector> = Vec::with_capacity(new_generators);
214 for _ in 0..new_generators {
215 new_outputs.push(FpVector::new(p, target_dimension));
216 }
217 if target_dimension == 0 {
218 self.outputs.push_checked(new_outputs, degree);
219 return;
220 }
221 for (i, new_output) in new_outputs.iter_mut().enumerate() {
222 new_output.as_slice_mut().assign(matrix.row(i));
223 }
224 self.outputs.push_checked(new_outputs, degree);
225 }
226
227 pub fn add_generators_from_rows(&self, degree: i32, rows: Vec<FpVector>) {
228 self.outputs.push_checked(rows, degree);
229 }
230
231 pub fn add_generators_from_rows_ooo(
234 &self,
235 degree: i32,
236 rows: Vec<FpVector>,
237 ) -> std::ops::Range<i32> {
238 self.outputs.push_ooo(rows, degree)
239 }
240
241 pub fn ooo_outputs(&self) -> Vec<i32> {
243 self.outputs.ooo_elements()
244 }
245
246 pub fn apply_to_generator(&self, result: &mut FpVector, coeff: u32, degree: i32, idx: usize) {
247 let output_on_gen = self.output(degree, idx);
248 result.add(output_on_gen, coeff);
249 }
250
251 pub fn set_image(&self, degree: i32, image: Option<Subspace>) {
252 self.images.push_checked(image, degree);
253 }
254
255 pub fn set_kernel(&self, degree: i32, kernel: Option<Subspace>) {
256 self.kernels.push_checked(kernel, degree);
257 }
258
259 pub fn set_quasi_inverse(&self, degree: i32, quasi_inverse: Option<QuasiInverse>) {
260 self.quasi_inverses.push_checked(quasi_inverse, degree);
261 }
262}
263
264impl<const U: bool, M: Module> ZeroHomomorphism<MuFreeModule<U, M::Algebra>, M>
265 for MuFreeModuleHomomorphism<U, M>
266where
267 M::Algebra: MuAlgebra<U>,
268{
269 fn zero_homomorphism(
270 source: Arc<MuFreeModule<U, M::Algebra>>,
271 target: Arc<M>,
272 degree_shift: i32,
273 ) -> Self {
274 Self::new(source, target, degree_shift)
275 }
276}
277
278impl<const U: bool, A: MuAlgebra<U>> MuFreeModuleHomomorphism<U, MuFreeModule<U, A>> {
279 pub fn hom_k(&self, t: i32) -> Vec<Vec<u32>> {
281 let source_dim = self.source.number_of_gens_in_degree(t + self.degree_shift);
282 let target_dim = self.target.number_of_gens_in_degree(t);
283 if target_dim == 0 {
284 return vec![];
285 }
286 let mut result = vec![vec![0; source_dim]; target_dim];
287
288 let offset = self.target.generator_offset(t, t, 0);
289 #[allow(clippy::needless_range_loop)]
290 for i in 0..source_dim {
291 let output = self.output(t + self.degree_shift, i);
292 #[allow(clippy::needless_range_loop)]
293 for j in 0..target_dim {
294 result[j][i] = output.entry(offset + j);
295 }
296 }
297 result
298 }
299}