algebra/module/
quotient_module.rs1use std::sync::Arc;
2
3use bivec::BiVec;
4use fp::{
5 matrix::Subspace,
6 vector::{FpSlice, FpSliceMut, FpVector},
7};
8
9use crate::module::{Module, ZeroModule};
10
11pub struct QuotientModule<M: Module> {
13 pub module: Arc<M>,
15 pub subspaces: BiVec<Subspace>,
17 pub basis_list: BiVec<Vec<usize>>,
20 pub truncation: i32,
22}
23
24impl<M: Module> std::fmt::Display for QuotientModule<M> {
25 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 write!(f, "Quotient of {}", self.module)
27 }
28}
29
30impl<M: Module> QuotientModule<M> {
31 pub fn new(module: Arc<M>, truncation: i32) -> Self {
32 module.compute_basis(truncation);
33
34 let p = module.prime();
35 let min_degree = module.min_degree();
36
37 let mut subspaces = BiVec::with_capacity(min_degree, truncation + 1);
38 let mut basis_list = BiVec::with_capacity(min_degree, truncation + 1);
39
40 for t in min_degree..=truncation {
41 let dim = module.dimension(t);
42 subspaces.push(Subspace::new(p, dim));
43 basis_list.push((0..dim).collect());
44 }
45 Self {
46 module,
47 subspaces,
48 basis_list,
49 truncation,
50 }
51 }
52
53 pub fn quotient(&mut self, degree: i32, element: FpSlice) {
54 if degree <= self.truncation {
55 self.subspaces[degree].add_vector(element);
56 self.flush(degree);
57 }
58 }
59
60 pub fn quotient_basis_elements(
61 &mut self,
62 degree: i32,
63 elements: impl std::iter::Iterator<Item = usize>,
64 ) {
65 self.subspaces[degree].add_basis_elements(elements);
66 self.flush(degree);
67 }
68
69 pub fn quotient_vectors(
73 &mut self,
74 degree: i32,
75 vecs: impl for<'a> FnMut(FpSliceMut<'a>) -> Option<()>,
76 ) {
77 if degree > self.truncation {
78 return;
79 }
80 self.subspaces[degree].add_vectors(vecs);
81 self.flush(degree);
82 }
83
84 fn flush(&mut self, degree: i32) {
85 self.basis_list[degree].clear();
86 self.basis_list[degree].extend(
87 self.subspaces[degree]
88 .pivots()
89 .iter()
90 .enumerate()
91 .filter_map(|(idx, &row)| if row < 0 { Some(idx) } else { None }),
92 );
93 }
94
95 pub fn quotient_all(&mut self, degree: i32) {
96 self.subspaces[degree].set_to_entire();
97 self.basis_list[degree] = Vec::new();
98 }
99
100 pub fn act_on_original_basis(
101 &self,
102 mut result: FpSliceMut,
103 coeff: u32,
104 op_degree: i32,
105 op_index: usize,
106 mod_degree: i32,
107 mod_index: usize,
108 ) {
109 if op_degree + mod_degree > self.truncation {
110 return;
111 }
112 self.module.act_on_basis(
113 result.copy(),
114 coeff,
115 op_degree,
116 op_index,
117 mod_degree,
118 mod_index,
119 );
120 self.reduce(op_degree + mod_degree, result)
121 }
122
123 pub fn reduce(&self, degree: i32, mut vec: FpSliceMut) {
124 if degree > self.truncation {
125 vec.set_to_zero()
126 } else {
127 self.subspaces[degree].reduce(vec);
128 }
129 }
130
131 pub fn old_basis_to_new(&self, degree: i32, mut new: FpSliceMut, old: FpSlice) {
132 for (i, idx) in self.basis_list[degree].iter().enumerate() {
133 new.add_basis_element(i, old.entry(*idx));
134 }
135 }
136}
137
138impl<M: Module> Module for QuotientModule<M> {
139 type Algebra = M::Algebra;
140
141 fn algebra(&self) -> Arc<Self::Algebra> {
142 self.module.algebra()
143 }
144
145 fn min_degree(&self) -> i32 {
146 self.module.min_degree()
147 }
148
149 fn max_computed_degree(&self) -> i32 {
150 self.module.max_computed_degree()
151 }
152
153 fn dimension(&self, degree: i32) -> usize {
154 if degree > self.truncation {
155 0
156 } else {
157 self.module.dimension(degree) - self.subspaces[degree].dimension()
158 }
159 }
160
161 fn act_on_basis(
162 &self,
163 result: FpSliceMut,
164 coeff: u32,
165 op_degree: i32,
166 op_index: usize,
167 mod_degree: i32,
168 mod_index: usize,
169 ) {
170 let target_deg = op_degree + mod_degree;
171 if target_deg > self.truncation {
172 return;
173 }
174
175 let mut result_ = FpVector::new(self.prime(), self.module.dimension(target_deg));
176 self.act_on_original_basis(
177 result_.as_slice_mut(),
178 coeff,
179 op_degree,
180 op_index,
181 mod_degree,
182 self.basis_list[mod_degree][mod_index],
183 );
184 self.reduce(target_deg, result_.as_slice_mut());
185 self.old_basis_to_new(target_deg, result, result_.as_slice());
186 }
187
188 fn basis_element_to_string(&self, degree: i32, idx: usize) -> String {
189 self.module
190 .basis_element_to_string(degree, self.basis_list[degree][idx])
191 }
192
193 fn max_degree(&self) -> Option<i32> {
194 Some(match self.module.max_degree() {
195 Some(max_degree) => std::cmp::min(max_degree, self.truncation),
196 None => self.truncation,
197 })
198 }
199}
200
201impl<M: ZeroModule> ZeroModule for QuotientModule<M> {
202 fn zero_module(algebra: Arc<M::Algebra>, min_degree: i32) -> Self {
203 Self::new(Arc::new(M::zero_module(algebra, min_degree)), min_degree)
204 }
205}