holos_tda/relative_interface/
builder.rs1use std::collections::BTreeSet;
2
3use crate::certificate::{CertificateError, CertificateLimits, ChangeColumn};
4use crate::field::{MODULUS_LIMIT, is_prime};
5use crate::filtration::{ComplexLimits, FilteredSimplicialComplex, FlagComplexParams, ScalarGrade};
6use crate::{Diagram, RipsParams, SparseDistanceMatrix, rips_persistence_sparse};
7
8use super::cancellation::{cancel_relative, simplex_boundary};
9use super::chain::check_chain_complex;
10use super::composition::{check_composition_children, merge_child_cells};
11use super::digest::{certificate_digest, diagrams_equal, source_digest};
12use super::model::{
13 InterfaceCancellation, InterfaceCell, RelativeInterfaceCertificate, RelativeInterfaceWork,
14};
15use super::reduction::reduce_core;
16use super::validation::{
17 canonical_vertices, cell_order, check_protected_cells, count_cells, enforce_cell_limits,
18 validate_parameters,
19};
20
21impl RelativeInterfaceCertificate {
22 pub fn build(
27 input: &SparseDistanceMatrix,
28 params: &RipsParams,
29 protected_vertices: &[usize],
30 limits: CertificateLimits,
31 ) -> Result<Self, CertificateError> {
32 let labels: Vec<_> = (0..input.len()).collect();
33 Self::build_labeled(input, &labels, params, protected_vertices, limits)
34 }
35
36 pub fn build_labeled(
42 input: &SparseDistanceMatrix,
43 labels: &[usize],
44 params: &RipsParams,
45 protected_vertices: &[usize],
46 limits: CertificateLimits,
47 ) -> Result<Self, CertificateError> {
48 validate_parameters(input, labels, params, protected_vertices, limits)?;
49 let complex = FilteredSimplicialComplex::from_flag_graph(
50 input,
51 labels,
52 FlagComplexParams {
53 max_dimension: params.max_dim + 1,
54 threshold: params.threshold,
55 limits: ComplexLimits {
56 max_vertices: limits.max_vertices,
57 max_edges: limits.max_edges,
58 max_triangles: limits.max_triangles,
59 max_higher_simplices: limits.max_higher_simplices,
60 },
61 },
62 )
63 .map_err(|error| CertificateError::new(error.to_string()))?;
64 let certificate = Self::build_complex(
65 &complex,
66 params.max_dim,
67 params.modulus,
68 protected_vertices,
69 limits,
70 )?;
71 let expected = rips_persistence_sparse(input, params)
72 .map_err(|error| CertificateError::new(error.to_string()))?;
73 if !diagrams_equal(&expected, certificate.diagram()) {
74 return Err(CertificateError::new(format!(
75 "relative interface differs from the compute engine: expected {:?}, got {:?}",
76 expected.bars,
77 certificate.diagram().bars
78 )));
79 }
80 Ok(certificate)
81 }
82
83 pub fn build_complex(
88 complex: &FilteredSimplicialComplex<ScalarGrade>,
89 max_dim: usize,
90 modulus: u32,
91 protected_vertices: &[usize],
92 limits: CertificateLimits,
93 ) -> Result<Self, CertificateError> {
94 if max_dim > limits.max_dimension
95 || u64::from(modulus) >= MODULUS_LIMIT
96 || !is_prime(modulus as u64)
97 {
98 return Err(CertificateError::new(
99 "relative interface dimension or coefficient field is invalid",
100 ));
101 }
102 if complex.simplices().len() != max_dim + 2 {
103 return Err(CertificateError::new(format!(
104 "relative interface needs simplex dimensions zero through {}, got zero through {}",
105 max_dim + 1,
106 complex.max_dimension()
107 )));
108 }
109 let labels: BTreeSet<_> = complex.vertex_labels().iter().copied().collect();
110 if protected_vertices
111 .iter()
112 .any(|vertex| !labels.contains(vertex))
113 {
114 return Err(CertificateError::new(
115 "protected vertex is outside the interface scope",
116 ));
117 }
118 let mut cells = Vec::with_capacity(complex.simplices().len());
119 for dimension in complex.simplices() {
120 let mut output = Vec::with_capacity(dimension.len());
121 for simplex in dimension {
122 output.push(InterfaceCell {
123 vertices: simplex.vertices().to_vec(),
124 value: simplex.grade().value(),
125 boundary: if simplex.dimension() == 0 {
126 Vec::new()
127 } else {
128 simplex_boundary(simplex.vertices(), modulus)
129 },
130 });
131 }
132 output.sort_by(cell_order);
133 cells.push(output);
134 }
135 Self::from_cells(cells, max_dim, modulus, protected_vertices, limits)
136 }
137
138 pub fn compose(
144 children: &[&Self],
145 protected_vertices: &[usize],
146 limits: CertificateLimits,
147 ) -> Result<Self, CertificateError> {
148 for child in children {
149 child.verify(limits)?;
150 }
151 Self::compose_trusted(children, protected_vertices, limits)
152 }
153
154 pub(crate) fn compose_trusted(
155 children: &[&Self],
156 protected_vertices: &[usize],
157 limits: CertificateLimits,
158 ) -> Result<Self, CertificateError> {
159 let first = check_composition_children(children)?;
160 let cells = merge_child_cells(children, first.max_dim)?;
161 Self::from_cells(
162 cells,
163 first.max_dim,
164 first.modulus,
165 protected_vertices,
166 limits,
167 )
168 }
169
170 fn from_cells(
171 input_cells: Vec<Vec<InterfaceCell>>,
172 max_dim: usize,
173 modulus: u32,
174 protected_vertices: &[usize],
175 limits: CertificateLimits,
176 ) -> Result<Self, CertificateError> {
177 let protected_vertices = canonical_vertices(protected_vertices)?;
178 let input_count = count_cells(&input_cells);
179 enforce_cell_limits(&input_cells, limits)?;
180 check_chain_complex(&input_cells, max_dim, modulus, limits)?;
181 let protected: BTreeSet<_> = protected_vertices.iter().copied().collect();
182 let (core_cells, cancellations) =
183 cancel_relative(input_cells.clone(), &protected, modulus)?;
184 check_protected_cells(&input_cells, &core_cells, &protected)?;
185 check_chain_complex(&core_cells, max_dim, modulus, limits)?;
186 let (columns, diagram, reduction_additions) = reduce_core(&core_cells, modulus, limits)?;
187 let work = RelativeInterfaceWork {
188 input_cells: input_count,
189 cancellations: cancellations.len(),
190 core_cells: count_cells(&core_cells),
191 reduction_additions,
192 };
193 let digest = certificate_digest(
194 max_dim,
195 modulus,
196 &protected_vertices,
197 &core_cells,
198 &columns,
199 &diagram,
200 );
201 Ok(Self {
202 max_dim,
203 modulus,
204 protected_vertices,
205 input_cells,
206 cancellations,
207 core_cells,
208 columns,
209 diagram,
210 digest,
211 work,
212 })
213 }
214
215 pub fn max_dim(&self) -> usize {
217 self.max_dim
218 }
219
220 pub fn modulus(&self) -> u32 {
222 self.modulus
223 }
224
225 pub fn protected_vertices(&self) -> &[usize] {
227 &self.protected_vertices
228 }
229
230 pub fn input_cells(&self) -> &[Vec<InterfaceCell>] {
232 &self.input_cells
233 }
234
235 pub fn cancellations(&self) -> &[InterfaceCancellation] {
237 &self.cancellations
238 }
239
240 pub fn core_cells(&self) -> &[Vec<InterfaceCell>] {
242 &self.core_cells
243 }
244
245 pub fn graded_columns(&self) -> &[Vec<ChangeColumn>] {
247 &self.columns
248 }
249
250 pub fn diagram(&self) -> &Diagram {
252 &self.diagram
253 }
254
255 pub fn digest(&self) -> &[u8; 32] {
257 &self.digest
258 }
259
260 pub fn source_digest(&self) -> [u8; 32] {
265 source_digest(
266 self.max_dim,
267 self.modulus,
268 &self.protected_vertices,
269 &self.input_cells,
270 )
271 }
272
273 pub fn work(&self) -> RelativeInterfaceWork {
275 self.work
276 }
277}