harness_space/complexes/simplicial.rs
1//! # Simplicial Complexes Module
2//!
3//! This module provides implementations for working with simplicial complexes, one of the most
4//! fundamental structures in algebraic topology. Simplicial complexes are built from simplices
5//! - geometric objects that generalize triangles to arbitrary dimensions.
6//!
7//! ## Mathematical Background
8//!
9//! A **simplex** is the simplest possible convex polytope in any given dimension:
10//! - **0-simplex**: A point (vertex)
11//! - **1-simplex**: A line segment (edge)
12//! - **2-simplex**: A triangle
13//! - **3-simplex**: A tetrahedron
14//! - **k-simplex**: The convex hull of k+1 affinely independent points
15//!
16//! A **simplicial complex** K is a collection of simplices that satisfies:
17//! 1. **Closure Property**: If σ ∈ K, then all faces of σ are also in K
18//! 2. **Intersection Property**: The intersection of any two simplices in K is either empty or a
19//! common face
20//!
21//! ## Simplicial vs Other Complex Types
22//!
23//! Simplicial complexes have several advantages:
24//! - **Natural Geometry**: Built from the simplest convex shapes
25//! - **Well-Established Theory**: Extensive literature and algorithms
26//! - **Efficient Computation**: Many optimized algorithms available
27//! - **Universal Approximation**: Any topological space can be triangulated
28//!
29//! However, they can be more complex than cubical complexes for some applications due to
30//! the varying face structures at different dimensions.
31//!
32//! ## Implementation Design
33//!
34//! ### Canonical Representation
35//!
36//! Simplices are represented by their **sorted vertex lists**, ensuring a unique canonical
37//! form for each geometric simplex regardless of how vertices were originally specified.
38//! This enables efficient deduplication and comparison.
39//!
40//! ### Orientation Conventions
41//!
42//! The simplicial boundary operator uses the **standard alternating orientation**:
43//! ```text
44//! ∂[v₀, v₁, ..., vₖ] = Σᵢ (-1)ⁱ [v₀, ..., v̂ᵢ, ..., vₖ]
45//! ```
46//! where v̂ᵢ indicates that vertex vᵢ is omitted. This ensures the fundamental property ∂² = 0.
47//!
48//! ### Integration with Generic Framework
49//!
50//! The [`Simplex`] type implements [`ComplexElement`], making it compatible with the generic
51//! [`Complex<T>`] framework. This allows:
52//! - Uniform algorithms for homology computation
53//! - Consistent interfaces across complex types
54//! - Reuse of lattice and poset operations
55//!
56//! ## Core Types
57//!
58//! ### [`Simplex`]
59//!
60//! The fundamental building block representing a k-dimensional simplex. Key features:
61//! - Vertex-based representation with automatic sorting
62//! - Dimension automatically determined from vertex count
63//! - Efficient face computation using combinatorial methods
64//! - Standard orientation for boundary operators
65//!
66//! ### [`SimplicialComplex`] (Type Alias)
67//!
68//! A type alias for `Complex<Simplex>` that provides:
69//! - Automatic closure property enforcement
70//! - Efficient face relationship tracking
71//! - Homology computation capabilities
72//! - Integration with poset and topology traits
73//!
74//! ## Usage Patterns
75//!
76//! ### Basic Simplex Creation
77//!
78//! ```rust
79//! use harness_space::complexes::Simplex;
80//!
81//! // Create by dimension and vertices
82//! let triangle = Simplex::new(2, vec![0, 1, 2]);
83//!
84//! // Create with automatic dimension detection
85//! let edge = Simplex::from_vertices(vec![3, 4]);
86//!
87//! // Vertices are automatically sorted for canonical representation
88//! let same_triangle = Simplex::new(2, vec![2, 0, 1]); // Same as first triangle
89//! assert!(triangle.same_content(&same_triangle));
90//! ```
91//!
92//! ### Building Simplicial Complexes
93//!
94//! ```rust
95//! use harness_space::complexes::{Simplex, SimplicialComplex};
96//!
97//! let mut complex = SimplicialComplex::new();
98//!
99//! // Add a triangle - automatically includes all faces
100//! let triangle = Simplex::new(2, vec![0, 1, 2]);
101//! let added = complex.join_element(triangle);
102//!
103//! // Complex now contains: 1 triangle + 3 edges + 3 vertices
104//! assert_eq!(complex.elements_of_dimension(2).len(), 1);
105//! assert_eq!(complex.elements_of_dimension(1).len(), 3);
106//! assert_eq!(complex.elements_of_dimension(0).len(), 3);
107//! ```
108//!
109//! ### Computing Homology
110//!
111//! ```rust
112//! use harness_algebra::algebras::boolean::Boolean;
113//! use harness_space::{
114//! complexes::{Simplex, SimplicialComplex},
115//! prelude::*,
116//! };
117//!
118//! // Create a triangle boundary (circle)
119//! let mut complex = SimplicialComplex::new();
120//! complex.join_element(Simplex::new(1, vec![0, 1]));
121//! complex.join_element(Simplex::new(1, vec![1, 2]));
122//! complex.join_element(Simplex::new(1, vec![2, 0]));
123//!
124//! // Compute homology over Z/2Z
125//! let h1 = complex.homology::<Boolean>(1);
126//! assert_eq!(h1.betti_number, 1); // One 1-dimensional hole
127//! ```
128//!
129//! ### Working with Face Relations
130//!
131//! ```rust
132//! # use harness_space::{complexes::{SimplicialComplex, Simplex}, prelude::*};
133//! # let mut complex = SimplicialComplex::new();
134//! # let triangle = Simplex::new(2, vec![0, 1, 2]);
135//! # let added = complex.join_element(triangle);
136//!
137//! // Get faces using the element's method
138//! let abstract_faces = added.faces(); // Returns simplices without IDs
139//!
140//! // Get faces that exist in the complex
141//! let complex_faces = complex.faces(&added); // Returns elements with IDs
142//!
143//! // Work with boundary operators
144//! let boundary_with_signs = added.boundary_with_orientations();
145//! for (face, orientation) in boundary_with_signs {
146//! println!("Face: {:?}, Orientation: {}", face.vertices(), orientation);
147//! }
148//! ```
149//!
150//! ## Standard Constructions
151//!
152//! ### Common Geometric Objects
153//!
154//! ```rust
155//! use harness_space::complexes::{Simplex, SimplicialComplex};
156//!
157//! // Point
158//! let point = Simplex::new(0, vec![0]);
159//!
160//! // Line segment
161//! let edge = Simplex::new(1, vec![0, 1]);
162//!
163//! // Triangle
164//! let triangle = Simplex::new(2, vec![0, 1, 2]);
165//!
166//! // Tetrahedron
167//! let tetrahedron = Simplex::new(3, vec![0, 1, 2, 3]);
168//! ```
169//!
170//! ### Topological Spaces
171//!
172//! ```rust
173//! # use harness_space::complexes::{SimplicialComplex, Simplex};
174//!
175//! // Circle (S¹) - triangle boundary
176//! let mut circle = SimplicialComplex::new();
177//! circle.join_element(Simplex::new(1, vec![0, 1]));
178//! circle.join_element(Simplex::new(1, vec![1, 2]));
179//! circle.join_element(Simplex::new(1, vec![2, 0]));
180//!
181//! // Sphere (S²) - tetrahedron boundary
182//! let mut sphere = SimplicialComplex::new();
183//! sphere.join_element(Simplex::new(2, vec![0, 1, 2]));
184//! sphere.join_element(Simplex::new(2, vec![0, 1, 3]));
185//! sphere.join_element(Simplex::new(2, vec![0, 2, 3]));
186//! sphere.join_element(Simplex::new(2, vec![1, 2, 3]));
187//! ```
188//!
189//! ## Performance Characteristics
190//!
191//! - **Face Computation**: O(kⁿ) where k is dimension and n is vertex count
192//! - **Boundary Operator**: O(k) where k is the number of faces
193//! - **Complex Construction**: O(f·log f) where f is the total number of faces
194//! - **Homology Computation**: O(n³) where n is the number of elements in relevant dimensions
195//!
196//! ## Implementation Notes
197//!
198//! ### Memory Layout
199//!
200//! Simplices store only their vertex indices (as `Vec<usize>`), making them lightweight.
201//! The dimension is cached for efficiency, and IDs are assigned only when needed.
202//!
203//! ### Ordering and Hashing
204//!
205//! Simplices are ordered lexicographically by their vertex lists, then by dimension.
206//! This provides a consistent total ordering suitable for use in sorted collections.
207//!
208//! ### Thread Safety
209//!
210//! Individual simplices are `Send + Sync` when their vertices are. Complexes use
211//! interior mutability patterns and may require careful synchronization for concurrent access.
212//!
213//! ## Related Modules
214//!
215//! - [`crate::complexes::cubical`]: Alternative cubical complex implementation
216//! - [`crate::complexes`]: Generic complex framework and algorithms
217//! - [`crate::homology`]: Homological algebra computations
218//! - [`crate::lattice`]: Underlying lattice structures for face relations
219//!
220//! ## References
221//!
222//! - Hatcher, A. "Algebraic Topology" - Comprehensive reference for simplicial complexes
223//! - Munkres, J. "Elements of Algebraic Topology" - Classic text with clear foundations
224//! - Edelsbrunner, H. "Computational Topology" - Algorithmic perspective on simplicial complexes
225
226use itertools::Itertools;
227
228use super::*;
229
230/// A simplex represents a $k$-dimensional geometric object, defined as the convex hull of $k+1$
231/// affinely independent vertices.
232///
233/// Simplices are the basic building blocks of [`SimplicialComplex`]es.
234/// - A 0-simplex (dimension 0) is a point.
235/// - A 1-simplex (dimension 1) is a line segment.
236/// - A 2-simplex (dimension 2) is a triangle.
237/// - A 3-simplex (dimension 3) is a tetrahedron.
238/// - ... and so on for higher dimensions.
239///
240/// In this implementation, vertices are represented by `usize` indices and are always stored in a
241/// sorted vector to ensure a canonical representation for each simplex.
242///
243/// # Fields
244/// * `vertices`: A sorted `Vec<usize>` of vertex indices that define the simplex.
245/// * `dimension`: The dimension of the simplex, equal to `vertices.len() - 1`.
246/// * `id`: An optional unique identifier assigned when the simplex is added to a complex.
247#[derive(Clone, Debug, Hash, PartialEq)]
248pub struct Simplex {
249 vertices: Vec<usize>,
250 dimension: usize,
251 id: Option<usize>,
252}
253
254impl Eq for Simplex {}
255
256impl PartialOrd for Simplex {
257 /// Provides a partial ordering for simplices.
258 ///
259 /// This implementation delegates to `cmp`, thus providing a total ordering.
260 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
261}
262
263impl Ord for Simplex {
264 /// Provides a total ordering for simplices, primarily for use in sorted collections (e.g.,
265 /// `BTreeSet` or when sorting `Vec<Simplex>`).
266 ///
267 /// The ordering is based on the lexicographical comparison of their sorted vertex lists,
268 /// then by dimension.
269 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
270 self.vertices.cmp(&other.vertices).then_with(|| self.dimension.cmp(&other.dimension))
271 }
272}
273
274impl Simplex {
275 /// Creates a new simplex of a given `dimension` from a set of `vertices`.
276 ///
277 /// The provided `vertices` will be sorted internally to ensure a canonical representation.
278 /// The simplex is created without an ID (id = None).
279 ///
280 /// # Arguments
281 /// * `dimension`: The dimension of the simplex (e.g., 0 for a point, 1 for an edge, 2 for a
282 /// triangle).
283 /// * `vertices`: A vector of `usize` vertex indices. The length of this vector must be `dimension
284 /// + 1`.
285 ///
286 /// # Panics
287 /// * If `vertices.len()` does not equal `dimension + 1`.
288 /// * If any vertex indices in `vertices` are repeated (i.e., vertices are not distinct).
289 pub fn new(dimension: usize, vertices: Vec<usize>) -> Self {
290 assert!(vertices.iter().combinations(2).all(|v| v[0] != v[1]));
291 assert!(vertices.len() == dimension + 1);
292 Self { vertices: vertices.into_iter().sorted().collect(), dimension, id: None }
293 }
294
295 /// Creates a new simplex from vertices, automatically determining the dimension.
296 ///
297 /// This is a convenience constructor that calculates the dimension based on the number
298 /// of vertices provided. The dimension will be `vertices.len() - 1`.
299 ///
300 /// # Arguments
301 /// * `vertices`: A vector of `usize` vertex indices. Must contain distinct values.
302 ///
303 /// # Examples
304 /// ```rust
305 /// # use harness_space::complexes::simplicial::Simplex;
306 /// // Create a point (0-simplex)
307 /// let point = Simplex::from_vertices(vec![0]);
308 /// assert_eq!(point.dimension(), 0);
309 ///
310 /// // Create an edge (1-simplex)
311 /// let edge = Simplex::from_vertices(vec![0, 1]);
312 /// assert_eq!(edge.dimension(), 1);
313 ///
314 /// // Create a triangle (2-simplex)
315 /// let triangle = Simplex::from_vertices(vec![0, 1, 2]);
316 /// assert_eq!(triangle.dimension(), 2);
317 /// ```
318 ///
319 /// # Panics
320 /// * If any vertex indices are repeated
321 /// * If the vertices vector is empty
322 pub fn from_vertices(vertices: Vec<usize>) -> Self {
323 let dimension = vertices.len().saturating_sub(1);
324 Self::new(dimension, vertices)
325 }
326
327 /// Creates a new simplex with a specific ID assigned.
328 ///
329 /// This method is primarily used internally by the complex management system when
330 /// adding elements to a [`Complex`]. It creates a copy of the current simplex with
331 /// the specified ID assigned.
332 ///
333 /// # Arguments
334 /// * `new_id`: The unique identifier to assign to this simplex
335 ///
336 /// # Returns
337 /// A new `Simplex` instance with the same mathematical content but with the specified ID
338 const fn with_id(mut self, new_id: usize) -> Self {
339 self.id = Some(new_id);
340 self
341 }
342
343 /// Returns a slice reference to the sorted vertex indices of the simplex.
344 ///
345 /// The vertices are always maintained in sorted order to ensure canonical representation.
346 /// This means that two simplices with the same vertex set (regardless of input order)
347 /// will have identical vertex arrays.
348 ///
349 /// # Returns
350 /// A slice `&[usize]` containing the vertex indices in ascending order
351 ///
352 /// # Examples
353 /// ```rust
354 /// # use harness_space::complexes::simplicial::Simplex;
355 /// let simplex = Simplex::new(2, vec![2, 0, 1]); // Input order doesn't matter
356 /// assert_eq!(simplex.vertices(), &[0, 1, 2]); // Always sorted
357 /// ```
358 pub fn vertices(&self) -> &[usize] { &self.vertices }
359
360 /// Returns the dimension of the simplex.
361 ///
362 /// The dimension $k$ is equal to the number of vertices minus one. This follows the
363 /// standard topological convention where:
364 /// - Points have dimension 0
365 /// - Line segments have dimension 1
366 /// - Triangles have dimension 2
367 /// - Tetrahedra have dimension 3
368 /// - etc.
369 ///
370 /// # Returns
371 /// The dimension as a `usize`
372 pub const fn dimension(&self) -> usize { self.dimension }
373
374 /// Returns the ID of the simplex if it has been assigned to a complex.
375 ///
376 /// Simplices start with no ID (`None`) when created. IDs are assigned automatically
377 /// when the simplex is added to a [`Complex`] via [`Complex::join_element`]. The ID
378 /// serves as a unique identifier for efficient storage and lookup operations within
379 /// the complex.
380 ///
381 /// # Returns
382 /// `Some(usize)` if the simplex has been assigned to a complex, `None` otherwise
383 pub const fn id(&self) -> Option<usize> { self.id }
384
385 /// Checks if this simplex has the same mathematical content as another.
386 ///
387 /// Two simplices are considered to have the same content if they have the same
388 /// dimension and the same set of vertices. This comparison ignores ID assignment
389 /// and is used for deduplication when adding elements to complexes.
390 ///
391 /// # Arguments
392 /// * `other`: The other simplex to compare against
393 ///
394 /// # Returns
395 /// `true` if the simplices represent the same geometric object, `false` otherwise
396 pub fn same_content(&self, other: &Self) -> bool {
397 self.dimension == other.dimension && self.vertices == other.vertices
398 }
399}
400
401impl ComplexElement for Simplex {
402 fn dimension(&self) -> usize { self.dimension }
403
404 /// Computes all $(k-1)$-dimensional faces of this $k$-simplex.
405 ///
406 /// A face is obtained by removing one vertex from the simplex's vertex set.
407 /// For example:
408 /// - The faces of a 2-simplex (triangle) `[v_0, v_1, v_2]` are its three 1-simplices (edges):
409 /// `[v_1, v_2]`, `[v_0, v_2]`, and `[v_0, v_1]`.
410 /// - The faces of a 1-simplex (edge) `[v_0, v_1]` are its two 0-simplices (vertices): `[v_1]` and
411 /// `[v_0]`.
412 /// - A 0-simplex has no faces (its dimension is -1, typically considered an empty set of faces).
413 ///
414 /// # Returns
415 /// A [`Vec<Simplex>`] containing all $(k-1)$-dimensional faces. If the simplex is
416 /// 0-dimensional, an empty vector is returned as it has no $( -1)$-dimensional faces in the
417 /// typical sense.
418 fn faces(&self) -> Vec<Self> {
419 if self.dimension == 0 {
420 return Vec::new();
421 }
422 self
423 .vertices
424 .clone()
425 .into_iter()
426 .combinations(self.dimension)
427 .map(Self::from_vertices)
428 .collect()
429 }
430
431 /// Computes the faces with their correct orientation coefficients for the simplicial boundary
432 /// operator.
433 ///
434 /// For a k-simplex σ = [v_0, v_1, ..., v_k], the boundary is:
435 /// ∂σ = Σ_{i=0}^k (-1)^i [v_0, ..., v̂_i, ..., v_k]
436 /// where v̂_i means vertex v_i is omitted.
437 fn boundary_with_orientations(&self) -> Vec<(Self, i32)> {
438 if self.dimension == 0 {
439 return Vec::new();
440 }
441
442 let mut faces_with_orientations = Vec::new();
443
444 // For each vertex position, create a face by omitting that vertex
445 for (i, _) in self.vertices.iter().enumerate() {
446 let mut face_vertices = self.vertices.clone();
447 face_vertices.remove(i);
448 let face = Self::from_vertices(face_vertices);
449
450 // Alternating sign: (-1)^i
451 let orientation = if i % 2 == 0 { 1 } else { -1 };
452 faces_with_orientations.push((face, orientation));
453 }
454
455 faces_with_orientations
456 }
457
458 fn id(&self) -> Option<usize> { self.id }
459
460 fn same_content(&self, other: &Self) -> bool { self.same_content(other) }
461
462 fn with_id(&self, new_id: usize) -> Self { self.clone().with_id(new_id) }
463}
464
465#[cfg(test)]
466mod tests {
467 use std::fmt::Debug;
468
469 use harness_algebra::{algebras::boolean::Boolean, modular, prime_field, rings::Field};
470
471 use super::*;
472 use crate::complexes::Complex;
473
474 modular!(Mod7, u32, 7);
475 prime_field!(Mod7);
476 // Helper trait bound alias for tests
477 trait TestField: Field + Copy + Debug {}
478 impl<T: Field + Copy + Debug> TestField for T {}
479
480 #[test]
481 fn test_simplex_construction() {
482 let vertex = Simplex::new(0, vec![0]);
483 assert_eq!(vertex.dimension(), 0);
484 assert_eq!(vertex.vertices(), &[0]);
485 assert_eq!(vertex.id(), None);
486
487 let edge = Simplex::new(1, vec![1, 0]); // Will be sorted
488 assert_eq!(edge.dimension(), 1);
489 assert_eq!(edge.vertices(), &[0, 1]); // Should be sorted
490 assert_eq!(edge.id(), None);
491
492 let triangle = Simplex::from_vertices(vec![2, 0, 1]);
493 assert_eq!(triangle.dimension(), 2);
494 assert_eq!(triangle.vertices(), &[0, 1, 2]); // Should be sorted
495 }
496
497 #[test]
498 fn test_simplex_faces() {
499 let simplex = Simplex::new(2, vec![0, 1, 2]);
500 let faces = simplex.faces();
501 assert_eq!(faces.len(), 3);
502 assert_eq!(faces[0].vertices(), &[0, 1]);
503 assert_eq!(faces[1].vertices(), &[0, 2]);
504 assert_eq!(faces[2].vertices(), &[1, 2]);
505 }
506
507 #[test]
508 fn test_simplex_with_id() {
509 let simplex = Simplex::new(1, vec![0, 1]);
510 assert_eq!(simplex.id(), None);
511
512 let simplex_with_id = simplex.with_id(42);
513 assert_eq!(simplex_with_id.id(), Some(42));
514 assert_eq!(simplex_with_id.vertices(), &[0, 1]); // Content unchanged
515 }
516
517 #[test]
518 fn test_simplex_same_content() {
519 let s1 = Simplex::new(1, vec![0, 1]);
520 let s2 = Simplex::new(1, vec![0, 1]);
521 let s3 = Simplex::new(1, vec![0, 2]);
522 let s4 = s1.clone().with_id(42); // Same content, different ID
523
524 assert!(s1.same_content(&s2));
525 assert!(!s1.same_content(&s3));
526 assert!(s1.same_content(&s4)); // Content equality ignores ID
527 }
528
529 #[test]
530 fn test_simplex_ordering() {
531 let s1 = Simplex::new(0, vec![0]);
532 let s2 = Simplex::new(0, vec![1]);
533 let s3 = Simplex::new(1, vec![0, 1]);
534
535 assert!(s1 < s2); // Same dimension, different vertices
536 assert!(s1 < s3); // Different dimension (vertices order first)
537 }
538
539 #[test]
540 fn test_simplicial_complex_basic() {
541 let mut complex: Complex<Simplex> = Complex::new();
542 let triangle = Simplex::new(2, vec![0, 1, 2]);
543 complex.join_element(triangle);
544
545 assert_eq!(complex.elements_of_dimension(2).len(), 1);
546 assert_eq!(complex.elements_of_dimension(1).len(), 3);
547 assert_eq!(complex.elements_of_dimension(0).len(), 3);
548 }
549
550 #[test]
551 fn test_chain_operations_with_simplices() {
552 let mut complex: Complex<Simplex> = Complex::new();
553
554 // Create two edges
555 let edge1 = Simplex::new(1, vec![0, 1]);
556 let edge2 = Simplex::new(1, vec![1, 2]);
557 let added_edge1 = complex.join_element(edge1);
558 let added_edge2 = complex.join_element(edge2);
559
560 let chain1 = Chain::from_item_and_coeff(&complex, added_edge1, 1_i32);
561 let chain2 = Chain::from_item_and_coeff(&complex, added_edge2, 2_i32);
562
563 let result = chain1 + chain2;
564
565 assert_eq!(result.items.len(), 2);
566 assert_eq!(result.coefficients, vec![1, 2]);
567 }
568
569 #[test]
570 fn test_chain_boundary_operations() {
571 let mut complex: Complex<Simplex> = Complex::new();
572
573 // Test edge boundary
574 let edge = Simplex::new(1, vec![0, 1]);
575 let added_edge = complex.join_element(edge);
576 let chain = Chain::from_item_and_coeff(&complex, added_edge, 1);
577
578 let boundary = chain.boundary();
579 assert_eq!(boundary.items.len(), 2); // Two vertices
580 assert_eq!(boundary.coefficients[0] + boundary.coefficients[1], 0); // Opposite signs
581
582 // Test triangle boundary
583 let triangle = Simplex::new(2, vec![0, 1, 2]);
584 let added_triangle = complex.join_element(triangle);
585 let triangle_chain = Chain::from_item_and_coeff(&complex, added_triangle, 1);
586
587 let triangle_boundary = triangle_chain.boundary();
588 assert_eq!(triangle_boundary.items.len(), 3); // Three edges
589 }
590
591 #[test]
592 fn test_boundary_squared_is_zero() {
593 let mut complex: Complex<Simplex> = Complex::new();
594
595 let triangle = Simplex::new(2, vec![0, 1, 2]);
596 let added_triangle = complex.join_element(triangle);
597 let chain = Chain::from_item_and_coeff(&complex, added_triangle, 1);
598
599 let boundary = chain.boundary();
600 let boundary_squared = boundary.boundary();
601
602 // Boundary of boundary should be empty (∂² = 0)
603 assert_eq!(boundary_squared.items.len(), 0);
604 assert_eq!(boundary_squared.coefficients.len(), 0);
605 }
606
607 #[test]
608 fn test_complex_chain_operations() {
609 let mut complex: Complex<Simplex> = Complex::new();
610
611 // Create two triangles sharing an edge
612 let triangle1 = Simplex::new(2, vec![0, 1, 2]);
613 let triangle2 = Simplex::new(2, vec![1, 2, 3]);
614 let added_t1 = complex.join_element(triangle1);
615 let added_t2 = complex.join_element(triangle2);
616
617 let chain1 = Chain::from_item_and_coeff(&complex, added_t1, 1);
618 let chain2 = Chain::from_item_and_coeff(&complex, added_t2, -1);
619
620 let combined_chain = chain1 + chain2;
621 let boundary = combined_chain.boundary();
622
623 // The boundary should have 4 edges (the shared edge [1,2] cancels out)
624 assert_eq!(boundary.items.len(), 4);
625
626 // Verify the edges
627 let edge_vertices: Vec<Vec<usize>> =
628 boundary.items.iter().map(|s| s.vertices().to_vec()).collect();
629
630 assert!(edge_vertices.contains(&vec![0, 1]));
631 assert!(edge_vertices.contains(&vec![0, 2]));
632 assert!(edge_vertices.contains(&vec![1, 3]));
633 assert!(edge_vertices.contains(&vec![2, 3]));
634
635 // The shared edge [1,2] should not be present because its coefficients cancel
636 assert!(!edge_vertices.contains(&vec![1, 2]));
637 }
638
639 fn test_simplicial_homology_point_generic<F: TestField>() {
640 let mut complex: Complex<Simplex> = Complex::new();
641 let p0 = Simplex::new(0, vec![0]);
642 complex.join_element(p0);
643
644 // H_0
645 let h0 = complex.homology::<F>(0);
646 assert_eq!(h0.dimension, 0, "H0: Dimension check");
647 assert_eq!(h0.betti_number, 1, "H0: Betti number for a point should be 1");
648 assert_eq!(h0.homology_generators.len(), 1, "H0: Should have one generator");
649
650 // H_1
651 let h1 = complex.homology::<F>(1);
652 assert_eq!(h1.dimension, 1, "H1: Dimension check");
653 assert_eq!(h1.betti_number, 0, "H1: Betti number for a point should be 0");
654 assert!(h1.homology_generators.is_empty(), "H1: Should have no generators");
655 }
656
657 #[test]
658 fn test_simplicial_homology_point_all_fields() {
659 test_simplicial_homology_point_generic::<Boolean>();
660 test_simplicial_homology_point_generic::<Mod7>();
661 }
662
663 fn test_simplicial_homology_circle_generic<F: TestField>() {
664 let mut complex: Complex<Simplex> = Complex::new();
665 let s01 = Simplex::new(1, vec![0, 1]);
666 let s12 = Simplex::new(1, vec![1, 2]);
667 let s02 = Simplex::new(1, vec![0, 2]);
668
669 complex.join_element(s01);
670 complex.join_element(s12);
671 complex.join_element(s02);
672
673 let h0 = complex.homology::<F>(0);
674 assert_eq!(h0.betti_number, 1, "H0: Betti for circle");
675
676 let h1 = complex.homology::<F>(1);
677 assert_eq!(h1.betti_number, 1, "H1: Betti for circle");
678 assert_eq!(h1.homology_generators.len(), 1, "H1: One 1D generator");
679
680 let h2 = complex.homology::<F>(2);
681 assert_eq!(h2.betti_number, 0, "H2: Betti for circle");
682 }
683
684 #[test]
685 fn test_simplicial_homology_circle_all_fields() {
686 test_simplicial_homology_circle_generic::<Boolean>();
687 test_simplicial_homology_circle_generic::<Mod7>();
688 }
689
690 fn test_simplicial_homology_filled_triangle_generic<F: TestField>() {
691 let mut complex: Complex<Simplex> = Complex::new();
692 let triangle012 = Simplex::new(2, vec![0, 1, 2]);
693 complex.join_element(triangle012);
694
695 let h0 = complex.homology::<F>(0);
696 assert_eq!(h0.betti_number, 1, "H0: Betti for triangle");
697
698 let h1 = complex.homology::<F>(1);
699 assert_eq!(h1.betti_number, 0, "H1: Betti for filled triangle");
700 assert!(h1.homology_generators.is_empty(), "H1: No 1D generators");
701
702 let h2 = complex.homology::<F>(2);
703 assert_eq!(h2.betti_number, 0, "H2: Betti for filled triangle");
704 assert!(h2.homology_generators.is_empty(), "H2: No 2D generators");
705 }
706
707 #[test]
708 fn test_simplicial_homology_filled_triangle_all_fields() {
709 test_simplicial_homology_filled_triangle_generic::<Boolean>();
710 test_simplicial_homology_filled_triangle_generic::<Mod7>();
711 }
712
713 fn test_simplicial_homology_sphere_surface_generic<F: TestField>() {
714 let mut complex: Complex<Simplex> = Complex::new();
715
716 let f1 = Simplex::new(2, vec![0, 1, 2]);
717 let f2 = Simplex::new(2, vec![0, 1, 3]);
718 let f3 = Simplex::new(2, vec![0, 2, 3]);
719 let f4 = Simplex::new(2, vec![1, 2, 3]);
720
721 complex.join_element(f1);
722 complex.join_element(f2);
723 complex.join_element(f3);
724 complex.join_element(f4);
725
726 let h0 = complex.homology::<F>(0);
727 assert_eq!(h0.betti_number, 1, "H0: Betti sphere");
728
729 let h1 = complex.homology::<F>(1);
730 assert_eq!(h1.betti_number, 0, "H1: Betti sphere");
731 assert!(h1.homology_generators.is_empty(), "H1: Generators sphere");
732
733 let h2 = complex.homology::<F>(2);
734 assert_eq!(h2.betti_number, 1, "H2: Betti sphere");
735 assert_eq!(h2.homology_generators.len(), 1, "H2: One 2D generator");
736
737 let h3 = complex.homology::<F>(3);
738 assert_eq!(h3.betti_number, 0, "H3: Betti sphere");
739 }
740
741 #[test]
742 fn test_simplicial_homology_sphere_surface_all_fields() {
743 test_simplicial_homology_sphere_surface_generic::<Boolean>();
744 test_simplicial_homology_sphere_surface_generic::<Mod7>();
745 }
746
747 #[test]
748 fn test_simplicial_incidence_poset_condition_1() {
749 // Condition 1: If [σ : τ] ≠ 0, then σ ⊲ τ and there are no cells between σ and τ
750 let mut complex: Complex<Simplex> = Complex::new();
751
752 // Create a triangle
753 let triangle = Simplex::new(2, vec![0, 1, 2]);
754
755 let added_triangle = complex.join_element(triangle);
756
757 // Get all elements
758 let vertices = complex.elements_of_dimension(0);
759 let edges = complex.elements_of_dimension(1);
760 let _triangles = complex.elements_of_dimension(2);
761
762 // Test that triangle's boundary consists only of direct faces (edges)
763 let boundary_with_orientations = added_triangle.boundary_with_orientations();
764 for (face, _orientation) in boundary_with_orientations {
765 assert_eq!(face.dimension(), 1);
766 assert!(edges.iter().any(|e| e.same_content(&face)));
767 }
768
769 // Test that edges' boundaries consist only of direct faces (vertices)
770 for edge in &edges {
771 let edge_boundary = edge.boundary_with_orientations();
772 for (vertex_face, _orientation) in edge_boundary {
773 assert_eq!(vertex_face.dimension(), 0);
774 assert!(vertices.iter().any(|v| v.same_content(&vertex_face)));
775 }
776 }
777 }
778
779 #[test]
780 fn test_simplicial_incidence_poset_condition_2() {
781 // Condition 2: For any σ ⊲ τ, Σ_γ∈P_X [σ : γ][γ : τ] = 0 (∂² = 0)
782 let mut complex: Complex<Simplex> = Complex::new();
783
784 // Test with a triangle
785 let triangle = Simplex::new(2, vec![0, 1, 2]);
786 let added_triangle = complex.join_element(triangle);
787
788 // Create a chain from the triangle
789 let triangle_chain = Chain::from_item_and_coeff(&complex, added_triangle, 1);
790
791 // Compute boundary of the triangle (should give edges)
792 let boundary_1 = triangle_chain.boundary();
793
794 // Compute boundary of the boundary (should be zero)
795 let boundary_2 = boundary_1.boundary();
796
797 // ∂² should be zero
798 assert_eq!(boundary_2.items.len(), 0, "∂² should be zero for simplicial complex");
799 assert_eq!(boundary_2.coefficients.len(), 0, "∂² should have no coefficients");
800
801 // Also test with a tetrahedron
802 let tetrahedron = Simplex::new(3, vec![0, 1, 2, 3]);
803 let mut tet_complex: Complex<Simplex> = Complex::new();
804 let added_tet = tet_complex.join_element(tetrahedron);
805
806 let tet_chain = Chain::from_item_and_coeff(&tet_complex, added_tet, 1);
807 let tet_boundary_1 = tet_chain.boundary();
808 let tet_boundary_2 = tet_boundary_1.boundary();
809
810 assert_eq!(tet_boundary_2.items.len(), 0, "∂² should be zero for 3D tetrahedron");
811 }
812
813 #[test]
814 fn test_simplicial_chain_complex_property() {
815 // Additional comprehensive test for the chain complex property
816 let mut complex: Complex<Simplex> = Complex::new();
817
818 // Create a more complex structure - multiple triangles sharing edges
819 let triangle1 = Simplex::new(2, vec![0, 1, 2]);
820 let triangle2 = Simplex::new(2, vec![1, 2, 3]);
821
822 let t1 = complex.join_element(triangle1);
823 let t2 = complex.join_element(triangle2);
824
825 // Test ∂² = 0 for individual triangles
826 let chain1 = Chain::from_item_and_coeff(&complex, t1, 1);
827 let chain2 = Chain::from_item_and_coeff(&complex, t2, 1);
828
829 assert_eq!(chain1.boundary().boundary().items.len(), 0);
830 assert_eq!(chain2.boundary().boundary().items.len(), 0);
831
832 // Test ∂² = 0 for linear combination
833 let combined_chain = chain1.add(chain2);
834 assert_eq!(combined_chain.boundary().boundary().items.len(), 0);
835
836 println!("✓ Simplicial chain complex property verified for complex structures");
837 }
838}