1use std::collections::{BTreeMap, BTreeSet, VecDeque};
4
5use crate::Result;
6
7use super::linear::{affine_solution, coordinate_vector, public_terms};
8use super::{Bigrade, BipersistenceModule, BipersistenceTerm};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12pub enum ClassExtensionKind {
13 Unique,
15 Ambiguous,
17 NoExtension,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct ClassExtension {
24 pub grade: Bigrade,
26 pub kind: ClassExtensionKind,
28 pub class: Vec<BipersistenceTerm>,
30 pub ambiguity: Vec<Vec<BipersistenceTerm>>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct ClassExtensionRegion {
37 pub region_index: usize,
39 pub kind: ClassExtensionKind,
41 pub ambiguity_rank: usize,
43 pub grades: Vec<Bigrade>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct CohomologyClassAtlas {
50 pub base_grade: Bigrade,
52 pub base_class: Vec<BipersistenceTerm>,
54 pub extensions: Vec<ClassExtension>,
56 pub regions: Vec<ClassExtensionRegion>,
58}
59
60impl BipersistenceModule {
61 pub fn class_atlas(
63 &self,
64 base_grade: Bigrade,
65 base_class: &[BipersistenceTerm],
66 ) -> Result<CohomologyClassAtlas> {
67 self.validate_grade(base_grade)?;
68 let base = coordinate_vector(
69 base_class,
70 self.node(base_grade)?.rank,
71 self.modulus(),
72 "bipersistence base class coordinates are not canonical",
73 )?;
74 if base.is_zero() {
75 return Err(crate::Error::InvalidInput(
76 "a bipersistence class atlas needs a nonzero base class".into(),
77 ));
78 }
79 let mut extensions = Vec::new();
80 for scale in base_grade.scale()..self.scale_count() {
81 for density in base_grade.density()..self.density_count() {
82 let grade = Bigrade::new(scale, density);
83 extensions.push(self.extension_at_grade(base_grade, grade, &base)?);
84 }
85 }
86 let regions = extension_regions(
87 base_grade,
88 self.scale_count(),
89 self.density_count(),
90 &extensions,
91 );
92 Ok(CohomologyClassAtlas {
93 base_grade,
94 base_class: public_terms(&base),
95 extensions,
96 regions,
97 })
98 }
99
100 fn extension_at_grade(
101 &self,
102 base_grade: Bigrade,
103 grade: Bigrade,
104 base: &super::linear::SparseVector,
105 ) -> Result<ClassExtension> {
106 let map = self.map(base_grade, grade)?;
107 let linear = self.linear_from_public(&map)?;
108 let (kind, class, ambiguity) = match affine_solution(&linear.columns, base, self.modulus())
109 {
110 None => (ClassExtensionKind::NoExtension, Vec::new(), Vec::new()),
111 Some((particular, kernel)) if kernel.is_empty() => (
112 ClassExtensionKind::Unique,
113 public_terms(&particular),
114 Vec::new(),
115 ),
116 Some((particular, kernel)) => (
117 ClassExtensionKind::Ambiguous,
118 public_terms(&particular),
119 kernel.iter().map(public_terms).collect(),
120 ),
121 };
122 Ok(ClassExtension {
123 grade,
124 kind,
125 class,
126 ambiguity,
127 })
128 }
129}
130
131fn extension_regions(
132 base: Bigrade,
133 scale_count: usize,
134 density_count: usize,
135 extensions: &[ClassExtension],
136) -> Vec<ClassExtensionRegion> {
137 let by_grade = extensions
138 .iter()
139 .map(|extension| (extension.grade, extension))
140 .collect::<BTreeMap<_, _>>();
141 let mut visited = BTreeSet::new();
142 let mut regions = Vec::new();
143 for extension in extensions {
144 if !visited.insert(extension.grade) {
145 continue;
146 }
147 let label = (extension.kind, extension.ambiguity.len());
148 let mut queue = VecDeque::from([extension.grade]);
149 let mut grades = Vec::new();
150 while let Some(grade) = queue.pop_front() {
151 grades.push(grade);
152 for neighbor in grade_neighbors(grade, base, scale_count, density_count) {
153 let candidate = by_grade[&neighbor];
154 if (candidate.kind, candidate.ambiguity.len()) == label && visited.insert(neighbor)
155 {
156 queue.push_back(neighbor);
157 }
158 }
159 }
160 grades.sort_unstable();
161 regions.push(ClassExtensionRegion {
162 region_index: regions.len(),
163 kind: label.0,
164 ambiguity_rank: label.1,
165 grades,
166 });
167 }
168 regions
169}
170
171fn grade_neighbors(
172 grade: Bigrade,
173 base: Bigrade,
174 scale_count: usize,
175 density_count: usize,
176) -> Vec<Bigrade> {
177 let mut neighbors = Vec::with_capacity(4);
178 if grade.scale() > base.scale() {
179 neighbors.push(Bigrade::new(grade.scale() - 1, grade.density()));
180 }
181 if grade.scale() + 1 < scale_count {
182 neighbors.push(Bigrade::new(grade.scale() + 1, grade.density()));
183 }
184 if grade.density() > base.density() {
185 neighbors.push(Bigrade::new(grade.scale(), grade.density() - 1));
186 }
187 if grade.density() + 1 < density_count {
188 neighbors.push(Bigrade::new(grade.scale(), grade.density() + 1));
189 }
190 neighbors
191}