1use std::{fmt::Write as _, sync::Arc};
2
3use anyhow::{Context, anyhow};
4use bivec::BiVec;
5use fp::vector::{FpSliceMut, FpVector};
6use serde::Deserialize;
7use serde_json::{json, value::Value};
8
9use crate::{
10 algebra::{Algebra, GeneratedAlgebra},
11 module::{Module, ModuleFailedRelationError, ZeroModule},
12};
13
14pub struct FiniteDimensionalModule<A: Algebra> {
15 algebra: Arc<A>,
16 pub name: String,
17 graded_dimension: BiVec<usize>,
18 gen_names: BiVec<Vec<String>>,
19 actions: BiVec<BiVec<Vec<Vec<FpVector>>>>,
21}
22
23impl<A: Algebra> std::fmt::Display for FiniteDimensionalModule<A> {
24 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25 write!(f, "{}", self.name)
26 }
27}
28
29impl<A: Algebra> Clone for FiniteDimensionalModule<A> {
30 fn clone(&self) -> Self {
31 Self {
32 algebra: Arc::clone(&self.algebra),
33 name: self.name.clone(),
34 graded_dimension: self.graded_dimension.clone(),
35 gen_names: self.gen_names.clone(),
36 actions: self.actions.clone(),
37 }
38 }
39}
40
41impl<A: Algebra> PartialEq for FiniteDimensionalModule<A> {
42 fn eq(&self, other: &Self) -> bool {
43 self.test_equal(other).is_ok()
44 }
45}
46
47impl<A: Algebra> Eq for FiniteDimensionalModule<A> {}
48
49impl<A: Algebra> FiniteDimensionalModule<A> {
50 pub fn test_equal(&self, other: &Self) -> Result<(), String> {
51 if self.graded_dimension != other.graded_dimension {
52 if self.graded_dimension.min_degree() != other.graded_dimension.min_degree() {
53 return Err(format!(
54 "Min degrees disagree. left.min_degree() = {} but right.min_degree() = {}.",
55 self.graded_dimension.min_degree(),
56 other.graded_dimension.min_degree()
57 ));
58 }
59 let mut disagreements = vec![];
60 for i in self.graded_dimension.min_degree()
61 ..std::cmp::max(self.graded_dimension.len(), other.graded_dimension.len())
62 {
63 if self.graded_dimension.get(i).copied().unwrap_or(0)
64 != other.graded_dimension.get(i).copied().unwrap_or(0)
65 {
66 disagreements.push(i);
67 }
68 }
69 if !disagreements.is_empty() {
70 return Err(format!(
71 "Graded dimensions disagree in positions {:?}. Left has graded \
72 dimensions:\n {:?}\nRight has graded dimension:\n {:?}\n",
73 disagreements, self.graded_dimension, other.graded_dimension
74 ));
75 }
76 }
77 let max_degree = std::cmp::min(self.actions.len(), other.actions.len());
78 if self.actions != other.actions {
79 let mut disagreements = vec![];
81 for input_degree in self.actions.min_degree()..max_degree {
82 for output_degree in self.actions[input_degree].min_degree()..max_degree {
83 for operation in 0..self.actions[input_degree][output_degree].len() {
84 for input_index in
85 0..self.actions[input_degree][output_degree][operation].len()
86 {
87 let self_action =
88 &self.actions[input_degree][output_degree][operation][input_index];
89 let other_action =
90 &other.actions[input_degree][output_degree][operation][input_index];
91 if self_action != other_action {
92 disagreements.push((
93 input_degree,
94 output_degree,
95 operation,
96 input_index,
97 self_action,
98 other_action,
99 ));
100 }
101 }
102 }
103 }
104 }
105
106 if !disagreements.is_empty() {
107 let mut err_string = "Actions disagree.\n".to_string();
108 for x in disagreements {
109 let _ = write!(
110 err_string,
111 " {} * {} disagreement.\n Left: {}\n Right: {}\n",
112 self.algebra.basis_element_to_string(x.1 - x.0, x.2),
113 self.basis_element_to_string(x.0, x.3),
114 self.element_to_string(x.1, x.4.as_slice()),
115 self.element_to_string(x.1, x.5.as_slice())
116 );
117 }
118 return Err(err_string);
119 }
120 }
121 Ok(())
122 }
123}
124
125impl<A: Algebra> Module for FiniteDimensionalModule<A> {
126 type Algebra = A;
127
128 fn algebra(&self) -> Arc<Self::Algebra> {
129 Arc::clone(&self.algebra)
130 }
131
132 fn min_degree(&self) -> i32 {
133 self.graded_dimension.min_degree()
134 }
135
136 fn max_computed_degree(&self) -> i32 {
137 i32::MAX
138 }
139
140 fn compute_basis(&self, _degree: i32) {}
141
142 fn dimension(&self, degree: i32) -> usize {
143 if degree < self.graded_dimension.min_degree() {
144 return 0;
145 }
146 if degree > self.graded_dimension.max_degree() {
147 return 0;
148 }
149 self.graded_dimension[degree]
150 }
151
152 fn basis_element_to_string(&self, degree: i32, idx: usize) -> String {
153 self.gen_names[degree][idx].clone()
154 }
155
156 fn act_on_basis(
157 &self,
158 mut result: FpSliceMut,
159 coeff: u32,
160 op_degree: i32,
161 op_index: usize,
162 mod_degree: i32,
163 mod_index: usize,
164 ) {
165 assert!(op_index < self.algebra().dimension(op_degree));
166 assert!(mod_index < self.dimension(mod_degree));
167 let output_dimension = self.dimension(mod_degree + op_degree);
168 if output_dimension == 0 {
169 return;
170 }
171 if op_degree == 0 {
172 result.add_basis_element(mod_index, coeff);
174 return;
175 }
176 let output = self.action(op_degree, op_index, mod_degree, mod_index);
177 result.add(output.as_slice(), coeff);
178 }
179
180 fn max_degree(&self) -> Option<i32> {
181 Some(self.graded_dimension.max_degree())
182 }
183}
184
185impl<A: Algebra> ZeroModule for FiniteDimensionalModule<A> {
186 fn zero_module(algebra: Arc<A>, min_degree: i32) -> Self {
187 Self::new(algebra, "zero".to_string(), BiVec::new(min_degree))
188 }
189}
190
191impl<A: Algebra> FiniteDimensionalModule<A> {
192 pub fn new(algebra: Arc<A>, name: String, graded_dimension: BiVec<usize>) -> Self {
193 let min_degree = graded_dimension.min_degree();
194 let max_degree = graded_dimension.len();
195 let degree_difference = max_degree - min_degree;
196 algebra.compute_basis(degree_difference);
197 let mut gen_names = BiVec::with_capacity(min_degree, max_degree);
198 for (i, dim) in graded_dimension.iter_enum() {
199 let mut names = Vec::with_capacity(*dim);
200 for j in 0..*dim {
201 names.push(format!("x{i}_{j}"));
202 }
203 gen_names.push(names);
204 }
205 let actions = Self::allocate_actions(&algebra, &graded_dimension);
206 Self {
207 algebra,
208 name,
209 graded_dimension,
210 gen_names,
211 actions,
212 }
213 }
214
215 pub fn set_basis_element_name(&mut self, degree: i32, idx: usize, name: String) {
216 self.gen_names[degree][idx] = name;
217 }
218
219 fn allocate_actions(
220 algebra: &Arc<A>,
221 graded_dimension: &BiVec<usize>,
222 ) -> BiVec<BiVec<Vec<Vec<FpVector>>>> {
223 let min_degree = graded_dimension.min_degree();
224 let max_degree = graded_dimension.len();
225 let mut result: BiVec<BiVec<Vec<Vec<FpVector>>>> =
226 BiVec::with_capacity(min_degree, max_degree);
227
228 for input_degree in min_degree..max_degree {
229 let mut outputs_vec: BiVec<Vec<Vec<FpVector>>> =
230 BiVec::with_capacity(input_degree, max_degree);
231 let number_of_inputs = graded_dimension[input_degree];
233 let mut ops_vec: Vec<Vec<FpVector>> = vec![Vec::with_capacity(number_of_inputs)];
234 for i in 0..number_of_inputs {
235 let mut result = FpVector::new(algebra.prime(), number_of_inputs);
236 result.set_entry(i, 1);
237 ops_vec[0].push(result);
238 }
239 outputs_vec.push(ops_vec);
240
241 for output_degree in input_degree + 1..max_degree {
242 let op_deg = output_degree - input_degree;
243 let number_of_operations = algebra.dimension(op_deg);
244 let number_of_inputs = graded_dimension[input_degree];
245 let number_of_outputs = graded_dimension[output_degree];
246
247 outputs_vec.push(vec![
248 vec![
249 FpVector::new(algebra.prime(), number_of_outputs);
250 number_of_inputs
251 ];
252 number_of_operations
253 ]);
254 }
255 assert!(outputs_vec.len() == max_degree);
256 result.push(outputs_vec);
257 }
258 assert!(result.len() == max_degree);
259 result
260 }
261
262 pub fn add_generator(&mut self, degree: i32, name: String) {
263 let old_max_degree = self.max_degree().unwrap();
264 let algebra = self.algebra();
265
266 self.graded_dimension.extend_with(degree, |_| 0);
267 self.graded_dimension[degree] += 1;
268
269 self.gen_names.extend_with(degree, |_| Vec::new());
270 self.gen_names[degree].push(name);
271
272 let min_degree = self.graded_dimension.min_degree();
273 let max_degree = self.graded_dimension.len();
274
275 if old_max_degree < degree {
277 self.actions.reserve((degree - old_max_degree) as usize);
278 for input_degree in min_degree..max_degree {
279 if input_degree <= old_max_degree {
280 self.actions[input_degree].reserve((degree - old_max_degree) as usize);
281 } else {
282 self.actions
283 .push(BiVec::with_capacity(input_degree, max_degree));
284
285 let number_of_inputs = self.dimension(input_degree);
287 let mut ops_vec: Vec<Vec<FpVector>> =
288 vec![Vec::with_capacity(number_of_inputs)];
289 for i in 0..number_of_inputs {
290 let mut result = FpVector::new(algebra.prime(), number_of_inputs);
291 result.set_entry(i, 1);
292 ops_vec[0].push(result);
293 }
294 self.actions[input_degree].push(ops_vec);
295 }
296
297 for output_degree in std::cmp::max(input_degree + 1, old_max_degree + 1)..max_degree
298 {
299 let op_deg = output_degree - input_degree;
301 let number_of_operations = algebra.dimension(op_deg);
302 let number_of_inputs = self.dimension(input_degree);
303 let number_of_outputs = self.dimension(output_degree);
304
305 self.actions[input_degree].push(vec![
306 vec![
307 FpVector::new(
308 algebra.prime(),
309 number_of_outputs
310 );
311 number_of_inputs
312 ];
313 number_of_operations
314 ]);
315 }
316 }
317 } else {
318 let new_dim = self.dimension(degree);
319
320 for output_degree in min_degree..max_degree {
322 let number_of_outputs = self.dimension(output_degree);
323 for v in &mut self.actions[degree][output_degree] {
325 v.push(FpVector::new(algebra.prime(), number_of_outputs));
326 }
327 }
328 for input_degree in min_degree..max_degree {
330 for v in &mut self.actions[input_degree][degree] {
332 for w in v {
334 w.extend_len(new_dim);
335 }
336 }
337 }
338
339 self.actions[degree][degree][0][new_dim - 1].set_entry(new_dim - 1, 1);
342 }
343 }
344
345 pub fn string_to_basis_element(&self, string: &str) -> Option<(i32, usize)> {
346 for (i, v) in self.gen_names.iter_enum() {
347 for (j, n) in v.iter().enumerate() {
348 if n == string {
349 return Some((i, j));
350 }
351 }
352 }
353 None
354 }
355
356 pub fn set_action(
357 &mut self,
358 operation_degree: i32,
359 operation_idx: usize,
360 input_degree: i32,
361 input_idx: usize,
362 output: &[u32],
363 ) {
364 assert!(operation_idx < self.algebra.dimension(operation_degree));
365 assert!(input_idx < self.dimension(input_degree));
366 let output_degree = input_degree + operation_degree;
367 let output_vector =
369 &mut self.actions[input_degree][output_degree][operation_idx][input_idx];
370 output_vector.copy_from_slice(output);
371 }
372
373 pub fn action(
376 &self,
377 operation_degree: i32,
378 operation_idx: usize,
379 input_degree: i32,
380 input_idx: usize,
381 ) -> &FpVector {
382 let output_degree = input_degree + operation_degree;
383 &self.actions[input_degree][output_degree][operation_idx][input_idx]
384 }
385
386 pub fn action_mut(
389 &mut self,
390 operation_degree: i32,
391 operation_idx: usize,
392 input_degree: i32,
393 input_idx: usize,
394 ) -> &mut FpVector {
395 let output_degree = input_degree + operation_degree;
396 &mut self.actions[input_degree][output_degree][operation_idx][input_idx]
397 }
398}
399
400impl<M: Module> From<&M> for FiniteDimensionalModule<M::Algebra> {
401 fn from(module: &M) -> Self {
403 let min_degree = module.min_degree();
404 let max_degree = module
405 .max_degree()
406 .expect("Can only convert to fininte dimensional module if bounded");
407 module.compute_basis(max_degree);
408
409 let mut graded_dimension = BiVec::with_capacity(min_degree, max_degree + 1);
410 for t in min_degree..=max_degree {
411 graded_dimension.push(module.dimension(t));
412 }
413 let mut result = Self::new(module.algebra(), module.to_string(), graded_dimension);
414 for t in min_degree..=max_degree {
415 for idx in 0..result.dimension(t) {
416 result.set_basis_element_name(t, idx, module.basis_element_to_string(t, idx));
417 }
418 }
419
420 let algebra = module.algebra();
421 for input_degree in min_degree..=max_degree {
422 for output_degree in (input_degree + 1)..=max_degree {
423 let output_dimension = result.dimension(output_degree);
424 if output_dimension == 0 {
425 continue;
426 }
427 let op_degree = output_degree - input_degree;
428
429 for input_idx in 0..result.dimension(input_degree) {
430 for op_idx in 0..algebra.dimension(op_degree) {
431 let output_vec: &mut FpVector =
432 result.action_mut(op_degree, op_idx, input_degree, input_idx);
433 module.act_on_basis(
434 output_vec.as_slice_mut(),
435 1,
436 op_degree,
437 op_idx,
438 input_degree,
439 input_idx,
440 );
441 }
442 }
443 }
444 }
445 result
446 }
447}
448
449impl<A: GeneratedAlgebra> FiniteDimensionalModule<A> {
450 pub fn from_json(algebra: Arc<A>, json: &Value) -> anyhow::Result<Self> {
451 let (graded_dimension, gen_names, gen_to_idx) = crate::module_gens_from_json(&json["gens"]);
452 let name = json["name"].as_str().unwrap_or("").to_string();
453
454 let mut result = Self::new(Arc::clone(&algebra), name, graded_dimension.clone());
455 for (i, dim) in graded_dimension.iter_enum() {
456 for j in 0..*dim {
457 result.set_basis_element_name(i, j, gen_names[i][j].clone());
458 }
459 }
460
461 let actions = Vec::<String>::deserialize(&json["actions"]).unwrap();
462 for action in actions {
463 result
464 .parse_action(&gen_to_idx, &action, false)
465 .with_context(|| format!("Failed to parse action: {action}"))?;
466 }
467 for input_degree in (result.min_degree()..=result.max_degree().unwrap()).rev() {
468 for output_degree in input_degree + 1..=result.max_degree().unwrap() {
469 result.extend_actions(input_degree, output_degree);
470 result.check_validity(input_degree, output_degree)?;
471 }
472 }
473 Ok(result)
474 }
475
476 pub fn to_json(&self, json: &mut Value) {
477 if !self.name.is_empty() {
478 json["name"] = Value::String(self.name.clone());
479 }
480 json["type"] = Value::from("finite dimensional module");
481 json["gens"] = json!({});
482 for (i, deg_i_gens) in self.gen_names.iter_enum() {
483 for g in deg_i_gens {
484 json["gens"][g] = Value::from(i);
485 }
486 }
487
488 json["actions"] = self.actions_to_json();
489 }
490
491 pub fn parse_action(
492 &mut self,
493 gen_to_idx: impl for<'a> Fn(&'a str) -> anyhow::Result<(i32, usize)>,
494 entry: &str,
495 overwrite: bool,
496 ) -> anyhow::Result<()> {
497 let algebra = self.algebra();
498
499 let (lhs, rhs) = entry
500 .split_once(" = ")
501 .ok_or_else(|| anyhow!("Invalid action: {entry}"))?;
502
503 let (action, g) = lhs
504 .rsplit_once(' ')
505 .ok_or_else(|| anyhow!("Invalid action: {entry}"))?;
506
507 let (op_deg, op_idx) = algebra
508 .basis_element_from_string(action)
509 .ok_or_else(|| anyhow!("Invalid algebra element: {action}"))?;
510
511 let (input_deg, input_idx) = gen_to_idx(g.trim())?;
512
513 let row = self.action_mut(op_deg, op_idx, input_deg, input_idx);
514
515 if overwrite {
516 row.set_to_zero();
517 }
518
519 if rhs == "0" {
520 return Ok(());
521 }
522
523 for item in rhs.split(" + ") {
524 let (coef, g) = match item.split_once(' ') {
525 Some((coef, g)) => (
526 str::parse(coef)
527 .map_err(|_| anyhow!("Invalid item on right-hand side: {item}"))?,
528 g,
529 ),
530 None => (1, item),
531 };
532 let (deg, idx) = gen_to_idx(g.trim())?;
533 if deg != input_deg + op_deg {
534 return Err(anyhow!(
535 "Degree of {g} is {deg} but degree of LHS is {}",
536 input_deg + op_deg
537 ));
538 }
539 row.add_basis_element(idx, coef);
540 }
541 Ok(())
542 }
543
544 pub fn check_validity(
545 &self,
546 input_deg: i32,
547 output_deg: i32,
548 ) -> Result<(), ModuleFailedRelationError> {
549 assert!(output_deg > input_deg);
550 let p = self.prime();
551 let algebra = self.algebra();
552 let op_deg = output_deg - input_deg;
553 let mut output_vec = FpVector::new(p, self.dimension(output_deg));
554 let mut tmp_output = FpVector::new(p, self.dimension(output_deg));
555 for idx in 0..self.dimension(input_deg) {
556 let relations = algebra.generating_relations(op_deg);
557 for relation in relations {
558 for &(coef, (deg_1, idx_1), (deg_2, idx_2)) in &relation {
559 let intermediate_dim = self.dimension(input_deg + deg_2);
560 tmp_output.set_scratch_vector_size(intermediate_dim);
561 self.act_on_basis(tmp_output.as_slice_mut(), 1, deg_2, idx_2, input_deg, idx);
562 self.act(
563 output_vec.as_slice_mut(),
564 coef,
565 deg_1,
566 idx_1,
567 deg_2 + input_deg,
568 tmp_output.as_slice(),
569 );
570 }
571
572 if !output_vec.is_zero() {
573 let mut relation_string = String::new();
574 for (coef, (deg_1, idx_1), (deg_2, idx_2)) in &relation {
575 let _ = write!(
576 relation_string,
577 "{} * {} * {} + ",
578 *coef,
579 &algebra.basis_element_to_string(*deg_1, *idx_1),
580 &algebra.basis_element_to_string(*deg_2, *idx_2)
581 );
582 }
583 for _ in 0..5 {
584 relation_string.pop();
585 }
586
587 let value_string = self.element_to_string(output_deg, output_vec.as_slice());
588 return Err(ModuleFailedRelationError {
589 relation: relation_string,
590 value: value_string,
591 });
592 }
593 }
594 }
595 Ok(())
596 }
597
598 pub fn extend_actions(&mut self, input_deg: i32, output_deg: i32) {
599 let p = self.prime();
600 let algebra = self.algebra();
601 let op_deg = output_deg - input_deg;
602 if self.dimension(output_deg) == 0 || self.dimension(input_deg) == 0 {
603 return;
604 }
605
606 let mut tmp_output = FpVector::new(p, self.dimension(output_deg));
607 let generators = algebra.generators(op_deg);
608 for idx in 0..self.dimension(input_deg) {
609 for op_idx in 0..algebra.dimension(op_deg) {
610 if !generators.contains(&op_idx) {
611 let mut output_vec = std::mem::replace(
612 &mut self.actions[input_deg][output_deg][op_idx][idx],
613 FpVector::new(p, 0),
614 );
615 let decomposition = algebra.decompose_basis_element(op_deg, op_idx);
616 for (coef, (deg_1, idx_1), (deg_2, idx_2)) in decomposition {
617 let intermediate_dim = self.dimension(input_deg + deg_2);
618 if intermediate_dim > tmp_output.len() {
619 tmp_output = FpVector::new(p, intermediate_dim);
620 }
621 self.act_on_basis(
622 tmp_output.slice_mut(0, intermediate_dim),
623 1,
624 deg_2,
625 idx_2,
626 input_deg,
627 idx,
628 );
629 self.act(
630 output_vec.as_slice_mut(),
631 coef,
632 deg_1,
633 idx_1,
634 deg_2 + input_deg,
635 tmp_output.slice(0, intermediate_dim),
636 );
637 tmp_output.set_to_zero();
638 }
639 let _ = std::mem::replace(
640 &mut self.actions[input_deg][output_deg][op_idx][idx],
641 output_vec,
642 );
643 }
644 }
645 }
646 }
647
648 fn actions_to_json(&self) -> Value {
649 let algebra = self.algebra();
650 let min_degree = self.min_degree();
651 let max_degree = self.graded_dimension.len();
652 let mut actions = Vec::new();
653 for input_degree in min_degree..max_degree {
654 for output_degree in (input_degree + 1)..max_degree {
655 if self.dimension(output_degree) == 0 {
656 continue;
657 }
658 let op_degree = output_degree - input_degree;
659 for op_idx in algebra.generators(op_degree) {
660 for input_idx in 0..self.dimension(input_degree) {
661 let vec = self.action(op_degree, op_idx, input_degree, input_idx);
662 if vec.is_zero() {
663 continue;
664 }
665 actions.push(format!(
666 "{} {} = {}",
667 algebra.generator_to_string(op_degree, op_idx),
668 self.gen_names[input_degree][input_idx],
669 self.element_to_string(output_degree, vec.as_slice())
670 ))
671 }
672 }
673 }
674 }
675 json!(actions)
676 }
677}
678
679#[cfg(test)]
680mod tests {
681 use super::*;
682 use crate::algebra::AdemAlgebra;
683
684 #[test]
685 fn test_module_check_validity() {
686 let p = fp::prime::ValidPrime::new(2);
687 let adem_algebra = Arc::new(AdemAlgebra::new(p, false));
688 adem_algebra.compute_basis(10);
689 let mut adem_module = FiniteDimensionalModule::new(
690 Arc::clone(&adem_algebra),
691 "".to_string(),
692 BiVec::from_vec(0, vec![1, 2, 1]),
693 );
694 adem_module.set_basis_element_name(0, 0, "x0".to_string());
695 adem_module.set_basis_element_name(1, 0, "x10".to_string());
696 adem_module.set_basis_element_name(1, 1, "x11".to_string());
697 adem_module.set_basis_element_name(2, 0, "x2".to_string());
698 adem_module.set_action(1, 0, 0, 0, &[1, 1]);
699 adem_module.set_action(1, 0, 1, 0, &[1]);
700 adem_module.set_action(1, 0, 1, 1, &[1]);
701 adem_module.set_action(2, 0, 0, 0, &[1]);
702 adem_module.check_validity(0, 2).unwrap();
703 }
704}