1use std::sync::Arc;
2
3use bivec::BiVec;
4use fp::{
5 prime::minus_one_to_the_n,
6 vector::{FpSlice, FpSliceMut, FpVector},
7};
8use once::OnceBiVec;
9
10use crate::{
11 algebra::{Algebra, Bialgebra},
12 module::{Module, ZeroModule, block_structure::BlockStructure},
13};
14
15pub struct TensorModule<M: Module, N: Module<Algebra = M::Algebra>> {
19 pub left: Arc<M>,
20 pub right: Arc<N>,
21 block_structures: OnceBiVec<BlockStructure>,
22}
23
24impl<M: Module, N: Module<Algebra = M::Algebra>> std::fmt::Display for TensorModule<M, N> {
25 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 write!(f, "{} (x) {}", self.left, self.right)
27 }
28}
29
30impl<A, M, N> TensorModule<M, N>
31where
32 A: Algebra + Bialgebra,
33 M: Module<Algebra = A>,
34 N: Module<Algebra = A>,
35{
36 pub fn new(left: Arc<M>, right: Arc<N>) -> Self {
37 Self {
38 block_structures: OnceBiVec::new(left.min_degree() + right.min_degree()),
39 left,
40 right,
41 }
42 }
43
44 pub fn seek_module_num(&self, degree: i32, index: usize) -> i32 {
45 self.block_structures[degree]
46 .index_to_generator_basis_elt(index)
47 .generator_degree
48 }
49
50 pub fn offset(&self, degree: i32, left_degree: i32) -> usize {
51 self.block_structures[degree]
52 .generator_to_block(left_degree, 0)
53 .start
54 }
55
56 fn act_helper(
57 &self,
58 mut result: FpSliceMut,
59 coeff: u32,
60 op_degree: i32,
61 op_index: usize,
62 mod_degree: i32,
63 input: FpSlice,
64 ) {
65 let algebra = self.algebra();
66 let p = self.prime();
67
68 let coproduct = algebra.coproduct(op_degree, op_index).into_iter();
69 let output_degree = mod_degree + op_degree;
70
71 let mut left_result = FpVector::new(p, 0);
72 let mut right_result = FpVector::new(p, 0);
73
74 for (op_deg_l, op_idx_l, op_deg_r, op_idx_r) in coproduct {
75 let mut idx = 0;
76 for left_deg in self.left.min_degree()..=(mod_degree - self.right.min_degree()) {
77 let right_deg = mod_degree - left_deg;
78
79 let left_source_dim = Module::dimension(&*self.left, left_deg);
83 let right_source_dim = Module::dimension(&*self.right, right_deg);
84
85 let left_target_dim = Module::dimension(&*self.left, left_deg + op_deg_l);
86 let right_target_dim = Module::dimension(&*self.right, right_deg + op_deg_r);
87
88 if left_target_dim == 0
89 || right_target_dim == 0
90 || left_source_dim == 0
91 || right_source_dim == 0
92 {
93 idx += left_source_dim * right_source_dim;
94 continue;
95 }
96
97 left_result.set_scratch_vector_size(left_target_dim);
98 right_result.set_scratch_vector_size(right_target_dim);
99
100 for i in 0..left_source_dim {
101 self.left.act_on_basis(
102 left_result.as_slice_mut(),
103 coeff,
104 op_deg_l,
105 op_idx_l,
106 left_deg,
107 i,
108 );
109
110 if left_result.is_zero() {
111 idx += right_source_dim;
112 continue;
113 }
114
115 for j in 0..right_source_dim {
116 let entry = input.entry(idx);
117 idx += 1;
118 if entry == 0 {
119 continue;
120 }
121 self.right.act_on_basis(
122 right_result.as_slice_mut(),
123 entry,
124 op_deg_r,
125 op_idx_r,
126 right_deg,
127 j,
128 );
129
130 if right_result.is_zero() {
131 continue;
132 }
133 result.add_tensor(
134 self.offset(output_degree, left_deg + op_deg_l),
135 minus_one_to_the_n(self.prime(), op_deg_r * left_deg),
136 left_result.as_slice(),
137 right_result.as_slice(),
138 );
139
140 right_result.set_to_zero();
141 }
142 left_result.set_to_zero();
143 }
144 }
145 }
146 }
147}
148impl<A, M, N> Module for TensorModule<M, N>
149where
150 A: Algebra + Bialgebra,
151 M: Module<Algebra = A>,
152 N: Module<Algebra = A>,
153{
154 type Algebra = A;
155
156 fn algebra(&self) -> Arc<A> {
157 self.left.algebra()
158 }
159
160 fn min_degree(&self) -> i32 {
161 self.left.min_degree() + self.right.min_degree()
162 }
163
164 fn max_computed_degree(&self) -> i32 {
165 self.block_structures.len()
166 }
167
168 fn compute_basis(&self, degree: i32) {
169 self.left.compute_basis(degree - self.right.min_degree());
170 self.right.compute_basis(degree - self.left.min_degree());
171 self.block_structures.extend(degree, |i| {
172 let mut block_sizes =
173 BiVec::with_capacity(self.left.min_degree(), i - self.right.min_degree() + 1);
174 for j in self.left.min_degree()..=i - self.right.min_degree() {
175 let mut block_sizes_entry = Vec::with_capacity(Module::dimension(&*self.left, j));
179 for _ in 0..Module::dimension(&*self.left, j) {
180 block_sizes_entry.push(Module::dimension(&*self.right, i - j))
181 }
182 block_sizes.push(block_sizes_entry);
183 }
184 assert_eq!(block_sizes.len(), i - self.right.min_degree() + 1);
185 BlockStructure::new(&block_sizes)
186 });
187 }
188
189 fn dimension(&self, degree: i32) -> usize {
190 self.block_structures[degree].total_dimension()
191 }
192
193 fn act_on_basis(
194 &self,
195 result: FpSliceMut,
196 coeff: u32,
197 op_degree: i32,
198 op_index: usize,
199 mod_degree: i32,
200 mod_index: usize,
201 ) {
202 let mut working_element = FpVector::new(self.prime(), self.dimension(mod_degree));
203 working_element.set_entry(mod_index, 1);
204
205 self.act(
206 result,
207 coeff,
208 op_degree,
209 op_index,
210 mod_degree,
211 working_element.as_slice(),
212 );
213 }
214
215 fn act(
216 &self,
217 mut result: FpSliceMut,
218 coeff: u32,
219 op_degree: i32,
220 op_index: usize,
221 mod_degree: i32,
222 input: FpSlice,
223 ) {
224 if op_degree == 0 {
225 result.add(input, coeff);
226 return;
227 }
228
229 let algebra = self.algebra();
230 let p = self.prime();
231 let decomposition = algebra.decompose(op_degree, op_index);
232 match decomposition.len() {
233 0 => panic!("Decomposition has length 0"),
234 1 => self.act_helper(result.copy(), coeff, op_degree, op_index, mod_degree, input),
235 n => {
236 let (op_degree, op_index) = decomposition[0];
237
238 let mut working_degree = mod_degree;
239 let mut working_element =
240 FpVector::new(p, self.dimension(working_degree + op_degree));
241 self.act_helper(
242 working_element.as_slice_mut(),
243 coeff,
244 op_degree,
245 op_index,
246 working_degree,
247 input,
248 );
249 working_degree += op_degree;
250
251 for &(op_degree, op_index) in &decomposition[1..n - 1] {
252 let mut new_element =
253 FpVector::new(p, self.dimension(working_degree + op_degree));
254 self.act_helper(
255 new_element.as_slice_mut(),
256 coeff,
257 op_degree,
258 op_index,
259 working_degree,
260 working_element.as_slice(),
261 );
262 working_element = new_element;
263 working_degree += op_degree;
264 }
265
266 let (op_degree, op_index) = decomposition[n - 1];
267 self.act_helper(
268 result.copy(),
269 coeff,
270 op_degree,
271 op_index,
272 working_degree,
273 working_element.as_slice(),
274 );
275 }
276 }
277 }
278
279 fn basis_element_to_string(&self, degree: i32, idx: usize) -> String {
280 let left_degree = self.seek_module_num(degree, idx);
281 let right_degree = degree - left_degree;
282 let inner_index = idx - self.offset(degree, left_degree);
283
284 let right_dim = Module::dimension(&*self.right, right_degree);
288
289 let left_index = inner_index / right_dim;
290 let right_index = inner_index % right_dim;
291
292 format!(
293 "{}.{}",
294 self.left.basis_element_to_string(left_degree, left_index),
295 self.right
296 .basis_element_to_string(right_degree, right_index)
297 )
298 }
299
300 fn max_degree(&self) -> Option<i32> {
301 Some(self.left.max_degree()? + self.right.max_degree()?)
302 }
303}
304
305impl<A, M, N> ZeroModule for TensorModule<M, N>
306where
307 A: Algebra + Bialgebra,
308 M: Module<Algebra = A> + ZeroModule,
309 N: Module<Algebra = A> + ZeroModule,
310{
311 fn zero_module(algebra: Arc<A>, min_degree: i32) -> Self {
312 Self::new(
313 Arc::new(M::zero_module(Arc::clone(&algebra), min_degree)),
314 Arc::new(N::zero_module(algebra, min_degree)),
315 )
316 }
317}