1use std::collections::{BTreeMap, BTreeSet};
4
5use crate::{Error, Result};
6
7use super::linear::{LinearMap, SparseVector, checked_term_sum, negate, nullspace, rank};
8use super::{
9 Bigrade, BipersistenceMap, BipersistenceModule, BipersistenceRectangle, BipersistenceRegion,
10};
11
12impl BipersistenceModule {
13 pub fn map(&self, lower: Bigrade, upper: Bigrade) -> Result<BipersistenceMap> {
18 self.validate_comparable(lower, upper)?;
19 let upper_node = self.node(upper)?;
20 let mut composed = LinearMap::identity(upper_node.rank);
21 let mut current = upper;
22 while current.scale() > lower.scale() {
23 let next = Bigrade::new(current.scale() - 1, current.density());
24 let cover = self.cover_linear(next, current)?;
25 composed = LinearMap::compose(&cover, &composed, self.modulus)?;
26 current = next;
27 }
28 while current.density() > lower.density() {
29 let next = Bigrade::new(current.scale(), current.density() - 1);
30 let cover = self.cover_linear(next, current)?;
31 composed = LinearMap::compose(&cover, &composed, self.modulus)?;
32 current = next;
33 }
34 Ok(self.public_map(lower, upper, &composed))
35 }
36
37 pub fn map_rank(&self, lower: Bigrade, upper: Bigrade) -> Result<usize> {
39 Ok(self.map(lower, upper)?.rank)
40 }
41
42 pub fn rectangle_rank(&self, rectangle: BipersistenceRectangle) -> Result<usize> {
47 self.validate_comparable(rectangle.lower, rectangle.upper)?;
48 let region = BipersistenceRegion::new(rectangle_grades(rectangle))?;
49 self.region_rank(®ion)
50 }
51
52 pub fn region_rank(&self, region: &BipersistenceRegion) -> Result<usize> {
59 self.validate_region(region)?;
60 let grades = region.grades();
61 let (offsets, ambient_rank) = self.region_offsets(grades)?;
62 if ambient_rank > self.limits.max_linear_variables {
63 return Err(Error::InvalidInput(format!(
64 "region direct-sum rank exceeds the limit {}",
65 self.limits.max_linear_variables
66 )));
67 }
68 if ambient_rank == 0 {
69 return Ok(0);
70 }
71 let (relations, equations) = self.region_relations(grades, &offsets)?;
72 let limit = nullspace(equations, ambient_rank, self.modulus);
73 let first = grades[0];
74 let first_offset = offsets[&first];
75 let first_rank = self.node(first)?.rank;
76 let images = limit_images(limit, first_offset, first_rank, self.modulus);
77 let relation_rank = rank(relations.clone(), ambient_rank, self.modulus);
78 let union_rank = rank(
79 relations.into_iter().chain(images).collect(),
80 ambient_rank,
81 self.modulus,
82 );
83 Ok(union_rank - relation_rank)
84 }
85
86 fn region_offsets(&self, grades: &[Bigrade]) -> Result<(BTreeMap<Bigrade, usize>, usize)> {
87 let mut offsets = BTreeMap::new();
88 let mut ambient_rank = 0usize;
89 for grade in grades {
90 offsets.insert(*grade, ambient_rank);
91 ambient_rank = ambient_rank
92 .checked_add(self.node(*grade)?.rank)
93 .ok_or_else(|| Error::InvalidInput("region direct-sum rank overflows".into()))?;
94 }
95 Ok((offsets, ambient_rank))
96 }
97
98 fn region_relations(
99 &self,
100 grades: &[Bigrade],
101 offsets: &BTreeMap<Bigrade, usize>,
102 ) -> Result<(Vec<SparseVector>, Vec<SparseVector>)> {
103 let mut relations = Vec::new();
104 let mut equations = Vec::new();
105 let mut coefficient_count = 0usize;
106 for (lower, upper) in comparable_pairs(grades) {
107 let map = self.map(lower, upper)?;
108 let linear = self.linear_from_public(&map)?;
109 let source_offset = offsets[&upper];
110 let target_offset = offsets[&lower];
111 append_map_relations(
112 &mut relations,
113 &linear,
114 source_offset,
115 target_offset,
116 self.modulus,
117 &mut coefficient_count,
118 self.limits,
119 )?;
120 append_map_equations(
121 &mut equations,
122 &linear,
123 source_offset,
124 target_offset,
125 self.modulus,
126 &mut coefficient_count,
127 self.limits,
128 )?;
129 }
130 Ok((relations, equations))
131 }
132
133 fn validate_region(&self, region: &BipersistenceRegion) -> Result<()> {
134 for &grade in region.grades() {
135 self.node(grade)?;
136 }
137 let mut reached = BTreeSet::from([region.grades()[0]]);
138 loop {
139 let before = reached.len();
140 for &grade in region.grades() {
141 if region.grades().iter().copied().any(|other| {
142 reached.contains(&other) && (grade.precedes(other) || other.precedes(grade))
143 }) {
144 reached.insert(grade);
145 }
146 }
147 if reached.len() == region.grades().len() {
148 return Ok(());
149 }
150 if reached.len() == before {
151 return Err(Error::InvalidInput(
152 "a bipersistence region must have a connected comparability graph".into(),
153 ));
154 }
155 }
156 }
157}
158
159fn comparable_pairs(grades: &[Bigrade]) -> Vec<(Bigrade, Bigrade)> {
160 let mut pairs = Vec::new();
161 for (position, &lower) in grades.iter().enumerate() {
162 for &upper in &grades[position + 1..] {
163 if lower.precedes(upper) {
164 pairs.push((lower, upper));
165 }
166 }
167 }
168 pairs
169}
170
171fn rectangle_grades(rectangle: BipersistenceRectangle) -> Vec<Bigrade> {
172 let mut grades = Vec::new();
173 for scale in rectangle.lower.scale()..=rectangle.upper.scale() {
174 for density in rectangle.lower.density()..=rectangle.upper.density() {
175 grades.push(Bigrade::new(scale, density));
176 }
177 }
178 grades
179}
180
181fn append_map_relations(
182 relations: &mut Vec<SparseVector>,
183 linear: &LinearMap,
184 source_offset: usize,
185 target_offset: usize,
186 modulus: u32,
187 coefficient_count: &mut usize,
188 limits: super::BipersistenceLimits,
189) -> Result<()> {
190 for (source, column) in linear.columns.iter().enumerate() {
191 let mut relation = SparseVector::default();
192 relation.insert(source_offset + source, 1, modulus);
193 for (&target, &coefficient) in &column.0 {
194 relation.insert(
195 target_offset + target,
196 negate(coefficient, modulus),
197 modulus,
198 );
199 }
200 *coefficient_count = checked_term_sum(*coefficient_count, relation.len(), limits)?;
201 relations.push(relation);
202 }
203 Ok(())
204}
205
206fn append_map_equations(
207 equations: &mut Vec<SparseVector>,
208 linear: &LinearMap,
209 source_offset: usize,
210 target_offset: usize,
211 modulus: u32,
212 coefficient_count: &mut usize,
213 limits: super::BipersistenceLimits,
214) -> Result<()> {
215 for target in 0..linear.target_rank {
216 let mut equation = SparseVector::default();
217 equation.insert(target_offset + target, 1, modulus);
218 for (source, column) in linear.columns.iter().enumerate() {
219 if let Some(&coefficient) = column.0.get(&target) {
220 equation.insert(
221 source_offset + source,
222 negate(coefficient, modulus),
223 modulus,
224 );
225 }
226 }
227 *coefficient_count = checked_term_sum(*coefficient_count, equation.len(), limits)?;
228 equations.push(equation);
229 }
230 Ok(())
231}
232
233fn limit_images(
234 limit: Vec<SparseVector>,
235 first_offset: usize,
236 first_rank: usize,
237 modulus: u32,
238) -> Vec<SparseVector> {
239 limit
240 .into_iter()
241 .map(|vector| {
242 let mut image = SparseVector::default();
243 for (&position, &coefficient) in vector.0.range(first_offset..first_offset + first_rank)
244 {
245 image.insert(position, coefficient, modulus);
246 }
247 image
248 })
249 .collect::<Vec<_>>()
250}