1use crate::tensor::Tensor10D;
4
5use super::connectivity::ConnectivitySummary;
6use super::csr_adjacency::{CsrHeader, CsrSummary};
7use super::{HalfEdge, INVALID_INDEX};
8
9const _: () = assert!(std::mem::size_of::<CsrHeader>() == 16);
14const _: () = assert!(std::mem::align_of::<CsrHeader>() == 4);
15const _: () = assert!(std::mem::size_of::<CsrSummary>() == 12);
16const _: () = assert!(std::mem::align_of::<CsrSummary>() == 4);
17const _: () = assert!(std::mem::size_of::<HalfEdge>() == 16);
18const _: () = assert!(std::mem::align_of::<HalfEdge>() == 4);
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum FeatureError {
22 OutputTooSmall { required: usize },
23 VertexOutOfRange { half_edge: usize, vertex: u32 },
24}
25
26pub fn encode_topology_features_10d(
43 positions: &[[f32; 3]],
44 half_edges: &[HalfEdge],
45 q: f32,
46 w: f32,
47 t: f32,
48 out: &mut [Tensor10D],
49) -> Result<usize, FeatureError> {
50 encode_topology_features_10d_with_connectivity(positions, half_edges, q, w, t, None, out)
51}
52
53pub fn encode_topology_features_10d_with_connectivity(
62 positions: &[[f32; 3]],
63 half_edges: &[HalfEdge],
64 q: f32,
65 w: f32,
66 t: f32,
67 connectivity: Option<&ConnectivitySummary>,
68 out: &mut [Tensor10D],
69) -> Result<usize, FeatureError> {
70 if out.len() < positions.len() {
71 return Err(FeatureError::OutputTooSmall {
72 required: positions.len(),
73 });
74 }
75 for (index, position) in positions.iter().copied().enumerate() {
76 out[index] = Tensor10D {
77 q,
78 v: 0.0,
79 w,
80 x: position[0],
81 y: position[1],
82 z: position[2],
83 t,
84 alpha: 0.0,
85 mu: 0.0,
86 sigma: 0.0,
87 };
88 }
89
90 let mut max_degree = 0.0f32;
91 for (edge_index, edge) in half_edges.iter().enumerate() {
92 let vertex = edge.origin as usize;
93 if vertex >= positions.len() {
94 return Err(FeatureError::VertexOutOfRange {
95 half_edge: edge_index,
96 vertex: edge.origin,
97 });
98 }
99 out[vertex].sigma += 1.0;
100 max_degree = max_degree.max(out[vertex].sigma);
101 if edge.twin == INVALID_INDEX {
102 out[vertex].mu = 1.0;
103 out[vertex].v = 3.0;
104 let destination = half_edges
105 .get(edge.next as usize)
106 .map(|next| next.origin as usize);
107 if let Some(destination) = destination.filter(|&v| v < positions.len()) {
108 out[destination].mu = 1.0;
109 out[destination].v = 3.0;
110 }
111 }
112 }
113 if max_degree > 0.0 {
114 for feature in &mut out[..positions.len()] {
115 feature.alpha = feature.sigma / max_degree;
116 }
117 }
118
119 if let Some(summary) = connectivity {
120 let component_class = summary.component_count as f32;
121 let genus_class = summary.genus.unwrap_or(0) as f32;
122 for feature in &mut out[..positions.len()] {
123 feature.sigma += component_class * 0.001;
124 if feature.mu == 0.0 {
125 feature.v = genus_class;
126 }
127 }
128 }
129
130 Ok(positions.len())
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::specialized_libs::computational_geometry::{
137 build_triangle_half_edges, compute_connectivity, EdgeSlot,
138 };
139
140 #[test]
141 fn mesh_graph_becomes_tensor_features() {
142 let positions = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
143 let triangles = [[0, 1, 2]];
144 let mut edges = [HalfEdge::default(); 3];
145 let mut slots = [EdgeSlot::default(); 8];
146 build_triangle_half_edges(3, &triangles, &mut edges, &mut slots).unwrap();
147
148 let mut features = [Tensor10D::default(); 3];
149 let n = encode_topology_features_10d(&positions, &edges, 2.0, 7.0, 11.0, &mut features)
150 .unwrap();
151 assert_eq!(n, 3);
152 assert!(features.iter().all(|feature| feature.v == 3.0));
153 assert!(features.iter().all(|feature| feature.mu == 1.0));
154 assert!(features.iter().all(|feature| feature.alpha == 1.0));
155 assert_eq!(features[1].x, 1.0);
156 assert_eq!(features[2].w, 7.0);
157 }
158
159 #[test]
160 fn connectivity_enrichment_tetrahedron() {
161 let positions: [[f32; 3]; 4] = [
163 [0.0, 0.0, 0.0],
164 [1.0, 0.0, 0.0],
165 [0.0, 1.0, 0.0],
166 [0.0, 0.0, 1.0],
167 ];
168 let triangles = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]];
169 let mut edges = [HalfEdge::default(); 12];
170 let mut slots = [EdgeSlot::default(); 32];
171 build_triangle_half_edges(4, &triangles, &mut edges, &mut slots).unwrap();
172
173 let mut labels = [0u32; 4];
174 let mut queue = [0u32; 4];
175 let mut visited = [false; 12];
176 let summary =
177 compute_connectivity(4, 4, &edges, &mut labels, &mut queue, &mut visited).unwrap();
178
179 let mut features = [Tensor10D::default(); 4];
180 encode_topology_features_10d_with_connectivity(
181 &positions,
182 &edges,
183 1.0,
184 2.0,
185 3.0,
186 Some(&summary),
187 &mut features,
188 )
189 .unwrap();
190
191 assert!(features.iter().all(|f| f.v == 0.0));
193 assert!(features.iter().all(|f| f.sigma > 3.0 && f.sigma < 3.002));
195 }
196
197 #[test]
198 fn connectivity_enrichment_single_triangle() {
199 let positions = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
200 let triangles = [[0, 1, 2]];
201 let mut edges = [HalfEdge::default(); 3];
202 let mut slots = [EdgeSlot::default(); 8];
203 build_triangle_half_edges(3, &triangles, &mut edges, &mut slots).unwrap();
204
205 let mut labels = [0u32; 1];
206 let mut queue = [0u32; 1];
207 let mut visited = [false; 3];
208 let summary =
209 compute_connectivity(3, 1, &edges, &mut labels, &mut queue, &mut visited).unwrap();
210
211 let mut features = [Tensor10D::default(); 3];
212 encode_topology_features_10d_with_connectivity(
213 &positions,
214 &edges,
215 1.0,
216 2.0,
217 3.0,
218 Some(&summary),
219 &mut features,
220 )
221 .unwrap();
222
223 assert!(features.iter().all(|f| f.v == 3.0));
225 assert!(features.iter().all(|f| f.sigma > 1.0 && f.sigma < 1.002));
227 }
228
229 #[test]
230 fn differential_vs_naive_oracle_multi_component() {
231 let positions: [[f32; 3]; 6] = [
233 [0.0, 0.0, 0.0],
234 [1.0, 0.0, 0.0],
235 [0.0, 1.0, 0.0],
236 [2.0, 0.0, 0.0],
237 [3.0, 0.0, 0.0],
238 [2.0, 1.0, 0.0],
239 ];
240 let triangles = [[0, 1, 2], [3, 4, 5]];
241 let mut edges = [HalfEdge::default(); 6];
242 let mut slots = [EdgeSlot::default(); 16];
243 build_triangle_half_edges(6, &triangles, &mut edges, &mut slots).unwrap();
244
245 let mut labels = [0u32; 2];
246 let mut queue = [0u32; 2];
247 let mut visited = [false; 6];
248 let summary =
249 compute_connectivity(6, 2, &edges, &mut labels, &mut queue, &mut visited).unwrap();
250 assert_eq!(summary.component_count, 2);
251
252 let mut features = [Tensor10D::default(); 6];
253 encode_topology_features_10d_with_connectivity(
254 &positions,
255 &edges,
256 1.0,
257 2.0,
258 3.0,
259 Some(&summary),
260 &mut features,
261 )
262 .unwrap();
263
264 assert!(features.iter().all(|f| (f.sigma - 1.002).abs() < 1e-5));
267 }
268}