1use std::{collections::HashMap, sync::Arc};
2
3use ahash::RandomState;
4use serde::{Deserialize, Serialize};
5
6use crate::linalg::{
7 field::Field,
8 graded::{BasisElement, GradedLinearMap, GradedVectorSpace},
9 grading::Grading,
10 matrix::Matrix,
11};
12
13use super::{
14 kcoalgebra::kCoalgebra, kmorphism::kComoduleMorphism, ktensor::kTensor, traits::Comodule,
15};
16
17#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
18#[allow(non_camel_case_types)]
19pub struct kBasisElement {
20 pub name: String,
21 pub generator: bool,
22 pub primitive: Option<usize>,
23 pub generated_index: usize,
24}
25
26impl BasisElement for kBasisElement {}
27
28#[derive(Clone, PartialEq)]
29#[allow(non_camel_case_types)]
30pub struct kComodule<G: Grading, F: Field, M: Matrix<F>> {
31 pub coalgebra: Arc<kCoalgebra<G, F, M>>,
32 pub space: GradedVectorSpace<G, kBasisElement>,
33 pub coaction: GradedLinearMap<G, F, M>,
34 pub tensor: kTensor<G>,
35}
36
37impl<G: Grading, F: Field, M: Matrix<F>> std::fmt::Debug for kComodule<G, F, M> {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "{:?}", self.space.0)
40 }
41}
42
43impl<G: Grading, F: Field, M: Matrix<F>> kComodule<G, F, M> {
44 pub fn verify(&self) -> bool {
45 for (&(m_gr, m_id), map) in &self.tensor.construct {
46 let &(t_gr, t_id) = map.get(&(G::zero(), 0)).unwrap();
47 if t_gr != m_gr {
48 return false;
49 };
50
51 let val = self.coaction.maps.get(&t_gr).unwrap().get(m_id, t_id);
52 if val != F::one() {
53 return false;
54 };
55 }
56 true
57 }
58
59 pub fn new(
60 coalgebra: Arc<kCoalgebra<G, F, M>>,
61 space: GradedVectorSpace<G, kBasisElement>,
62 coaction: GradedLinearMap<G, F, M>,
63 tensor: kTensor<G>,
64 ) -> Self {
65 let com = Self {
66 coalgebra,
67 space,
68 coaction,
69 tensor,
70 };
71 debug_assert!(com.verify());
72 com
73 }
74
75 pub fn find_cogens(&self, limit: G) -> usize {
76 let mut temp_coac = self.coaction.clone();
77
78 self.space.0.iter().for_each(|(g, els)| {
79 (0..els.len()).into_iter().for_each(|domain| {
80 let (_, codomain) = self.tensor.construct[&(*g, domain)][&(G::zero(), 0)];
81 temp_coac
82 .maps
83 .get_mut(&g)
84 .unwrap()
85 .set(domain, codomain, F::zero());
86 })
87 });
88
89 temp_coac
90 .maps
91 .iter()
92 .filter(|(&gr, _)| gr <= limit)
93 .map(|(_, map)| {
94 let kernel = map.kernel();
95 kernel.codomain()
96 })
97 .sum()
98 }
99}
100
101impl<G: Grading, F: Field, M: Matrix<F>> Comodule<G> for kComodule<G, F, M> {
102 type Element = kBasisElement;
103 type Coalgebra = kCoalgebra<G, F, M>;
104 type Morphism = kComoduleMorphism<G, F, M>;
105
106 fn get_generators(&self) -> Vec<(usize, G, Option<String>)> {
107 self.space
108 .0
109 .iter()
110 .flat_map(|(k, v)| {
111 v.iter().filter_map(|b| {
112 if b.generator {
113 Some((b.generated_index, *k, None))
114 } else {
115 None
116 }
117 })
118 })
119 .collect()
120 }
121
122 fn zero_comodule(coalgebra: Arc<Self::Coalgebra>) -> Self {
123 Self {
124 coalgebra: coalgebra,
125 space: GradedVectorSpace::new(),
126 coaction: GradedLinearMap::empty(),
127 tensor: kTensor::new(),
128 }
129 }
130
131 fn fp_comodule(coalgebra: Arc<Self::Coalgebra>) -> Self {
132 let zero = G::zero();
133
134 let el = kBasisElement {
136 name: "fp".to_string(),
137 generator: false,
138 primitive: None,
139 generated_index: 0,
140 };
141
142 let space_map: HashMap<G, Vec<kBasisElement>, RandomState> =
143 [(zero, vec![el])].into_iter().collect();
144 let space = GradedVectorSpace::from(space_map);
145
146 let coact_map: HashMap<G, M, RandomState> = [(zero, M::identity(1))].into_iter().collect();
147 let coaction = GradedLinearMap::from(coact_map);
148
149 debug_assert_eq!(
150 coalgebra
151 .space
152 .0
153 .get(&zero)
154 .expect("Coalgebra has no element in grade zero")
155 .len(),
156 1,
157 "Coalgebra is not a connected coalgebra"
158 );
159
160 let mut dimensions = HashMap::default();
161 dimensions.insert(zero, 1);
162
163 let mut construct = HashMap::default();
164 let mut first_entry = HashMap::default();
165 first_entry.insert((zero, 0), (zero, 0));
166 construct.insert((zero, 0), first_entry);
167
168 let mut deconstruct = HashMap::default();
169 deconstruct.insert((zero, 0), ((zero, 0), (zero, 0)));
170
171 let tensor = kTensor {
172 construct,
173 deconstruct,
174 dimensions,
175 };
176
177 Self {
178 coalgebra,
179 space,
180 coaction,
181 tensor,
182 }
183 }
184
185 fn direct_sum(&mut self, other: &mut Self) {
186 self.coaction.block_sum(&mut other.coaction);
187
188 let self_dimensions = self.space.0.iter().map(|(g, v)| (*g, v.len())).collect();
189
190 other.space.0.iter_mut().for_each(|(g, other_els)| {
191 self.space
192 .0
193 .entry(*g)
194 .and_modify(|self_els| {
195 self_els.extend(other_els.drain(0..));
196 })
197 .or_insert(other_els.drain(0..).collect());
198 });
199
200 self.tensor.direct_sum(&mut other.tensor, &self_dimensions);
201
202 debug_assert!(self.verify());
203 }
204
205 fn cofree_comodule(coalgebra: Arc<Self::Coalgebra>, index: usize, grade: G, limit: G) -> Self {
206 let coaction: HashMap<G, M, RandomState> = coalgebra
207 .coaction
208 .maps
209 .iter()
210 .filter_map(|(g, v)| {
211 let sum = *g + grade;
212 if sum <= limit {
213 Some((*g + grade, v.clone()))
214 } else {
215 None
216 }
217 })
218 .collect();
219 let space = coalgebra
220 .space
221 .0
222 .iter()
223 .filter_map(|(g, v)| {
224 let sum = *g + grade;
225 if sum <= limit {
226 let k_basis: Vec<kBasisElement> = v
227 .iter()
228 .map(|basis| {
229 let mut el = basis.clone();
230 el.generated_index = index;
231 el
232 })
233 .collect();
234 Some((*g + grade, k_basis))
235 } else {
236 None
237 }
238 })
239 .collect();
240 let tensor = coalgebra.tensor.add_and_restrict(grade, limit);
241
242 kComodule::new(
243 coalgebra,
244 GradedVectorSpace(space),
245 GradedLinearMap::from(coaction),
246 tensor,
247 )
248 }
249}