algebra/module/module_trait.rs
1use std::sync::Arc;
2
3use auto_impl::auto_impl;
4use fp::{
5 prime::ValidPrime,
6 vector::{FpSlice, FpSliceMut},
7};
8use itertools::Itertools;
9
10use crate::algebra::Algebra;
11
12/// A bounded below module over an algebra.
13///
14/// To accommodate for infinite modules (e.g. modules in a free resolution), every module is
15/// potentially only define up to a degree. The extent to which the module is defined is kept track
16/// by two functions:
17///
18/// - [`Module::max_computed_degree`] gives the maximum degree for which the module is fully
19/// defined. It is guaranteed that the module will never change up to this degree in the future.
20///
21/// - [`Module::compute_basis`] extends the internal data to support querying data up to (and
22/// including) a given degree. In general, we can run this beyond the max computed degree.
23///
24/// A useful example to keep in mind is a [`FreeModule`](crate::module::FreeModule), where we have
25/// specified the generators up to some degree `t`. Then `t` is the max computed degree, while
26/// `compute_basis` computes data such as the offset of existing generators in potentially higher
27/// degrees.
28#[auto_impl(Box)]
29pub trait Module: std::fmt::Display + std::any::Any + Send + Sync {
30 type Algebra: Algebra;
31
32 /// The algebra the module is over.
33 fn algebra(&self) -> Arc<Self::Algebra>;
34
35 /// The minimum degree of the module, which is required to be bounded below
36 fn min_degree(&self) -> i32;
37
38 /// Compute internal data of the module so that we can query information up to degree `degree`.
39 /// This should be run by the user whenever they want to query such information.
40 ///
41 /// This function must be idempotent, and defaults to a no-op.
42 ///
43 /// See [`Module`] documentation for more details.
44 #[allow(unused_variables)]
45 fn compute_basis(&self, degree: i32) {}
46
47 /// The maximum `t` for which the module is fully defined at `t`. See [`Module`] documentation
48 /// for more details.
49 fn max_computed_degree(&self) -> i32;
50
51 /// The dimension of a module at the given degree
52 fn dimension(&self, degree: i32) -> usize;
53 fn act_on_basis(
54 &self,
55 result: FpSliceMut,
56 coeff: u32,
57 op_degree: i32,
58 op_index: usize,
59 mod_degree: i32,
60 mod_index: usize,
61 );
62
63 /// The name of a basis element. This is useful for debugging and printing results.
64 fn basis_element_to_string(&self, degree: i32, idx: usize) -> String;
65
66 /// Whether this is the unit module.
67 fn is_unit(&self) -> bool {
68 self.min_degree() == 0 && self.max_degree() == Some(0) && self.dimension(0) == 1
69 }
70
71 /// The prime the module is over, which should be equal to the prime of the algebra.
72 fn prime(&self) -> ValidPrime {
73 self.algebra().prime()
74 }
75
76 /// `max_degree` is the a degree such that if t > `max_degree`, then `self.dimension(t) = 0`.
77 fn max_degree(&self) -> Option<i32> {
78 None
79 }
80
81 /// Maximum degree of a generator under the Steenrod action. Every element in higher degree
82 /// must be obtainable from applying a Steenrod action to a lower degree element.
83 fn max_generator_degree(&self) -> Option<i32> {
84 self.max_degree()
85 }
86
87 fn total_dimension(&self) -> usize {
88 let max_degree = self
89 .max_degree()
90 .expect("total_dimension requires module to be bounded");
91
92 (self.min_degree()..=max_degree)
93 .map(|i| self.dimension(i))
94 .sum()
95 }
96
97 /// The length of `input` need not be equal to the dimension of the module in said degree.
98 /// Missing entries are interpreted to be 0, while extra entries must be zero.
99 ///
100 /// This flexibility is useful when resolving to a stem. The point is that we have elements in
101 /// degree `t` that are guaranteed to not contain generators of degree `t`, and we don't know
102 /// what generators will be added in degree `t` yet.
103 fn act(
104 &self,
105 mut result: FpSliceMut,
106 coeff: u32,
107 op_degree: i32,
108 op_index: usize,
109 input_degree: i32,
110 input: FpSlice,
111 ) {
112 assert!(input.len() <= self.dimension(input_degree));
113 let p = self.prime();
114 for (i, v) in input.iter_nonzero() {
115 self.act_on_basis(
116 result.copy(),
117 (coeff * v) % p,
118 op_degree,
119 op_index,
120 input_degree,
121 i,
122 );
123 }
124 }
125
126 fn act_by_element(
127 &self,
128 mut result: FpSliceMut,
129 coeff: u32,
130 op_degree: i32,
131 op: FpSlice,
132 input_degree: i32,
133 input: FpSlice,
134 ) {
135 assert_eq!(input.len(), self.dimension(input_degree));
136 assert_eq!(op.len(), self.algebra().dimension(op_degree));
137 let p = self.prime();
138 for (i, v) in op.iter_nonzero() {
139 self.act(
140 result.copy(),
141 (coeff * v) % p,
142 op_degree,
143 i,
144 input_degree,
145 input,
146 );
147 }
148 }
149
150 fn act_by_element_on_basis(
151 &self,
152 mut result: FpSliceMut,
153 coeff: u32,
154 op_degree: i32,
155 op: FpSlice,
156 input_degree: i32,
157 input_index: usize,
158 ) {
159 assert_eq!(op.len(), self.algebra().dimension(op_degree));
160 let p = self.prime();
161 for (i, v) in op.iter_nonzero() {
162 self.act_on_basis(
163 result.copy(),
164 (coeff * v) % p,
165 op_degree,
166 i,
167 input_degree,
168 input_index,
169 );
170 }
171 }
172
173 /// Gives the name of an element. The default implementation is derived from
174 /// [`Module::basis_element_to_string`] in the obvious way.
175 fn element_to_string(&self, degree: i32, element: FpSlice) -> String {
176 let result = element
177 .iter_nonzero()
178 .map(|(idx, value)| {
179 let coeff = if value == 1 {
180 "".to_string()
181 } else {
182 format!("{value} ")
183 };
184 let basis_elt = self.basis_element_to_string(degree, idx);
185 format!("{coeff}{basis_elt}")
186 })
187 .join(" + ");
188 if result.is_empty() {
189 "0".to_string()
190 } else {
191 result
192 }
193 }
194}
195
196#[derive(Debug)]
197pub struct ModuleFailedRelationError {
198 pub relation: String,
199 pub value: String,
200}
201
202impl std::fmt::Display for ModuleFailedRelationError {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 write!(
205 f,
206 "Relation failed:\n {} != 0\nInstead it is equal to {}\n",
207 &self.relation, &self.value
208 )
209 }
210}
211
212impl std::error::Error for ModuleFailedRelationError {}