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}